Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

MFC requires DWORD memory alignment for bitmaps. Than cv::Mat mat must be continuous (no ROI nor sparse) and mat.cols % 4 = 0. In case could use this:

Mat mat;
mat=imread("aaa.bmp");
//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 4 - (x % 4);
if(padding==4)
  padding = 0;

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

MFC requires DWORD memory alignment for bitmaps. Than bitmaps rows , than cv::Mat mat must be continuous be:

  1. Continuous (no ROI nor sparse)
  2. Byte based pixel type (RGB, RGBA,GRAY)
  3. and mat.cols (bpp * mat.cols) % 4 == 0.

Looking at How the image matrix is stored in the memory? you can see that for a cv::Mat BytesForRows = bpp * mat.cols where bpp is number of byte for each pixel.

  • For 32bit RGBA/GBRA images all is fine because 1pix=4bytes than rows are always DWORD aligned.
  • For GRAY images, 1pix=1byte than you will have DWORD alignment on rows only if cols%4==0
  • For RGB/BGR images, 1pix=3byte than you need (3*cols)%4==0 bytes. You could add needed bytes at the end of each cv::Mat rows but this is a bit hard in OpenCV so is easier to add needed pixel instead of needed bytes.

Definitely you should have cols as multiple of 4: cols % 4 == 0. In case could use this:This is fine for GRAY and RGB images because 4colsbpp is always a multiple of 4 than DWORD aligned on rows.

here the code:

Mat mat;
mat=imread("aaa.bmp");

if ( mat.type() == CV_8UC1 ) {
    cout << "not working because a greyscale palette is needed";
    return;
}
else if ((mat.type() != CV_8UC3) && (mat.type() != CV_8UC4)) {
    cout << "image type not supported";
    return;
}

//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 0;
if (mat.type() != CV_8UC4) // padding is not needed for 32bit images
{
    padding = 4 - (x % 4);
if(padding==4)
    if(padding==4 || )
        padding = 0;
}

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

MFC requires DWORD memory alignment for bitmaps rows , than cv::Mat mat must be:

  1. Continuous (no ROI nor sparse)
  2. Byte based pixel type (RGB, RGBA,GRAY)
  3. and (bpp * mat.cols) % 4 == 0.

Looking at How the image matrix is stored in the memory? you can see that for a cv::Mat BytesForRows = bpp * mat.cols where bpp is number of byte for each pixel.

  • For 32bit RGBA/GBRA images all is fine because 1pix=4bytes than rows are always DWORD aligned.
  • For GRAY images, 1pix=1byte than you will have DWORD alignment on rows only if cols%4==0
  • For RGB/BGR images, 1pix=3byte than you need (3*cols)%4==0 bytes. You could add needed bytes at the end of each cv::Mat rows but this is a bit hard in OpenCV so is easier to add needed pixel instead of needed bytes.

Definitely you should have cols as multiple of 4: cols % 4 == 0. This is fine for GRAY and RGB images because 4colsbpp is always a multiple of 4 than DWORD aligned on rows.

here the code:

Mat mat;
mat=imread("aaa.bmp");

if ( mat.type() == CV_8UC1 ) {
    cout << "not working because a greyscale palette is needed";
    return;
}
else if ((mat.type() != CV_8UC3) && (mat.type() != CV_8UC4)) {
    cout << "image type not supported";
    return;
}

//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 0;
if (mat.type() != CV_8UC4) // padding is not needed for 32bit images
{
    padding = 4 - (x % 4);
    if(padding==4 || )
if(padding==4)
        padding = 0;
}

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

  • MFC requires DWORD memory alignment for bitmaps rows , than
  • When you prepare an image for MFC the matrix elements needs to be stored continuously without gaps at the end of each row.

Than cv::Mat mat must be:

  1. Continuous (no ROI nor sparse) sparse). Mat::isContinuous() checks the case.
  2. Byte based pixel type (RGB, RGBA,GRAY)RGBA,GRAY). you could use cv::cvtColor
  3. and (bpp * mat.cols) % 4 == 0. You could use cv::copyMakeBorder or cv::cvtColor

Looking If the image is not continuous (eg is a ROI) you should use in-place operation with care because it would be not continuous again.

About memory alignment, looking at How the image matrix is stored in the memory? you can see that for a cv::Mat BytesForRows = bpp * mat.cols where bpp is number of byte for each pixel.

  • For 32bit RGBA/GBRA images all is fine because 1pix=4bytes than rows are always DWORD aligned.
  • For GRAY images, 1pix=1byte than you will have DWORD alignment on rows only if cols%4==0
  • For RGB/BGR images, 1pix=3byte than you need (3*cols)%4==0 bytes. You could add needed bytes at the end of each cv::Mat rows but this is a bit hard in OpenCV so is easier to add needed pixel instead of needed bytes.

Definitely you should have 32bit image or image cols as multiple of 4: cols % 4 == 0. This is fine for GRAY and RGB images because 4colsbpp is always a multiple of 4 than DWORD aligned on rows.

here the code:

Mat mat;
mat=imread("aaa.bmp");

if ( mat.type() == CV_8UC1 ) {
    cout << "not working because a greyscale palette is needed";
needed or you could use cv::cvtColor";
    return;
}
else if ((mat.type() != CV_8UC3) && (mat.type() != CV_8UC4)) {
    cout << "image type not supported";
supported or you could use cv::cvtColor";
    return;
}

//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 0;
if (mat.type() != CV_8UC4) // padding is not needed for 32bit images
{
    padding = 4 - (x % 4);
    if(padding==4)
        padding = 0;
}

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

As alternative, instead of cv::copyMakeBorder you could use cv::cvtColor(mat,xxx2BGRA) . This is slower (because of math operations) and memory consuming (because of needed channels) but will works for all imput format. Example: given a BGR image 3(CH)x800(R)x801(C):

  • copyMakeBorder(..,3) will creates a new BGR image 3CHx800Rx804C adds 38003 =7KByte
  • cvtColor(...,BGR2BGRA) will creates a new BGRA image 4CHx800Rx801C adds 1800801 = 626KByte

remeber to use in-place operation with care with discontinuous Mat.

  • MFC requires DWORD memory alignment for bitmaps rows
  • When you prepare an image for MFC the matrix elements needs to be stored continuously without gaps at the end of each row.

Than cv::Mat mat must be:

  1. Continuous (no ROI nor sparse). Mat::isContinuous() checks the case.
  2. Byte based pixel type (RGB, RGBA,GRAY). you could use cv::cvtColor
  3. and (bpp * mat.cols) % 4 == 0. You could use cv::copyMakeBorder or cv::cvtColor

If the image is not continuous (eg is a ROI) you should use in-place operation with care because it would be not continuous again.

About memory alignment, looking at How the image matrix is stored in the memory? you can see that for a cv::Mat BytesForRows = bpp * mat.cols where bpp is number of byte for each pixel.

  • For 32bit RGBA/GBRA images all is fine because 1pix=4bytes than rows are always DWORD aligned.
  • For GRAY images, 1pix=1byte than you will have DWORD alignment on rows only if cols%4==0
  • For RGB/BGR images, 1pix=3byte than you need (3*cols)%4==0 bytes. You could add needed bytes at the end of each cv::Mat rows but this is a bit hard in OpenCV so is easier to add needed pixel instead of needed bytes.

Definitely you should have 32bit image or image cols as multiple of 4: cols % 4 == 0. This is fine for GRAY and RGB images because 4cols4 x cols x bpp is always a multiple of 4 than DWORD aligned on rows.

here the code:

Mat mat;
mat=imread("aaa.bmp");

if ( mat.type() == CV_8UC1 ) {
    cout << "not working because a greyscale palette is needed or you could use cv::cvtColor";
    return;
}
else if ((mat.type() != CV_8UC3) && (mat.type() != CV_8UC4)) {
    cout << "image type not supported or you could use cv::cvtColor";
    return;
}

//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 0;
if (mat.type() != CV_8UC4) // padding is not needed for 32bit images
{
    padding = 4 - (x % 4);
    if(padding==4)
        padding = 0;
}

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

As alternative, instead of cv::copyMakeBorder you could use cv::cvtColor(mat,xxx2BGRA) . This is slower (because of math operations) and memory consuming (because of needed channels) but will works for all imput format. Example: given a BGR image 3(CH)x800(R)x801(C)3(CH) x 800(R) x 801(C):

  • copyMakeBorder(..,3) will creates a new BGR image 3CHx800Rx804C3CH x 800R x 804C adds 38003 x 800 x 3 =7KByte
  • cvtColor(...,BGR2BGRA) will creates a new BGRA image 4CHx800Rx801C4CH x 800R x 801C adds 18001 x 800 x 801 = 626KByte

remeber to use in-place operation with care with discontinuous Mat.

  • MFC requires DWORD memory alignment for bitmaps rows
  • When you prepare an image for MFC the matrix elements needs to be stored continuously without gaps at the end of each row.

Than cv::Mat mat must be:

  1. Continuous (no ROI nor sparse). Mat::isContinuous() checks the case.
  2. Byte based pixel type (RGB, RGBA,GRAY). you could use cv::cvtColor
  3. and (bpp * mat.cols) % 4 == 0. You could use cv::copyMakeBorder or cv::cvtColor

If the image is not continuous (eg is a ROI) you should use in-place operation with care because it would be not continuous again.

About memory alignment, looking at How the image matrix is stored in the memory? you can see that for a cv::Mat BytesForRows = bpp * mat.cols where bpp is number of byte for each pixel.

  • For 32bit RGBA/GBRA images all is fine because 1pix=4bytes than rows are always DWORD aligned.
  • For GRAY images, 1pix=1byte than you will have DWORD alignment on rows only if cols%4==0
  • For RGB/BGR images, 1pix=3byte than you need (3*cols)%4==0 bytes. You could add needed bytes at the end of each cv::Mat rows but this is a bit hard in OpenCV so is easier to add needed pixel instead of needed bytes.

Definitely you should have 32bit image or image cols as multiple of 4: cols % 4 == 0. This is fine for GRAY and RGB images because 4 x cols x bpp is always a multiple of 4 than DWORD aligned on rows.

here the code:

Mat mat;
mat=imread("aaa.bmp");

if ( mat.type() == CV_8UC1 ) {
    cout << "not working because a greyscale palette is needed or you could use cv::cvtColor";
    return;
}
else if ((mat.type() != CV_8UC3) && (mat.type() != CV_8UC4)) {
    cout << "image type not supported or you could use cv::cvtColor";
    return;
}

//really you could avoid this because is done by MFC StretchDIBits
//resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
int x=mat.cols;
int y=mat.rows;

int padding = 0;
if (mat.type() != CV_8UC4) // padding is not needed for 32bit images
{
    padding = 4 - (x % 4);
    if(padding==4)
        padding = 0;
}

cv::Mat matTmp;
if (padding > 0 || mat.isContinuous() == false)
{
  // Adding needed columns on the right (max 3 px)
  cv::copyMakeBorder(mat, matTmp, 0, 0, 0, padding, cv::BORDER_CONSTANT, 0);
}
else
{
  matTmp = mat;
}

Now you have right memory shape for a MFC bitmap. You can draw it avoiding the border using:

//real cols in the bitmap header
pheader->biWidth    = matTmp.cols;

//wanted cols (original cols) for drawing
StretchDIBits( dc.GetSafeHdc()
            ,0, 0, rc.Width(), rc.Height()
            ,0, 0, x, y,
            matTmp.data, &bmi, DIB_RGB_COLORS, SRCCOPY);

Finally your code will not work in case the image is grey scale because you need to prepare right palette in BITMAPINFOHEADER

As alternative, instead of cv::copyMakeBorder you could use cv::cvtColor(mat,xxx2BGRA) . This is slower (because of math operations) and memory consuming (because of needed channels) but will works for all imput input format. Example: given a BGR image 3(CH) x 800(R) x 801(C):

  • copyMakeBorder(..,3) will creates a new BGR image 3CH x 800R x 804C adds 3 x 800 x 3 =7KByte
  • cvtColor(...,BGR2BGRA) will creates a new BGRA image 4CH x 800R x 801C adds 1 x 800 x 801 = 626KByte

remeber to use in-place operation with care with discontinuous Mat.