Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert ATL::CImage to cv::Mat?

Tags:

c++

opencv

atl

mfc

I want to convert from ATL::CImage to cv::Mat for image handling in opencv(C++). Could you please help to convert this object?

I got CImage from windows screen shot(Using MFC). Then, I want to handle image in OpenCV Mat object.

I did not know how to convert.

  • C++ Project(VC 2017)
  • MFC
  • OpenCV 3.4.6

CImage image;
int cx;
int cy;
CWnd* pWndDesktop = CWnd::GetDesktopWindow();
CWindowDC srcDC(pWndDesktop);

Rect rcDesktopWindow;
::GetWindowRect(pWndDesktop->m_hWnd, %rcDesktopWindow);

cx = (rcDesktopWindow.right - rcDesktopWindow.left);
cy = (rcDesktopWindow.bottom - rcDesktopWindow.top);

image.create(cx, cy, srcDC.GetDeviceCaps(BITPIXEL));

CDC* pDC = CDC::FromHandle(image.GetDC());
pDC->BitBlt(0, 0, cx, cy, &srcDC, 0, 0, SRCCOPY);

image.ReleaseDC();

cv::Mat mat;

// I want set image to mat!
mat = image???

Can not convert ATL::Image to cv::Mat.

like image 617
Kevin Kim Avatar asked Oct 16 '22 15:10

Kevin Kim


1 Answers

CImage creates a bottom-top bitmap if height is positive. You have to pass a negative height to create top-bottom bitmap for mat

Use CImage::GetBits to retrieve the bits as follows:

HDC hdc = GetDC(0);
RECT rc;
GetClientRect(GetDesktopWindow(), &rc);
int cx = rc.right;
int cy = rc.bottom;

CImage image;
image.Create(cx, -cy, 32);

BitBlt(image.GetDC(), 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
image.ReleaseDC();
ReleaseDC(0, hdc);

cv::Mat mat;
mat.create(cy, cx, CV_8UC4);
memcpy(mat.data, image.GetBits(), cy * cx * 4);

//or borrow pixel data from CImage 
cv::Mat mat(cy, cx, CV_8UC4, image.GetBits()); 

Or force a deep copy as follows:

cv::Mat mat;
mat = cv::Mat(cy, cx, CV_8UC4, image.GetBits()).clone();

Note, CImage makes its own allocation for pixel data. And Mat needs to make its own allocation, or it has to borrow from CImage which can be tricky.

If you are just taking a screen shot, you can do that with plain Windows API, then write directly to cv::Mat. This way there is a single allocation (a bit faster) and mat does not rely on other objects. Example:

void foo()
{
    HDC hdc = ::GetDC(0);

    RECT rc;
    ::GetClientRect(::GetDesktopWindow(), &rc);
    int cx = rc.right;
    int cy = rc.bottom;
    cv::Mat mat;
    mat.create(cy, cx, CV_8UC4);

    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, cx, cy);
    HDC memdc = CreateCompatibleDC(hdc);
    HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
    BitBlt(memdc, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);

    BITMAPINFOHEADER bi = { sizeof(bi), cx, -cy, 1, 32, BI_RGB };
    GetDIBits(hdc, hbitmap, 0, cy, mat.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

    //GDI cleanup:
    SelectObject(memdc, oldbmp);
    DeleteDC(memdc);
    DeleteObject(hbitmap);
    ::ReleaseDC(0, hdc);
}


Edit:
Changed mat.data = (unsigned char*)image.GetBits(); to
memcpy(mat.data, image.GetBits(), cy * cx * 4);

Changed ReleaseDC(0, hdc) to ::ReleaseDC(0, hdc) to avoid conflict with CWnd::ReleaseDC(dc)

like image 156
Barmak Shemirani Avatar answered Oct 23 '22 11:10

Barmak Shemirani