You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

问题根源

  1. Stride对齐错误:GDI+要求位图的行字节数(Stride)必须满足4字节对齐规则,即Stride是大于等于图片宽度的最小4的倍数。直接用img.Width作为8位位图的Stride,当图片宽度无法被4整除时(缩小后的图片常出现这种情况),会触发参数无效异常。
  2. 通道提取逻辑错误:原始位图的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:50:56