2010-09-10 11 views
7

Estoy capturando una parte de la pantalla y escaneando a través de los píxeles para un determinado rango de color.GetDIBits y recorrer los píxeles usando X, Y

Miré MSDN's Capturing an Image ejemplo y sé cómo utilizar las funciones.

Puedo obtener los bits en una matriz, pero no estoy seguro de cómo hacerlo de manera que pueda recorrerlo como lo haría con una imagen. Un pseudo-ejemplo (que estoy seguro que está muy lejos):

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

eso es básicamente lo que quiero hacer, por lo que si el rojo, el azul y el verde es un color determinado, sabré lo que coordinar sus at (x, y) en la imagen.

Simplemente no sé cómo usar GetDIBits de tal manera, y cómo configurar la matriz de forma adecuada para poder lograr esto.

Respuesta

11

Aparte de las buenas respuestas ya dadas, aquí hay un ejemplo de cómo para obtener una estructura de matriz simple sobre la que caminar.(Se puede utilizar por ejemplo Goz' code para la iteración.)

GetDIBits reference @ MSDN

Tienes que seleccionar DIB_RGB_COLORS como bandera para uUsage y configurar el BITMAPINFO structure y la BITMAPINFOHEADER structure que contiene. Cuando establece biClrUsed y biClrImportant en cero, hay una tabla de colores "no", por lo que puede leer los píxeles del mapa de bits que obtiene de GetDIBits como una secuencia de valores RGB. Usando 32 como recuento de bits (biBitCount) establece la estructura de datos de acuerdo con MSDN:

El mapa de bits tiene un máximo de 2^32 colores. Si el miembro biCompression del BITMAPINFOHEADER es BI_RGB, el miembro bmiColors de BITMAPINFO es NULL. Cada DWORD en la matriz de mapa de bits representa las intensidades relativas de azul, verde y rojo, respectivamente, para un píxel. El byte alto en cada DWORD no se utiliza.

Dado que una MS LONG es exactamente 32 bits de longitud (el tamaño de una DWORD), usted no tiene que prestar atención a el acolchado (como se describe en el).

Código:

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

En el código falta una instrucción que se inicializa en MyBMInfo .bmiHeader.biSize = sizeof (MyBMInfo); – xMRi

+0

@xMRi Tienes razón, gracias por detectar el error. Creo que ahora está arreglado, moví la inicialización antes de la primera llamada a 'GetDIBits'; AFAIK no es necesario reiniciarlo después de la primera llamada. – dyp

+0

Hmmm. No estoy seguro de eso. Intenté esto en mi código y si hago la segunda llamada a GetDIBits aparece un error que indica que la estructura local tiene un buffer Overflow en la pila ... Finalmente volví a una solución si inicializaba la estructura manualmente. En algunos casos, no desea tener una compresión. Reanudar la información reutilizará la compresión ... No estoy seguro si esto se cumple. Pero no tuve tiempo de verificar esto más. – xMRi

2

No es tan fácil. Su algoritmo dependerá de la profundidad de color de la imagen. Si es 256 o menos, no tendrá colores de píxeles, sino indecisos en una paleta de colores. Los píxeles de 16 bits pueden ser RGB555 o RGB565, las imágenes de 24 bits serán RGB888, y las imágenes de 32 bits serán RGBA o ARGB. Necesitará BITMAPINFOHEADER para averiguarlo.

Una vez que encuentre a cabo, los datos de píxeles simplemente será una matriz de tamaño ancho * altura * (BitsPerPixel/8)

+0

+1. Alternativamente, puede intentar copiar/corregir en un mapa de bits con profundidad de color fija/conocida. –

+0

Creo que OP selecciona el mapa de bits en DeviceContext, por lo que tiene control total sobre el mapa de bits. Usando 24 bit y sin compresión, permite leerlo como RGB888 fácilmente. – dyp

9

GetDIBits devuelve una matriz unidimensional de valores. Para un mapa de bits que tiene M píxeles de ancho por N píxeles de alto y usa color de 24 bits, los primeros (M * 3) bytes serán la primera fila de píxeles. Eso puede ser seguido por algunos bytes de relleno. Depende del BITMAPINFOHEADER. Generalmente hay relleno para hacer que el ancho sea un múltiplo de 4 bytes. Entonces, si su mapa de bits tiene 33 píxeles de ancho, en realidad habrá (36 * 3) bytes por fila.

Este "píxeles más relleno" se denomina "zancada". Para mapas de bits RGB, puede calcular la zancada con: stride = (biWidth * (biBitCount/8) + 3) & ~3, donde biWidth y biBitCount están tomados del BITMAPINFOHEADER.

No estoy seguro de cómo desea recorrer la matriz. Si quieres ir píxel por píxel desde la parte superior izquierda a la inferior derecha (suponiendo que esto es un mapa de bits de arriba hacia abajo):

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

eh esto es horrible stride = (biWidth * (biBitCount/8) + 3) & ~3 - me llevó un tiempo entenderlo (redondea al próximo LONG = 4 [bytes]). Pero una forma fácil de prestar atención al relleno cuando sea necesario. – dyp

+1

finalmente un ejemplo que toma en consideración ... en lugar de requerir una conversión a 32 bits primero :) – rogerdpack

2

En el enlace que publique se crea un mapa de bits de 32 bits así que voy supongamos que está leyendo desde un mapa de bits de 32 bits (esta suposición puede ser incorrecta).

Por lo tanto cambiar su bucle a la siguiente debe funcionar:

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

cosas a tener en cuenta:

1.Arrays son 0 con sede en C/C++
2. Usted estaba pisando 3 píxeles horizontal y verticalmente cada vez. Lo que significaba que no visitaste cada píxel.
3. Un mapa de bits generalmente está organizado de forma tal que hay tramos de "altura" de píxeles de "ancho". Por lo tanto, debe pasar por cada píxel en un lapso y luego pasar al siguiente lapso.
4. Como ya se indicó, asegúrese de estar leyendo correctamente los píxeles. en el modo de 16 bits es más complejo

0

Algunos sorpresa de MSDN:

La tabla consiste en un conjunto de estructuras de datos RGBQUAD. (La tabla para el formato BITMAPCOREINFO está construida con la estructura de datos RGBTRIPLE ). Los bytes rojo, verde y azul están en orden inverso (rojo posición de intercambio con azul) según la convención de Windows.

así, los colores están en orden BGR en la memoria después GetDIBits()

Cuestiones relacionadas