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

时间:2014-09-22 14:31:23

标签: c# .net winforms lockbits

使用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成为原始的,但所有像素都不是黄色的黑色。

2 个答案:

答案 0 :(得分:3)

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

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,您需要进行一些简单的修改。

答案 1 :(得分:0)

以下内容适用于具有未编入索引的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结构可能会非常慢。