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

C#图像处理中使用Marshal.Copy与LockBits实现灰度滤镜的问题求助

现有代码核心错误点

  • 双重循环逻辑完全错误:外层GetPixel的像素遍历逻辑和内层LockBits逻辑完全脱节,你在循环里每处理1个像素就对整张图执行一次锁、读、改红色通道、写回操作,不仅效率比原来的GetPixel还低,也根本没有执行灰度赋值的逻辑,反而把所有像素的红色通道强制设为255,自然不会出现灰度效果
  • 锁模式错误:LockBits传入的是ImageLockMode.ReadOnly,该模式下修改后的像素数据无法写回位图
  • 像素字节顺序处理错误:GDI+中位图的字节存储顺序是*BGR(蓝、绿、红)*而非RGB,24位位图每3个字节为一组,顺序是索引0=B,1=G,2=R
  • 没有处理步长(Stride)的对齐偏移:位图每行的实际字节数是4的整数倍,存在填充字节,不能直接按宽度*3遍历所有像素,需要按行计算偏移

修正后的实现代码

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace dnm2709normalimg
{
    class GrayFilter
    {
        public static Bitmap DoGray(Bitmap bmp)
        {
            // 锁定位图全部区域,使用读写模式
            BitmapData bmpData = bmp.LockBits(
                new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.ReadWrite, 
                bmp.PixelFormat);

            // 获取像素数据的起始地址
            IntPtr ptr = bmpData.Scan0;
            // 计算全部像素的字节数
            int byteCount = Math.Abs(bmpData.Stride) * bmp.Height;
            byte[] pixelBytes = new byte[byteCount];

            // 把像素数据复制到字节数组
            Marshal.Copy(ptr, pixelBytes, 0, byteCount);

            // 计算单像素的字节数(兼容24位RGB、32位ARGB格式)
            int pixelSize = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;

            // 逐行遍历像素
            for (int y = 0; y < bmp.Height; y++)
            {
                // 计算当前行的起始偏移(解决Stride对齐填充问题)
                int rowOffset = y * bmpData.Stride;
                for (int x = 0; x < bmp.Width; x++)
                {
                    // 计算当前像素的起始索引
                    int pixelOffset = rowOffset + x * pixelSize;
                    // 按BGR顺序取色
                    byte b = pixelBytes[pixelOffset];
                    byte g = pixelBytes[pixelOffset + 1];
                    byte r = pixelBytes[pixelOffset + 2];

                    // 灰度计算:采用人眼感知权重公式,比平均法效果更自然
                    byte gray = (byte)(r * 0.299 + g * 0.587 + b * 0.114);

                    // 把三个通道都赋值为灰度值
                    pixelBytes[pixelOffset] = gray;     // B通道
                    pixelBytes[pixelOffset + 1] = gray; // G通道
                    pixelBytes[pixelOffset + 2] = gray; // R通道
                    // 32位带Alpha通道的格式保留原Alpha值
                    if (pixelSize >=4)
                    {
                        pixelBytes[pixelOffset + 3] = pixelBytes[pixelOffset + 3];
                    }
                }
            }

            // 把修改后的字节数组写回位图
            Marshal.Copy(pixelBytes, 0, ptr, byteCount);
            // 解锁位图
            bmp.UnlockBits(bmpData);

            return bmp;
        }
    }
}

注意事项

  • 不要在像素遍历循环内部执行LockBits/UnlockBits操作,只需锁一次整张图,处理完全部像素后再解锁,才能获得比GetPixel高百倍的性能提升
  • 如果仅需处理24位RGB格式,可以固定pixelSize=3,省略格式判断逻辑进一步提升性能
  • 灰度计算如果需要保持和你原来的平均值逻辑一致,可以把灰度计算代码替换为byte gray = (byte)((r + g + b) / 3);

内容的提问来源于stack exchange,提问作者user16815856

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 04:48:02