Ask Your Question

How to fix resized image in MFC?

asked 2015-06-26 05:53:18 -0600

Poky gravatar image

Hi, it was ok when run following code to paint Mat on Device Contro/DC/CDC:

Visual Studio 2010, MFC, OpenCV 247

image description

void CMainDlg::OnPaint()
    CRect rc; GetClientRect(&rc);
    CClientDC dc(this);

    Mat mat; mat=imread("aaa.bmp");
    int x=mat.cols;
    int y=mat.rows;

    // Create BitmapInfo
    BITMAPINFOHEADER* pheader= &bmi.bmiHeader;
    pheader->biSize         = sizeof( BITMAPINFOHEADER );
    pheader->biPlanes       = 1;
    pheader->biCompression   = BI_RGB;
    pheader->biXPelsPerMeter = 100;
    pheader->biYPelsPerMeter = 100;
    pheader->biClrUsed      = 0;
    pheader->biClrImportant  = 0;
    pheader->biWidth    = x;
    pheader->biHeight   = -y;
    pheader->biBitCount = 24;
    bmi.bmiHeader.biSizeImage = 0;

    // Stretch Image
    StretchDIBits( dc.GetSafeHdc()
                ,0,0,rc.Width(), rc.Height()

    // Compare
    imshow("CV imshow()",mat);

in a MFC dialog(CDialogEx), but when I resize the mat like this

void CMainDlg::OnPaint()
    CRect rc; GetClientRect(&rc);
    CClientDC dc(this);

    Mat mat; mat=imread("aaa.bmp");
    int x=mat.cols;
    int y=mat.rows;
    // ...some code...

the image got in shift!? why??


image description

There's same case in:

  1. Move mat to global area.

  2. paint in other function(out of OnPaint())

edit retag flag offensive close merge delete



I don't think that is an opencv problem.You have got some strange pixel in right image

LBerger gravatar imageLBerger ( 2015-06-26 07:25:54 -0600 )edit

I think that it is something about the interpretation of the pixels... Try with double or triple size, see what is happening

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-06-26 08:32:05 -0600 )edit

1 answer

Sort by ยป oldest newest most voted

answered 2015-06-26 10:04:47 -0600

pklab gravatar image

updated 2015-06-30 12:14:50 -0600

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

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

//really you could avoid this because is done by MFC StretchDIBits
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);
        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);
  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,

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 input format. Example: given a BGR image 3 ... (more)

edit flag offensive delete link more


Can you post also some explanation why mat.cols % 4 == 0?

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-06-29 02:18:19 -0600 )edit

I'd do control the client rectangle with if(int pad=%rc.Width()%4) rc.right-=pad; within WM_SIZE, It's OK now. Thank you very much!!

Poky gravatar imagePoky ( 2015-06-29 05:50:33 -0600 )edit

@thdrksdfthmn: is current version of my answer more clear ? @Poky: if you feel you might mark the answer as correct

pklab gravatar imagepklab ( 2015-06-29 13:38:13 -0600 )edit

@Poky your code seems wrong because pad should be 4 - (cols%4) . If cols=13, pad must be 3 while 13%4=1. In addition if cols%4=0 pad must be 0 while 4 - (cols%4)=4 than you should avoid the case. Look at the code

pklab gravatar imagepklab ( 2015-06-29 13:51:22 -0600 )edit

it is more clear, but shouldn't it be better to use cv::cvtColor instead of padding? Or does the cv::copyMakeBorder make the Mat continuous, and cv::cvtColor doesn't? Or is it because it may be a ROI?

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-06-30 02:27:11 -0600 )edit

Yes but cvtColor uses more memory and it's bit slower. Issues will comes if you use inplace operations with ROI...I've integrated my answer

pklab gravatar imagepklab ( 2015-06-30 05:23:59 -0600 )edit

@pklab, no, if WindowWidth(1rc.Width()1) is 13, then pad=13%4=1. rc.right-=1 so that rc.Width()=12. Finally the mat resized to rc.Width()=mat.cols=12 is OK!(Since I'd - the pad, but u + padding.)

4 is OK, that's if [some width] =4n, pad=[some width]%4=0. So that [some width]-=0 is OK.

Poky gravatar imagePoky ( 2015-07-03 00:20:25 -0600 )edit

Yes this it's works if you can loose cols on the right. If Width = 13 and you will use 12 you will loose rightmost column. If you want to draw your mat exactly, you have to add a border of 3pix on the right so that Width = 16. Finally tell to StretchDIBits to draw just first 13 cols.

pklab gravatar imagepklab ( 2015-07-03 04:30:25 -0600 )edit

Question Tools

1 follower


Asked: 2015-06-26 05:53:18 -0600

Seen: 3,099 times

Last updated: Jun 30 '15