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

Posted

技术标签:

【中文标题】使用 Image 类在 c# 中丢失图像质量(减少颜色数量)【英文标题】:Losing image quality in c# using Image class (reduces amount of colors) 【发布时间】:2012-11-02 12:52:57 【问题描述】:

我有一个 c# 程序,它打开一个 .tif 图像,然后提供保存它的选项。但是,保存图像时质量总是会下降。

(编辑:我在保存图像时传递了一些参数,以便质量为 100% 并且没有压缩,但实际唯一颜色的数量从 254 变为 16,即使图像属性显示 8bpp)

(EDIT2:有问题的图像是每像素 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 更新:我更改了代码以将其保存为 100% 质量并消除压缩,但保存图像中唯一颜色的 # 仍为 16,我仍然可以看到差异两个图像的质量。也许问题出在打开图像时的构造函数中? 【参考方案1】:

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

保存为“.bmp”时是否也遇到同样的问题?如果是,那么转换为 8 位格式之前已经发生了,您必须弄清楚您的程序在哪里执行此操作并在那里解决问题。 如果只有 tiff 编码器转换为 8 位,则必须先在单独的步骤中进行 8 位转换。创建一个 8 位图像,用灰度调色板填充Image.Palette,然后将位图数据复制过来。

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

【讨论】:

谢谢!这有很大帮助。我正在考虑切换到 WPF,因为最后我还需要支持 16 位灰度图像。尽管就获取像素值而言,这可能会带来问题。【参考方案2】:

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

只是把它作为 B 计划扔出去,以防万一你自己的方法最终无法在一致的基础上为你工作。

【讨论】:

【参考方案3】:

Image.Save 默认使用 75% 的质量设置。您可以尝试使用允许您指定质量设置参数的方法的其他重载之一。见this question.

【讨论】:

我试过传参数,去掉lzw压缩以及设置质量为100,但是保存的图像中的颜色数保持在16【参考方案4】:

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

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

true 告诉它使用“嵌入式颜色管理”,并且使用 Image 代替 Bitmap 可以避免任何可能在幕后发生的图像投射。

【讨论】:

谢谢,但我已经考虑过这一点并修改了我的代码——仍然是同样的问题(结果是 16 色而不是 250 色) Im 完全没有想法,那么,目前,我会环顾四周,看看我还能发现什么 :-)

以上是关于使用 Image 类在 c# 中丢失图像质量(减少颜色数量)的主要内容,如果未能解决你的问题,请参考以下文章

c#:以 100% 质量保存 JPEG 时降低图像质量

使用 c# 进行高质量 JPEG 压缩

如何让我的Buffered Image类在我的GUI中显示?

在颤振中使用 image.asset 时图像质量下降

iText7 图像质量优化器不能作为文档工作

如何使用 UIImageView 的内容模式 UIViewContentModeScaleAspectFit 保持高度不变来避免丢失图像质量?