2012-08-28 15 views
5

Sto cercando di aumentare la mia classe di rilevamento delle immagini usando i lockbit, tuttavia questo causa problemi con il codice e quindi non funziona. Come posso utilizzare i lockbits e getpixel allo stesso tempo per accelerare il rilevamento delle immagini o qualcuno può mostrarmi un'alternativa altrettanto veloce?Elaborazione delle immagini con lockbits, alternativa a getpixel?

codice:

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

Il metodo LockBits è inutile ... copia i pixel in un array di byte, ma non si utilizza mai quell'array –

+4

Il punto di utilizzo di LockBits è di ** interrompere ** utilizzando GetPixel. –

risposta

1

Ok da dove cominciare. Meglio capisci cosa stai facendo con lockBits. Prima di tutto assicurati di non sovrascrivere il tuo array di byte con.

LockBits(small);    
LockBits(large); 

a causa della seconda chiamata tutta la prima chiamata non fa altro che bloccare la vostra immagine e che non è buona dal momento che non si sblocca di nuovo. Quindi aggiungi un altro array di byte che rappresenta l'immagine. Si può fare qualcosa di simile

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

e cambiare il metodo di LockBits

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

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

dove PixelsLarge e PixelsSmall sono globali e Pixel non è Coloro 2 contengono l'immagine. Ora devi confrontarlo. Ora devi confrontare ogni "serie di byte", quindi devi conoscere il Pixelformat. È 32b/pix 24 o solo 8 (ARGB, RGB, scala di grigi) Prendiamo le immagini ARGB. In questo caso un set sarebbe composto da 4 byte (= 32/8) Non sono sicuro dell'ordine, ma penso che l'ordine di un set sia ABGR o BGRA.

Spero che questo possa aiutarti. Se non capisci come confrontare i pixel giusti, chiedi di nuovo. Ah e non dimenticarti di usare il comando UnlockBits.

4

Non si vorrebbe fare affidamento su getPixel() per l'elaborazione delle immagini; va bene effettuare una chiamata occasionale per ottenere un valore in punti (ad esempio durante il passaggio del mouse), ma in generale è preferibile eseguire l'elaborazione delle immagini nella memoria immagini o in alcuni array 2D che è possibile convertire in una bitmap quando necessario.

Per iniziare, si potrebbe provare a scrivere un metodo che utilizza LockBits/UnlockBits per estrarre un array che è conveniente manipolare. Una volta terminato di manipolare l'array, è possibile riportarlo in una bitmap utilizzando una funzione LockBits/UnlockBits diversa.

Ecco alcuni esempi di codice che ho utilizzato in passato. La prima funzione restituisce una matrice 1D di valori da una bitmap. Poiché si conosce la larghezza della bitmap, è possibile convertire questo array 1D in un array 2D per un'ulteriore elaborazione. Una volta terminata l'elaborazione, è possibile chiamare la seconda funzione per convertire nuovamente l'array 1D (modificato) in una bitmap.

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

Questi metodi rende ipotesi circa il formato di pixel bitmap che non può funzionare per voi, ma spero l'idea generale è chiara: uso LockBits/UnLockBits per estrarre un array di byte da una bitmap in modo che si può scrivere e esegui il debug degli algoritmi più facilmente, quindi usa nuovamente LockBits/UnlockBits per scrivere di nuovo l'array su una bitmap.

Per la portabilità, è consigliabile che i metodi restituiscano i tipi di dati desiderati anziché manipolare le variabili globali all'interno dei metodi stessi.

Se si utilizza getPixel(), la conversione in/dagli array come descritto in precedenza potrebbe velocizzare notevolmente il codice per un piccolo investimento nello sforzo di codifica.

+1

Mi hai salvato almeno parecchi mal di testa, grazie mille. –

+0

Dovresti comunque controllare il passo dei byte di destinazione e copiare per riga. Dopo tutto, su un nuovo oggetto che potrebbe differire dai dati a 8 bit ottenuti. E se 'Array1DFromBitmap' non compatta i dati esattamente alla larghezza, dovrebbe sicuramente produrre quel passo, oppure i dati non possono essere elaborati correttamente. – Nyerguds

Problemi correlati