适用于 .NET 的双位图(TIFF)图像转换器






4.82/5 (44投票s)
如何为您的应用程序添加位图图像编辑支持

引言
.NET 框架为生成和操作位图图像提供了丰富的功能,但缺少一个对图像处理至关重要的功能——修改然后保存修改后的双位图(即黑白或每像素一比特)图像的能力。双位图图像通常用于文档管理和文档成像应用程序中处理扫描文档。双位图图像最常以 TIFF(标记图像文件格式)文件格式存储,并使用 CCITT Group IV 压缩算法。
问题
.NET 框架支持加载和显示双位图图像,但仅限于此。 .NET 中的所有绘图都需要一个 `Graphics` 对象,但无法从双位图图像创建 `Graphics` 对象。如果您不相信,现在就去试试。我等着……
以下是一些演示此问题的代码(假设 *Bitonal-In.tif* 是一个双位图图像)
Bitmap originalBitmap = new Bitmap(@"Bitonal-In.tif");
Graphics g2 = Graphics.FromImage(originalBitmap);
此代码将生成一个“无法从具有索引像素格式的图像创建 Graphics 对象”的异常,从而阻碍我们修改双位图图像的愿望。我知道,我也不敢相信。
解决方案(差不多)
我们可以通过将双位图图像转换为 RGB(红/绿/蓝)位图进行修改来解决之前的异常。本文附带的 `Converter` 类包含一个 `static` 方法 `ConvertToRGB`,用于执行此操作。此方法的代码如下:
public static Bitmap ConvertToRGB(Bitmap original)
{
Bitmap newImage = new Bitmap(original.Width, original.Height,
PixelFormat.Format32bppArgb);
newImage.SetResolution(original.HorizontalResolution,
original.VerticalResolution);
Graphics g = Graphics.FromImage(newImage);
g.DrawImageUnscaled(original, 0, 0);
g.Dispose();
return newImage;
}
这样我们就可以得到一个位图,用于创建 `Graphics` 对象并修改图像,一切又都回归正常了。嗯……差不多,但还不是完全。
一个新的、不同的问题
我们现在可以愉快地修改和显示我们的图像(尽管内存占用量更大,因为是每像素 32 位的 RGB 图像),但如果我们想将修改后的图像以双位图格式保存回磁盘,我们再次受阻。以下代码在尝试将我们 32 位 RGB 图像保存回双位图格式时将生成一个“参数无效”的异常。
// Get an ImageCodecInfo object that represents the TIFF codec.
ImageCodecInfo imageCodecInfo = GetEncoderInfo("image/tiff");
System.Drawing.Imaging.Encoder encoder =
System.Drawing.Imaging.Encoder.Compression;
EncoderParameters encoderParameters = new EncoderParameters(1);
// Save the bitmap as a TIFF file with group IV compression.
EncoderParameter encoderParameter = new EncoderParameter(encoder,
(long)EncoderValue.CompressionCCITT4);
encoderParameters.Param[0] = encoderParameter;
bitonalBitmap.Save(@"Bitonal-Out.tif", imageCodecInfo, encoderParameters);
问题源于 .NET 框架无法将 RGB 图像编码为双位图文件格式。本文的主要目的是解决此问题。
解决方案(这次是认真的)
虽然 .NET 框架确实支持保存双位图图像,但它没有提供将 RGB 图像转换为双位图图像的方法,而这正是问题的关键。我们不能使用从双位图到 RGB 的相同方法,因为我们无法创建新的双位图图像并获得一个 `Graphics` 对象在其上绘图。我们必须求助于完全不同的东西——**直接的图像字节操作**(啊!他说什么了!?)。
虽然深入研究位图的内存结构超出了本文的范围,但我会简要提及任务。 32 位 RGB 位图对位图中的每个像素(图像元素)使用四个字节的内存。每个字节分别用于表示像素的红色、绿色和蓝色,还有一个字节用于表示像素的 Alpha(或透明度)。 RGB 值 255-255-255 表示白色,0-0-0 表示黑色。另一方面,双位图图像使用一个比特来表示图像中的每个像素,并且每八个像素打包到表示图像的每个字节中。
.NET 中的 `BitmapData` 类提供了一个 `LockBits` 方法,该方法使我们能够直接访问位图的图像字节。我们可以使用此方法将现有图像的图像字节检索到 `byte[]` 中,修改图像字节,然后将图像字节写回位图,从而修改位图。要将 RGB 位图转换为双位图位图,我们按以下步骤进行:
- 将原始 RGB 图像的图像字节复制到字节数组中。
- 创建一个新的双位图图像,其尺寸与源图像相同。
- 创建一个大小合适的 `byte` 数组,以容纳双位图图像的比特。
- 遍历源数据中的像素,如果红、绿、蓝值的总和超过某个阈值,则在目标数据中设置相应的比特。
- 将目标字节数组复制回新的双位图位图。
注释
在寻找此问题的解决方案时,我遇到了其他执行此类转换的代码片段,但它们都存在同一个问题:它们非常 S....L....O....W..... 本文提供的该方法在我(一台 3GHz 的 P4 电脑)上以大约 100 毫秒的时间完成了典型 300 DPI(每英寸点数)图像的转换。虽然许多 C# 图像应用程序依赖指针算法和 `unsafe` 代码块,但本文中的代码被认为**完全安全**,无需诉诸这种过时的方法。
可改进之处
本文提供的 RGB 到双位图的转换方法执行图像的阈值转换,其中目标像素根据源图像像素的亮度变为黑色或白色。对代码的一个有益改进是添加半调或抖动算法,以从彩色图像中产生更高质量的输出。本文提供的该方法是为文档成像应用程序编写的,在这些应用程序中,源图像已经是双位图图像,因此对我来说这不是必需的,但我可以看到如果此代码用于从具有更多样化源颜色的彩色图像生成双位图图像,它可能会很有用。
示例项目
项目附带的示例项目是一个 Windows Forms 应用程序,它使用 `Converter` 类将现有双位图图像转换为 RGB,在其上绘制一些文本,然后将其以双位图格式保存回磁盘。该项目包含演示该技术的最小代码量。