Friday, February 22, 2013

Fast Image resizeing for space saving uploads

Since I started writing this blog there has been one thing that I have been doing manually that I feel like a burden. I take pictures (8MP) of things I build, then I have to download them to my computer and then sort them. After that I want to upload them to my SkyDrive but I have limited space so I have to shrink them. I use Gimp for this, but the manual way. I have read that you can script everything but it seems too hard.
I decided to write my own application that does exactly what I need and not one thing more.

ImageResizer main window

My setup is simple but very peculiar to everyone else's. I put the images in my blog image folder, I create a folder to put the rejects (the ones I will not be uploading) and then create a backup of the files in a subfolder. And finally, after all this, I shrink the images to have a maximum of 1000px on any of the dimensions. Anyone can point out that this is not the right way (had arguments regarding my choices of organizing stuff) but it's the way I like it.

So in comes my small app that has a small window with a text box in which you can drag and drop folders. For each folder it creates the backup folders and stores the files there and then shrinks the files in the given folder to a set maximum dimension.

ImageResizer settings window
I also used 2 persistent settings to ease the use of the app, one will save the maximum dimension you want and the other let's you skip over files that are actually smaller than what the desired size is. If you uncheck the last option, files will be enlarged to fit the requested dimensions.
You can get the source over on SourceForge using git or you can directly download the exe from here: Release.7z
There is no explicit license attached to this so you can do whatever you want with it, I am happy if it helped someone else as well.

As for the actual code itself, it is very straightforward. First of all you need to implement the DragEnter and DragDrop events so that the TextBox can handle you passing it some folders. I found this question over on StackOverflow very helpful.

You use the DragEnter event so that you get a visual cue that you are allowed to drag-drop over there and do the processing you need in the DragDrop event handler. This is all done sequentially so that means the UI will block when you give it folders that have some files in them. It can be improved if for example a ThreadPool was used to start threads for each folder that has been dropped. But since this has only one job to do, after the drag-drop you leave it alone to finish (also there is no cue that it has finished, apart from the fact that it starts responding to clicks).

Another question on SO solved the problem of how to actually resize proportional the dimensions of the image. The top answer has a great way of determining the largest dimension only needing one Math.Min call.

The only caveat of that code is that it does not know (or use) the original image format and thus uses the default. When testing that solution it was clear that I needed to give the actual ImageFormat parameter when saving the new image. The test: a 3MB image (8MP) saved without an ImageFormat came out as a 1.3MB image (1000px max dimension) as opposed to when I explicitly set the ImageFormat.Jpeg when it came out at ~100Kb (same max dimension).
The way that I give the format is to look at the image extension. So the code used to save the image is like this:

Image mare = Image.FromFile(file);
string ext = Path.GetFileName(file);
ext = ext.Replace(Path.GetFileNameWithoutExtension(file), "");
Image mica = ScaleImage(mare, maxDimension, maxDimension);
mare.Dispose();
System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Jpeg;
switch (ext)
{
                    case ".BMP":
                    case ".bmp": format = System.Drawing.Imaging.ImageFormat.Bmp;
                        break;
                    case ".PNG":
                    case ".png": format = System.Drawing.Imaging.ImageFormat.Png;
                        break;
                    case ".JPG":
                    case ".jpg": format = System.Drawing.Imaging.ImageFormat.Jpeg;
                        break;
                    default:
                        format = System.Drawing.Imaging.ImageFormat.Jpeg;
                        break;
}
mica.Save(file,format);

file is a string that has the full path to a file;
ScaleImage is the function stolen from SO question;
This was developed in VisualStudio 2010 using .Net 4, you need to have the .Net 4 redistributable components for this app to run.

I hope now that I will write more post because I got rid of one annoyance and it will be faster to do things in the future. Isn't it ironic that I didn't have to use the resizer for this post?

Enjoy the last days of winter!

2 comments:

  1. Replies
    1. Did not know about this but it seems like it is a bit overkill, my images are JPG from a very old digital camera.

      Delete