C#中用原始像素数组创建Bitmap部分图片失败求助
通道分离灰度图转换问题:部分图片处理失败
问题现象
- 可正常处理的图片:正常尺寸的原图
- 无法处理的图片:同一图片的缩小版本
处理缩小版图片时抛出System.ArgumentException: 参数无效异常。
当前实现代码
提取红色通道像素数组
public byte[] GetPixels(Bitmap bmp) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); int length = bmpData.Stride * bmpData.Height; int bytesPerPixel = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8; byte[] pixelData = new byte[length]; byte[] newPixelData = new byte[length / bytesPerPixel]; Marshal.Copy(bmpData.Scan0, pixelData, 0, length); for(int i = 0; i < newPixelData.Length; i++) { newPixelData[i] = pixelData[i*bytesPerPixel]; } bmp.UnlockBits(bmpData); bmp.Dispose(); return newPixelData; }
转换为8位索引灰度图
public Bitmap makeBitmap(byte[] bytes) { Bitmap bmp; PixelFormat pxFormat = PixelFormat.Format8bppIndexed; int stride = img.Width; unsafe { fixed (byte* ptr = bytes) { bmp = new Bitmap(img.Width, img.Height, stride, pxFormat, (IntPtr)ptr); } } bmp.SetResolution(img.HorizontalResolution,img.VerticalResolution); ColorPalette palette = bmp.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i,i,i); } bmp.Palette = palette; return bmp; }
问题根源
- Stride对齐错误:GDI+要求位图的行字节数(Stride)必须满足4字节对齐规则,即Stride是大于等于图片宽度的最小4的倍数。直接用
img.Width作为8位位图的Stride,当图片宽度无法被4整除时(缩小后的图片常出现这种情况),会触发参数无效异常。 - 通道提取逻辑错误:原始位图的Stride可能包含填充字节,原代码中
i*bytesPerPixel的遍历方式会错误读取填充区域的数据,导致通道提取结果异常。
修复方案
1. 修正通道提取逻辑(正确跳过填充字节)
public byte[] GetRedChannelPixels(Bitmap bmp) { int width = bmp.Width; int height = bmp.Height; BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat); int bytesPerPixel = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8; byte[] redChannelData = new byte[width * height]; unsafe { byte* ptr = (byte*)bmpData.Scan0; int rowStride = bmpData.Stride; for (int y = 0; y < height; y++) { byte* rowPtr = ptr + y * rowStride; for (int x = 0; x < width; x++) { // 适配BGR/BGRA格式的红色通道位置 int redIndex = x * bytesPerPixel + (bytesPerPixel - 2); redChannelData[y * width + x] = rowPtr[redIndex]; } } } bmp.UnlockBits(bmpData); bmp.Dispose(); return redChannelData; }
2. 修正灰度图创建逻辑(处理Stride对齐)
public Bitmap CreateGrayscaleBitmapFromChannel(byte[] channelData, int width, int height, float dpiX, float dpiY) { PixelFormat format = PixelFormat.Format8bppIndexed; // 计算符合4字节对齐要求的Stride int stride = ((width + 3) / 4) * 4; byte[] bitmapBuffer = new byte[stride * height]; // 将通道数据复制到对齐后的缓冲区 for (int y = 0; y < height; y++) { Array.Copy(channelData, y * width, bitmapBuffer, y * stride, width); } Bitmap bmp; unsafe { fixed (byte* ptr = bitmapBuffer) { bmp = new Bitmap(width, height, stride, format, (IntPtr)ptr); } } bmp.SetResolution(dpiX, dpiY); // 设置灰度调色板 ColorPalette palette = bmp.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = palette; return bmp; }
3. 调用示例
using (Bitmap originalBmp = new Bitmap("your-image-path.png")) { byte[] redChannel = GetRedChannelPixels(originalBmp); Bitmap redGrayscale = CreateGrayscaleBitmapFromChannel(redChannel, originalBmp.Width, originalBmp.Height, originalBmp.HorizontalResolution, originalBmp.VerticalResolution); redGrayscale.Save("red-channel-grayscale.png"); redGrayscale.Dispose(); }
额外优化建议
- 使用
unsafe直接遍历内存,避免复制整个像素数组,降低大图片的内存占用。 - 用
using语句管理Bitmap对象,确保资源及时释放。
内容的提问来源于stack exchange,提问作者hackn101
相关产品推荐
相关产品推荐

