Lossless Image Converting in C#

Images can be easily categorized into love-hate relationships when it comes to actually working with them from the programmer’s point of view. Nowadays images are a must in almost every Windows application and we tend to run into images quite often. In this series we are going to get into the details of image resizing and conversion.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 7
February 13, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement
Throughout this two-part article we are going to create a seemingly simple, yet powerful, Lightweight Image Manipulation application. As the title suggests, we'll do this in the C# language using the .NET environment and relying on GDI+ and Graphics. At the end you will find the source code of this project in its entirety available for you to download.

However, I truly think that following along won't be a hard task if you have a bit of programming experience and especially if you are familiar with the syntax of C#. Thus, during this tutorial I am going to assume that the reader knows the syntax well enough - at least on the intermediate level - so we won't begin with the Hello Word example.

We should be advancing quite steadily and at a relatively fast pace, so hopefully nobody gets bored to tears. Before we approach a situation we clarify in plain English what we want to accomplish and then we formulate an algorithm that accomplishes this. After this, each subroutine will get thoroughly explained as well as the reason for using the required methods.

Since this is a two-part series, in order to maintain the simplicity of the tutorial and to make things easy to follow, we will also split our application into two parts. First we will cover lossless conversion and then the second part will contain the resizing process along with color quantization. Color quantization must be discussed in order to understand the theory beyond the GIF extension and how to preserve high quality.

Finally at the end of the second part we will attach the source code for the entire project so you can download it and have fun with it. I have used Visual Studio 2005 but it should work with any Visual C# IDE in the .NET environment. A partial source code will also be attached at the end of this first part, particularly the conversion section.

All of this being said, since you're anxiously waiting to begin... here we go now.

First Things First

The first thing is the third habit of highly effective people as described in Stephen Covey's book. It has been said that it applies to every area of our lives. That's true and here we can also include programming. For that reason we fire up our Visual Studio and start a new Windows Application in C#. Then we add the following namespaces:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Drawing.Imaging;

We design and architect our form so it looks the fanciest with the components we require. That is, a PictureBox for a preview thumbnail, a few Buttons (Select, Quit, Convert), a ComboBox where we include the available extensions for conversion, an OpenFileDialog and SaveFileDialog, and finally we add the appropriate label(s).

Check out the way my form looks. Please pay attention to how I have additionally noted the name of each component with the arrow. This way you can follow along during this tutorial while I showcase my code snippets as well as my explanations when I'm referring to particular components. So please design your form appropriately.

(Form Design - Screenshot)

Now that we have architected our form we can continue. Although the above screenshot doesn't show the OpenFileDialog and SaveFileDialog, we shouldn't forget to add those components. I have named them SelectPicture and saveFileDialog1, respectively. Once those are also created, let's move on!

Here are a few additional details regarding our main form. I have named it clsMain. Its exact size is (455, 318) - (width, height). I have disabled the maximizeBox and the FormBorderStyle is set to FixedSingle. Its title is visible from my screenshot: Lightweight Image Manipulation. Now let's fasten our seat belts and speed up!

Check out the attached code snippet. With these we jump-start our application.

public clsMain()

{

   InitializeComponent();

}

static void Main()

{

   Application.Run(new clsMain());

}

private void clsMain_Load(object sender, EventArgs e)

{

   this.lbl_extension.Hide();

   this.ComboBox1.Hide();

   this.btnConvert.Hide();

   SelectPicture.Filter = "All Image files|*.bmp; *.gif; *.jpg; *.ico; " + "*.emf; *.wmf|Bitmap Files (*.bmp; *.gif; *.jpg; " + "*.ico)|*.bmp; *.gif; *.jpg; *.ico|" + "Meta Files (*.emf; *.wmf; *.png)|*.emf; *.wmf; *.png";

}

Keep in mind that some of these might have been automatically generated by the Windows Form Designer, so just recheck their existence [here I'm referring to clsMain() and Main()]. As you can notice from the above block of code, we hide the components we don't need to see now until the image isn't selected (please note that we don't hide the btnSelectPicture!). We also configure the filter of SelectPicture for images.

The Real Thing

Now the time has come for us to cover the "real thing." That is, writing the required subroutines and functions that accomplish our goal - image conversion. You could see that previously we hid all of the components excluding the SelectPicture and Quit buttons. Clicking on btnQuit is self-explanatory. It contains nothing but this.Close(). So let's see what happens once we click on btnSelectPicture.

private void btnSelectPicture_Click(object sender,System.EventArgs e)

{

   if (SelectPicture.ShowDialog() == DialogResult.OK)

   {

       picThumbnail.Image = Image.FromFile(SelectPicture.FileName);

       this.Text = String.Concat("Lightweight Image Manipulation (" + SelectPicture.FileName + ")");

   }

   this.lbl_extension.Show();

   this.btnConvert.Show();

   this.ComboBox1.Show();

}

The above code makes our SelectPicture component appear so we can look for the location of our source image. Once we've found it and clicked on OK, that 'if' condition becomes validated and the thumbnail image appears. Oh, and speaking of picThumbnail - we have set its SizeMode to StretchImage.

We move on to change the title of our form to visually point out that we have loaded the source image file. After this, we display our components because we can use them from now on - lbl_extension, btnConvert, ComboBox1. Speaking of ComboBox1, I added the following data items: BMP, JPG, GIF, and PNG, each in a new line.

At this time, all that's left for us to see is what happens when we click btnConvert.

private void btnConvert_Click(object sender, EventArgs e)

{

   if (ComboBox1.SelectedIndex != -1)

   {

       string newExtension = (string)ComboBox1.SelectedItem;

       switch (newExtension.ToUpper())

       {

          case "GIF": saveFileDialog1.Filter = "Graphics Interchange Format (*.gif)
|*.gif"; break;

          case "BMP": saveFileDialog1.Filter = "Bitmap Format (*.bmp)|*.bmp"; break;

          case "JPG": saveFileDialog1.Filter = "Joint Photographic Experts Group
Format (*.jpg)|*.jpg"; break;

          case "PNG": saveFileDialog1.Filter = "Portable Network Graphics Format
(*.png)|*.png"; break;

       }

       this.saveFileDialog1.ShowDialog();

   }

   else

       MessageBox.Show("Please select the desired extension format!", "Select
Extension", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

The algorithm, in plain English, goes like this: we examine whether ComboBox1 is empty or not. If it's not, then the IF condition gets validated and we enter. We grab the selected extension and examine it through a switch. For each extension, we set the appropriate filters for saveFileDialog1. After this, we can just make it appear. If the user didn't select an extension at the combobox, then we display an error message-box.

Now we need to cover the code that gets executed as soon as our saveFileDialog1 receives a click on OK. That is, the destination path was selected and we are ready to convert! Here we go now. It's quite short and straightforward.

private void saveFileDialog1_FileOk(object sender, CancelEventArgs e)

{

   string newExtension = (string)ComboBox1.SelectedItem;

   string newName = saveFileDialog1.FileName;

   Image img = Image.FromFile(SelectPicture.FileName);

   saveFile(img, newName, newExtension);

}

The last code snippet is the saveFile method that we created. It does all the work!

private void saveFile(Image img, string NewFileName, string NewFileExtension)

{

   EncoderParameters codecParams = new EncoderParameters(1);

   codecParams.Param[0] = new EncoderParameter(Encoder.Quality, 100L);

   ImageCodecInfo[] encoders;

   encoders = ImageCodecInfo.GetImageEncoders();

   bool success; success = false;

   try

   {

       switch (NewFileExtension.ToUpper())

       {

          case "PNG": img.Save(NewFileName, encoders[4], codecParams); success =
true; break;

          case "BMP": img.Save(NewFileName, encoders[0], codecParams); success =
true; break;

          case "JPG": img.Save(NewFileName, encoders[1], codecParams); success =
true; break;

          case "GIF": img.Save(NewFileName, encoders[2], codecParams); success =
true; break;

       }

       catch

       {

          MessageBox.Show("Failed to save image to " + NewFileName, "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);

          return;

       }

       if (success == true)

          MessageBox.Show("Image file saved to " + NewFileName, "Image Saved",
MessageBoxButtons.OK, MessageBoxIcon.Information);

}

As you can see from the above lengthy block of code, the arguments for our method are the image we are working on, the new file name, and the extension for the new destination file. We then configure our encoder parameters for 100% quality (ergo lossless conversion). We create a success variable for the purpose of error handling.

Switch is used to delimit each case of conversion depending on the chosen extension. The file saving process uses Image.Save, which saves the image to the specified stream using the specified encoder and encoder parameters. In our case the stream of the image is the object img. The entire switch block is right in the center of a try/catch block. Again, that's what we use to preserve error handling.

In a nutshell, or perhaps the whole nut in its entirety, this is how we convert images using the .NET environment on C# programming language. I really hope that my instructions and the code fragments were straightforward enough and that you could follow along easily.

Taking a Break

Finally we have created the groundwork for our Lightweight Image Manipulation application. At this stage our little utility software is able to convert images. Certainly we cannot say that's some outstanding function, but still, the purpose of this article was to learn how to design, implement, and code these kinds of applications.

The attached source code for the first part, because the resizing is going to be covered in the next segment, can be found below. It is a ZIP archive and you should just extract the whole archive to your chosen destination. As I already mentioned, the project was developed with Visual Studio 2005.

I also want to point out the fact that if you play around with the image conversion application we've just finished programming, you may notice that if you convert to GIF extension, then the conversion by no means will be lossless. The destination image becomes very pixelated and a bit blurry. Some of the color palettes become misplaced. I am going to attach two example images so you can catch my drift easier.

(The image on left is in original JPG format, the one on right is converted to GIF.)

"Why does that happen?" In short, it happens because of color quantization. Regardless of the encoder settings (such as high quality) we choose, our image quality gets destroyed due to the loss conversion. A better term for color quantization is color reduction. However, the de facto standard usage in DSP and computing is quantization.

By its very nature, the GIF extension format doesn't allow more than 256 palette entries. And an image always has a particular number of colors in each of its palettes. Therefore, quantization is required for each GIF image file, especially when converting 16-, 24- or higher bit images. It is necessary to do a quantization algorithm when resizing any image to GIF format. With other formats, this isn't an issue.

So as you had noticed, the "default" quantization algorithm of .NET's GDI+ is optimized for speed and not quality. It is a web development environment and it makes sense that speed is more important than quality, especially back in those days when we were struggling with dial-up connections. To improve our GIF conversion, we'll stick with an independent and different quantization algorithm, but that's for the next part!

Until next time, keep coding and remember - Perfect practice makes perfect!

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 8 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials