在ListControl上显示图像缩略图(opencv, gdiplus)

生成图像缩略图有以下几个步骤:
- 1.读取图像
- 2.按照预期的缩略图大小,缩小图像
- 3.保存或显示到相关控件

经过在网上多方查找并亲自测试,总结出以下两种方法,并显示到ListControl控件

一、List Control初始化

缩略图最终存放在CImageList的变量里,具体步骤如下
- 1. 申明变量,并与控件绑定
- 2. 根据需要设置list控件,确定每行中各列的表头及其宽度
- 3. 生成缩略图列表
- 4. 将CListControl与CImageList绑定
直接上代码吧

    #define THUMBNAIL_WIDTH     100
    #define THUMBNAIL_HEIGHT    75

    CImageList m_thumbnailList;     // 缩略图列表
    CListCtrl m_listImgFile;        // 列表控件,已与具体控件绑定

    // Image List -- list control init
    CRect rect;
    m_listImgFile.GetClientRect(&rect);
    int iLength = rect.Width();
    m_listImgFile.SetExtendedStyle(m_listImgFile.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
    m_listImgFile.InsertColumn(0, _T("图像ID"), LVCFMT_CENTER, iLength / 3);
    m_listImgFile.InsertColumn(2, _T("文件"), LVCFMT_CENTER, iLength / 3);
    m_listImgFile.InsertColumn(3, _T("宽度"), LVCFMT_CENTER, iLength / 6);
    m_listImgFile.InsertColumn(4, _T("高度"), LVCFMT_CENTER, iLength / 6);
    m_thumbnailList.Create(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, ILC_MASK|ILC_COLOR32, 0, 0);  // 生成缩略图列表
    m_listImgFile.SetImageList(&m_thumbnailList, LVSIL_SMALL);          // 将CImageList与List Control绑定

以下是生成缩略图的方法,因为list控件只接受CBitmap*的图像变量,所以无论哪种方法,最后都需要把图像转换成CBitmap*的格式。

二、用OpenCV生成缩略图

前提条件:对于OpenCV3.0以后的版本,需要2.0版本里的两个文件CvvImage.h和CvvImage.cpp,用来将Mat格式转换成CBitmap格式。当然如果对CBitmap和Mat结构很了解,也可以直接硬转。

先上转换函数

CBitmap* MatToCBitmap(Mat img)
{
    CDC dc;
    CDC memDC;

    if (!dc.CreateDC(_T("DISPLAY"), NULL, NULL, NULL))
        return NULL;

    if (!memDC.CreateCompatibleDC(&dc))
        return NULL;

    CBitmap *bmp = new CBitmap();
    CBitmap* pOldBitmap;

    bmp->CreateCompatibleBitmap(&dc, img.cols, img.rows);
    pOldBitmap = memDC.SelectObject(bmp);

    CvvImage cvImage; // you will need OpenCV_2.2.0- to use CvvImage 
    cvImage.CopyOf(&IplImage(img));
    cvImage.Show(memDC.m_hDC, 0, 0, img.cols, img.rows, 0, 0);
    cvImage.Destroy();

    memDC.SelectObject(pOldBitmap);
    memDC.DeleteDC();
    dc.DeleteDC();

    return bmp;
}

从这个函数里可以看出来,实际的转换过程就是把Mat转换给CvvImage,然后申请一块内存,CvvImage把图像显示到这块内存中,然后返回这块内存的图像句柄。
这里有个隐患,bmp这个变量是new出来的,但是最后没有释放,可能会引起内存泄漏

先上代码:

    String fileName;
    Mat m_img;
    Mat cvimg = imread(fileName, 1);
    resize(cvimg, m_img, cv::Size(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT), .0, .0, CV_INTER_AREA);
    CBitmap* bmpImg = MatToCBitmap(m_img);
    //m_thumbnailList.Add(bmpImg, COLORREF(0));
    m_thumbnailList.Replace(i, bmpImg, NULL);

    // 给List Control添加数据
    CString tmp;
    tmp.Format(_T("%d"), i+1);
    m_listImgFile.InsertItem(i, tmp, i);
    m_listImgFile.SetItemText(i, 1, imgName);
    tmp.Format(_T("%d"), cvimg.cols);
    m_listImgFile.SetItemText(i, 2, tmp);
    tmp.Format(_T("%d"), cvimg.rows);
    m_listImgFile.SetItemText(i, 3, tmp);

    delete bmpImg;
    bmpImg = NULL;

生成比较简单,直接resize就可以了,插值方法用CV_INTER_AREA,一般opencv缩放时,放大的插值用CV_INTER_LINEAR,缩小的插值用CV_INTER_AREA。
代码中的变量i,是图像的序号,因为我打开了多个图像文件。
最后delete bmpImg,是为了防止MatToCBitmap中的内存泄漏。

三、用Gdiplus生成缩略图

前提条件:需要引用gdiplus.h,同时还要对gdiplus初始化,用完后要释放

#include 
using namespace Gdiplus;

ULONG_PTR   m_gdiplusToken;

// initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);

释放gdiplus

// release GDI+ resource
GdiplusShutdown(m_gdiplusToken);

生成缩略图并显示

        String fileName;
        Mat m_img;
        Matcv img = imread(fileName, 1);
        Bitmap img(fileName);
        Bitmap* pThumbnail = static_cast(img.GetThumbnailImage(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, NULL, NULL));
        // attach the thumbnail bitmap handle to an CBitmap object
        pThumbnail->GetHBITMAP(NULL, &hBmp);
        pImage = new CBitmap();
        pImage->Attach(hBmp);
        // add bitmap to our image list
        m_thumbnailList.Replace(i, pImage, NULL);

        // 给List Control添加数据
        CString tmp;
        tmp.Format(_T("%d"), i+1);
        m_listImgFile.InsertItem(i, tmp, i);
        m_listImgFile.SetItemText(i, 1, imgName);
        tmp.Format(_T("%d"), cvimg.cols);
        m_listImgFile.SetItemText(i, 2, tmp);
        tmp.Format(_T("%d"), cvimg.rows);
        m_listImgFile.SetItemText(i, 3, tmp);

        delete pImage;
        delete pThumbnail;

显示部分两种方法都一样。在最开始的时候我用opencv读取了文件,目的是为了得到图像的长宽。

四、说明

有两件事情需要说明一下
- 1.给CImageList中增加图像
有两种方法,Add(…)和Replace(…)

(1)m_thumbnailList.Add(bmpImg, COLORREF(0));
(2)m_thumbnailList.Replace(i, bmpImg, NULL);

我自己测试后发现Add函数需要一个模板COLORREF,加了这个模板缩略图变成全黑,由于时间关系,我没有深入研究直接采用了第(2)个方法。

  • 2.缩略图在list控件中的位置
    试了好久只能放在第一列,没搞定放在中间列,好像只有InsertItem可以带图像序号的参数,而InsertItem只对第1列设置,对其他列不能用InsertItem了。

要是有童鞋有解决方法,能共享一下最好…………


以上方法测试没有问题,还是截张图吧。
因为是代码的片段,可能有的参数没有申明,领会精神吧。^_^
在ListControl上显示图像缩略图(opencv, gdiplus)_第1张图片

你可能感兴趣的:(VC编程,OpenCV)