2012-08-28 4 views
5

Ich versuche, meine Bilderkennungsklasse mit Lockbits zu erhöhen, aber das verursacht Probleme mit dem Code und daher wird es nicht ausgeführt. Wie kann ich Lockbits und getpixel gleichzeitig verwenden, um die Bilderfassung zu beschleunigen, oder kann mir jemand eine Alternative zeigen, die genauso schnell ist?Bildverarbeitung mit Lockbits, Alternative zu getpixel?

Code:

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

Ihre LockBits-Methode ist nutzlos ... es kopiert die Pixel in ein Byte-Array, aber Sie verwenden nie das Array –

+4

Der Sinn der Verwendung von LockBits ist ** mit GetPixel ** zu stoppen. –

Antwort

1

Ok wo ich anfangen soll. Besser verstehst du, was du mit lockBits tust. Stellen Sie zunächst sicher, dass Sie Ihr Byte-Array nicht mit überschreiben.

LockBits(small);    
LockBits(large); 

aufgrund der zweiten Anruf alle der erste Anruf tut, ist Ihr Bild Sperren und das ist nicht gut, da Sie es nicht wieder entsperren ist. Fügen Sie also ein weiteres Byte-Array hinzu, das das Bild darstellt. Sie können wie diese

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

etwas tun und Ihre Lockbits Methode ändern

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

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

wo PixelsLarge und PixelsSmall sind Globals und Pixel ist nicht Die 2 Ihr Bild enthalten. Jetzt musst du es vergleichen. Jetzt müssen Sie jeden "Satz von Bytes" vergleichen, daher müssen Sie das Pixelformat kennen. Ist es 32b/pix 24 oder nur 8 (ARGB, RGB, Graustufen) Nehmen wir ARGB-Bilder. In diesem Fall würde ein Satz aus 4 Bytes bestehen (= 32/8) Ich bin mir nicht sicher über die Reihenfolge, aber ich denke, die Reihenfolge eines Satzes ist ABGR oder BGRA.

Hoffe, das kann Ihnen helfen. Wenn Sie nicht herausfinden, wie Sie die richtigen Pixel vergleichen, fragen Sie erneut. Ah und vergiss nicht den UnlockBits Befehl zu benutzen.

4

Sie möchten sich nicht auf getPixel() für die Bildverarbeitung verlassen; Es ist in Ordnung, einen gelegentlichen Anruf zu tätigen, um einen Punktwert zu erhalten (z. B. bei mouseover), aber im Allgemeinen ist es besser, Bildverarbeitung im Bildspeicher oder in einem 2D-Array durchzuführen, das Sie bei Bedarf in eine Bitmap konvertieren können.

Um zu beginnen, könnten Sie versuchen, eine Methode zu schreiben, die LockBits/UnlockBits verwendet, um ein Array zu extrahieren, das bequem zu bearbeiten ist. Sobald Sie mit der Bearbeitung des Arrays fertig sind, können Sie es mit einer anderen LockBits/UnlockBits-Funktion wieder in eine Bitmap schreiben.

Hier ist ein Beispielcode, den ich in der Vergangenheit verwendet habe. Die erste Funktion gibt ein 1D-Array von Werten aus einer Bitmap zurück. Da Sie die Breite der Bitmap kennen, können Sie dieses 1D-Array zur weiteren Verarbeitung in ein 2D-Array konvertieren. Sobald Sie mit der Verarbeitung fertig sind, können Sie die zweite Funktion aufrufen, um das (modifizierte) 1D-Array wieder in ein Bitmap umzuwandeln.

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; 
} 

Diese Methoden machen Annahmen über das Bitmap-Pixelformat, das nicht für Sie arbeitet, aber ich hoffe, dass die allgemeine Idee ist klar: Verwendung LockBits/UnLockBits ein Array von Bytes aus einer Bitmap zu extrahieren, so dass Sie schreiben können und Debug-Algorithmen am einfachsten, und verwenden Sie dann erneut LockBits/UnlockBits, um das Array erneut in ein Bitmap zu schreiben.

Für Portabilität würde ich empfehlen, dass Ihre Methoden die gewünschten Datentypen zurückgeben, anstatt globale Variablen innerhalb der Methoden selbst zu manipulieren.

Wenn Sie getPixel() verwendet haben, kann die Konvertierung in/aus Arrays wie oben beschrieben Ihren Code für eine kleine Investition von Programmieraufwand erheblich beschleunigen.

+1

Du hast mir wenigstens einige Kopfschmerzen erspart, danke eine Tonne. –

+0

Sie sollten jedoch die Schrittweite der Zielbytes überprüfen und pro Zeile kopieren. Immerhin auf ein neues Objekt, das sich von den 8-Bit-Daten unterscheiden kann. Und wenn 'Array1DFromBitmap' die Daten nicht genau auf die Breite komprimiert, sollte sie definitiv diesen Schritt ausgeben oder die Daten können nicht korrekt verarbeitet werden. – Nyerguds