2010-09-10 18 views
7

Przechodzę przez część ekranu i skanuję piksele w określonym zakresie kolorów.GetDIBits i przechodzenie między pikselami za pomocą X, Y

Spojrzałem na przykład MSDN's Capturing an Image i wiem, jak korzystać z funkcji.

Mogę dostać bity do tablicy, ale nie jestem pewien, jak to zrobić w taki sposób, że mogę przechodzić przez niego tak jak ja obraz. Pseudo-przykład (który jestem pewien, jest daleko):

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

To w zasadzie to, co chcę zrobić, więc jeśli czerwony, niebieski i zielony jest pewien kolor, będę wiedział co koordynować to na (x, y) na obrazie.

Po prostu nie wiem, jak używać GetDIBits w taki sposób i jak poprawnie skonfigurować tablicę, aby móc to zrobić.

Odpowiedz

11

Oprócz dobrych odpowiedzi już podanych, oto przykład jak dostać prostą strukturę tablicy iść dalej.(Można użyć np Goz' code dla iteracji.)

GetDIBits reference @ MSDN

Musisz wybrać DIB_RGB_COLORS jak flaga dla uUsage i skonfigurować BITMAPINFO structure i BITMAPINFOHEADER structure zawiera. Gdy ustawisz biClrUsed i biClrImportant na zero, pojawi się tabela kolorów "nie", dzięki czemu możesz odczytać piksele mapy bitowej otrzymanej z GetDIBits jako sekwencję wartości RGB. Korzystanie 32 jako liczby bitów (biBitCount) tworzy strukturę danych według MSDN:

bitowa ma maksymalnie 2^32 kolorów. Jeśli element biCompression elementu BITMAPINFOHEADER jest BI_RGB, element z BITMAPINFO jest NULL. Każdy DWORD w macierzy bitmap reprezentuje względne intensywności odpowiednio niebieskiego, zielonego i czerwonego piksela. Wysoki bajt w każdym DWORD nie jest używany.

Ponieważ MS LONG ma długość dokładnie 32 bit (wielkości DWORD), nie trzeba zwracać uwagę na obicia (jak opisano w).

Kod:

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

W kodzie brakuje brakującej instrukcji, która inicjuje MyBMInfo .bmiHeader.biSize = sizeof (MyBMInfo); – xMRi

+0

@xMRi Masz rację, dziękuję za zauważenie błędu. Myślę, że to jest naprawione teraz, przeniosłem inicjalizację przed pierwszym wywołaniem 'GetDIBits'; AFAIK nie wymaga ponownej inicjalizacji po pierwszym połączeniu. – dyp

+0

Hmmm. Nie jestem tego pewny. Próbowałem tego w moim kodzie i jeśli wykonam drugie wywołanie funkcji GetDIBits, otrzymuję komunikat o błędzie, że lokalna struktura otrzymuje bufor Overflow na stosie ... W końcu powróciłem do rozwiązania, gdybym ręcznie zainicjował strukturę. W niektórych przypadkach nie chcesz mieć kompresji. Odrzucenie informacji spowoduje ponowne użycie kompresji ... Nie jestem pewien, czy to prawda. Ale nie miałem czasu, aby to sprawdzić dalej. – xMRi

2

To nie takie proste. Twój algorytm będzie zależeć od głębi kolorów obrazu. Jeśli jest to 256 lub mniej, nie będziesz miał kolorów w pikselach, ale zmieni się w paletę kolorów. 16-bitowe piksele mogą być RGB555 lub RGB565, 24-bitowe obrazy będą RGB888, a 32-bitowe obrazy będą RGBA lub ARGB. Będziesz potrzebował BITMAPINFOHEADER, aby się dowiedzieć.

Po dowiedzieć się, dane pikseli będzie tylko tablicą rozmiar szerokość * wysokość * (BitsPerPixel/8)

+0

+1. Alternatywnie możesz spróbować skopiować/blit do bitmapy z ustaloną/znaną głębią kolorów. –

+0

Myślę, że OP wybiera bitmapę do DeviceContext, więc ma pełną kontrolę nad bitmapą. Użycie 24 bitów bez kompresji pozwala na łatwe odczytanie go jako RGB888. – dyp

9

GetDIBits zwraca jednowymiarową tablicę wartości. W przypadku bitmapy o szerokości M pikseli o wysokości N pikseli i 24-bitowym kolorze pierwsze (M * 3) bajty będą pierwszym rzędem pikseli. Po tym mogą wystąpić niektóre bajty dopełnienia. To zależy od BITMAPINFOHEADER. Zwykle jest wypełnienie, aby szerokość była wielokrotnością 4 bajtów. Jeśli więc twoja mapa bitowa ma szerokość 33 pikseli, faktycznie będzie (36 * 3) bajtów na wiersz.

Ta "piksele plus dopełnienie" nazywa się "krokiem". W przypadku bitmap RGB można obliczyć krok z: stride = (biWidth * (biBitCount/8) + 3) & ~3, gdzie biWidth i biBitCount są pobierane z BITMAPINFOHEADER.

Nie jestem pewien, jak chcesz przejść przez tablicę. Jeśli chcesz iść piksel po pikselu od góry od lewej do obniżenia prawo (przy założeniu, że jest to mapa bitowa 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

huh to jest okropne stride = (biWidth * (biBitCount/8) + 3) & ~3 - zajęło mi trochę czasu, aby zrozumieć (zaokrągla do następnego LONG = 4 [bajty]). Ale łatwy sposób, aby zwrócić uwagę na wypełnienie, gdy trzeba. – dyp

+1

wreszcie przykład, który bierze pod uwagę ... zamiast wymagać konwersji na 32-bitowe pierwsze :) – rogerdpack

2

w linku publikowanych utworzyć 32-bitową bitmapę więc będę załóżmy, że czytasz z 32-bitowej bitmapy (to założenie może być nieprawidłowe).

Dlatego zmieniając pętlę na następujące powinny działać:

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

Rzeczy pamiętać:

1.Arrays wynoszą 0 siedzibą w C/C++
2. Zostałaś intensywniejszej 3 piksele poziomo i pionowo za każdym razem. Co oznaczało, że nie odwiedzasz każdego piksela.
3. Mapa bitowa jest zazwyczaj zorganizowana w taki sposób, że występują "wysokości" rozpiętości pikseli "szerokości". Dlatego powinieneś przejść przez każdy piksel w przęśle, a następnie przejść do następnego zakresu.
4. Jak już wskazano, upewnij się, że prawidłowo odczytujesz piksele. w trybie 16-bitowym jej bardziej złożonej

0

Niektóre niespodzianka z MSDN:

tabela składa się z tablicy struktur danych RGBQUAD. (Tabela dla formatu BITMAPCOREINFO jest zbudowana na podstawie struktury RGBTRLE danych .) Czerwone, zielone i niebieskie bajty są w odwrotnej kolejności (czerwony zamieniają się miejscami na niebiesko) z konwencji Windows.

tak, kolory są BGR celu w pamięci po GetDIBits()

Powiązane problemy