使用 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# 中丢失图像质量(减少颜色数量)的主要内容,如果未能解决你的问题,请参考以下文章
如何让我的Buffered Image类在我的GUI中显示?
如何使用 UIImageView 的内容模式 UIViewContentModeScaleAspectFit 保持高度不变来避免丢失图像质量?