Ask Your Question
0

How to fix resized image in MFC?

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

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
    BITMAPINFO bmi;
    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()
                ,0,0,x,y,
                mat.data,&bmi,DIB_RGB_COLORS,SRCCOPY);

    // 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");
    resize(mat,mat,cv::Size(rc.Width(),rc.Height()));
    int x=mat.cols;
    int y=mat.rows;
    // ...some code...
}

the image got in shift!? why??

Resoult:

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

Comments

1

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 -0500 )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 -0500 )edit

1 answer

Sort by ยป oldest newest most voted
4

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

pklab gravatar image

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

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

edit flag offensive delete link more

Comments

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

thdrksdfthmn gravatar imagethdrksdfthmn ( 2015-06-29 02:18:19 -0500 )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 -0500 )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 -0500 )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 -0500 )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 -0500 )edit
1

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 -0500 )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 -0500 )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 -0500 )edit
Login/Signup to Answer

Question Tools

1 follower

Stats

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

Seen: 1,532 times

Last updated: Jun 30 '15