flynny
asked on
reading 32 bit bitmaps
Hi all,
I have a method which was reading in a 24bit bitmap and accessing the individual pixels( i really oly want to do some processing when a pixel is black.)
I've tried to change this to work with 32bit bitmaps however the output im getting is strange. can anyone tell me what i'm doing wrong please?
void readBitmap(LPTSTR path)
{
CString szFilename(path);
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, szFilename ,
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED IBSECTION) ;
//got bitmap now get and read the byte array
//first read the header
HDC hdc = GetDC(NULL);
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSiz e = sizeof(BITMAPINFOHEADER);
if(GetDIBits(hdc, hBmp, 0, 0, NULL, &BitmapInfo, DIB_RGB_COLORS))
{
//got the header now need to read it
BYTE *pBits = new BYTE[BitmapInfo.bmiHeader. biSizeImag e];
if (pBits)
{
// zero out the bitmap bit buffers
memset(pBits, 0, BitmapInfo.bmiHeader.biSiz eImage);
//get the height and width of the image
long h = BitmapInfo.bmiHeader.biHei ght;
long w = BitmapInfo.bmiHeader.biWid th;
BYTE rVal, gVal, bVal;
int x, y = 0;
logmm.open( logzz );
logmm << "starting.. \n";
logmm.close();
//now iterate through the image getting the each pixel
for (x = 0; x < w; x++)
{
//iterate down the column first to try and find first black pixel
for (y = h-1; y > -1; y--)
{
GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei ght, pBits, &BitmapInfo, DIB_RGB_COLORS);
//will need to change to included 24 bitmaps etc.
rVal = pBits[(x * 4) + 2]; // red
gVal = pBits[(x * 4) + 1]; //green
bVal = pBits[(x * 4) + 0]; //blue
if(rVal == 0 && gVal == 0 && bVal == 0)
{
logmm.open( logzz, ios::app );
logmm << "#";
logmm.close();
//first load in one of the training bitmaps
int z;
for(z=0; z<10; z++)
{
CString szFilename;
szFilename.Format("C:\\dat a\\%d.bmp" , z);
HBITMAP tBmp = (HBITMAP)::LoadImage(NULL, _T(szFilen ame),
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED IBSECTION) ;
//get the bitmap header of this bitmap to find height width
//for cutting out subsection of bitmap
BITMAPINFO tBitmapInfo = {0};
tBitmapInfo.bmiHeader.biSi ze = sizeof(BITMAPINFOHEADER);
GetDIBits(hdc, tBmp, 0, 0, NULL, &tBitmapInfo, DIB_RGB_COLORS);
//now got header can get the height and width
long th = tBitmapInfo.bmiHeader.biHe ight;
long tw = tBitmapInfo.bmiHeader.biWi dth;
//now get subsection of bitmap
HBITMAP subsection = CreateSubBitmap(hBmp, x, 0, tw, th);
bool same = CompareBitmaps(tBmp, subsection);
}
else
{
logmm.open( logzz, ios::app );
logmm << "_";
logmm.close();
}
}
logmm.open( logzz, ios::app );
logmm << "\n";
logmm.close();
}
// clean up
delete[] pBits;
}
}
}
regards,
Matt.
I have a method which was reading in a 24bit bitmap and accessing the individual pixels( i really oly want to do some processing when a pixel is black.)
I've tried to change this to work with 32bit bitmaps however the output im getting is strange. can anyone tell me what i'm doing wrong please?
void readBitmap(LPTSTR path)
{
CString szFilename(path);
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL,
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED
//got bitmap now get and read the byte array
//first read the header
HDC hdc = GetDC(NULL);
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSiz
if(GetDIBits(hdc, hBmp, 0, 0, NULL, &BitmapInfo, DIB_RGB_COLORS))
{
//got the header now need to read it
BYTE *pBits = new BYTE[BitmapInfo.bmiHeader.
if (pBits)
{
// zero out the bitmap bit buffers
memset(pBits, 0, BitmapInfo.bmiHeader.biSiz
//get the height and width of the image
long h = BitmapInfo.bmiHeader.biHei
long w = BitmapInfo.bmiHeader.biWid
BYTE rVal, gVal, bVal;
int x, y = 0;
logmm.open( logzz );
logmm << "starting.. \n";
logmm.close();
//now iterate through the image getting the each pixel
for (x = 0; x < w; x++)
{
//iterate down the column first to try and find first black pixel
for (y = h-1; y > -1; y--)
{
GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei
//will need to change to included 24 bitmaps etc.
rVal = pBits[(x * 4) + 2]; // red
gVal = pBits[(x * 4) + 1]; //green
bVal = pBits[(x * 4) + 0]; //blue
if(rVal == 0 && gVal == 0 && bVal == 0)
{
logmm.open( logzz, ios::app );
logmm << "#";
logmm.close();
//first load in one of the training bitmaps
int z;
for(z=0; z<10; z++)
{
CString szFilename;
szFilename.Format("C:\\dat
HBITMAP tBmp = (HBITMAP)::LoadImage(NULL,
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED
//get the bitmap header of this bitmap to find height width
//for cutting out subsection of bitmap
BITMAPINFO tBitmapInfo = {0};
tBitmapInfo.bmiHeader.biSi
GetDIBits(hdc, tBmp, 0, 0, NULL, &tBitmapInfo, DIB_RGB_COLORS);
//now got header can get the height and width
long th = tBitmapInfo.bmiHeader.biHe
long tw = tBitmapInfo.bmiHeader.biWi
//now get subsection of bitmap
HBITMAP subsection = CreateSubBitmap(hBmp, x, 0, tw, th);
bool same = CompareBitmaps(tBmp, subsection);
}
else
{
logmm.open( logzz, ios::app );
logmm << "_";
logmm.close();
}
}
logmm.open( logzz, ios::app );
logmm << "\n";
logmm.close();
}
// clean up
delete[] pBits;
}
}
}
regards,
Matt.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
hi itsme,
i've changed this to how you recommended and unfortunately its is still not working. after testing it a bit it appears to be just reading the first line in the file and repeating this
so a crude example woul dbe f the file was
xoooxxxooo
oooooooooo
oooooooooo
oooooooooo
then it will read in
xoooxxxooo
xoooxxxooo
xoooxxxooo
xoooxxxooo
any ideas why it does this?
i've changed this to how you recommended and unfortunately its is still not working. after testing it a bit it appears to be just reading the first line in the file and repeating this
so a crude example woul dbe f the file was
xoooxxxooo
oooooooooo
oooooooooo
oooooooooo
then it will read in
xoooxxxooo
xoooxxxooo
xoooxxxooo
xoooxxxooo
any ideas why it does this?
well, that would be happening because of the following code:
//now iterate through the image getting the each pixel
for (x = 0; x < w; x++)
{
//iterate down the column first to try and find first black pixel
for (y = h-1; y > -1; y--)
{
GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei ght, pBits, &BitmapInfo, DIB_RGB_COLORS);
//will need to change to included 24 bitmaps etc.
rVal = pBits[(x * 4) + 2]; // red
gVal = pBits[(x * 4) + 1]; //green
bVal = pBits[(x * 4) + 0]; //blue
What this does is calls GetDIBits() with a different start position (from h-1 to 0) but of the same height. The documentation shows GetDIBits() as the following:
int GetDIBits(
HDC hdc, // handle to DC
HBITMAP hbmp, // handle to bitmap
UINT uStartScan, // first scan line to set
UINT cScanLines, // number of scan lines to copy
LPVOID lpvBits, // array for bitmap bits
LPBITMAPINFO lpbi, // bitmap data buffer
UINT uUsage // RGB or palette index
);
What you should do instead is read in the whole bitmap and then test each pixel from there. But you also have to take into consideration the stride of the bitmap (the buffer of information at the ends of each scanline). I don't remember where I found the formula to calculate the stride, but it's served me well:
// assuming you know width and height already
BYTE *pBits = new BYTE[3 * width * height];
BITMAPINFO bmpInfo;
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight = -height; // GetDIBits reads scanlines in from bottom to top for some reason
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCou nt = 32;
bmpInfo.bmiHeader.biCompre ssion = BI_RGB;
unsigned long stride = (width * (bmpInfo.bmiHeader.biBitCo unt / 8) + 3) & ~3; // buffer of pixels
unsigned long incrementer;
for(int i = 0; i < height; i++)
{
incrementer = (stride * i);
for(int j = 0; j < width; j++)
{
pBIts[incrementer + 2]; // red
pBits[incrementer + 1]; // green
pBits[incrementer + 0]; // blue
incrementer += 3;
}
}
Implement that and see if it works for you.
//now iterate through the image getting the each pixel
for (x = 0; x < w; x++)
{
//iterate down the column first to try and find first black pixel
for (y = h-1; y > -1; y--)
{
GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei
//will need to change to included 24 bitmaps etc.
rVal = pBits[(x * 4) + 2]; // red
gVal = pBits[(x * 4) + 1]; //green
bVal = pBits[(x * 4) + 0]; //blue
What this does is calls GetDIBits() with a different start position (from h-1 to 0) but of the same height. The documentation shows GetDIBits() as the following:
int GetDIBits(
HDC hdc, // handle to DC
HBITMAP hbmp, // handle to bitmap
UINT uStartScan, // first scan line to set
UINT cScanLines, // number of scan lines to copy
LPVOID lpvBits, // array for bitmap bits
LPBITMAPINFO lpbi, // bitmap data buffer
UINT uUsage // RGB or palette index
);
What you should do instead is read in the whole bitmap and then test each pixel from there. But you also have to take into consideration the stride of the bitmap (the buffer of information at the ends of each scanline). I don't remember where I found the formula to calculate the stride, but it's served me well:
// assuming you know width and height already
BYTE *pBits = new BYTE[3 * width * height];
BITMAPINFO bmpInfo;
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight
bmpInfo.bmiHeader.biPlanes
bmpInfo.bmiHeader.biBitCou
bmpInfo.bmiHeader.biCompre
unsigned long stride = (width * (bmpInfo.bmiHeader.biBitCo
unsigned long incrementer;
for(int i = 0; i < height; i++)
{
incrementer = (stride * i);
for(int j = 0; j < width; j++)
{
pBIts[incrementer + 2]; // red
pBits[incrementer + 1]; // green
pBits[incrementer + 0]; // blue
incrementer += 3;
}
}
Implement that and see if it works for you.
Sorry, I forgot to make the call to GetDIBits(). It should be:
GetDIBits(hdc, hBmp, 0, height, pBits, &bmpInfo, DIB_RGB_COLORS);
After instantiating all the values for bmpInfo, of course.
GetDIBits(hdc, hBmp, 0, height, pBits, &bmpInfo, DIB_RGB_COLORS);
After instantiating all the values for bmpInfo, of course.
>>>> GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei ght, pBits,
>>>> &BitmapInfo, DIB_RGB_COLORS);
The second GetDIBits should read the *whole* bitmap. So, it should be made *before* both loops.
if (GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei ght, pBits,
&BitmapInfo, DIB_RGB_COLORS)
{
int wid = BitmapInfo.bmiHeader.biWid th;
int hgt = BitmapInfo.bmiHeader.biHei ght;
RGBQUAD* pCols = (RGBQUAD*)pBits;
for (int r = 0; r < hgt; ++r)
{
for (int c = 0; c < wid; ++c)
{
RGBQUAD col = pCols[r*wid + c];
if (col.rgbBlue < ....
...
}
}
}
I don't know whether the above solves *all* of your problems but actually I don't understand your current logic where you have a LoadBitmap deeply nested in a loop on pixels ....
>>>> &BitmapInfo, DIB_RGB_COLORS);
The second GetDIBits should read the *whole* bitmap. So, it should be made *before* both loops.
if (GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei
&BitmapInfo, DIB_RGB_COLORS)
{
int wid = BitmapInfo.bmiHeader.biWid
int hgt = BitmapInfo.bmiHeader.biHei
RGBQUAD* pCols = (RGBQUAD*)pBits;
for (int r = 0; r < hgt; ++r)
{
for (int c = 0; c < wid; ++c)
{
RGBQUAD col = pCols[r*wid + c];
if (col.rgbBlue < ....
...
}
}
}
I don't know whether the above solves *all* of your problems but actually I don't understand your current logic where you have a LoadBitmap deeply nested in a loop on pixels ....
Please ignore most of what I said... I've been looking through some of my old code and apparently stride doesn't apply to images loaded from file, so I apologize for that.
ASKER
>>I don't know whether the above solves *all* of your problems but actually I don't understand your current logic where you have a LoadBitmap deeply nested in a loop on pixels ....
i want to do some ocring on a given bitmap. so i load in the bitmap i want to ocr first and then as i'm only interested in the back pixels loop throuh this bitmap until i detect a black pixel. at this point i want to test from this x column onwards (depends on the size of the training data bitmap) against some test data i have saved. thats why the loadbitmap is deeply nested, as a that point i have found a black pixel and want to then begin the comparisons.
i want to do some ocring on a given bitmap. so i load in the bitmap i want to ocr first and then as i'm only interested in the back pixels loop throuh this bitmap until i detect a black pixel. at this point i want to test from this x column onwards (depends on the size of the training data bitmap) against some test data i have saved. thats why the loadbitmap is deeply nested, as a that point i have found a black pixel and want to then begin the comparisons.
ASKER
>> The second GetDIBits should read the *whole* bitmap. So, it should be made *before* both loops.
ok i ee so what would the value of y be to make sure would this be 0?
so GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei ght, pBits,
&BitmapInfo, DIB_RGB_COLORS)
becomes GetDIBits(hdc, hBmp, 0, BitmapInfo.bmiHeader.biHei ght, pBits,
&BitmapInfo, DIB_RGB_COLORS)
ok i ee so what would the value of y be to make sure would this be 0?
so GetDIBits(hdc, hBmp, y, BitmapInfo.bmiHeader.biHei
&BitmapInfo, DIB_RGB_COLORS)
becomes GetDIBits(hdc, hBmp, 0, BitmapInfo.bmiHeader.biHei
&BitmapInfo, DIB_RGB_COLORS)
If you are working only with 32 bit you can try this way, changing the file functions if you are not using MFC:
void ReadBmp32(char *filename)
{
BITMAPINFOHEADER bmiHeader;
BITMAPFILEHEADER bmfHeader;
CFile file;
unsigned long pixel32;
unsigned long address, rowsize;
unsigned int row, col;
if(file.Open(filename, CFile::modeRead)) {
// Read file header
if(file.Read((LPSTR)&bmfHe ader, sizeof(bmfHeader)) == sizeof(bmfHeader)) {
// File type should be 'BM'
if(bmfHeader.bfType == ((WORD) ('M' << 8) | 'B')) {
// Read info header
if(file.Read((LPSTR)&bmiHe ader, sizeof(bmiHeader)) == sizeof(bmiHeader)) {
rowsize = abs(bmiHeader.biWidth) * 4;
for(row = 0; row < abs(bmiHeader.biHeight); row++) {
address = (row * rowsize);
file.Seek(bmfHeader.bfOffB its+addres s, CFile::begin);
for(col = 0; col < bmiHeader.biWidth; col++) {
file.Read(&pixel32, 4);
if(pixel32 == 0) {
// black pixel, put your code here
}
}
}
}
}
}
}
file.Close();
}
void ReadBmp32(char *filename)
{
BITMAPINFOHEADER bmiHeader;
BITMAPFILEHEADER bmfHeader;
CFile file;
unsigned long pixel32;
unsigned long address, rowsize;
unsigned int row, col;
if(file.Open(filename, CFile::modeRead)) {
// Read file header
if(file.Read((LPSTR)&bmfHe
// File type should be 'BM'
if(bmfHeader.bfType == ((WORD) ('M' << 8) | 'B')) {
// Read info header
if(file.Read((LPSTR)&bmiHe
rowsize = abs(bmiHeader.biWidth) * 4;
for(row = 0; row < abs(bmiHeader.biHeight); row++) {
address = (row * rowsize);
file.Seek(bmfHeader.bfOffB
for(col = 0; col < bmiHeader.biWidth; col++) {
file.Read(&pixel32, 4);
if(pixel32 == 0) {
// black pixel, put your code here
}
}
}
}
}
}
}
file.Close();
}
ASKER
hi guys i managed to get it working using this code
void readBitmap(LPTSTR path, CArrayBITMAP array)
{
CString szFilename(path);
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL, szFilename ,
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED IBSECTION) ;
BITMAP WorkBmp;
GetObject(hBmp, sizeof(WorkBmp), &WorkBmp);
int dBitCount = WorkBmp.bmBitsPixel;
int bitsPerPixel = dBitCount/8;
int dWidth = WorkBmp.bmWidth;
int dActualWidth = WorkBmp.bmWidth + WorkBmp.bmWidth % bitsPerPixel;
int dHeight = WorkBmp.bmHeight;
int x, y;
int r,g,b;
for(x=0;x<dWidth;x++)
{
for (y=dHeight - 1;y>=0;y--)
{
r = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 2);
g = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 1);
b = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 0);
if(r==0 && g==0 && b==0)
{
}
}
}
}
void readBitmap(LPTSTR path, CArrayBITMAP array)
{
CString szFilename(path);
HBITMAP hBmp = (HBITMAP)::LoadImage(NULL,
IMAGE_BITMAP,0,0,
LR_LOADFROMFILE|LR_CREATED
BITMAP WorkBmp;
GetObject(hBmp, sizeof(WorkBmp), &WorkBmp);
int dBitCount = WorkBmp.bmBitsPixel;
int bitsPerPixel = dBitCount/8;
int dWidth = WorkBmp.bmWidth;
int dActualWidth = WorkBmp.bmWidth + WorkBmp.bmWidth % bitsPerPixel;
int dHeight = WorkBmp.bmHeight;
int x, y;
int r,g,b;
for(x=0;x<dWidth;x++)
{
for (y=dHeight - 1;y>=0;y--)
{
r = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 2);
g = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 1);
b = *((BYTE*)WorkBmp.bmBits + y * dActualWidth * bitsPerPixel + x * bitsPerPixel + 0);
if(r==0 && g==0 && b==0)
{
}
}
}
}
>>>> int bitsPerPixel = dBitCount/8;
You should name it 'bytesPerPixel' ;-)
You should name it 'bytesPerPixel' ;-)
ASKER
r 150, g 150, b 150
(this is the colour of the line surrounding the image)