Click here to Skip to main content
15,881,248 members
Articles / Multimedia / GDI+
Article

ImageConverter - Converts images to a specific image format, changing sizes on the flow

Rate me:
Please Sign up or sign in to vote.
4.71/5 (24 votes)
13 Sep 2004CPOL5 min read 106.5K   4.7K   96   18
ImageConverter allows images to be resized and written to a selected image format.

Sample Image - ImageConverter

Introduction

Every week I take dozens of pictures during the soccer games of Kevin, my 7 year old son. Most of these pictures are placed on a web site created for his team. And every week I find myself performing the ever recurring task of resizing the images (mostly 3072 x 2048) to something more friendly (e.g.: 768 x 512). Why? Just for those less fortunate that have no ADSL or so.

Of course, every repeating task is prone to be automated, and a first application was born to resize the pictures according to my wishes. To cope with different source and target paths, as well as different ways of resizing, the application needed a user interface. Working on the application and its (simple, yet effective) user interface, generalizing the use of the application is evident. The next step then is to share it with all of you and hope you find the application or some code snippet useful.

Using Image Converter

Just download the zipped executable, unzip it, and open it. After you have opened ImageConverter, you can open the image files that you want to convert by pressing the Open... button. The target path for the converted images is automatically set to the source path, if not set already. By pressing the Browse... button, you can select an alternative path for the converted images.

Before you start the conversion process, you can define the image format after conversion, you can define how the images should be named, and you can define the required image size.

For naming the target images, you can use the original name, and the corresponding extension for the target image format is added. If a file with the resulting name already exists, it will be overwritten. Another option is to use a prefix defined by you. Each target image file will get a unique name based on this prefix and a sequence number.

The target size can be a specified size, which means that the original image will be stretched. If you are just interested in obtaining images for which the largest of width or height is less than or equal to the specified maximum, the image will be scaled if either width or height is larger than the specified maximum. The third option is to scale each image by the specified percentage.

Using the code

The main code for the application is in FormImageConverter.cs whilst the code for the Thumbnail control I use is in Thumbnail.cs.

Selecting Images

The images to be converted are selected by invoking the ShowDialog method of a standard OpenFileDialog instance. The selected image files are processed by the DisplayThumbnails method, which disposes off any Thumbnail control still present. Then for each image file, a Thumbnail control is created and added to panelThumbnails.Controls. Note that if the file to be loaded is not a (valid) image file, an OutOfMemoryException is thrown and the Thumbnail control will not be created.

Conversion Process Thread

Because I want some progress information displayed during the conversion process, the conversion process takes place in another thread. The thread is created and started from the event handler for the Convert button.

C#
private void buttonConvert_Click(object sender, System.EventArgs e)
{
    this.Cursor = Cursors.WaitCursor;
    _convertThread = new Thread(new ThreadStart(ConvertImageFiles));
    _convertThread.Start();
}

The ConvertImageFiles method takes care of determining the extension and image format for the target image, as well as the name for the image file. Using this information and knowing the size for the target image, the original image as represented by the Thumbnail control can be converted.

C#
private void ConvertImageFiles()
{
    this.Cursor = Cursors.WaitCursor;
    _converting = true;
    this.EnableControls();
    try
    {
        ...
        // determine the target size
        System.Drawing.Size targetSize = this.DetermineTargetSize(thumbnail.Image);
        System.Drawing.Image targetImage = null; 

        try
        {
            // the new image is created and written based on
            // the original, the target size and the desired image format
            targetImage = new Bitmap(thumbnail.Image, targetSize);
            targetImage.Save(targetFileName, targetImageFormat); 
            // invoke the delegate for updating progress information
            this.Invoke(_imageInfoDelegate, 
                 new object[] { thumbnail, targetFileName, targetSize });
            targetImage.Dispose();
        }
        catch (System.Threading.ThreadAbortException e)
        {
            throw e;
        }
        catch {}
        ...
    }
    catch {}
    finally
    {
        this.Cursor = Cursors.Default;
        _converting = false;
        this.EnableControls();
    }
}

To prevent the user from changing the settings during the conversion process, the controls on the main form are disabled at the start, and the Close button becomes a Cancel button. Pressing the Cancel button causes the thread to be aborted by calling _convertThread.Abort method, which causes a System.Threading.ThreadAbortException to be thrown. The exception is caught and re-thrown until the outer try-catch is reached, and the controls can be enabled again.

To update the progress information during the conversion process, a delegate is executed using the Invoke method on the main form. The progress information uses the thumbnail, the target file name, and the target size. These objects are put in an object array, which holds the arguments for the DisplayImageInfo method to be called.

C#
// the delegate
private delegate void ImageInfoDelegate(Thumbnail thumbnail, 
              string targetFile, System.Drawing.Size toSize);

private ImageInfoDelegate _imageInfoDelegate = null;

_imageInfoDelegate = new ImageInfoDelegate(DisplayImageInfo);

// invoke the delegate for updating progress information
this.Invoke(_imageInfoDelegate, new object[] { thumbnail, 
                                 targetFileName, targetSize });

Enabling/Disabling Controls

As noted, the various controls on the main form are disabled during the conversion process and the Close button changed into a Cancel button. Also, if no images have been selected or no target path defined, the Convert button is disabled. This is implemented in the EnableControls method, which has to be called every time the conditions queried in the method have changed.

C#
private void EnableControls()
{
    this.buttonConvert.Enabled = 
        !_converting &&
        this.panelThumbnails.Controls.Count > 0 &&
        this.labelTargetPathValue.Text.Trim() != "";
    this.buttonBrowseTargetPath.Enabled = !_converting;
    this.buttonOpen.Enabled = !_converting;
    this.groupBoxOnConvert.Enabled = !_converting;
    this.groupBoxTargetSize.Enabled = !_converting;
    this.comboBoxTargetFormat.Enabled = !_converting;
    this.buttonClose.Text = _converting ? "Cancel" : "Close";
}

Thumbnail Control

The Thumbnail control is a UserControl on which a Panel is placed that contains a PictureBox and two Labels. The PictureBox holds the thumbnail image, the file name Label, and the original image size Label. The border around the control is obtained by setting the control's DockPadding to 2 for all sides. The thin line between the PictureBox and the file name Label is the BackColor of the underlying Panel 'shining' through. This is accomplished by setting both Labels to dock on the bottom and by giving the PictureBox the size of the remaining space with the height set to just one pixel less.

Thumbnail Control

The main task of the Thumbnail control is to show a thumbnail for each loaded image file and to present easy access to the corresponding image and its file path. The file path is supplied to the constructor, and the file is loaded using the FromFile method.

C#
public Thumbnail(string filePath)
{
    InitializeComponent();
    _filePath = filePath;
    try
    {
        _image = System.Drawing.Image.FromFile(_filePath);
    }
    catch (System.OutOfMemoryException e)
    {
        // file does not represent a valid image
        throw e;
    }

    ...

Loading a file that does not represent a (recognizable) image causes an OutOfMemoryException, so the exception is caught and thrown again to inform the code constructing the thumbnail.

If it is loadable, the size for the thumbnail image is calculated and the visual feedback is set.

C#
    ...

    int max = Math.Min(this.pictureBox.Width, this.pictureBox.Height);
    int width = _image.Width;
    int height = _image.Height;
    // determine the size for the thumbnail image
    if (_image.Width > max || _image.Height > max)
    {
        if (_image.Width > _image.Height)
        {
            width = max;
            height = (int) (_image.Height * max / _image.Width);
        }
        else
        {
            width = (int) (_image.Width * max / _image.Height);
            height = max;
        }
    }
    // set feedback information
    this.pictureBox.Image = new Bitmap(_image, width, height);
    this.labelFileName.Text = Path.GetFileName(_filePath);
    this.labelImageSize.Text = string.Format("{0} x {1}", 
                       _image.Size.Width, _image.Size.Height);
}

Conclusion

I found working on the ImageConverter application quite satisfying. Of course, it saves me a lot of time resizing pictures before displaying them on the soccer team's web site. But this small application also shows various aspects that can be found in developing a general C# application. Such aspects include the creation of user controls to get that specific user interface feeling you want, using threads for progress information, and utilizing the power of certain built-in .NET framework methods.

History

First version on September 8th, 2004.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Netherlands Netherlands
Eric-Paul Jansen lives and works in Leusden, the Netherlands. He works for Inforay International, a leading supplier of Business Activity Monitoring solutions.

For the last seven years he has been, and still is, responsible for a meta modeling application for BAM environments. The developments started in C++, but the application has been completely rewritten using C#.

In his spare time, Eric-Paul likes to work on model railroading and on creating software, e.g. for controlling model railroads.

Comments and Discussions

 
GeneralMy vote of 4 Pin
Jack-Allen-Wong22-Feb-13 3:34
Jack-Allen-Wong22-Feb-13 3:34 
QuestionImageConverter - Converts images to a specific image format, changing sizes on the flow save to specific folder Pin
Anbarasan Gopal28-Aug-12 10:18
Anbarasan Gopal28-Aug-12 10:18 
GeneralMy vote of 4 Pin
xiaomayi201115-Mar-12 18:29
xiaomayi201115-Mar-12 18:29 
GeneralMy vote of 1 Pin
mrmaylibrids1-Jun-09 6:52
mrmaylibrids1-Jun-09 6:52 
GeneralGreat Work! Pin
ruben ruvalcaba8-Oct-07 17:21
ruben ruvalcaba8-Oct-07 17:21 
Generalcross thread exceptions... Pin
rusky9-Jul-07 12:05
rusky9-Jul-07 12:05 
GeneralRe: cross thread exceptions... Pin
Eric-Paul9-Jul-07 20:37
Eric-Paul9-Jul-07 20:37 
GeneralRe: cross thread exceptions... Pin
ryanoc33326-Sep-07 11:34
ryanoc33326-Sep-07 11:34 
GeneralRe: cross thread exceptions... Pin
Gokhan B.13-Jun-08 0:31
Gokhan B.13-Jun-08 0:31 
GeneralRe: cross thread exceptions... Pin
ryanoc33331-May-12 3:24
ryanoc33331-May-12 3:24 
GeneralRe: cross thread exceptions... Pin
maddineni.naveen5-Apr-13 2:40
maddineni.naveen5-Apr-13 2:40 
GeneralLicence Pin
Kunwar Ashok Singh17-Apr-07 22:18
Kunwar Ashok Singh17-Apr-07 22:18 
AnswerRe: Licence Pin
Eric-Paul17-Apr-07 22:22
Eric-Paul17-Apr-07 22:22 
GeneralIcons Pin
Doncp20-Jun-05 15:31
Doncp20-Jun-05 15:31 
GeneralHello Pin
stedy62-Feb-05 23:08
stedy62-Feb-05 23:08 
GeneralRe: Hello Pin
Eric-Paul8-Feb-05 11:15
Eric-Paul8-Feb-05 11:15 
Hi, I just had to take some time finding out what was going on.
It appears that the original call
targetImage.Save(targetFileName, targetImageFormat);
indead does some nasty things to the quality.

As it seams, this can be prevented by using specific encoding to ensure the quality. The EncoderParameter in the third line concerns the Quality and is set to 100 (percent??).
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, (long) 100);
targetImage.Save(targetFileName, myImageCodecInfo, encoderParameters);


Using this code instead of the original targetImage.Save call will generate a file which is slightly larger than the original.
Note that this is specific to jpeg.
GeneralRe: Hello Pin
calcium1-Jun-05 2:35
calcium1-Jun-05 2:35 
GeneralVery nice and thank you Pin
M.Lansdaal21-Dec-04 9:44
M.Lansdaal21-Dec-04 9:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.