如何使用LockBits将位图中非黑色的像素着色为黄色

本文关键字:像素 黄色 黑色 LockBits 何使用 位图 | 更新日期: 2023-09-27 18:23:51

使用GetPixel和SetPixel很容易,但速度很慢,所以我尝试使用LockBits。

我有这个方法,我做了很久以前比较两个图像:

public static Bitmap FastComparison(Bitmap bmp1,Bitmap bmp2)
    {
       tolerancenumeric = 15;
       int tolerance = tolerancenumeric * tolerancenumeric + 
                       tolerancenumeric * tolerancenumeric + 
                       tolerancenumeric * tolerancenumeric; //dr * dr + dg * dg + db * db;
       bmp3 = new Bitmap(512,512);  
       PixelFormat pxf = PixelFormat.Format24bppRgb;
       Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
       BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadWrite, pxf);
       BitmapData bmpData2 = bmp2.LockBits(rect, ImageLockMode.ReadWrite, pxf);
       BitmapData bmpData3 = bmp3.LockBits(rect, ImageLockMode.ReadWrite, pxf);
       IntPtr ptr1 = bmpData1.Scan0;
       IntPtr ptr2 = bmpData2.Scan0;
       IntPtr ptr3 = bmpData3.Scan0;
       int numBytes = bmpData1.Stride * bmp1.Height;
       byte[] rgbValues1 = new byte[numBytes];
       Marshal.Copy(ptr1, rgbValues1, 0, numBytes);
       bmp1.UnlockBits(bmpData1);
       byte[] rgbValues2 = new byte[numBytes];
       Marshal.Copy(ptr2, rgbValues2, 0, numBytes);
       bmp2.UnlockBits(bmpData2);

       for (int counter = 0; counter < rgbValues1.Length; counter += 3)
       {
          int  dr, dg, db;
          dr = (int)rgbValues2[counter] - (int)rgbValues1[counter];
          dg = (int)rgbValues2[counter + 1] - (int)rgbValues1[counter + 1];
          db = (int)rgbValues2[counter + 2] - (int)rgbValues1[counter + 2];
          int error = dr * dr + dg * dg + db * db;
          int y, x;
          y = (counter / 3) / 512;
          x = (counter - y * 512 * 3)/3;
          if ((x == 479) && (y == 474))
          {
             Byte r1, g1, b1, r2, g2, b2;
             r1 = rgbValues1[counter];
             b1 = rgbValues1[counter+1];
             g1 = rgbValues1[counter+2];
             r2 = rgbValues2[counter];
             b2 = rgbValues2[counter+1];
             g2 = rgbValues2[counter+2];
           }
           if (error < tolerance)
           {
             rgbValues1[counter] = 0; 
             rgbValues1[counter + 1] = 0;
             rgbValues1[counter + 2] = 0;
           }
         }
         Marshal.Copy(rgbValues1, 0, ptr3, numBytes);
         bmp3.UnlockBits(bmpData3);
         return bmp3;
       }

但现在我也想使用LockBits,但只使用一个图像,并将所有非黑色像素涂成黄色。

我开始了新的方法:

public Bitmap ChangeColors(Bitmap bmp1)
        {
            bmpColors = new Bitmap(512, 512);
            PixelFormat pxf = PixelFormat.Format24bppRgb;
            Rectangle rect = new Rectangle(0, 0, bmp1.Width, bmp1.Height);
            BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadWrite, pxf);
            IntPtr ptr1 = bmpData1.Scan0;
            int numBytes = bmpData1.Stride * bmp1.Height;
            byte[] rgbValues1 = new byte[numBytes];
            Marshal.Copy(ptr1, rgbValues1, 0, numBytes);
            bmp1.UnlockBits(bmpData1);
            for (int counter = 0; counter < rgbValues1.Length; counter += 3)
            {
                int y, x;
                y = (counter / 3) / 512;
                x = (counter - y * 512 * 3) / 3;
                Byte r1, g1, b1;
                r1 = rgbValues1[counter];
                b1 = rgbValues1[counter + 1];
                g1 = rgbValues1[counter + 2];
            }
            return bmpColors;
        }

但不确定如何制作,因此位图bmpColors将是原始的,但所有像素都不是黄色中的黑色。

如何使用LockBits将位图中非黑色的像素着色为黄色

简单地测试字节并相应地设置它们如何?

Byte r1, g1, b1;
r1 = rgbValues1[counter];         // should be + 2 !!
b1 = rgbValues1[counter + 1];     // should be + 0 !!
g1 = rgbValues1[counter + 2];     // should be + 1 !!
if (r1 + b1 + g1 == 0 ) 
{
    r1 = 255;
    g1 = 255;
}

当然,这是假设一个真正的黑色和一个基本的黄色对你来说是可以的。。

如果你需要更多的控制,你需要更多像这样的代码

if (r1 + b1 + g1 < threshold)

对于黑色色调,以及可能的黄色:

    r1 = myYellow_R;
    g1 = myYellow_G;
    b1 = myYellow_B;

BTW:您需要检查这些指数;上次我查看LockBits数组中的数据时被反转了:不是RGB(更不用说RBG了),而是BGR!(对于32bpp BGRA!)

由于您使用的是旧方法,请确保您的像素格式是可以的;如果是32bpp,则需要进行一些简单的修改。

以下内容应该适用于具有未索引的8字节颜色的格式,这就是您正在使用的:

public static class BitmapHelper
{
    //http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Advanced/PixelFormat.cs
    //http://msdn.microsoft.com/en-us/magazine/cc534995.aspx#id0070035
    private static void GetPixelFormatData(this PixelFormat pixelFormat, out byte bitsPerPixel, out byte bitsPerChannel, out bool hasAlpha, out bool premultiplyAlpha)
    {
        switch (pixelFormat)
        {
            //Specifies that the format is 24 bits per pixel; 8 bits each are used for the red, green, and blue components.
            case PixelFormat.Format24bppRgb:
                ///       Specifies that pixel format is 24 bits per pixel. The
                ///       color information specifies 16777216 shades of color of which 8 bits are red, 8
                ///       bits are green and 8 bits are blue.
                bitsPerPixel = 24;
                bitsPerChannel = 8;
                hasAlpha = false;
                premultiplyAlpha = false;
                break;
            //Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components.
            case PixelFormat.Format32bppArgb:
                bitsPerPixel = 32;
                bitsPerChannel = 8;
                hasAlpha = true;
                premultiplyAlpha = false;
                break;
            //Specifies that the format is 32 bits per pixel; 8 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied, according to the alpha component.
            case PixelFormat.Format32bppPArgb:
                bitsPerPixel = 32;
                bitsPerChannel = 8;
                hasAlpha = true;
                premultiplyAlpha = true;
                break;
            //Specifies that the format is 32 bits per pixel; 8 bits each are used for the red, green, and blue components. The remaining 8 bits are not used.
            case PixelFormat.Format32bppRgb:
                bitsPerPixel = 32;
                bitsPerChannel = 8;
                hasAlpha = false;
                premultiplyAlpha = false;
                break;
            //Specifies that the format is 48 bits per pixel; 16 bits each are used for the red, green, and blue components.
            case PixelFormat.Format48bppRgb:
                bitsPerPixel = 48;
                bitsPerChannel = 16;
                hasAlpha = false;
                premultiplyAlpha = false;
                break;
            //Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components.
            case PixelFormat.Format64bppArgb:
                bitsPerPixel = 64;
                bitsPerChannel = 16;
                hasAlpha = true;
                premultiplyAlpha = false;
                break;
            //Specifies that the format is 64 bits per pixel; 16 bits each are used for the alpha, red, green, and blue components. The red, green, and blue components are premultiplied according to the alpha component.
            case PixelFormat.Format64bppPArgb:
                bitsPerPixel = 64;
                bitsPerChannel = 16;
                hasAlpha = true;
                premultiplyAlpha = true;
                break;
            default:
                throw new ArgumentException("Unsupported Pixel Format " + pixelFormat.ToString());
        }
    }
    // Derived by experimentation.
    const int BlueIndex = 0;
    const int GreenIndex = 1;
    const int RedIndex = 2;
    const int AlphaIndex = 3;
    public delegate bool TransformColorFunc(ref byte r, ref byte g, ref byte b, ref byte a);
    public static void PaintSafe(Bitmap bmp, TransformColorFunc filter)
    {
        BitmapData bData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
        try
        {
            byte bitsPerPixel;
            byte bitsPerChannel;
            bool hasAlpha;
            bool premultiplyAlpha;
            bmp.PixelFormat.GetPixelFormatData(out bitsPerPixel, out bitsPerChannel, out hasAlpha, out premultiplyAlpha);
            if (bitsPerChannel != 8)
                throw new ArgumentException();
            if ((bitsPerPixel % 8) != 0)
                throw new ArgumentException();
            if ((!hasAlpha && bitsPerPixel < 24) || (hasAlpha && bitsPerPixel < 32))
                throw new ArgumentException();
            int size = bData.Stride * bData.Height;
            byte[] data = new byte[size];
            System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);
            for (int iPixel = 0; iPixel < size; iPixel += bitsPerPixel / 8)
            {
                // Format is b, g, r, [a if present.]
                byte b = data[iPixel + BlueIndex];
                byte g = data[iPixel + GreenIndex];
                byte r = data[iPixel + RedIndex];
                byte a;
                if (hasAlpha)
                    a = data[iPixel + AlphaIndex];
                else
                    a = 255;
                if (filter(ref r, ref g, ref b, ref a))
                {
                    // Format is b, g, r, [a if present.]
                    data[iPixel + BlueIndex] = b;
                    data[iPixel + GreenIndex] = g;
                    data[iPixel + RedIndex] = r;
                    if (hasAlpha)
                        data[iPixel + AlphaIndex] = a;
                }
            }
            System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);
        }
        finally
        {
            bmp.UnlockBits(bData);
        }
    }
}

然后称之为:

    static bool TransformNonBlackToYellow(ref byte r, ref byte g, ref byte b, ref byte a)
    {
        if (r != 0 || g != 0 || b != 0)
        {
            r = 255;
            g = 255;
            b = 0;
            a = 255;
            return true;
        }
        return false;
    }
    BitmapHelper.PaintSafe(bitmap, TransformNonBlackToYellow);

注意,我没有让我的过滤器函数采用Color,因为从Argb值构建Color结构可能会非常慢。