4

I have a c# program that opens a .tif image and later offers the option to save it. However, there is always a drop in quality when saving the image.

(EDIT:I passed some parameters while saving the image so that the quality is at 100 % and there is no compression, but the number of actual unique colors go from 254 to 16, even though the image properties show 8bpp)

(EDIT2: The image in question is a grayscale image at 8 bits per pixel - 256 colors/shades of gray - This doesn't happen with a 24 bits per pixel color image that I tested where all the colors are retained. I am starting to think that the image class may only support 16 shades of gray)

How do I avoid this?

Here's the code for opening the image:

public Image imageImport()
{
    Stream myStream = null;
    OpenFileDialog openTifDialog = new OpenFileDialog();
    openTifDialog.Title = "Open Desired Image";
    openTifDialog.InitialDirectory = @"c:\";
    openTifDialog.Filter = "Tiff only (*.tif)|*.tif";
    openTifDialog.FilterIndex = 1;
    openTifDialog.RestoreDirectory = true;
    if (openTifDialog.ShowDialog() == DialogResult.OK)
    {   
        try
        {
            if ((myStream = openTifDialog.OpenFile()) != null)
            {
                using (myStream)
                {
                    String tifFileName= openTifDialog.FileName;
                    imgLocation = tifFileName;
                    Bitmap tifFile = new Bitmap(tifFileName);
                    return tifFile;
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
        }
    }
    return null;
}

This is the way I save the image:

private void saveImage(Image img)
{
    SaveFileDialog sf = new SaveFileDialog();
    sf.Title = "Select File Location";
    sf.Filter = " bmp (*.bmp)|*.bmp|jpeg (*.jpg)|*.jpg|tiff (*.tif)|*.tif";
    sf.FilterIndex = 4;
    sf.RestoreDirectory = true;
    sf.ShowDialog();

    // If the file name is not an empty string open it for saving.
    if (sf.FileName != "")
    {
        // Saves the Image via a FileStream created by the OpenFile method.
        System.IO.FileStream fs =
           (System.IO.FileStream)sf.OpenFile();

        // Saves the Image in the appropriate ImageFormat based upon the
        // File type selected in the dialog box.
        // NOTE that the FilterIndex property is one-based.
        switch (sf.FilterIndex)
        {
           case 1:
               img.Save(fs,
                   System.Drawing.Imaging.ImageFormat.Bmp);
           break;

           case 2:
               img.Save(fs,
                   System.Drawing.Imaging.ImageFormat.Jpeg);
           break;

           case 3://EDITED -STILL DOES NOT RESOLVE THE ISSUE
               ImageCodecInfo codecInfo = ImageClass.GetEncoderInfo(ImageFormat.Tiff);
               EncoderParameters parameters = new EncoderParameters(2);
               parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,100L);
               parameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)EncoderValue.CompressionNone);
               img.Save(fs,codecInfo, parameters);
           break;
       }
       fs.Close();
    }
}

Even if I don't resize or change the image in any ways, I experience a loss in quality. any advice?

EdwinG
  • 213
  • 5
  • 13
  • update: looking at the info on irfanview of the saved image and comparing it to the original, I noticed two things: one is that where in compression in the original image says none, in the saved image it says LZW. The other thing I noticed is that while the color remain 8 bits per pixel in both images (256 colors), the unique color count in the saved image is 16, while the unique color count in the original is 251 – EdwinG Nov 02 '12 at 13:34
  • update: I changed my code to save it as 100% quality as well as eliminating compression, however the # of unique colors in the saved image is still at 16, and I can still see the difference in quality in the two images. Perhaps the problem is in the constructor while opening the image? – EdwinG Nov 02 '12 at 15:05

4 Answers4

1

System.Drawing has poor support for 8-bit images. When converting from 24 or 32-bit images to 8-bit; it'll always use a fixed default color palette. That default color palette only contains 16 shades of grey, the other entries are various colors.

Do you have the same problem when saving as '.bmp'? If yes, then the conversion to the 8-bit format already happened earlier, you'll have to figure out where your program does that and fix the issue there. If it's only the tiff encoder that converts to 8-bit, you'll have to do the 8-bit conversion in a separate step first. Create an 8-bit image, fill Image.Palette with a gray-scale palette, and then copy the bitmap data over.

But System.Drawing has poor support for 8-bit images, and several methods (e.g. SetPixel) will just throw InvalidOperationException when dealing with such images. You will probably have to use unsafe code (with LockBits etc.) to copy the bitmap data. If I were you, I'd look if there are alternative graphics libraries you could use.

Daniel
  • 14,823
  • 1
  • 50
  • 56
  • Thanks! that helped a lot. I am considering switching to WPF, since in the end I also need to support 16 bit gray-scale images. That might pose a problem though as far as getting pixel values is concerned. – EdwinG Nov 09 '12 at 20:22
0

I had issues with using the .NET libraries to find good balances of image quality and size. I gave up rolling my own and tried out a few imaging libraries. I found http://imageresizing.net/ to produce consistently good results, much better than I was able to do.

Just throwing that out there as a plan B in case the roll your own method doesn't wind up working well on a consistent basis for you.

Shan Plourde
  • 8,128
  • 2
  • 25
  • 40
0

Image.Save by default uses a quality setting of 75%. You could try using one of the other overloads of the method that allows you to specify quality setting parameters. See this question.

Community
  • 1
  • 1
endofzero
  • 1,775
  • 1
  • 21
  • 31
  • I have tried passing parameters, eliminating the lzw compression as well as setting the quality to 100, but the number of colors in the saved image stays at 16 – EdwinG Nov 02 '12 at 14:54
0

Only one suggestion really....when loading the Image you use new Bitmap(fileName)... Rather than using Bitmap have you considered using

Image tiffImage = Image.FromFile(tiffFileName, true);

The true tells it to use "embedded color management", and using Image instead of Bitmap avoids any image casting that might be occurring behind the scenes.

Nevyn
  • 2,535
  • 4
  • 15
  • 32
  • thanks, but I have already thought about this and modified my code -- still the same problem (a result of 16 colors instead of 250) – EdwinG Nov 02 '12 at 19:40
  • Im all out of Ideas then, for the moment, I'll take a look around and see what else I can uncover :-) – Nevyn Nov 05 '12 at 16:58