【问题标题】:How can I color pixels that are not black in bitmap in yellow using LockBits?如何使用 LockBits 将位图中非黑色的像素着色为黄色?
【发布时间】:2014-09-22 14:31:22
【问题描述】:

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

【问题讨论】:

  • 我很困惑。您的标题谈到了 GetPixel 的性能(臭名昭著),但您似乎没有在代码中使用 GetPixel。
  • 更改了标题。我想知道如何使用 LockBits 来做到这一点。将所有非黑色像素着色为黄色。如果像素为黑色,则将其保留为黑色,否则将其着色为黄色。
  • 听起来像是彩色矩阵的工作。 msdn.microsoft.com/en-us/library/6tf7sa87(v=vs.110).aspx

标签: c# .net winforms lockbits


【解决方案1】:

简单地测试字节并相应地设置它们怎么样?

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;

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

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

【讨论】:

    【解决方案2】:

    以下内容应该适用于您正在使用的未索引 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 结构可能会非常慢。

    【讨论】:

      猜你喜欢
      • 2014-10-28
      • 1970-01-01
      • 1970-01-01
      • 2019-12-13
      • 1970-01-01
      • 2015-05-05
      • 2017-02-05
      • 2015-02-04
      • 1970-01-01
      相关资源
      最近更新 更多