Resizing Images

D

dj1072

I am trying to write a C# console application that resizes images and
copies them to a new folder.

I am resizing them by creating a new bitmap object and saving it out
as an image (at zero compression, full quality).

I can get the image to resize fine, but the image quality is bad
(badly pixelated with lots of artifacts). Google searching has shown
that this has to do with the downsampling that .NET uses and that it's
unavoidable.

Is that the truth? Is there no way to get a crisp, near-original-
quality JPEG output from the .NET framework?

If that's the case, can you recommend some other free image processing
mechanisms that I could integrate with my application?


Thanks!
DJ
 
D

dj1072

Pete, thank you so much! I didn't know about the HighQualityBicubic
option which seems to have done the trick. My .NET image now looks
like an image saved directly from a desktop graphics program!

If anyone is interested, below is the code I ended up using to resize
a large image and save it out as a small image in a different
location.

The following web page was also helpful: http://snippets.dzone.com/posts/show/1485



public static void ImageResize(string strSourceImagePath,
string strDestImagePath, int iMaxWidth, int iMaxHeight)
{
if (File.Exists(strSourceImagePath))
{
Image imgPhoto = System.Drawing.Image.FromFile
(strSourceImagePath);
Int32 iWidth = imgPhoto.Width;
Int32 iHeight = imgPhoto.Height;
Int32 iNewWidth = imgPhoto.Width;
Int32 iNewHeight = imgPhoto.Height;

// set the new image dimensions based on the maximum
dimensions allowed.
if (iWidth >= iHeight)
{
Decimal decProp = (Decimal)iMaxWidth / (Decimal)
iWidth;
iNewWidth = iMaxWidth;
iNewHeight = (Int32) Math.Round((Decimal)iHeight *
decProp, 0);
}
else
{
Decimal decProp = (Decimal)iMaxHeight / (Decimal)
iHeight;
iNewHeight = iMaxHeight;
iNewWidth = (Int32)Math.Round((Decimal)iWidth *
decProp, 0);
}

// Create a new blank canvas. The resized image will
be drawn on this canvas.
Bitmap bmPhoto = new Bitmap(iNewWidth, iNewHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(96, 96);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.InterpolationMode =
InterpolationMode.HighQualityBicubic;
grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;

// draw our image on the new canvas
grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0,
iNewWidth, iNewHeight), 0, 0, iWidth, iHeight, GraphicsUnit.Pixel);

// create a codec so that we can set the compression
and quality levels.
ImageCodecInfo encJPEG = FindEncoder
(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder qualityEncoder =
System.Drawing.Imaging.Encoder.Quality;
System.Drawing.Imaging.Encoder compressionEncoder =
System.Drawing.Imaging.Encoder.Compression;
EncoderParameters myEncoderParameters = new
EncoderParameters(2);
EncoderParameter myQualityEncoderParameter = new
EncoderParameter(qualityEncoder, 100L);
EncoderParameter myCompressionEncoderParameter = new
EncoderParameter(compressionEncoder, 0);
myEncoderParameters.Param[0] =
myQualityEncoderParameter;
myEncoderParameters.Param[1] =
myCompressionEncoderParameter;

// Save out to a file. We dispose of all objects to
make sure the files don't stay locked.
bmPhoto.Save(strDestImagePath, encJPEG,
myEncoderParameters);
imgPhoto.Dispose();
bmPhoto.Dispose();
grPhoto.Dispose();
}
}

public static ImageCodecInfo FindEncoder(ImageFormat format)
//source: http://msdn.microsoft.com/en-us/library/ms142148(VS.80).aspx
{
if (format == null)
{
throw new ArgumentNullException("format");
}
foreach (ImageCodecInfo codec in
ImageCodecInfo.GetImageEncoders())
{
if (codec.FormatID.Equals(format.Guid))
{
return codec;
}
}
return null;
}
 
D

dj1072

Pete, thanks for your advice here. I am making progress and have
implemented most of your suggestions.
If (and only if) you have time, could you respond to my ongoing
ignorance?...


-- it is more idiomatic to use the Size struct, instead of
keeping
individual width/height variables. Doing so makes some of the
later
Graphics calls more convenient too.

Can you explain why it's better to use the Size struct than w & h
variables, and how to do so.

-- It is also idiomatic to use the "using" statement when dealing
with
IDisposable objects. Doing so is also a good way to avoid mistakes
such
as disposing objects in the wrong order (such as disposing a Bitmap
instance before you've disposed the Graphics instance that draws into
that
Bitmap instance, as you've done here). :)

I use the "using" statements at the top of my program.cs. Are you
saying that I should be using them in my ImageResizing method as well?
Did I get the order right this time? Are there any other objects I
should be disposing of?

My entire program.cs appears below.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;


namespace JICS_Photos
{
class Program
{
static void Main(string [ ] args)
{
String strIDPhotoPath = @"\\gotest\InetPub\photos\upload
\";
String strJICSPhotoPath = @"\\gordon\admsoft\jenzabar
\ICSFileServer\IDPhotos\";

synergyDataSetTableAdapters.selectAccountBarcodeJICSIDTableAdapter
taPeople = new
JICS_Photos.synergyDataSetTableAdapters.selectAccountBarcodeJICSIDTableAdapter
();
synergyDataSet.selectAccountBarcodeJICSIDDataTable
dtPeople = new synergyDataSet.selectAccountBarcodeJICSIDDataTable();
taPeople.Fill(dtPeople);
foreach (DataRow drPerson in dtPeople.Rows)
{
//Console.WriteLine(dr["account_id"].ToString() + ".jpg
\t" + dr["barcode"].ToString() + "jpg");
if (File.Exists(strIDPhotoPath + drPerson
["account_id"].ToString() + ".jpg"))
{
String strFullSourcePath = strIDPhotoPath +
drPerson["account_id"].ToString() + ".jpg";
String strFullDestPath = strJICSPhotoPath +
drPerson["barcode"].ToString() + ".jpg";
ImageResize(strFullSourcePath, strFullDestPath,
130, 130);
Console.WriteLine("transforming " +
strFullSourcePath + " to " + strFullDestPath);
}
}
}

public static void ImageResize(string strSourceImagePath,
string strDestImagePath, int iMaxWidth, int iMaxHeight)
{
if (File.Exists(strSourceImagePath))
{
Image imgPhoto = System.Drawing.Image.FromFile
(strSourceImagePath);
Int32 iWidth = imgPhoto.Width;
Int32 iHeight = imgPhoto.Height;
Int32 iNewWidth = imgPhoto.Width;
Int32 iNewHeight = imgPhoto.Height;

float scaleWidth = (float)iMaxWidth / iWidth,
scaleHeight = (float)iMaxHeight / iHeight;
float scaleOutput = Math.Min(scaleWidth, scaleHeight);


iNewWidth = (int) Math.Round(scaleOutput * iWidth, 0);
iNewHeight = (int) Math.Round(scaleOutput * iHeight,
0);


// Create a new blank canvas. The resized image will
be drawn on this canvas.
Bitmap bmPhoto = new Bitmap(iNewWidth, iNewHeight,
PixelFormat.Format24bppRgb);
bmPhoto.SetResolution(96, 96);
Graphics grPhoto = Graphics.FromImage(bmPhoto);
grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
grPhoto.InterpolationMode =
InterpolationMode.HighQualityBicubic;
grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;

// draw our image on the new canvas
grPhoto.DrawImage(imgPhoto, 0, 0, iNewWidth,
iNewHeight);

// create a codec so that we can set the compression
and quality levels.
ImageCodecInfo encJPEG = FindEncoder
(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder qualityEncoder =
System.Drawing.Imaging.Encoder.Quality;
System.Drawing.Imaging.Encoder compressionEncoder =
System.Drawing.Imaging.Encoder.Compression;
EncoderParameters myEncoderParameters = new
EncoderParameters(1);
EncoderParameter myQualityEncoderParameter = new
EncoderParameter(qualityEncoder, 100L);
myEncoderParameters.Param[0] =
myQualityEncoderParameter;

// Save out to a file. We dispose of all objects to
make sure the files don't stay locked.
bmPhoto.Save(strDestImagePath, encJPEG,
myEncoderParameters);
imgPhoto.Dispose();
bmPhoto.Dispose();
grPhoto.Dispose();
}
}

public static ImageCodecInfo FindEncoder(ImageFormat format)
//source: http://msdn.microsoft.com/en-us/library/ms142148(VS.80).aspx
{
if (format == null)
{
throw new ArgumentNullException("format");
}
foreach (ImageCodecInfo codec in
ImageCodecInfo.GetImageEncoders())
{
if (codec.FormatID.Equals(format.Guid))
{
return codec;
}
}
return null;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top