Win7下用C#生成Gray8位图出现异常结果的问题求助
Win7下WPF生成Gray8格式BMP像素值异常的原因与解决方法
问题原因
Win7系统自带的WindowsCodecs.dll中的BMP编码器存在兼容性bug:当处理PixelFormats.Gray8(8位灰度,256级)格式图像时,会错误地将8位灰度数据量化为4位灰度(仅16级),再把4位值按值 × 17(255/15=17)映射回8位,最终出现你观察到的input / 16 * 17的像素值。Win10版本的编码器已修复该问题,可正确保留8位灰度信息。
解决方案
方案1:转换为RGB24格式后编码
Win7的BMP编码器对RGB格式处理正常,可将Gray8图像转换为24位RGB(R=G=B=灰度值)后再编码,代码修改如下:
var width = 4; var height = 4; var rect = new Int32Rect() { Width = width, Height = height }; var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null); var data = new byte[width * height]; for (var i = 0; i < data.Length; i++) { data[i] = (byte)(i * 2); } bitmap.WritePixels(rect, data, width, 0); // 将Gray8转换为RGB24格式 var rgbBitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Rgb24, null); var rgbData = new byte[width * height * 3]; for (int i = 0; i < data.Length; i++) { rgbData[i * 3] = data[i]; // B通道 rgbData[i * 3 + 1] = data[i]; // G通道 rgbData[i * 3 + 2] = data[i]; // R通道 } rgbBitmap.WritePixels(rect, rgbData, width * 3, 0); // 使用RGB24图像编码 var encoder = new BmpEncoder(); encoder.Frames.Add(BitmapFrame.Create(rgbBitmap)); using (var s = new FileStream(filename, FileMode.Create)) { encoder.Save(s); }
方案2:手动构造标准8位灰度BMP文件
绕过WPF的BmpEncoder,直接按照BMP格式规范生成带256级灰度调色板的8位灰度BMP,彻底避免编码器兼容性问题:
var width = 4; var height = 4; var data = new byte[width * height]; for (var i = 0; i < data.Length; i++) { data[i] = (byte)(i * 2); } using (var fs = new FileStream(filename, FileMode.Create)) using (var bw = new BinaryWriter(fs)) { // 写入BMP文件头 bw.Write((byte)'B'); bw.Write((byte)'M'); int rowPad = (4 - (width % 4)) % 4; int fileSize = 54 + 256 * 4 + width * height + rowPad * height; bw.Write(fileSize); bw.Write((ushort)0); bw.Write((ushort)0); bw.Write(54 + 256 * 4); // 像素数据起始偏移 // 写入BITMAPINFOHEADER bw.Write(40); // 信息头大小 bw.Write(width); bw.Write(height); bw.Write((ushort)1); // 平面数 bw.Write((ushort)8); // 位深度 bw.Write((uint)0); // 无压缩 bw.Write((uint)(width * height + rowPad * height)); // 像素数据总大小 bw.Write(96); // 水平DPI bw.Write(96); // 垂直DPI bw.Write((uint)256); // 调色板颜色数 bw.Write((uint)256); // 重要颜色数 // 写入256级灰度调色板(R=G=B=i,Alpha=0) for (int i = 0; i < 256; i++) { bw.Write((byte)i); // B bw.Write((byte)i); // G bw.Write((byte)i); // R bw.Write((byte)0); // Alpha } // 写入像素数据(BMP行存储顺序为从下到上,每行需对齐到4字节) for (int y = height - 1; y >= 0; y--) { int rowStart = y * width; bw.Write(data, rowStart, width); // 补位到4字节边界 if (rowPad > 0) { bw.Write(new byte[rowPad]); } } }
注意:原代码中
encoder.Frames.Add(BitmapFrame.Create(bitmap);存在语法错误,需修正为encoder.Frames.Add(BitmapFrame.Create(bitmap));
内容的提问来源于stack exchange,提问作者mingpepe
相关产品推荐
相关产品推荐

