【问题标题】:cv::imread image is not show on MFC's picture boxcv::imread 图像未显示在 MFC 的图片框上
【发布时间】:2019-12-21 16:52:24
【问题描述】:

我正在构建基于对话框的 MFC 应用程序。 当我单击按钮时,我可以从文件资源管理器中选择图像,并且该图像也被加载到cv::imread()。并且还显示在优化校准中。

我可以使用以下代码在优化校准中加载并显示图像。

void CMFCApplication3Dlg::OnBnClickedButton1()
{
    cv::Mat src = cv::imread("D:/source/repos/Testing_Photos/Large/cavalls.png");
    Display(src);

}

但不是下面的代码。

void CMFCApplication3Dlg::OnBnClickedButton1()
{
    TCHAR szFilter[] = _T("PNG (*.png)|*.png|JPEG (*.jpg)|*.jpg|Bitmap (*.bmp)|*.bmp||");
    CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, AfxGetMainWnd());

    if (dlg.DoModal() == IDC_BUTTON1)
    {
        CString cstrImgPath = dlg.GetPathName();
        CT2CA pszConvertedAnsiString(cstrImgPath);
        std::string strStd(pszConvertedAnsiString);
        cv::Mat src = cv::imread(strStd);

        Display(src);
    }
}

以下是“显示”功能。

void CMFCApplication3Dlg::Display(cv::Mat& mat) {

    CStatic * PictureB = (CStatic *)GetDlgItem(IDC_STATIC);

    CWnd* cwn = (CWnd *)GetDlgItem(IDC_STATIC);
    CDC* wcdc = cwn->GetDC();
    HDC whdc = wcdc->GetSafeHdc();

    RECT rec;
    cwn->GetClientRect(&rec);

    cv::Size matSize;
    matSize = cv::Size(rec.right, rec.bottom);

    BITMAPINFO bitmapinfo;

    bitmapinfo.bmiHeader.biBitCount = 24;
    bitmapinfo.bmiHeader.biWidth = mat.cols;
    bitmapinfo.bmiHeader.biHeight = -mat.rows;
    bitmapinfo.bmiHeader.biPlanes = 1;
    bitmapinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bitmapinfo.bmiHeader.biCompression = BI_RGB;
    bitmapinfo.bmiHeader.biClrImportant = 0;
    bitmapinfo.bmiHeader.biClrUsed = 0;
    bitmapinfo.bmiHeader.biSizeImage = 0;
    bitmapinfo.bmiHeader.biXPelsPerMeter = 0;
    bitmapinfo.bmiHeader.biYPelsPerMeter = 0;


    StretchDIBits(
        whdc,
        0, 0,
        matSize.width, matSize.height,
        0, 0,
        mat.cols, mat.rows,
        mat.data,
        &bitmapinfo,
        DIB_RGB_COLORS,
        SRCCOPY
    );

}

我对 MFC 很陌生,我真的不知道我哪里错了。 请帮我! 谢谢。

【问题讨论】:

  • 为了调试它,您必须验证每个子步骤。 1) cv::Mat src 是否正确加载、添加检测或在调试器中查看其成员。 2) BITMAPINFO 是否被正确初始化? 3) StretchDIBits 的设置是否正确?您可以在 MSDN 或 SO 上找到有关如何通过 StrechDIBits 使用该结构的示例。 4) 检查StretchDIBits 的返回值,可能还有GetLastError(如果API 实际使用)。 5) 任何其他 GDI 绘图调用是否适用于这些窗口值,例如您可以设置背景颜色吗?您可能需要 WM_PAINT 消息?
  • 作为一般规则,应始终进行渲染以响应收到WM_PAINT 消息。但是由于您已经拥有CStatic 控件,因此您不需要渲染任何内容。只需构建您的图像,然后致电CStatic::SetBitmap。渲染随后由控件处理。

标签: opencv mfc


【解决方案1】:

这不是正确的绘画方法。每次有绘画请求时,图片都会被删除。

CFileDialog 会发生这种情况,它会在您绘制图像后立即强制重新绘制。您必须回复OnPaintOnDrawItem,或使用CStatic::SetBitmap,如评论中所述。

你可以用CImage类做到这一点,不需要OpenCV:

CImage img;
if(S_OK == img.Load(L"unicode.jpg"))
{
    CStatic *control = (CStatic*)GetDlgItem(IDC_STATIC);
    control->ModifyStyle(0, SS_BITMAP);
    auto oldbmp = control->SetBitmap(img.Detach());
    if(oldbmp)  
        DeleteObject(oldbmp);
}

或者你可以使用OpenCV创建HBITMAP句柄。

请注意,OpenCV 不处理 Unicode 文件名。 CW2A 将 Unicode 转换为 ANSI 字符编码。如果一个或多个代码点无法在当前活动的代码页中表示,则会失败。要解决这个问题,我们可以使用CFilestd::ifstream 打开文件,将其读取为二进制文件,然后改为使用cv::imdecode 打开。示例:

//open the file from unicode path:
const wchar_t *filename = L"unicode.jpg";
std::ifstream fin(filename, std::ios::binary);
if(!fin.good())
    return;

//read from memory
std::vector<char> vec(std::istreambuf_iterator<char>(fin), {});
cv::Mat src = cv::imdecode(vec, cv::IMREAD_COLOR);
if(!src.data)
    return;

//create hbitmap
BITMAPINFOHEADER bi = { sizeof(bi), src.cols, -src.rows, 1, 24 };
CClientDC dc(this);
auto hbmp = CreateDIBitmap(dc, &bi, CBM_INIT, src.data, (BITMAPINFO*)&bi, 0);

//send hbitmap to control
auto control = (CStatic*)GetDlgItem(IDC_STATIC);
auto oldbmp = control->SetBitmap(hbmp);
if (oldbmp)
    DeleteObject(oldbmp);

【讨论】:

  • SetBitmap 将当前位图的所有权转移到控件中,以及将前一个位图的所有权移出控件。实现似乎没有考虑到这一点,过早地删除了新的位图,并可能泄露了前一个位图。一个简单的解决方法是使用以下模式:img.Attach(control-&gt;SetBitmap(img.Detach()));。这恰当地处理了双向所有权的转移。
  • @IInspectable 是的,我的方法是错误的,已更正。 img.Attach(control-&gt;SetBitmap(img.Detach())) 不起作用,因为在第一次调用SetBitmap 时,它返回NULL,而CImage::Attach 将抛出调试断言。我们必须在删除之前检查旧位图,或者重新附加到CImage
  • 我不知道CImage::Attach() 在传递nullptr 时会断言。毕竟,它确实有一个CImage::Detach() 成员,所以不拥有任何资源似乎是一种有效的状态。
  • @IInspectable 是的,它看起来像一个小设计缺陷。他们应该让该方法接受NULL 输入。
猜你喜欢
  • 2013-10-17
  • 2015-05-27
  • 2012-11-11
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 1970-01-01
  • 2021-05-23
相关资源
最近更新 更多