More Fun with Images (or System.Drawing Redux)

It’s high time I wrote another little piece on what I have been programming in my spare time. I mentioned before that I have been playing with digital photos, extracting EXIF dates with Omar’s photo library (very handy) and posting them to the web for friends and family. Well, Pavel Lebedinsky mentioned in the comments of that entry that GetImageThumbnail gave worse results than using DrawImage with InterpolationMode.HighQualityBicubic. I took it to heart and started playing around with the classes in System.Drawing. I also found this article on The Code Project which gave a nice example of how to do exactly what I wanted – just scale down my digital pictures to a more web appropriate size and make thumbnails too – using DrawImage. I had been just creating a new Bitmap based off an instance that loads one of my jpegs and passing in the new width and height to the constructor. The simplicity is what I really liked about my method, but anyhow… in looking at my scaled down bitmaps I could see that they were a little grainy. I decided to run a comparison against the code in the Code Project example.

Sure enough, the DrawImage with InterpolationMode.HighQualityBicubic did produce a better quality image at 480 pixels across. See CreateImage vs. CreateImage2 below. CreateImage2 calls into The Code Project example.

Next I tried Pavel’s suggestion that GetThumbnailImage produces a worse image than one with DrawImage and HiQualityBicubic. I set my thumbnail width at 96 pixels and gave it a shot. It turns out that the thumbnail extracted from the original image via GetThumbnailImage is less grainy. That’s not to say if you had an image without an embedded thumbnail it would be better than the DrawImage solution, but for my case, with our 3.2 megapixel Olympus, it is the best code path so far.

As a matter of fact, we are looking for a new camera that is more pocket-sized (Canon Elph sized) at something like 4 megapixel. Anybody have a camera they like and recommend?

            /// <summary>

            /// The main entry point for the application.

            /// </summary>

            [STAThread]

            static void Main(string[] args)

            {

                  CreateImage("145.jpg", "145_1.jpg", 480, ImageFormat.Jpeg);

                  CreateImage2("145.jpg", "145_2.jpg", 480, ImageFormat.Jpeg); // Better quality at 480 pixels wide

                  CreateImage2("145.jpg", "145_3.jpg", 96, ImageFormat.Jpeg);

                  CreateThumbnailImage("145.jpg", "145_4.jpg"); // Better quality at 96 pixels wide if image has an embedded thumbnail

            }

            internal static void CreateImage(string strOriginalImage, string strOutputImage, int intWidth, ImageFormat imgFmt)

            {

                  Bitmap bmp = new Bitmap(strOriginalImage);

                  double dblRatio = Convert.ToDouble(bmp.Height) / Convert.ToDouble(bmp.Width);

                  int intHeight = Convert.ToInt32(intWidth * dblRatio);

                  Image img = new Bitmap(bmp, intWidth, intHeight);

                  img.Save(strOutputImage, imgFmt);

                  bmp.Dispose();

                  img.Dispose();

            }

            internal static void CreateImage2(string strOriginalImage, string strOutputImage, int intWidth, ImageFormat imgFmt)

            {

                  Bitmap bmp = new Bitmap(strOriginalImage);

                  double dblRatio = Convert.ToDouble(bmp.Height) / Convert.ToDouble(bmp.Width);

                  int intHeight = Convert.ToInt32(intWidth * dblRatio);

                  Image img = FixedSize(bmp, intWidth, intHeight);

                  img.Save(strOutputImage, imgFmt);

                  bmp.Dispose();

                  img.Dispose();

            }

            internal static Image FixedSize(Image imgPhoto, int Width, int Height)

            {

                  int sourceWidth = imgPhoto.Width;

                  int sourceHeight = imgPhoto.Height;

                  int sourceX = 0;

                  int sourceY = 0;

                  int destX = 0;

                  int destY = 0;

                  float nPercent = 0;

                  float nPercentW = 0;

                  float nPercentH = 0;

                  nPercentW = ((float)Width/(float)sourceWidth);

                  nPercentH = ((float)Height/(float)sourceHeight);

                  if(nPercentH < nPercentW)

                  {

                        nPercent = nPercentH;

                        destX = System.Convert.ToInt16((Width -

                              (sourceWidth * nPercent))/2);

                  }

                  else

                  {

                        nPercent = nPercentW;

                        destY = System.Convert.ToInt16((Height -

                              (sourceHeight * nPercent))/2);

                  }

                  int destWidth = (int)(sourceWidth * nPercent);

                  int destHeight = (int)(sourceHeight * nPercent);

                  Bitmap bmPhoto = new Bitmap(Width, Height,

                        PixelFormat.Format24bppRgb);

                  bmPhoto.SetResolution(imgPhoto.HorizontalResolution,

                        imgPhoto.VerticalResolution);

                  Graphics grPhoto = Graphics.FromImage(bmPhoto);

                  grPhoto.Clear(Color.Red);

                  grPhoto.InterpolationMode =

                        InterpolationMode.HighQualityBicubic;

                  grPhoto.DrawImage(imgPhoto,

                        new Rectangle(destX,destY,destWidth,destHeight),

                        new Rectangle(sourceX,sourceY,sourceWidth,sourceHeight),

                        GraphicsUnit.Pixel);

                  grPhoto.Dispose();

                  return bmPhoto;

            }

            internal static void CreateThumbnailImage(string strOriginalImage, string strThumbnailImage)

            {

                  Image.GetThumbnailImageAbort cbImg = new Image.GetThumbnailImageAbort(ThumbnailCallback);

                  Bitmap bmp = new Bitmap(strOriginalImage);

                  double dblRatio = Convert.ToDouble(bmp.Height) / Convert.ToDouble(bmp.Width);

                  int intHeight = Convert.ToInt32(96.0 * dblRatio);

                  Image img = bmp.GetThumbnailImage(96, intHeight, cbImg, IntPtr.Zero);

                  img.Save(strThumbnailImage);

                  bmp.Dispose();

                  img.Dispose();

            }

            internal static bool ThumbnailCallback()

            {

                  return false;

            }