Link to home
Start Free TrialLog in
Avatar of flynny
flynnyFlag for United Kingdom of Great Britain and Northern Ireland

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_CREATEDIBSECTION);

      //got bitmap now get and read the byte array
      //first read the header
      HDC hdc = GetDC(NULL);
      BITMAPINFO BitmapInfo = {0};
      BitmapInfo.bmiHeader.biSize = 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.biSizeImage];

       if (pBits)
       {
               // zero out the bitmap bit buffers
               memset(pBits, 0, BitmapInfo.bmiHeader.biSizeImage);

                     //get the height and width of the image
               long h = BitmapInfo.bmiHeader.biHeight;
               long w = BitmapInfo.bmiHeader.biWidth;

               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.biHeight, 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:\\data\\%d.bmp", z);


                                             HBITMAP tBmp = (HBITMAP)::LoadImage(NULL,_T(szFilename),
                                                            IMAGE_BITMAP,0,0,
                                                            LR_LOADFROMFILE|LR_CREATEDIBSECTION);

                                          //get the bitmap header of this bitmap to find height width
                                          //for cutting out subsection of bitmap
                                          BITMAPINFO tBitmapInfo = {0};
                                          tBitmapInfo.bmiHeader.biSize = 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.biHeight;
                                          long tw = tBitmapInfo.bmiHeader.biWidth;

                                          //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.

Avatar of flynny
flynny
Flag of United Kingdom of Great Britain and Northern Ireland image

ASKER

i apologise i'e removed one o the brackets when pasting the code in. ust to mention it does compile and every pixels is appearing to be

r 150, g 150, b 150

(this is the colour of the line surrounding the image)
ASKER CERTIFIED SOLUTION
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of flynny

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?
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.biHeight, 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.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;

unsigned long stride = (width * (bmpInfo.bmiHeader.biBitCount / 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.
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, y, BitmapInfo.bmiHeader.biHeight, 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.biHeight, pBits,
             &BitmapInfo, DIB_RGB_COLORS)
        {
            int wid = BitmapInfo.bmiHeader.biWidth;
            int hgt = BitmapInfo.bmiHeader.biHeight;
            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.
Avatar of flynny

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.
Avatar of flynny

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.biHeight, pBits,
             &BitmapInfo, DIB_RGB_COLORS)

becomes GetDIBits(hdc, hBmp, 0, BitmapInfo.bmiHeader.biHeight, pBits,
             &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)&bmfHeader, sizeof(bmfHeader)) == sizeof(bmfHeader)) {
          // File type should be 'BM'
      if(bmfHeader.bfType == ((WORD) ('M' << 8) | 'B')) {
             // Read info header
        if(file.Read((LPSTR)&bmiHeader, sizeof(bmiHeader)) == sizeof(bmiHeader)) {

          rowsize = abs(bmiHeader.biWidth) * 4;
         
          for(row = 0; row < abs(bmiHeader.biHeight); row++) {
            address = (row * rowsize);
            file.Seek(bmfHeader.bfOffBits+address, CFile::begin);
           
            for(col = 0; col < bmiHeader.biWidth; col++) {
              file.Read(&pixel32, 4);
              if(pixel32 == 0) {
                    // black pixel, put your code here
              }
            }

          }
        }
      }
    }
  }

  file.Close();
}
Avatar of flynny

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_CREATEDIBSECTION);

      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'   ;-)