Как конвертировать ATL :: CImage в cv :: Mat? - PullRequest
4 голосов
/ 15 мая 2019

Я хочу преобразовать из ATL::CImage в cv::Mat для обработки изображений в opencv (C ++). Не могли бы вы помочь преобразовать этот объект?

Я получил CImage из скриншота Windows (с помощью MFC). Затем я хочу обработать изображение в объекте OpenCV Mat.

Я не знал, как конвертировать.

  • Проект C ++ (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???

Невозможно преобразовать ATL::Image в cv::Mat.

1 Ответ

3 голосов
/ 15 мая 2019

CImage создает растровое изображение снизу вверх, если высота положительна.Вы должны передать отрицательную высоту, чтобы создать верхний-нижний битовый массив для mat

. Используйте CImage::GetBits для получения битов следующим образом:

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()); 

Или принудительно выполните глубокое копирование следующим образом:

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

Обратите внимание: CImage делает свое собственное распределение для данных пикселей.И Mat нужно сделать собственное выделение, или он должен позаимствовать у CImage, что может быть сложно.

Если вы просто делаете снимок экрана, вы можете сделать это с помощью простого Windows API, тогдапишите прямо на cv::Mat.Таким образом, происходит одно выделение (немного быстрее), и mat не зависит от других объектов.Пример:

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);
}


Редактировать:
Изменено mat.data = (unsigned char*)image.GetBits(); на
memcpy(mat.data, image.GetBits(), cy * cx * 4);

Изменено ReleaseDC(0, hdc) на ::ReleaseDC(0, hdc), чтобы избежать конфликта с CWnd::ReleaseDC(dc)

...