opencv3.0+VS2013平台人脸检测MFC

来源:互联网 发布:excel制作数据录入窗体 编辑:程序博客网 时间:2024/06/16 12:42

OpenCV3.0+VS2013平台人脸检测MFC

说明:
本程序构建于VS2013基本对话框,调用了OPENCV3.0库,可以通过打开摄像头、打开图片,打开视频获取数据文件,再进行检测(最后有效果图),三者数据流之间可以任意切换。几点说明:视频和摄像机的显示和检测用到了定时器;在代码的现实中,图片是显示在控件中,用到CvvImage类,而OPENCV3.0库中没有这个类了,需要自己添加相应cpp和h文件到工程目录中。
实现函数位于1420190255Dlg.cpp中,成员变量和位于1420190255Dlg.h文件,少量全局变量在1420190255Dlg.h中。本文中的代码,网上有很多。本文只是为了展示MFC的框架以及代码的移植实现。

一、程序代码

1、1420190255Dlg.h

有注释部分是基本是自己添加部分(下同).

// 1420190255Dlg.h : 头文件#pragma once// CMy1420190255Dlg 对话框class CMy1420190255Dlg : public CDialogEx{// 构造public:    CMy1420190255Dlg(CWnd* pParent = NULL); // 标准构造函数// 对话框数据    enum { IDD = IDD_MY1420190255_DIALOG };    protected:    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持public:    IplImage* m_pImg, *m_pImg_=0;   // 文件读取图像    IplImage* m_pRstImg;            // 处理后的图像    BOOL m_bDrawFlag;           // 是否有图像    int m_nFileType;            // 打开文件类型    float m_fTime;              // 检测时间    int m_nNum;                 // 检测人脸个数    // 摄像头    // 本版本CvCapture 不能启动设备,用VideoCapture替代;    Mat m_Frame, m_Frame_;      // 从摄像机capture获取的    IplImage m_frame, m_frame_; // 从Mat图像转换而来    VideoCapture cap;    // 视频 (同上)    VideoCapture capture ;    Mat m_vFrame, m_vFrame_;    IplImage m_vframe, m_vframe_;    void DrawImageToHDC( IplImage *pImg, UINT nID);  // 图片显示到控件    void ClearControlImage(UINT nID);  // 清空画面(用以加载另外的图片,避免大小不同而不能完全覆盖)    void face_detect(IplImage * img);  // 检测图片子函数// 实现protected:    HICON m_hIcon;    // 生成的消息映射函数    virtual BOOL OnInitDialog();    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);    afx_msg void OnPaint();    afx_msg HCURSOR OnQueryDragIcon();    DECLARE_MESSAGE_MAP()public:// 消息映射函数       afx_msg void OnBnClickedGetCam();     // 打开摄像头    afx_msg void OnBnClickedVideo();      // 打开视频    afx_msg void OnBnClickedPicture();    // 打开图片afx_msg void OnTimer(UINT_PTR nIDEvent);  // 定时器}

2、1420190255Dlg.cpp

这部分,贴出添加的初始化的变量,以及函数实现。代码中有一些解释。 就不进行整体的说明了。

(1) 引用

2个cpp类的引用,我放在了stdafx.h中

// 1420190255Dlg.cpp : 实现文件#include "CvvImage.h" //高版本OpenCV不存在该类,需要自己创建及添加//#include "opencv2/opencv.hpp"//#include "opencv2/highgui.hpp"</span>

stdafx.h中用到了宏定义,以避免重复定义:

// stdafx.h : 标准系统包含文件的包含文件,#ifndef _OPCV_3_0_HEADER_       // 支持 opencv#include "opencv2/opencv.hpp"#include "opencv2/highgui.hpp"using namespace cv;#endif
(2) 全局变量,构造函数初始化
static CvMemStorage * storage = 0;static CvHaarClassifierCascade *cascade = 0;static CvScalar colors[] = {    // 8种标记颜色    { 0, 0, 255 }, { 0, 128, 255 }, { 0, 255, 255 }, { 0, 255, 0 },    { 255, 128, 0 }, { 255, 255, 0 }, { 255, 0, 0 }, { 255, 0, 255 } };BOOL bDttFlag = FALSE; // 检测按钮标记BOOL bPlayOver = FALSE; // 视频播放结束标记

下面是构造函数的初始化,消息映射表, 数据交换

CMy1420190255Dlg::CMy1420190255Dlg(CWnd* pParent /*=NULL*/) :CDialogEx(CMy1420190255Dlg::IDD, pParent){    m_pImg = NULL;    m_pRstImg = NULL;    m_bDrawFlag = NULL;    m_fTime = 0;    m_nNum = 0;    m_nFileType = 0;  // 1-摄像,2-视频, 3-图片     // 加载分类器 opencv1.0中文件,924kb    const char * cascade_name = "haarcascade_frontalface_alt.xml";    cascade = (CvHaarClassifierCascade*)cvLoad(cascade_name);    if (cascade == NULL)        MessageBox("Load HaarCascade error!\n");    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}    //BEGIN_MESSAGE_MAP(CMy1420190255Dlg, CDialogEx)    ON_WM_SYSCOMMAND()    ON_WM_PAINT()    ON_BN_CLICKED(IDC_GETCAM, &CMy1420190255Dlg::OnBnClickedGetCam)    ON_BN_CLICKED(IDC_PICTURE, &CMy1420190255Dlg::OnBnClickedPicture)    ON_BN_CLICKED(IDC_Detect, &CMy1420190255Dlg::OnBnClickedDetect)    ON_WM_TIMER()    ON_BN_CLICKED(IDC_Video, &CMy1420190255Dlg::OnBnClickedVideo)<span END_MESSAGE_MAP()void CMy1420190255Dlg::DoDataExchange(CDataExchange* pDX){    CDialogEx::DoDataExchange(pDX);    DDX_Text(pDX, IDC_TIME, m_fTime);    DDX_Text(pDX, IDC_NUM, m_nNum);}
(3) 画图到控件
void CMy1420190255Dlg::DrawImageToHDC(IplImage * pImg, UINT nID){    CvvImage image;    CRect rect;    //获取设备上下文    CDC *pDC = GetDlgItem(nID)->GetDC();    HDC hDC = pDC->GetSafeHdc();    //获取PictureBox边框    GetDlgItem(nID)->GetClientRect(&rect);    //防止去除PictureBox的边框    rect.top = rect.top + 1;    rect.bottom = rect.bottom - 1;    rect.left = rect.left + 1;    rect.right = rect.right - 1;    //图像基本参数    int nWidth = pImg->width;    int nHeight = pImg->height;    //等比例缩放,防止图像变形    if ((rect.right - rect.left) < nWidth || (rect.bottom - rect.top)<nHeight)    {        if ((float)nWidth / (float)nHeight>(float)(rect.right - rect.left) / (float)(rect.bottom - rect.top))        {            rect.bottom = nHeight*(rect.right - rect.left) / nWidth + rect.top;        }        else        {            rect.right = nWidth*(rect.bottom - rect.top) / nHeight + rect.left;        }    }    else    {        rect.right = nWidth + rect.left;        rect.bottom = nHeight + rect.top;    }    //获取图像,绘制图像设备    image.CopyOf(pImg);    image.DrawToHDC(hDC, &rect);    //释放资源    ReleaseDC(pDC);    image.Destroy();}
(4) 清空控件图片
// 实际是用和对话框颜色的图像覆盖对话框void CMy1420190255Dlg::ClearControlImage(UINT nID){    CvvImage image;    CRect rect;    //获取设备上下文    CDC *pDC = GetDlgItem(nID)->GetDC();    HDC hDC = pDC->GetSafeHdc();    //获取PictureBox边框    GetDlgItem(nID)->GetClientRect(&rect);    //创建与图像窗口大小一致的图像    IplImage* pImg = cvCreateImage(cvSize(rect.right - rect.left, rect.bottom - rect.left), IPL_DEPTH_8U, 3);    //设置为背景色,通过取色发现,背景颜色为(240240240)    cvSet(pImg, cvScalar(240, 240, 240));    //防止去除PictureBox的边框    rect.top = rect.top + 1;    rect.bottom = rect.bottom - 1;    rect.left = rect.left + 1;    rect.right = rect.right - 1;    //获取图像,绘制图像设备    image.CopyOf(pImg);    image.DrawToHDC(hDC, &rect);    //释放资源    ReleaseDC(pDC);    cvReleaseImage(&pImg);    image.Destroy();}
(5) 人脸检测子函数
void CMy1420190255Dlg::face_detect(IplImage * img){    double scale = 1.3;    IplImage * copyImg = cvCloneImage(img);    IplImage * gray = cvCreateImage(cvSize(copyImg->width, copyImg->height), 8, 1);    IplImage * small_img = cvCreateImage(cvSize(cvRound(copyImg->width / scale), cvRound(copyImg->height / scale)), 8, 1);    cvCvtColor(copyImg, gray, CV_BGR2GRAY);     // 灰度化    cvResize(gray, small_img, CV_INTER_LINEAR); // 尺度    cvEqualizeHist(small_img, small_img);       // 均衡化    cvClearMemStorage(storage);    m_fTime = (float)cvGetTickCount();    CvSeq * faces = cvHaarDetectObjects(small_img, cascade, storage,        1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/, cvSize(30, 30));    m_fTime = (float)cvGetTickCount() - m_fTime;    m_fTime = (float)(m_fTime / (cvGetTickFrequency() * 1000));    if ( ! (m_nNum = faces->total)) m_fTime = 0;    for (int i = 0; i < (faces ? faces->total : 0); i++)    {        CvRect * r = (CvRect *)cvGetSeqElem(faces, i);        CvPoint center;        int radius;        center.x = cvRound((r->x + r->width * 0.5) *scale);        center.y = cvRound((r->y + r->height * 0.5) *scale);        radius = cvRound((r->width + r->height) * 0.25 * scale);        cvCircle(copyImg, center, radius, colors[i % 8], 3, 8, 0);    }    cvReleaseImage(&m_pRstImg);    m_pRstImg = cvCloneImage(copyImg);    cvReleaseImage(&copyImg);    cvReleaseImage(&gray);    cvReleaseImage(&small_img);}
(6) 定时器
void CMy1420190255Dlg::OnTimer(UINT_PTR nIDEvent){    // TODO:  在此添加消息处理程序代码和/或调用默认值    switch (nIDEvent)    {    case 1:     // 摄像机        cap >> m_Frame;        m_frame = m_Frame;  //Mat 转化为 IplImage         if (!&m_frame) return;        cvReleaseImage(&m_pImg);        m_pImg = cvCloneImage(&m_frame);        face_detect(m_pImg);        DrawImageToHDC(m_pRstImg, IDC_SHOWPIC);        UpdateData(FALSE);        break;    case 2:      //  视频        capture >> m_vFrame;        if (m_vFrame.data)        {            m_vframe = m_vFrame;    //Mat 转化为 IplImage                  cvReleaseImage(&m_pImg);            m_pImg = cvCloneImage(&m_vframe);            face_detect(m_pImg);            DrawImageToHDC(m_pRstImg, IDC_SHOWPIC);            UpdateData(FALSE);        }        else        {            bPlayOver = TRUE;            KillTimer(2);            MessageBox("视频检测结束");        }        break;    case 3:     // 仅显示获取的祯图片        if (m_nFileType == 1)        {            cap >> m_Frame_;            m_frame_ = m_Frame_;            if (!&m_frame_) return;            cvReleaseImage(&m_pImg_);            m_pImg_ = cvCloneImage(&m_frame_);            DrawImageToHDC(m_pImg_, IDC_SHOWPIC);            UpdateData(FALSE);        }        if (m_nFileType == 2)        {            capture >> m_vFrame_;            if (m_vFrame_.data)            {                m_vframe_ = m_vFrame_;                cvReleaseImage(&m_pImg_);                m_pImg_ = cvCloneImage(&m_vframe_);                DrawImageToHDC(m_pImg_, IDC_SHOWPIC);                UpdateData(FALSE);            }            else            {                KillTimer(3);                bPlayOver = TRUE;                MessageBox("视频播放结束");            }        }        break;    default:    break;    }    CDialogEx::OnTimer(nIDEvent);}
(7) 以下是4个按钮的消息响应函数

摄像头按钮,视频文件按钮,图片文件按钮,检测按钮

//打开摄像头void CMy1420190255Dlg::OnBnClickedGetCam(){    // TODO: 在此添加控件通知处理程序代码    ClearControlImage(IDC_SHOWPIC);    m_nNum = 0;    m_fTime = 0;     UpdateData(FALSE);    storage = cvCreateMemStorage(0);    if (capture.isOpened()) // 如果视频打开,释放,关闭定时器3    {        KillTimer(3);        capture.release();        if (bDttFlag == TRUE)             KillTimer(2); // 检测状态,关闭定时器1    }    bDttFlag = FALSE;    m_nFileType = 1;    if (!cap.isOpened())    {        cap.open(CV_CAP_ANY);        SetTimer(3, 40, NULL); // 显示 摄像头 或者 视频的 图像;}
//打开视频文件按钮void CMy1420190255Dlg::OnBnClickedVideo(){    // TODO:  在此添加控件通知处理程序代码    storage = cvCreateMemStorage(0);    ClearControlImage(IDC_SHOWPIC);    m_nNum = 0; m_fTime = 0; UpdateData(FALSE);    if (cap.isOpened()) // 如果摄像头打开,释放,关闭定时器3    {        KillTimer(3);        cap.release();        if (bDttFlag == TRUE)   KillTimer(1); // 检测状态,关闭定时器1    }    bDttFlag = FALSE;    bPlayOver = FALSE;    m_nFileType = 2;    CString FilePathName;    const char* AviPathName;    if (!capture.isOpened())    {        CFileDialog FileOpenDlg(TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "视频文件(*.avi;*.mp4;*.wmv)|*.avi;*.mp4;*.wmv|所有文件(*.*)|*.*||");        if (FileOpenDlg.DoModal() != IDOK) return;        FilePathName = FileOpenDlg.GetPathName();        AviPathName = FilePathName.GetBuffer(sizeof(FilePathName)); // CString转const char*        capture = VideoCapture(AviPathName);        SetTimer(3, 40, NULL);  // 显示 摄像头 或者 视频的 图像;    }    else    {        KillTimer(2);        KillTimer(3);        capture.release();        CFileDialog FileOpenDlg(TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "视频文件(*.avi;*.mp4;*.wmv)|*.avi;*.mp4;*.wmv|所有文件(*.*)|*.*||");        if (FileOpenDlg.DoModal() != IDOK) return;        FilePathName = FileOpenDlg.GetPathName();        AviPathName = FilePathName.GetBuffer(sizeof(FilePathName)); // CString转const char*        capture = VideoCapture(AviPathName);        SetTimer(3, 40, NULL);  // 显示 摄像头 或者 视频的 图像;    }}
//打开图片文件按钮void CMy1420190255Dlg::OnBnClickedPicture(){    // TODO: 在此添加控件通知处理程序代码    ClearControlImage(IDC_SHOWPIC);    m_nNum = 0;     m_fTime = 0;     UpdateData(FALSE);    if (cap.isOpened()) //如果摄像头打开,释放,关闭定时器3    {        KillTimer(3);        cap.release();        if (bDttFlag == TRUE)             KillTimer(1);  // 检测状态,关闭定时器1    }    if (capture.isOpened())  // 如果视频打开,释放,关闭定时器3    {        KillTimer(3);        capture.release();        if (bDttFlag == TRUE)             KillTimer(2);  // 检测状态,关闭定时器1    }    bDttFlag = FALSE;    storage = cvCreateMemStorage(0);    m_nFileType = 3;    CString ImagePathName;    if (m_pImg == NULL)    {  //获取打开文件路径        CFileDialog FileOpenDlg(TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"图像文件(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|所有文件(*.*)|*.*||");        if (FileOpenDlg.DoModal() != IDOK) return;        ImagePathName = FileOpenDlg.GetPathName();        m_pImg = cvLoadImage(ImagePathName);        m_bDrawFlag = true;        ClearControlImage(IDC_SHOWPIC);        DrawImageToHDC(m_pImg, IDC_SHOWPIC);    }    else    {        CFileDialog FileOpenDlg(TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "图像文件(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|所有文件(*.*)|*.*||");        if (FileOpenDlg.DoModal() != IDOK) return;        ImagePathName = FileOpenDlg.GetPathName();        cvReleaseImage(&m_pImg);        m_pImg = cvLoadImage(ImagePathName);        ClearControlImage(IDC_SHOWPIC);        DrawImageToHDC(m_pImg, IDC_SHOWPIC);    }}

// 人脸检测按钮

void CMy1420190255Dlg::OnBnClickedDetect(){    KillTimer(3);    bDttFlag = TRUE;    ClearControlImage(IDC_SHOWPIC);    // TODO: 在此添加控件通知处理程序代码    switch (m_nFileType)    {    case 1: //摄像机        if (cap.isOpened())            SetTimer(1, 40, NULL); // 显示摄像机检测结果        else            MessageBox("摄像机开启失败!");        break;    case 2: // 视频        if (capture.isOpened())        {            if (!bPlayOver)                {                    SetTimer(2, 40, NULL); // 显示视频检测结果                    bPlayOver = FALSE;                }            else                MessageBox("视频已结束!");        }        else            MessageBox("视频读取失败!");        break;    case 3: // 图片        face_detect(m_pImg);        DrawImageToHDC(m_pRstImg, IDC_SHOWPIC);        UpdateData(FALSE);        break;    default: // 没有文件,“0”        break;    }    m_nFileType = 0; // 使得打开某个文件,点击检测之后,继续点击无效}

二、程序界面

程序不复杂,就不写整体的总结。要说明的是,这个程序可以在3个按钮实时的进行数据文件选择,所以两两之间逻辑关系,用到了几个标记,需要自己好好梳理流程。视频文件播放没有做成循环,可以在文件读完后从新定位到第一帧,实现请参考opencv代码。定时器取的40ms,对应的帧率问25fps。不同视频文件,播放和检测的画面播放可能较快或者减慢,需要统一视频文件的帧率或者在程序中设定。

附件(CvvImage类):

//CvvImage.cpp#include "StdAfx.h"#include "CvvImage.h"// Construction/DestructionCV_INLINE RECT NormalizeRect(RECT r);CV_INLINE RECT NormalizeRect(RECT r) {    int t;    if (r.left > r.right){        t = r.left;        r.left = r.right;        r.right = t;    }    if (r.top > r.bottom) {        t = r.top;        r.top = r.bottom;        r.bottom = t;    }    return r;}CV_INLINE CvRect RectToCvRect(RECT sr);CV_INLINE CvRect RectToCvRect(RECT sr) {    sr = NormalizeRect(sr);    return cvRect(sr.left, sr.top, sr.right - sr.left, sr.bottom - sr.top);}CV_INLINE RECT CvRectToRect(CvRect sr);CV_INLINE RECT CvRectToRect(CvRect sr) {    RECT dr;    dr.left = sr.x;    dr.top = sr.y;    dr.right = sr.x + sr.width;    dr.bottom = sr.y + sr.height;    return dr;}CV_INLINE IplROI RectToROI(RECT r);CV_INLINE IplROI RectToROI(RECT r) {    IplROI roi;    r = NormalizeRect(r);    roi.xOffset = r.left;    roi.yOffset = r.top;    roi.width = r.right - r.left;    roi.height = r.bottom - r.top;    roi.coi = 0;    return roi;}void  FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin) {    assert(bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32));    BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);    memset(bmih, 0, sizeof(*bmih));    bmih->biSize = sizeof(BITMAPINFOHEADER);    bmih->biWidth = width;    bmih->biHeight = origin ? abs(height) : -abs(height);    bmih->biPlanes = 1;    bmih->biBitCount = (unsigned short)bpp;    bmih->biCompression = BI_RGB;    if (bpp == 8) {        RGBQUAD* palette = bmi->bmiColors;        int i;        for (i = 0; i < 256; i++) {            palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;            palette[i].rgbReserved = 0;        }    }}CvvImage::CvvImage() { m_img = 0; }void CvvImage::Destroy() { cvReleaseImage(&m_img); }CvvImage::~CvvImage() { Destroy(); }bool  CvvImage::Create(int w, int h, int bpp, int origin) {    const unsigned max_img_size = 10000;    if ((bpp != 8 && bpp != 24 && bpp != 32) || (unsigned)w >= max_img_size || (unsigned)h >= max_img_size ||        (origin != IPL_ORIGIN_TL && origin != IPL_ORIGIN_BL)) {        assert(0); // most probably, it is a programming error        return false;    }    if (!m_img || Bpp() != bpp || m_img->width != w || m_img->height != h) {        if (m_img && m_img->nSize == sizeof(IplImage))            Destroy();        m_img = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, bpp / 8);    }    if (m_img)        m_img->origin = origin == 0 ? IPL_ORIGIN_TL : IPL_ORIGIN_BL;    return m_img != 0;}void  CvvImage::CopyOf(CvvImage& image, int desired_color) {    IplImage* img = image.GetImage();    if (img)  {        CopyOf(img, desired_color);    }}#define HG_IS_IMAGE(img)                                                  \    ((img) != 0 && ((const IplImage*)(img))->nSize == sizeof(IplImage) && \    ((IplImage*)img)->imageData != 0)void  CvvImage::CopyOf(IplImage* img, int desired_color){    if (HG_IS_IMAGE(img))   {        int color = desired_color;        CvSize size = cvGetSize(img);        if (color < 0)            color = img->nChannels > 1;        if (Create(size.width, size.height, (!color ? 1 : img->nChannels > 1 ? img->nChannels : 3) * 8, img->origin)) {            cvConvertImage(img, m_img, 0);        }    }}bool  CvvImage::Load(const char* filename, int desired_color){    IplImage* img = cvLoadImage(filename, desired_color);    if (!img)  return false;    CopyOf(img, desired_color);    cvReleaseImage(&img);    return true;}bool  CvvImage::LoadRect(const char* filename, int desired_color, CvRect r){    if (r.width < 0 || r.height < 0) return false;    IplImage* img = cvLoadImage(filename, desired_color);    if (!img)      return false;    if (r.width == 0 || r.height == 0)   {        r.width = img->width;        r.height = img->height;        r.x = r.y = 0;    }    if (r.x > img->width || r.y > img->height || r.x + r.width < 0 || r.y + r.height < 0)   {        cvReleaseImage(&img);        return false;    }    if (r.x < 0)   {        r.width += r.x;        r.x = 0;    }    if (r.y < 0)   {        r.height += r.y;        r.y = 0;    }    if (r.x + r.width > img->width)     r.width = img->width - r.x;    if (r.y + r.height > img->height)      r.height = img->height - r.y;    cvSetImageROI(img, r);    CopyOf(img, desired_color);    cvReleaseImage(&img);    return true;}bool  CvvImage::Save(const char* filename){    if (!m_img)      return false;    cvSaveImage(filename, m_img);    return true;}void  CvvImage::Show(const char* window){    if (m_img)      cvShowImage(window, m_img);}void  CvvImage::Show(HDC dc, int x, int y, int w, int h, int from_x, int from_y){    if (m_img && m_img->depth == IPL_DEPTH_8U)   {        uchar buffer[sizeof(BITMAPINFOHEADER)+1024];        BITMAPINFO* bmi = (BITMAPINFO*)buffer;        int bmp_w = m_img->width, bmp_h = m_img->height;        FillBitmapInfo(bmi, bmp_w, bmp_h, Bpp(), m_img->origin);        from_x = MIN(MAX(from_x, 0), bmp_w - 1);        from_y = MIN(MAX(from_y, 0), bmp_h - 1);        int sw = MAX(MIN(bmp_w - from_x, w), 0);        int sh = MAX(MIN(bmp_h - from_y, h), 0);        SetDIBitsToDevice(            dc, x, y, sw, sh, from_x, from_y, from_y, sh,            m_img->imageData + from_y*m_img->widthStep,            bmi, DIB_RGB_COLORS);    }}void  CvvImage::DrawToHDC(HDC hDCDst, RECT* pDstRect){    if (pDstRect && m_img && m_img->depth == IPL_DEPTH_8U && m_img->imageData)   {        uchar buffer[sizeof(BITMAPINFOHEADER)+1024];        BITMAPINFO* bmi = (BITMAPINFO*)buffer;        int bmp_w = m_img->width, bmp_h = m_img->height;        CvRect roi = cvGetImageROI(m_img);        CvRect dst = RectToCvRect(*pDstRect);        if (roi.width == dst.width && roi.height == dst.height)     {            Show(hDCDst, dst.x, dst.y, dst.width, dst.height, roi.x, roi.y);            return;        }        if (roi.width > dst.width)     {            SetStretchBltMode(hDCDst, HALFTONE);  // handle to device context        }        else     {            SetStretchBltMode(hDCDst, COLORONCOLOR);        }        FillBitmapInfo(bmi, bmp_w, bmp_h, Bpp(), m_img->origin);        ::StretchDIBits(            hDCDst,            dst.x, dst.y, dst.width, dst.height,            roi.x, roi.y, roi.width, roi.height,            m_img->imageData, bmi, DIB_RGB_COLORS, SRCCOPY);    }}void  CvvImage::Fill(int color){    cvSet(m_img, cvScalar(color & 255, (color >> 8) & 255, (color >> 16) & 255, (color >> 24) & 255));}
// CvvImage.h#pragma once#ifndef CVVIMAGE_CLASS_DEF#define CVVIMAGE_CLASS_DEF#include "opencv2/opencv.hpp"class  CvvImage{public:    CvvImage();    virtual ~CvvImage();    virtual bool  Create(int width, int height, int bits_per_pixel, int image_origin = 0);    virtual bool  Load(const char* filename, int desired_color = 1);    virtual bool  LoadRect(const char* filename,        int desired_color, CvRect r);#if defined WIN32 || defined _WIN32    virtual bool  LoadRect(const char* filename,        int desired_color, RECT r)    {        return LoadRect(filename, desired_color,            cvRect(r.left, r.top, r.right - r.left, r.bottom - r.top));    }#endif    virtual bool  Save(const char* filename);    virtual void  CopyOf(CvvImage& image, int desired_color = -1);    virtual void  CopyOf(IplImage* img, int desired_color = -1);    IplImage* GetImage() { return m_img; };    virtual void  Destroy(void);    int Width() { return !m_img ? 0 : !m_img->roi ? m_img->width : m_img->roi->width; };    int Height() { return !m_img ? 0 : !m_img->roi ? m_img->height : m_img->roi->height; };    int Bpp() { return m_img ? (m_img->depth & 255)*m_img->nChannels : 0; };    virtual void  Fill(int color);    virtual void  Show(const char* window);#if defined WIN32 || defined _WIN32    virtual void  Show(HDC dc, int x, int y, int width, int height, int from_x = 0, int from_y = 0);    virtual void  DrawToHDC(HDC hDCDst, RECT* pDstRect);#endifprotected:    IplImage*  m_img;};//typedef CvvImage CImage;namespace cv{    typedef CvvImage CImage;}#endif

完整的工程文件打包(包含了最初的控制台程序)点击下载。(其他opencv版本,修改引用和添加依赖库,重新编译即可)

2 0