如何从整数数组创建16位灰度Bitmap?修复访问违例异常
问题原因
抛出System.AccessViolationException是代码里几个明确的错误导致的:
- 数据类型长度不匹配:C#中
int类型占4字节(32位),但Format16bppGrayScale格式每个像素仅占2字节(16位)。原代码调用Marshal.Copy时传入的复制长度是width * height,该重载会按源数组的元素类型计算总复制字节数,实际会写入width*height*4字节,远超出位图预分配的width*height*2内存范围,直接越界写触发访问违例。 - 未考虑位图步长对齐规则:GDI+生成的位图扫描行默认按4字节边界对齐,若行像素总字节数不是4的倍数,会在行尾自动填充空字节。直接连续复制整段数据不考虑步长偏移,哪怕类型匹配也会出现像素错位。
- 额外逻辑缺陷:
Format16bppGrayScale是GDI+中兼容性极差的像素格式,原代码最后调用Clone做格式转换的逻辑在多数Windows环境下会抛出异常或生成全黑图像,完全不可靠。
修复代码
先将源int[]中0-65535范围的累加值转为16位无符号整数(ushort,正好匹配16位灰度像素的存储长度),再按位图实际步长逐行复制数据,彻底避免越界和错位问题:
/// <summary> /// 将多帧8位图累加得到的整数数组转换为16bpp灰度位图 /// </summary> /// <param name="image">像素累加数组,取值范围0-65535</param> /// <param name="width">图像宽度</param> /// <param name="height">图像高度</param> /// <returns>Format16bppGrayScale格式的Bitmap对象</returns> public static Bitmap To16bppGrayscaleBitmap(this int[] image, int width, int height) { if (image.Length < width * height) throw new ArgumentException("像素数组长度与传入的宽高参数不匹配"); // 将32位int数组转换为匹配16位像素格式的ushort数组 ushort[] pixelBuffer = new ushort[width * height]; for (int i = 0; i < pixelBuffer.Length; i++) { // 钳位像素值到合法范围,避免累加值溢出16位导致像素错乱 pixelBuffer[i] = (ushort)Math.Clamp(image[i], ushort.MinValue, ushort.MaxValue); } Bitmap resultBmp = new Bitmap(width, height, PixelFormat.Format16bppGrayScale); BitmapData bmpData = resultBmp.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format16bppGrayScale); try { int stride = bmpData.Stride; IntPtr scan0Ptr = bmpData.Scan0; // 逐行复制像素,自动适配步长对齐 for (int row = 0; row < height; row++) { IntPtr currentRowPtr = IntPtr.Add(scan0Ptr, row * stride); Marshal.Copy(pixelBuffer, row * width, currentRowPtr, width); } } finally { resultBmp.UnlockBits(bmpData); } return resultBmp; }
使用提示
- 如果最终需要输出其他像素格式,不要使用
Clone方法做格式转换。可以先通过上述方法生成标准16bpp灰度图,再创建目标格式的位图,通过Graphics.DrawImage绘制完成转换,该方式在所有Windows版本下行为一致,不会出现黑图或报错。 - 如果你累加的8位图像总张数不超过256张,累加值最大为255*255=65025,不会超出16位范围,可以去掉
Math.Clamp钳位逻辑提升处理速度。 - 不要试图省略
int[]到ushort[]的转换步骤直接复制内存,32位和16位数据的元素步长完全不匹配,强行复制只会得到错乱的图像。
内容的提问来源于stack exchange,提问作者m4gik
相关产品推荐
相关产品推荐

