2012-08-28 58 views
5

我想增加我的图像检测类使用lockbits,但这会导致代码的问题,因此它不会运行。我怎样才能在同一时间使用lockbits和getpixel来加速图像检测,或者有人可以向我展示一种同样快速的替代方案?带lockbits的图像处理,替代getpixel?

代码:

static IntPtr Iptr = IntPtr.Zero; 
    static BitmapData bitmapData = null; 
    static public byte[] Pixels { get; set; } 
    static public int Depth { get; private set; } 
    static public int Width { get; private set; } 
    static public int Height { get; private set; } 

    static public void LockBits(Bitmap source) 

    { 
      // Get width and height of bitmap 
      Width = source.Width; 
      Height = source.Height; 

      // get total locked pixels count 
      int PixelCount = Width * Height; 

      // Create rectangle to lock 
      Rectangle rect = new Rectangle(0, 0, Width, Height); 

      // get source bitmap pixel format size 
      Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat); 


      // Lock bitmap and return bitmap data 
      bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, 
             source.PixelFormat); 

      // create byte array to copy pixel values 
      int step = Depth/8; 
      Pixels = new byte[PixelCount * step]; 
      Iptr = bitmapData.Scan0; 

      // Copy data from pointer to array 
      Marshal.Copy(Iptr, Pixels, 0, Pixels.Length); 

    } 


    static public bool SimilarColors(int R1, int G1, int B1, int R2, int G2, int B2, int Tolerance) 
    { 
     bool returnValue = true; 
     if (Math.Abs(R1 - R2) > Tolerance || Math.Abs(G1 - G2) > Tolerance || Math.Abs(B1 - B2) > Tolerance) 
     { 
      returnValue = false; 
     } 
     return returnValue; 
    } 


    public bool findImage(Bitmap small, Bitmap large, out Point location) 
    { 
     unsafe 
     { 
      LockBits(small); 
      LockBits(large); 
      //Loop through large images width 
      for (int largeX = 0; largeX < large.Width; largeX++) 
      { 
       //And height 
       for (int largeY = 0; largeY < large.Height; largeY++) 
       { 
        //Loop through the small width 
        for (int smallX = 0; smallX < small.Width; smallX++) 
        { 
         //And height 
         for (int smallY = 0; smallY < small.Height; smallY++) 
         { 
          //Get current pixels for both image 
          Color currentSmall = small.GetPixel(smallX, smallY); 
          Color currentLarge = large.GetPixel(largeX + smallX, largeY + smallY); 
          //If they dont match (i.e. the image is not there) 

          if (!colorsMatch(currentSmall, currentLarge)) 
           //Goto the next pixel in the large image 

           goto nextLoop; 
         } 
        } 
        //If all the pixels match up, then return true and change Point location to the top left co-ordinates where it was found 
        location = new Point(largeX, largeY); 
        return true; 
       //Go to next pixel on large image 
       nextLoop: 
        continue; 
       } 
      } 
      //Return false if image is not found, and set an empty point 
      location = Point.Empty; 
      return false; 
     } 
    } 
+0

你的LockBits方法是无用的......它将像素复制到一个字节数组,但你永远不会使用该数组 –

+4

使用LockBits的目的是**使用GetPixel停止**。 –

回答

1

确定从哪里开始。更好地理解你在用lockBits做什么。 首先确保你不覆盖你的字节数组。

LockBits(small);    
LockBits(large); 

由于第二次调用所有的第一个电话确实是锁定你的形象,因为你不会再次打开它,是不是好。 因此添加另一个表示图像的字节数组。 你可以做这样的事情

LockBits(small, true);    
LockBits(large, false); 

,改变你的Lockbits方法

static public void LockBits(Bitmap source, bool flag)       
{ 
... 
Marshal.Copy(Iptr, Pixels, 0, Pixels.Length); 

if(flag) 
    PixelsSmall=Pixels; 
else 
    PixelsLarge=Pixels; 
} 

其中PixelsLarge和PixelsSmall是全局和像素不 这2个包含图像。现在你必须对它进行比较。 现在你必须比较每个“字节集”,因此你必须知道像素格式。 它是32b/pix 24还是只有8(ARGB,RGB,灰度) 让我们拿ARGB图像。在这种情况下,一组将包含4个字节(= 32/8) 我不知道有关顺序,但我认为一组的顺序是ABGR或BGRA。

希望这可以帮助你。如果你不知道如何比较正确的像素,然后再问。啊,不要忘记使用UnlockBits命令。

4

您不想依赖getPixel()进行图像处理;可以偶尔调用一个点值(例如在鼠标悬停时),但通常情况下,最好在图像内存或某些二维数组中进行图像处理,并在必要时将其转换为位图。

首先,您可以尝试编写一个使用LockBits/UnlockBits来提取便于操作的数组的方法。一旦完成对数组的操作,就可以使用不同的LockBits/UnlockBits函数将其写回位图。

以下是我过去使用的一些示例代码。第一个函数从位图返回一维数组值。既然你知道位图的宽度,你可以将这个一维数组转换为二维数组进行进一步处理。一旦完成处理,您可以调用第二个函数将(已修改的)1D数组再次转换为位图。

public static byte[] Array1DFromBitmap(Bitmap bmp){ 
    if (bmp == null) throw new NullReferenceException("Bitmap is null"); 

    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
    BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat); 
    IntPtr ptr = data.Scan0; 

    //declare an array to hold the bytes of the bitmap 
    int numBytes = data.Stride * bmp.Height; 
    byte[] bytes = new byte[numBytes]; 

    //copy the RGB values into the array 
    System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numBytes); 

    bmp.UnlockBits(data); 

    return bytes;   
} 

public static Bitmap BitmapFromArray1D(byte[] bytes, int width, int height) 
{ 
    Bitmap grayBmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed); 
    Rectangle grayRect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height); 
    BitmapData grayData = grayBmp.LockBits(grayRect, ImageLockMode.ReadWrite, grayBmp.PixelFormat); 
    IntPtr grayPtr = grayData.Scan0; 

    int grayBytes = grayData.Stride * grayBmp.Height; 
    ColorPalette pal = grayBmp.Palette; 

    for (int g = 0; g < 256; g++){ 
     pal.Entries[g] = Color.FromArgb(g, g, g); 
    } 

    grayBmp.Palette = pal; 

    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, grayPtr, grayBytes); 

    grayBmp.UnlockBits(grayData); 
    return grayBmp; 
} 

这些方法使有关,可能不适合你的位图的像素格式的假设,但我希望的总体思路是明确的:使用LockBits/UnlockBits从位图中提取字节数组,这样你可以写并最容易调试算法,然后再次使用LockBits/UnlockBits将该数组再次写入Bitmap。

为便于携带,我建议您的方法返回所需的数据类型,而不是在方法本身内部操作全局变量。

如果您一直在使用getPixel(),那么如上所述转换到/从数组转换可以大大加快您的代码,只需少量编码工作。

+1

你救了我至少几次头痛,谢谢你。 –

+0

您应该检查目标字节的跨度,并且每行复制一次。毕竟,一个新的对象可能与您获得的8位数据不同。如果'Array1DFromBitmap'没有精确地将数据压缩到宽度,那么它肯定应该输出该跨度,否则数据将无法正确处理。 – Nyerguds