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)