【问题标题】:How to convert ATL::CImage to cv::Mat?如何将 ATL::CImage 转换为 cv::Mat?
【发布时间】:2019-10-02 01:30:21
【问题描述】:

我想将 ATL::CImage 转换为 cv::Mat 以在 opencv(C++) 中进行图像处理。 你能帮忙转换一下这个对象吗?

我从 Windows 屏幕截图(使用 MFC)中获得了 CImage。 然后,我想处理 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

【问题讨论】:

  • 你可以去掉中间人,只用一个函数来获取屏幕截图作为一个 Mat。这可能会有所帮助:stackoverflow.com/questions/34466993/opencv-desktop-capturestackoverflow.com/questions/14148758/…
  • 最简单(但相当愚蠢)的方法是将其保存到文件中(似乎有一个成员函数可以做到这一点),然后使用imread 将其加载回来。您似乎也可以保存到内存流中,因此您可以将生成的缓冲区提供给imdecode 并避免使用文件系统。最好的方法是获得一个具有已知布局的像素缓冲区,Mat 可以理解(行主要,理想情况下从上到下,所以你不需要翻转它).. 然后你只需要创建一个 Mat标题以匹配数据。只需要从 Mat 转到 GDI 对象,所以需要研究。

标签: c++ opencv mfc atl


【解决方案1】:

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)发生冲突

【讨论】:

  • 通过重置 data 成员,您可能会泄漏缓冲区(或者至少是自找麻烦 - 并不想深入研究实现以找到任何可能的陷阱,因为弄乱其暴露的内部状态)。只需使用appropriate constructor 创建一个正确包装该缓冲区的标头。解决这个问题,我会修改我的投票。
  • 提及Mat 只有在image 在范围内时才有效。
  • @DanMašek 您不必深入研究源代码。您可以阅读“暴露”的data 的文档,并且可以检查是否存在泄漏。在这种情况下不会发生泄漏,因为Mat 会跟踪它自己的分配。您的建议肯定更有效、更安全,但问题是 Mat mat; 由提问者重写...
  • @DanMašek,是的,额外分配效率不高(公平地说,这比将图像保存到磁盘并再次读取要好)我更新了答案以避免额外分配并修复了可能的问题指针。
  • “比储蓄更好”——当然,这只是在最少研究下想到的最省力的方法。您的方法(除了我抱怨的那一点)将是我想要的(纯 WinAPI 解决方案甚至更好)。感谢您的更新。
【解决方案2】:
#include <opencv2\opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <atlimage.h>

using namespace cv;

Mat CImage2Mat(CImage cimg)
{
    BITMAP bmp;
    ::GetObject(cimg.Detach(), sizeof(BITMAP), &bmp);

    int nChannels = bmp.bmBitsPixel == 1 ? 1 : bmp.bmBitsPixel / 8;
    int depth = bmp.bmBitsPixel == 1 ? IPL_DEPTH_1U : IPL_DEPTH_8U;

    IplImage* img = cvCreateImageHeader(cvSize(bmp.bmWidth, bmp.bmHeight), depth, nChannels);

    img->imageData = (char*)malloc(bmp.bmHeight * bmp.bmWidth * nChannels * sizeof(char));
    memcpy(img->imageData, (char*)(bmp.bmBits), bmp.bmHeight * bmp.bmWidth * nChannels);
    cvFlip(img, img, 0);
    return cvarrToMat(static_cast<IplImage*>(img));
}

用法:

CImage cimg;
cimg.Load(L"resim.png");

Mat img = CImage2Mat(cimg);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-22
    • 2018-07-17
    • 1970-01-01
    • 2016-08-16
    • 2018-04-16
    • 2011-06-28
    • 2015-12-07
    • 1970-01-01
    相关资源
    最近更新 更多