C# 如何将 get GetPixel / SetPixel 颜色处理转换为 Lockbits?

Mao*_*gue 2 c# image-processing lockbits getpixel argb

编辑:我非常感谢您的回复。这里我最需要的是示例代码,用于说明我如何处理嵌套循环中的几行代码,因为这是在 GetPixel/SetPixel 中可以正常工作的,但也是我无法使用 Lockbits 正常工作的。谢谢

我正在尝试将图像处理过滤器从 GetPixel / SetPixel 转换为 Lockbits,以缩短处理时间。 我也在 Stack Overflow、MSDN 和其他网站上看到过 Lockbits 教程,但我做错了。 我从一个非常简单的滤镜开始,它只是减少绿色以创建红色和紫色效果。这是我的代码:

   private void redsAndPurplesToolStripMenuItem_Click(object sender, EventArgs e)
    {
        // Get bitmap from picturebox
        Bitmap bmpMain = (Bitmap)pictureBoxMain.Image.Clone();

        // search through each pixel via x, y coordinates, examine and make changes. Dont let values exceed 255 or fall under 0.  
        for (int y = 0; y < bmpMain.Height; y++)
            for (int x = 0; x < bmpMain.Width; x++)
            {
                bmpMain.GetPixel(x, y);
                Color c = bmpMain.GetPixel(x, y);
                int myRed = c.R, myGreen = c.G, myBlue = c.B;
                myGreen -= 128;
                if (myGreen < 0) myGreen = 0; 
                bmpMain.SetPixel(x, y, Color.FromArgb(255, myRed, myGreen, myBlue));
            }

        // assign the new bitmap to the picturebox
        pictureBoxMain.Image = (Bitmap)bmpMain;

        // Save a copy to the HD for undo / redo.
        string myString = Environment.GetEnvironmentVariable("temp", EnvironmentVariableTarget.Machine);
        pictureBoxMain.Image.Save(myString + "\\ColorAppRedo.png", System.Drawing.Imaging.ImageFormat.Png);
    }
Run Code Online (Sandbox Code Playgroud)

因此 GetPixel / SetPixel 代码工作正常,但速度很慢。所以我尝试了这个:

    private void redsAndPurplesToolStripMenuItem_Click(object sender, EventArgs e)
    {
        // Get bitmap from picturebox
        Bitmap bmpMain = (Bitmap)pictureBoxMain.Image.Clone();

        Rectangle rect = new Rectangle(Point.Empty, bmpMain.Size); 
        BitmapData bmpData = bmpMain.LockBits(rect, ImageLockMode.ReadOnly, bmpMain.PixelFormat); 

        // search through each pixel via x, y coordinates, examine and make changes. Dont let values exceed 255 or fall under 0.  
        for (int y = 0; y < bmpMain.Height; y++)
            for (int x = 0; x < bmpMain.Width; x++)
            {
                bmpMain.GetPixel(x, y);
                Color c = new Color(); 
                int myRed = c.R, myGreen = c.G, myBlue = c.B;
                myGreen -= 128;
                if (myGreen < 0) myGreen = 0; 
                bmpMain.SetPixel(x, y, Color.FromArgb(255, myRed, myGreen, myBlue));

            }

        bmpMain.UnlockBits(bmpData); 

        // assign the new bitmap to the picturebox
        pictureBoxMain.Image = (Bitmap)bmpMain;

        // Save a copy to the HD for undo / redo.
        string myString = Environment.GetEnvironmentVariable("temp", EnvironmentVariableTarget.Machine);
        pictureBoxMain.Image.Save(myString + "\\ColorAppRedo.png", System.Drawing.Imaging.ImageFormat.Png);
    } 
Run Code Online (Sandbox Code Playgroud)

当它到达嵌套循环的第一行时, 会抛出错误“System.Drawing.dll 中发生了类型为‘System.InvalidOperationException’的未处理异常附加信息:位图区域已被锁定”。

我意识到这一定是初学者的错误,如果有人能够演示将这个非常简单的过滤器转换为 Lockbits 的正确方法,我将不胜感激。非常感谢

iul*_*000 5

scan0 返回的数组采用以下格式 BGRA BGRA BGRA BGRA ... 等等,其中 B = 蓝色、G = 绿色、R = 红色、A = Alpha。

4 像素宽、3 像素高的非常小的位图示例。

BGRA BGRA BGRA BGRA
BGRA BGRA BGRA BGRA
BGRA BGRA BGRA BGRA 

stride = width*bytesPerPixel = 4*4 = 16 bytes
height = 3
maxLenght = stride*height= 16*3 = 48 bytes
Run Code Online (Sandbox Code Playgroud)

要达到图像中的某个像素 (x, y),请使用以下公式

int certainPixel = bytesPerPixel*x + stride * y;
B = scan0[certainPixel + 0];
G = scan0[certainPixel + 1];
R = scan0[certainPixel + 2];
A = scan0[certainPixel + 3];
Run Code Online (Sandbox Code Playgroud)
    public unsafe void Test(Bitmap bmp)
    {
        int width = bmp.Width;
        int height = bmp.Height;
        //TODO determine bytes per pixel
        int bytesPerPixel = 4; // we assume that image is Format32bppArgb
        int maxPointerLenght = width * height * bytesPerPixel;
        int stride = width * bytesPerPixel;
        byte R, G, B, A;

        BitmapData bData = bmp.LockBits(
            new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
            ImageLockMode.ReadWrite, bmp.PixelFormat);


        byte* scan0 = (byte*)bData.Scan0.ToPointer();

        for (int i = 0; i < maxPointerLenght; i += 4)
        {
            B = scan0[i + 0];
            G = scan0[i + 1];
            R = scan0[i + 2];
            A = scan0[i + 3];

            // do anything with the colors
            // Set the green component to 0
            G = 0;
            // do something with red
            R = R < 54 ? (byte)(R + 127) : R;
            R = R > 255 ? 255 : R;
        }


        bmp.UnlockBits(bData);
    }
Run Code Online (Sandbox Code Playgroud)

你可以自己测试一下。在绘画或任何其他程序中创建一个非常小的位图(几个像素宽/高),并在方法的开头放置一个断点。