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

C#中使用XOR修改Bitmap像素颜色后读取值异常求助

问题分析与解决思路

你的问题主要由三个核心原因导致,咱们逐个拆解:

1. MPEG4编码的有损压缩是主因

你使用的VideoCodec.MPEG4是有损压缩编码,编码器在写入视频时会为了减小文件体积,对像素数据进行不可逆的压缩优化(比如丢弃人眼不敏感的细节)。这就导致你修改后的精确像素值(比如250)在写入视频后,会被编码器“调整”,重新读取时自然得不到原始修改的值。

2. Bitmap像素格式不匹配引发的隐式转换

你代码里创建了PixelFormat.Format24bppRgb的Bitmap,但reader.ReadVideoFrame()返回的帧格式未必是这个(比如可能是32bppArgb)。直接image = videoFrame赋值后,后续用GetPixel/SetPixel操作时,会发生隐式的像素格式转换,这可能悄悄改变你的像素值。另外,24bppRgb格式本身没有Alpha通道,你代码里存储的A[j,k]其实是无效的(24bpp的Color.A默认是255,但实际Bitmap里并没有存储这个通道)。

3. GetPixel/SetPixel的低效与潜在问题

这两个方法是逐像素调用的,不仅效率极低,而且在处理不同格式的Bitmap时,会触发.NET的颜色管理逻辑,可能导致像素值被意外转换(比如色域映射),进一步加剧数值偏差。


解决方案

方案1:使用无损视频编码(优先推荐,保证像素精确性)

如果需要精确保留修改后的像素值,必须使用无损编码格式。比如改用VideoCodec.RawVideo(完全无压缩,文件体积会很大),或者配置支持无损模式的编码(比如AVC的无损配置,需要额外参数)。

修改写入代码:

writer.Open(@"E:\Video\Result.MP4", reader.Width, reader.Height, reader.FrameRate, VideoCodec.RawVideo);

方案2:统一Bitmap像素格式,避免隐式转换

在读取视频帧后,将其转换为你需要的Format24bppRgb格式,确保后续操作的一致性:

Bitmap videoFrame = reader.ReadVideoFrame();
// 转换为24bppRgb格式的Bitmap
Bitmap image = new Bitmap(videoFrame, reader.Width, reader.Height, PixelFormat.Format24bppRgb);
videoFrame.Dispose(); // 及时释放原帧资源

方案3:用LockBits直接操作内存(替代GetPixel/SetPixel)

这是处理Bitmap像素的高效且可靠的方式,直接操作内存中的字节数组,避免隐式转换问题。注意:24bppRgb格式在内存中的存储顺序是BGR,不是RGB,这是很多人踩坑的点!

修改你的Myfunc方法核心逻辑:

public static void Myfunc(VideoFileReader reader, VideoFileWriter writer)
{
    byte[] mykey = { 0xff, 0x55, 0x00 };
    for (int i = 0; i < reader.FrameCount; i++)
    {
        using (Bitmap videoFrame = reader.ReadVideoFrame())
        {
            // 转换为24bppRgb格式
            using (Bitmap image = new Bitmap(videoFrame, reader.Width, reader.Height, PixelFormat.Format24bppRgb))
            {
                // 锁定Bitmap内存,直接操作字节
                BitmapData bmpData = image.LockBits(
                    new Rectangle(0, 0, image.Width, image.Height),
                    ImageLockMode.ReadWrite,
                    image.PixelFormat);

                IntPtr ptr = bmpData.Scan0;
                int totalBytes = bmpData.Stride * image.Height;
                byte[] pixelBytes = new byte[totalBytes];

                // 从内存复制像素数据到数组
                System.Runtime.InteropServices.Marshal.Copy(ptr, pixelBytes, 0, totalBytes);

                // 遍历每个像素,执行XOR操作(注意BGR顺序)
                for (int y = 0; y < image.Height; y++)
                {
                    for (int x = 0; x < image.Width; x++)
                    {
                        int byteIndex = y * bmpData.Stride + x * 3;
                        pixelBytes[byteIndex] ^= mykey[0];     // B通道
                        pixelBytes[byteIndex + 1] ^= mykey[0]; // G通道
                        pixelBytes[byteIndex + 2] ^= mykey[0]; // R通道
                    }
                }

                // 把修改后的数据复制回Bitmap内存
                System.Runtime.InteropServices.Marshal.Copy(pixelBytes, 0, ptr, totalBytes);
                image.UnlockBits(bmpData);

                // 写入处理后的帧
                writer.WriteVideoFrame(image);
            }
        }
    }
}

验证方式

修改后,你可以先不写入视频,而是直接在内存中检查修改后的像素值(比如用GetPixel查看image的像素),确认XOR操作生效后,再写入无损编码的视频,此时重新读取就能得到预期的像素值了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:16:40