2010-09-10 13 views
7

Sto afferrando una parte dello schermo e scansionando i pixel per un determinato intervallo di colori.GetDIBits e loop through pixel utilizzando X, Y

Ho esaminato l'esempio MSDN's Capturing an Image e so come utilizzare le funzioni.

Posso ottenere i bit in un array, ma non sono sicuro di come farlo in modo tale da poterlo scorrere come se fosse un'immagine. Una pseudo-example (che sono sicuro che è lontano):

for (x = 1; x <= Image.Width; x += 3) 
{ 
    for (y = 1; y <= Image.Height; y += 3) 
    { 
     red = lpPixels[x]; 
     green = lpPixels[x + 1]; 
     blue = lpPixels[x + 2]; 
    } 
} 

Questo è fondamentalmente ciò che voglio fare, quindi, se rosso, blu e verde è un certo colore, saprò cosa di coordinate che è a (x, y) nell'immagine.

Semplicemente non so come utilizzare GetDIBits in questo modo e come impostare l'array in modo appropriato per essere in grado di farlo.

risposta

11

Oltre alle buone risposte già fornite, ecco un esempio di come ottenere una struttura di array semplice su cui camminare.(È possibile utilizzare ad esempio Goz' code per l'iterazione.)

GetDIBits reference @ MSDN

È necessario selezionare DIB_RGB_COLORS come bandiera per uUsage e impostare il BITMAPINFO structure e BITMAPINFOHEADER structure esso contiene. Quando si imposta biClrUsed e biClrImportant a zero, c'è una tabella dei colori "no", quindi è possibile leggere i pixel della bitmap ottenuta da GetDIBits come una sequenza di valori RGB. Utilizzando 32 come numero di bit (biBitCount) imposta la struttura di dati secondo MSDN:

La bitmap ha un massimo di 2^32 colori. Se il membro biCompression di BITMAPINFOHEADER è BI_RGB, il membro bmiColors di BITMAPINFO è NULL. Ogni DWORD nell'array bitmap rappresenta le intensità relative di blu, verde e rosso, rispettivamente, per un pixel. Il byte alto in ogni DWORD non viene utilizzato.

Dal momento che un MS LONG è esattamente a 32 bit lungo (delle dimensioni di un DWORD), non si deve prestare attenzione a imbottitura (come descritto nella).

Codice:

HDC hdcSource = NULL; // the source device context 
HBITMAP hSource = NULL; // the bitmap selected into the device context 

BITMAPINFO MyBMInfo = {0}; 
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader); 

// Get the BITMAPINFO structure from the bitmap 
if(0 == GetDIBits(hdcSource, hSource, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) 
{ 
    // error handling 
} 

// create the pixel buffer 
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage]; 

// We'll change the received BITMAPINFOHEADER to request the data in a 
// 32 bit RGB format (and not upside-down) so that we can iterate over 
// the pixels easily. 

// requesting a 32 bit image means that no stride/padding will be necessary, 
// although it always contains an (possibly unused) alpha channel 
MyBMInfo.bmiHeader.biBitCount = 32; 
MyBMInfo.bmiHeader.biCompression = BI_RGB; // no compression -> easier to use 
// correct the bottom-up ordering of lines (abs is in cstdblib and stdlib.h) 
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight); 

// Call GetDIBits a second time, this time to (format and) store the actual 
// bitmap data (the "pixels") in the buffer lpPixels 
if(0 == GetDIBits(hdcSource, hSource, 0, MyBMInfo.bmiHeader.biHeight, 
        lpPixels, &MyBMInfo, DIB_RGB_COLORS)) 
{ 
    // error handling 
} 
// clean up: deselect bitmap from device context, close handles, delete buffer 
+0

Nel codice c'è un'istruzione mancante che si inizializza su MyBMInfo .bmiHeader.biSize = sizeof (MyBMInfo); – xMRi

+0

@xMRi Hai ragione, grazie per aver individuato l'errore. Penso che sia corretto ora, ho spostato l'inizializzazione prima della prima chiamata a 'GetDIBits'; AFAIK non è necessario inizializzarlo di nuovo dopo la prima chiamata. – dyp

+0

Hmmm. Non ne sono sicuro. Ho provato questo nel mio codice e se faccio la seconda chiamata a GetDIBits ottengo un errore che la struttura locale ottiene un buffer Overflow nello stack ... Alla fine sono tornato a una soluzione dove inizializzo la struttura manualmente. In alcuni casi non si desidera avere una compressione. Resuscitare l'informazione riutilizzerà la compressione ... Non sono sicuro che ciò sia vero. Ma non ho avuto il tempo di controllarlo ulteriormente. – xMRi

2

Non è così semplice. Il tuo algoritmo dipenderà dalla profondità del colore dell'immagine. Se è pari o inferiore a 256, non avrai colori pixel, ma indi in una tavolozza di colori. I pixel a 16 bit potrebbero essere RGB555 o RGB565, le immagini a 24 bit saranno RGB888 e le immagini a 32 bit saranno RGBA o ARGB. Avrai bisogno del BITMAPINFOHEADER per scoprirlo.

Una volta a scoprire, i dati dei pixel sarà solo un array di larghezza di dimensioni * altezza * (BitsPerPixel/8)

+0

+1. In alternativa puoi provare copia/blit su una bitmap con profondità di colore fissa/nota. –

+0

Penso che l'OP selezioni la bitmap nel DeviceContext, così ha il pieno controllo sulla bitmap. Utilizzando 24 bit e nessuna compressione, è possibile leggerlo facilmente come RGB888. – dyp

9

GetDIBits restituisce un array monodimensionale di valori. Per una bitmap larga M pixel di N pixel alta e utilizza colori a 24 bit, i primi byte (M * 3) saranno la prima riga di pixel. Questo può essere seguito da alcuni byte di padding. Dipende dal BITMAPINFOHEADER. Di solito c'è il padding per rendere la larghezza un multiplo di 4 byte. Quindi se la tua bitmap è larga 33 pixel, ci saranno effettivamente (36 * 3) byte per riga.

Questo "pixel più padding" è chiamato "stride". Per bitmap RGB, è possibile calcolare il passo con: stride = (biWidth * (biBitCount/8) + 3) & ~3, dove biWidth e biBitCount vengono presi da BITMAPINFOHEADER.

Non sono sicuro di come si desidera attraversare la matrice. Se si vuole andare pixel-by-pixel da sinistra in alto a basso a destra (sempre che sia una bitmap top-down):

for (row = 0; row < Image.Height; ++row) 
{ 
    int rowBase = row*stride; 
    for (col = 0; col < Image.Width; ++col) 
    { 
     red = lpPixels[rowBase + col]; 
     // etc. 
    } 
} 
+0

e questo è terribile stride = (biWidth * (biBitCount/8) + 3) & ~3 - mi ci è voluto un po 'per capire (arrotonda al prossimo LONG = 4 [byte]). Ma un modo semplice per prestare attenzione al padding quando è necessario. – dyp

+1

finalmente un esempio che prende in considerazione ... invece di richiedere prima una conversione a 32 bit :) – rogerdpack

2

Nel link che pubblichi si crea una a 32-bit bitmap quindi mi supponi di stare leggendo da una bitmap a 32 bit (questa ipotesi potrebbe essere errata).

quindi cambiare il vostro ciclo per il seguente dovrebbe funzionare:

char* pCurrPixel = (char*)lpPixels; 
for (y = 0; y < Image.Height; y++) 
{ 
    for (x = 0; x < Image.Width; x++) 
    { 
     red = pCurrPixel[0]; 
     green = pCurrPixel[1]; 
     blue = pCurrPixel[2]; 

     pCurrPixel += 4; 
    } 
} 

cose da tenere a mente:

1.Arrays sono 0 con sede in C/C++
2. Eri entrando 3 pixel orizzontalmente e verticalmente ogni volta. Il che significa che non stai visitando ogni pixel.
3. Una bitmap di solito è organizzata in modo tale che ci siano "spanne" di altezza di "larghezza" pixel. Pertanto, è necessario scorrere ciascun pixel in un intervallo e quindi passare alla durata successiva.
4. Come già indicato, assicurarsi di leggere correttamente i pixel. nella modalità a 16 bit è più complesso

0

una certa sorpresa da MSDN:

La tabella è costituita da una matrice di strutture di dati RGBQUAD. (La tabella per il formato BITMAPCOREINFO è costruita con la struttura di dati RGBTRIPLE .) I byte rosso, verde e blu sono nell'ordine inverso (rosso posizioni swap con blu) dalla convenzione di Windows.

modo, i colori sono in ordine BGR in memoria dopo GetDIBits()

Problemi correlati