You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将CBitmap转换为cv::Mat?求正确实现方案

我之前在项目里也遇到过CBitmap转cv::Mat的坑,试了好几种方法才找到稳定的实现,给你分享两个靠谱的方案,顺便说下常见的踩坑点,应该能解决你的问题:

方法一:纯GDI实现(不依赖额外库)

这个方法直接用Windows GDI API获取位图数据,兼容性好,不需要额外链接库。核心是处理DDB(设备相关位图)和DIB(设备无关位图)的区别——如果你的CBitmap是DDB,不能直接访问像素数据,必须通过GetDIBits导出为DIB格式。

#include <opencv2/opencv.hpp>
#include <afxwin.h> // MFC头文件,包含CBitmap定义

cv::Mat CBitmapToMat(CBitmap& bmp) {
    BITMAP bmpInfo;
    bmp.GetBitmap(&bmpInfo);

    const int width = bmpInfo.bmWidth;
    const int height = bmpInfo.bmHeight;
    const int bitsPerPixel = bmpInfo.bmBitsPixel;
    const int channels = bitsPerPixel / 8;

    void* pPixelData = bmpInfo.bmBits;
    std::vector<BYTE> tempBuffer;

    // 处理DDB(无法直接访问bmBits的情况)
    if (!pPixelData) {
        CDC tempDC;
        tempDC.CreateCompatibleDC(nullptr);
        CBitmap* pOldBmp = tempDC.SelectObject(&bmp);

        // 计算DIB数据所需的内存(考虑行对齐)
        const int bufferSize = ((width * bitsPerPixel + 31) / 32) * 4 * height;
        tempBuffer.resize(bufferSize);

        BITMAPINFOHEADER biHeader = {0};
        biHeader.biSize = sizeof(BITMAPINFOHEADER);
        biHeader.biWidth = width;
        biHeader.biHeight = -height; // 负高度表示像素从上到下存储(和OpenCV一致)
        biHeader.biPlanes = 1;
        biHeader.biBitCount = bitsPerPixel;
        biHeader.biCompression = BI_RGB;

        // 从DC中提取DIB数据
        GetDIBits(tempDC.m_hDC, bmp.m_hObject, 0, height, tempBuffer.data(), 
                  reinterpret_cast<BITMAPINFO*>(&biHeader), DIB_RGB_COLORS);
        pPixelData = tempBuffer.data();

        tempDC.SelectObject(pOldBmp);
    }

    // 根据位图格式创建对应的cv::Mat
    cv::Mat resultMat;
    switch (bitsPerPixel) {
        case 24:
            // Windows 24位DIB是BGR通道顺序,和OpenCV默认格式一致,无需转换
            resultMat = cv::Mat(height, width, CV_8UC3, pPixelData, bmpInfo.bmWidthBytes);
            break;
        case 8:
            // 8位灰度图(如果带调色板,需确保调色板是灰度映射,否则要额外处理)
            resultMat = cv::Mat(height, width, CV_8UC1, pPixelData, bmpInfo.bmWidthBytes);
            break;
        case 32:
            // 32位一般是BGRA(或BGRX,X为填充位),可按需转成BGR
            resultMat = cv::Mat(height, width, CV_8UC4, pPixelData, bmpInfo.bmWidthBytes);
            // 如果不需要Alpha通道:cv::cvtColor(resultMat, resultMat, cv::COLOR_BGRA2BGR);
            break;
        default:
            throw std::runtime_error("Unsupported bitmap format (only 8/24/32 bits are supported)");
    }

    // 返回数据拷贝,避免临时内存释放后Mat悬空
    return resultMat.clone();
}
方法二:GDI+实现(代码更简洁)

如果你可以接受链接GDI+库,这个方法代码更简洁,能自动处理更多格式细节,比如通道转换、行对齐等。

#include <opencv2/opencv.hpp>
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib") // 链接GDI+库

using namespace Gdiplus;

cv::Mat CBitmapToMatGDIPlus(CBitmap& bmp) {
    BITMAP bmpInfo;
    bmp.GetBitmap(&bmpInfo);
    const int width = bmpInfo.bmWidth;
    const int height = bmpInfo.bmHeight;

    // 初始化GDI+
    GdiplusStartupInput startupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &startupInput, nullptr);

    // 将CBitmap转换为GDI+ Bitmap对象
    HBITMAP hBmp = static_cast<HBITMAP>(bmp);
    Bitmap* pGdiBitmap = Bitmap::FromHBITMAP(hBmp, nullptr);

    // 锁定Bitmap获取像素数据
    BitmapData bitmapData;
    const Rect pixelRect(0, 0, width, height);
    pGdiBitmap->LockBits(&pixelRect, ImageLockModeRead, PixelFormat24bppRGB, &bitmapData);

    // 创建cv::Mat并转换通道顺序(GDI+是RGB,OpenCV默认是BGR)
    cv::Mat resultMat(height, width, CV_8UC3, bitmapData.Scan0, bitmapData.Stride);
    cv::cvtColor(resultMat, resultMat, cv::COLOR_RGB2BGR);

    // 清理资源
    pGdiBitmap->UnlockBits(&bitmapData);
    delete pGdiBitmap;
    GdiplusShutdown(gdiplusToken);

    return resultMat.clone();
}
常见踩坑点(为什么你的代码之前跑不通?)
  • 行对齐问题:Windows位图的每行像素数据是按4字节对齐的,不能直接用width * channels作为步长,必须使用bmWidthBytes(GDI)或Stride(GDI+)。
  • 通道顺序混乱:Windows DIB是BGR顺序,GDI+是RGB顺序,和OpenCV默认的BGR格式要对应,否则图像颜色会出现红蓝反转。
  • DDB vs DIB混淆:如果你的CBitmap是设备相关位图(DDB),直接访问bmBits会得到空指针,必须用GetDIBits或GDI+导出数据。
  • 悬空指针问题:如果直接用临时内存的指针创建cv::Mat,当临时内存释放后,Mat会变成无效的悬空指针,必须调用clone()拷贝数据。

内容的提问来源于stack exchange,提问作者Bobby Dillanl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:16:00