使用Image类在c#中失去图像质量(减少颜色数量)

4
我有一个C#程序,可以打开.tif格式的图片,并提供保存选项。然而,在保存图片时总是会出现质量下降的情况。
(编辑:我在保存图片时传递了一些参数,使得质量达到了100%且没有压缩,但实际独特颜色的数量从254变为16,尽管图像属性显示为8bpp)
(编辑2:所涉及的图像为灰度图像,每像素8位 - 256种灰度颜色/阴影 - 在我测试的24位每像素彩色图像中不会发生这种情况,其中所有颜色都被保留。我开始认为图像类可能仅支持16个灰度级别)
如何避免这种情况?
以下是打开图像的代码:
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;
}

这是我保存图像的方法:
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();
    }
}

即使我没有调整或更改图片的任何方式,我仍然会遇到质量损失。有什么建议吗?

更新:查看保存图像的IrfanView信息并将其与原始图像进行比较,我注意到两件事情:一是在原始图像中压缩位置为“无”,而在保存的图像中它说LZW。另一件我注意到的事情是,虽然两个图像的颜色仍然是每像素8位(256种颜色),但保存的图像中唯一的颜色计数为16,而原始图像中唯一的颜色计数为251。 - EdwinG
更新:我已经更改了代码,将其保存为100%的质量,并消除了压缩,但是保存的图像中独特颜色的数量仍然为16,并且我仍然可以看到两个图像之间的质量差异。也许问题在于构造函数在打开图像时出现了问题? - EdwinG
4个回答

1

System.Drawing对8位图像的支持较差。当从24或32位图像转换为8位时,它将始终使用固定的默认调色板。该默认调色板仅包含16种灰度色阶,其他条目是各种颜色。

如果保存为“.bmp”时出现相同问题,则转换为8位格式已经在早期发生,您需要找出程序在哪里执行此操作并修复该问题。 如果只有tiff编码器将其转换为8位,则必须首先进行8位转换。创建一个8位图像,用灰度调色板填充Image.Palette,然后复制位图数据。

但是,System.Drawing对8位图像的支持较差,而且几种方法(例如SetPixel)在处理此类图像时会抛出InvalidOperationException。您可能需要使用不安全代码(使用LockBits等)来复制位图数据。如果我是您,我会看看是否有可用的替代图形库。


谢谢!那帮了我很多。我正在考虑转换到WPF,因为最终我也需要支持16位灰度图像。然而,这可能会在获取像素值方面造成问题。 - EdwinG

0

只有一个建议...在加载图像时,您使用new Bitmap(fileName)... 而不是使用Bitmap,您是否考虑过使用

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

使用 true 可以启用“嵌入式颜色管理”,而使用 Image 而不是 Bitmap 可以避免在幕后发生任何图像转换。


谢谢,但我已经考虑过这个问题并修改了我的代码——仍然存在同样的问题(由于只有16种颜色而不是250种)。 - EdwinG
目前我的想法都用完了,那我会四处看看,看能否找到其他的灵感 :-) - Nevyn

0

Image.Save 默认使用 75% 的质量设置。你可以尝试使用该方法的其他重载之一,它允许你指定质量设置参数。请参阅此问题


我已经尝试传递参数,消除LZW压缩以及将质量设置为100,但保存图像中的颜色数量仍保持在16。 - EdwinG

0

我在使用.NET库来寻找图像质量和大小的良好平衡方面遇到了问题。我放弃了自己编写代码,尝试了一些图像库。我发现 http://imageresizing.net/ 能够产生始终如一的良好结果,比我能够做到的要好得多。

只是提供一个备选方案B,以防自己编写的方法不能在稳定的情况下良好运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接