如何在不安全代码中设置图像像素时避免“噪声”

5
我正在一个C#的WinForms项目中使用"不安全"代码创建(然后修改)位图,大约每30ms执行一次。我遇到的问题是,在我没有特别更改任何内容的情况下,有时会在生成的位图中出现“噪点”或随机像素。
例如,我创建了一个100x100的位图。使用BitmapDataLockBits,我遍历位图并将某些像素更改为特定颜色。然后我UnlockBits并设置一个picturebox来使用该图像。我设置的所有像素都是正确的,但是我没有特别设置的像素有时会呈现似乎随机的颜色。
如果我设置每个像素,则噪音消失了。但是,出于性能原因,我更喜欢仅设置最少量的像素。
有人可以解释这是为什么吗?
以下是一些示例代码:
// Create new output bitmap
Bitmap Output_Bitmap = new Bitmap(100, 100);

// Lock the output bitmap's bits
Rectangle Output_Rectangle = new Rectangle(
    0,
    0,
    Output_Bitmap.Width,
    Output_Bitmap.Height);
BitmapData Output_Data = Output_Bitmap.LockBits(
    Output_Rectangle,
    ImageLockMode.WriteOnly,
    PixelFormat.Format32bppRgb);

const int PixelSize = 4;
unsafe
{
    for (int y = 0; y < Output_Bitmap.Height; y++)
    {
        for (int x = 0; x < Output_Bitmap.Width/2; x++)
        {
            Byte* Output_Row = (Byte*)Output_Data.Scan0 + y * Output_Data.Stride;
            Output_Row[(x * PixelSize) + 2] = 255;
            Output_Row[(x * PixelSize) + 1] = 0;
            Output_Row[(x * PixelSize) + 0] = 0;
        }
    }
}

// Unlock the bits
Output_Bitmap.UnlockBits(Output_Data);

// Set picturebox to use bitmap
pbOutput.Image = Output_Bitmap;

在这个例子中,我只设置了图像的左半部分(在内部for循环中是宽度/2)。右半部分将在纯黑背景上具有随机噪声。
2个回答

5
这有点推测性质,因为我不知道这些类的具体实现细节,但我有一个猜测。
当您调用 new Bitmap(100, 100) 时,代表位图像素的内存区域是 未初始化 的,因此包含在分配之前这些内存位置中的任何随机垃圾。第一次写入位图时,您仅设置了其中一部分位置,其他位置显示随机内存垃圾。
如果是这种情况,则必须确保在第一次更新新的 Bitmap 时写入 每个 像素。后续更新只需要更新更改的像素即可。

刚创建 Output_Bitmap 后,我也试了一个快速的 using (Graphics g = Graphics.FromImage(Output_Bitmap)) g.FillRectangle(Brushes.Black, 0, 0, Output_Bitmap.Width, Output_Bitmap.Height);,但结果仍然相同。 - JYelton
@JYelton,这可能行不通,因为您随后要求特定的像素格式,我可以想象如果它与默认值不同,则需要进行新的分配? - Doug McClean
我现在正在运行两个循环;一个用于将所有像素设置为黑色,另一个用于设置特定的像素。它似乎工作得很好,尽管性能显然受到了影响。@Doug 我会查看你提供的零内存方法,它可能取代第一个循环。谢谢。 - JYelton
@JYelton - 另一个您可以考虑的优化是仅在您打算更新的区域上调用 LockBits(但更新锁定的整个区域)。我怀疑问题实际上发生在内存与设备上下文之间的驱动程序转换时,通过锁定尽可能小的区域,您将节省大量时间。 - JSBձոգչ

3

在位图上创建一个图形对象,并在创建位图后放置Graphics.Clear()调用,以避免位图内存的未定义状态。

您还应该从使用Format32bppRgb更改为使用Format32PbppRgb,因为您没有设置alpha字节。或者切换到24 bpp格式。


位图对象没有 .Clear() 方法。 - JYelton
True,但是如果您在位图对象上创建一个图形对象并调用graphics.clear(color.black),则应该能够清除位图。 - OutputBitmap
另外,您是不是指的是 Format32bppPArgb 而不是 Format32PbppRgb - JYelton
使用24bpp可能会更好,因为我不需要alpha通道。感谢您的建议。然而,闪烁/随机像素仍然存在。 - JYelton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接