Travel Through Pixels in Bmp

Travel through pixels in BMP

When you want to doing image processing
on huge images GetPixel() method takes long time
but I think my algorithm takes less
time than other answers , for example
you can test this code on 800 * 600
pixels image.


Bitmap bmp = new Bitmap("SomeImage");

// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
byte[] r = new byte[bytes / 3];
byte[] g = new byte[bytes / 3];
byte[] b = new byte[bytes / 3];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

int count = 0;
int stride = bmpData.Stride;

for (int column = 0; column < bmpData.Height; column++)
{
for (int row = 0; row < bmpData.Width; row++)
{
b[count] = (byte)(rgbValues[(column * stride) + (row * 3)]);
g[count] = (byte)(rgbValues[(column * stride) + (row * 3) + 1]);
r[count++] = (byte)(rgbValues[(column * stride) + (row * 3) + 2]);
}
}

Exception on traveling through pixels BMP C#

Just change Height and Width. This is such an example of looking too far in your own code - this brings back so many memories..

for(int i=0;i<BMP.Height;i++)
{
for(int j=0;j<BMP.Width;j++)
{
color = BMP.GetPixel(j,i);
}
}

What is the most efficient way to loop through picture pixels

from https://stackoverflow.com/a/6094092/1856345

Bitmap bmp = new Bitmap("SomeImage");

// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
byte[] r = new byte[bytes / 3];
byte[] g = new byte[bytes / 3];
byte[] b = new byte[bytes / 3];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

int count = 0;
int stride = bmpData.Stride;

for (int column = 0; column < bmpData.Height; column++)
{
for (int row = 0; row < bmpData.Width; row++)
{
b[count] = rgbValues[(column * stride) + (row * 3)];
g[count] = rgbValues[(column * stride) + (row * 3) + 1];
r[count++] = rgbValues[(column * stride) + (row * 3) + 2];
}
}

Converting a Bitmap into RGB Pixels

Look at this link:
http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx
Careful with images in 24 bit, because (if I remember it good) they need line align to 4 bytes.

Scan BitMap for Specific pixel color, and give x, y location in c++

hBitmap = (HBITMAP)SelectObject(hdcMemory, hBitmapOld);

That's going to overwrite hBitmap, you don't want that. You should change that line to:

SelectObject(hdcMemory, hBitmapOld);

Then you can use GetDIBits

Also you have already copied the bitmap to memory dc. You can use GetPixel to obtain the color. There is no need to call GetDIBits

HBITMAP hBitmapOld = (HBITMAP)SelectObject(hdcMemory, hBitmap);
COLORREF color = GetPixel(hdcMemory, x, y);
BYTE red = GetRValue(color);
BYTE green = GetGValue(color);
BYTE blue = GetBValue(color);
...
SelectObject(hdcMemory, hBitmapOld);
DeleteObject(hBitmap);
DeleteDC(hdcMemory);
RleaseDC(0, hdcSource);

When you are finished also call DeleteObject(hBitmap)

Using GetDIBits: 32-bit bitmap format is BGRA not RGBA, therefore the first byte is blue, not red. You should specifically request 32-bit bitmap or check the bit-count before treating the bits as 32-bit bitmap.

#include <iostream>
#include <vector>
#include <Windows.h>

int main()
{
HWND target = FindWindow("Notepad", 0);
if(!target)
{
printf("error, no window\n");
return 0;
}

RECT rc;
GetWindowRect(target, &rc);
int x = rc.left;
int y = rc.top;
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;

int screen_w = GetSystemMetrics(SM_CXFULLSCREEN);
int screen_h = GetSystemMetrics(SM_CYFULLSCREEN);

HDC hdc = GetDC(HWND_DESKTOP);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, screen_w, screen_h);
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, w, h, hdc, x, y, CAPTUREBLT | SRCCOPY);
SelectObject(memdc, oldbmp);

BITMAPINFOHEADER infohdr = { sizeof(infohdr), w, h, 1, 32 };
int size = w * h * 4;
std::vector<BYTE> bits(size);
int res = GetDIBits(hdc, hbitmap, 0, h, &bits[0],
(BITMAPINFO*)&infohdr, DIB_RGB_COLORS);
if(res != h)
{
std::cout << "error\n";
return 0;
}

BYTE *ptr = bits.data();
//for(y = 0; y < h; y++)
for(y = h - 1; y >= 0; y--) //bitmaps bits start from bottom, not top
{
for(x = 0; x < w; x++)
{
BYTE blu = ptr[0];
BYTE grn = ptr[1];
BYTE red = ptr[2];
ptr += 4;
}
}

SelectObject(memdc, oldbmp);
DeleteObject(hbitmap);
ReleaseDC(HWND_DESKTOP, hdc);
DeleteDC(memdc);

return 0;
}

Note, the process should be DPI aware in case user is not using default DPI settings.

How to get a pixel array from TBitmap?

This is just an addon to existing answer (with additional info after the OP edit)

Bitmap file-format has align bytes on each row (so there usually are some bytes at the end of each line that are not pixels) up to some ByteLength (present in bmp header). Those create the skew and diagonal like lines. In your case the size discrepancy is 4 bytes per row:

(xs + align)*ys  + header = size
(658+ 4)*492 + 94 = 325798

but beware the align size depends on image width and bmp header ...

Try this instead:

    // create bmp
Graphics::TBitmap *bmp=new Graphics::TBitmap;
// bmp->Assign(???); // a) copy image from ???
bmp->SetSize(658,492); // b) in case you use Assign do not change resolution
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf8bit;
// bmp->Canvas->Draw(0,0,???); // b) copy image from ???
// here render your text using
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->Brush->Color=clWhite;
bmp->Canvas->Font->Color=clBlack;
bmp->Canvas->Font->Name = "Tahoma";
bmp->Canvas->Font->Size = 8;
bmp->Canvas->TextOutA(5,5,"Text");
// Byte data
for (int y=0;y<bmp->Height;y++)
{
BYTE *p=(BYTE*)bmp->ScanLine[y]; // pf8bit -> BYTE*
// here send/write/store ... bmp->Width bytes from p[]
}
// Canvas->Draw(0,0,bmp); // just renfder it on Form
delete bmp; bmp=NULL;

mixing GDI winapi calls for pixel array access (bitblt etc...) with VCL bmDIB bitmap might cause problems and resource leaks (hence the error on exit) and its also slower then usage of ScanLine[] (if coded right) so I strongly advice to use native VCL functions (as I did in above example) instead of the GDI/winapi calls where you can.

for more info see:

  • #4. GDI Bitmap
  • Delphi / C++ builder Windows 10 1709 bitmap operations extremely slow
  • Draw tbitmap with scale and alpha channel faster

Also you mention your image source is camera. If you use pf8bit it mean its palette indexed color which is relatively slow and ugly if native GDI algo is used (to convert from true/hi color camera image) for better transform see:

  • Effective gif/image color quantization?
  • simple dithering

How do I find out if BMP image contains BGR or BGRA pixels from header?

The first 54 bytes in the bitmap file is the header. Read BITMAPINFOHEADER which contains bitmap's width and height, as well as biBitCount

For a 32-bit (4-byte) image biBitCount is 32 (BGRA)

For a 24-bit (3-byte) image biBitCount is 24 (BGR)

biBitCount can also be 16-bit, or 8, 4, 1 for palette based bitmaps.



Related Topics



Leave a reply



Submit