当前位置: 代码迷 >> VC/MFC >> 基于opencv的MFC多摄影机视场标定软件
  详细解决方案

基于opencv的MFC多摄影机视场标定软件

热度:807   发布时间:2016-05-02 03:13:36.0
基于opencv的MFC多摄像机视场标定软件

一、项目需求
确定网络摄像机的监控视场在全景地图上的精确位置,并采用简单而高效的方法将其所在视场标定并投影到全景地图上,项目要求能够将多路网络摄像机的视场标定在全景地图上,并实现这样的功能:当调取当前摄像机视频播放时,其他摄像机处于暂停状态。

二、项目分析
1、如何将视频投影到全景地图上,实质上就是如何将一幅幅视频帧图像与全景图像进行配准,关于图像配准方面的研究已经有很多方法,本项目采用基于互信息的图像配准算法寻找四对精确特征匹配点对。
2、设计一个多摄像机视场标定的人机交互界面的软件系统,要求能够将多路网络摄像机的视场标定在全景地图上,并实现调取当前摄像机视频播放时,其他摄像机处于暂停状态。

三、最终软件界面设计如下:
这里写图片描述

四、部分代码如下:

// CamCalibrationDlg.h : 头文件//#pragma once#include "cv.h"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include <opencv2/core/core.hpp>#include "opencv2/calib3d/calib3d.hpp"#include "highgui.h"#include "cxcore.h"#include "CvvImage.h"#include <vector>#include "afxwin.h"using namespace cv;using namespace std;// CCamCalibrationDlg 对话框class CCamCalibrationDlg : public CDialogEx{// 构造public:    CCamCalibrationDlg(CWnd* pParent = NULL);   // 标准构造函数// 对话框数据    enum { IDD = IDD_CAMCALIBRATION_DIALOG };    protected:    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现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()private:    //全屏显示    BOOL bFullScreen;    CRect rectFullScreen;    WINDOWPLACEMENT m_struOldWndpl;//结构中包含了有关窗口在屏幕上位置的信息    WINDOWPLACEMENT m_struOldWndpPic;//PICTURE控件在屏幕上位置的信息    vector<Point2f> m_newpoints; // 需要利用互信息子函数更新得到的匹配点集(要求四个)     vector<Point2f> m_points1;// 视频帧图像匹配点集(要求四个)    vector<Point2f> m_points2;// 全景图像匹配点集,初始匹配点集(要求四个)    Mat TheImage; //全景图    Mat CamImage; //视频帧图像    CStatic m_TheImage;//是全景图界面的显示窗口    CStatic m_CamImage;//是视频帧图像界面的显示窗口    CRect m_TheImageRect;//显示全景窗口的矩形窗    CRect m_CamImageRect;//显示视频窗口的矩形窗    CString m_pt;    //与编辑框绑定的成员变量,将获取的点集中显示在编辑框中    double  m_scale;    //全景图缩放比率    CvCapture *capture;    int g_m_run;  //视频暂停标志    Mat H1; //投影变换矩阵1    Mat H2; //投影变换矩阵2    Mat H3; //投影变换矩阵3    Mat H4; //投影变换矩阵4    Mat H5; //投影变换矩阵5    Mat H6; //投影变换矩阵6    //存储投影后四个顶点的坐标    vector<Point2f> m_pointsh1;    vector<Point2f> m_pointsh2;    vector<Point2f> m_pointsh3;    vector<Point2f> m_pointsh4;    vector<Point2f> m_pointsh5;    vector<Point2f> m_pointsh6;    int m_Radio;     int m_Pointdownflag;public:    void ShowMatImgToWnd(CWnd* pWnd, cv::Mat img);    afx_msg void OnBnClickedPictureOpen();    double Entropy(Mat img);//单幅图像信息熵计算    double ComEntropy(Mat img1, Mat img2, double img1_entropy, double img2_entropy);// 两幅图像联合信息熵计算    Point2f Refresh_MacthPoints(Point2f point1, Point2f point2);//查找全景图上四个精确匹配点集    Point2f MousePointToImgPixel(Mat img, CRect rect, Point2f point);// 鼠标点击坐标转换为实际图像坐标《分辨率转换》    afx_msg void OnBnClickedVideoPreview();    afx_msg void OnBnClickedVideoPlay();    afx_msg void OnBnClickedVideoPause();    // afx_msg void OnTimer(UINT_PTR nIDEvent);    afx_msg void OnBnClickedRadio1();    afx_msg void OnBnClickedRadio2();    afx_msg void OnBnClickedRadio3();    afx_msg void OnBnClickedRadio4();    afx_msg void OnBnClickedRadio5();    afx_msg void OnBnClickedRadio6();    afx_msg void OnBnClickedPictureChangeSize();    afx_msg void OnBnClickedPicturePoints();    afx_msg void OnBnClickedVideoPoints();    afx_msg void OnBnClickedSavePoints();    afx_msg void OnBnClickedAdjustPoints();    afx_msg void OnBnClickedQuit();    afx_msg void OnBnClickedHomgtaphyFind();    afx_msg void OnBnClickedCalibration();    Mat  showFinal(Mat src1, Mat src2); //将标定后的组合图显示出来    afx_msg void OnMouseMove(UINT nFlags, CPoint point);    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);    ////全屏显示    afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);    afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);    void drawpic(IplImage* img, unsigned int id);    //绘图到&nbsp;MFC 的 Picture Control 控件相关函数    //参数一为 OpenCV&nbsp;的图像数据结构类,参数二为 Picture Control 控件的id};
// CamCalibrationDlg.cpp : 实现文件//#include "stdafx.h"#include "CamCalibration.h"#include "CamCalibrationDlg.h"#include "afxdialogex.h"#ifdef _DEBUG#define new DEBUG_NEW#endif#define   WIDTHBYTES(bits) (((bits)+31)/32*4)//用于使图像宽度所占字节数为4byte的倍数  // 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx{public:    CAboutDlg();// 对话框数据    enum { IDD = IDD_ABOUTBOX };    protected:    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现protected:    DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){    CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP()// CCamCalibrationDlg 对话框CCamCalibrationDlg::CCamCalibrationDlg(CWnd* pParent /*=NULL*/)    : CDialogEx(CCamCalibrationDlg::IDD, pParent){    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}void CCamCalibrationDlg::DoDataExchange(CDataExchange* pDX){    CDialogEx::DoDataExchange(pDX);    DDX_Control(pDX, IDC_PICTURE, m_TheImage);//自定义控件和IDC界面控件绑定    DDX_Control(pDX, IDC_VIDEO, m_CamImage);//自定义控件和IDC界面控件绑定    DDX_Text(pDX, IDC_EDIT_POINTSHOW, m_pt);//自定义控件和IDC编辑框绑定    DDX_Text(pDX, IDC_EDIT_RESIZE, m_scale);//自定义控件和IDC编辑框绑定}BEGIN_MESSAGE_MAP(CCamCalibrationDlg, CDialogEx)    ON_WM_SYSCOMMAND()    ON_WM_PAINT()    ON_WM_QUERYDRAGICON()    ON_BN_CLICKED(IDC_PICTURE_OPEN, &CCamCalibrationDlg::OnBnClickedPictureOpen)    ON_BN_CLICKED(IDC_VIDEO_PREVIEW, &CCamCalibrationDlg::OnBnClickedVideoPreview)    ON_BN_CLICKED(IDC_VIDEO_PLAY, &CCamCalibrationDlg::OnBnClickedVideoPlay)    ON_BN_CLICKED(IDC_VIDEO_PAUSE, &CCamCalibrationDlg::OnBnClickedVideoPause)    // ON_WM_TIMER()    ON_BN_CLICKED(IDC_RADIO1, &CCamCalibrationDlg::OnBnClickedRadio1)    ON_BN_CLICKED(IDC_RADIO2, &CCamCalibrationDlg::OnBnClickedRadio2)    ON_BN_CLICKED(IDC_RADIO3, &CCamCalibrationDlg::OnBnClickedRadio3)    ON_BN_CLICKED(IDC_RADIO4, &CCamCalibrationDlg::OnBnClickedRadio4)    ON_BN_CLICKED(IDC_RADIO5, &CCamCalibrationDlg::OnBnClickedRadio5)    ON_BN_CLICKED(IDC_RADIO6, &CCamCalibrationDlg::OnBnClickedRadio6)    ON_BN_CLICKED(IDC_PICTURE_CHANGE_SIZE, &CCamCalibrationDlg::OnBnClickedPictureChangeSize)    ON_BN_CLICKED(IDC_PICTURE_POINTS, &CCamCalibrationDlg::OnBnClickedPicturePoints)    ON_BN_CLICKED(IDC_VIDEO_POINTS, &CCamCalibrationDlg::OnBnClickedVideoPoints)    ON_BN_CLICKED(IDC_ADJUST_POINTS, &CCamCalibrationDlg::OnBnClickedAdjustPoints)    ON_BN_CLICKED(IDC_QUIT, &CCamCalibrationDlg::OnBnClickedQuit)    ON_BN_CLICKED(IDC_HOMGTAPHY_FIND, &CCamCalibrationDlg::OnBnClickedHomgtaphyFind)    ON_BN_CLICKED(IDC_CALIBRATION, &CCamCalibrationDlg::OnBnClickedCalibration)    ON_WM_MOUSEMOVE()    ON_WM_LBUTTONDOWN()    ON_WM_LBUTTONDBLCLK()    ON_WM_GETMINMAXINFO()    ON_BN_CLICKED(IDC_SAVE_POINTS, &CCamCalibrationDlg::OnBnClickedSavePoints)END_MESSAGE_MAP()// CCamCalibrationDlg 消息处理程序BOOL CCamCalibrationDlg::OnInitDialog(){    CDialogEx::OnInitDialog();    // 将“关于...”菜单项添加到系统菜单中。    // IDM_ABOUTBOX 必须在系统命令范围内。    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);    ASSERT(IDM_ABOUTBOX < 0xF000);    CMenu* pSysMenu = GetSystemMenu(FALSE);    if (pSysMenu != NULL)    {        BOOL bNameValid;        CString strAboutMenu;        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);        ASSERT(bNameValid);        if (!strAboutMenu.IsEmpty())        {            pSysMenu->AppendMenu(MF_SEPARATOR);            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);        }    }    // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动    //  执行此操作    SetIcon(m_hIcon, TRUE);         // 设置大图标    SetIcon(m_hIcon, FALSE);        // 设置小图标    ShowWindow(SW_MAXIMIZE);    // TODO:  在此添加额外的初始化代码    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE}void CCamCalibrationDlg::OnSysCommand(UINT nID, LPARAM lParam){    if ((nID & 0xFFF0) == IDM_ABOUTBOX)    {        CAboutDlg dlgAbout;        dlgAbout.DoModal();    }    else    {        CDialogEx::OnSysCommand(nID, lParam);    }}// 如果向对话框添加最小化按钮,则需要下面的代码//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,//  这将由框架自动完成。void CCamCalibrationDlg::OnPaint(){    if (IsIconic())    {        CPaintDC dc(this); // 用于绘制的设备上下文        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);        // 使图标在工作区矩形中居中        int cxIcon = GetSystemMetrics(SM_CXICON);        int cyIcon = GetSystemMetrics(SM_CYICON);        CRect rect;        GetClientRect(&rect);        int x = (rect.Width() - cxIcon + 1) / 2;        int y = (rect.Height() - cyIcon + 1) / 2;        // 绘制图标        dc.DrawIcon(x, y, m_hIcon);    }    else    {        CDialogEx::OnPaint();    }}//当用户拖动最小化窗口时系统调用此函数取得光标//显示。HCURSOR CCamCalibrationDlg::OnQueryDragIcon(){    return static_cast<HCURSOR>(m_hIcon);}// 将图像显示到对应的图像框void CCamCalibrationDlg::ShowMatImgToWnd(CWnd* pWnd, cv::Mat img){    if (img.empty())        return;    CDC *pDC = pWnd->GetDC();    HDC hDC = pDC->GetSafeHdc();    CRect rect;    pWnd->GetClientRect(&rect);    IplImage Iimg = img;    CvvImage cimg;    cimg.CopyOf(&Iimg); // 复制图片    cimg.DrawToHDC(hDC, &rect); // 将图片绘制到显示控件的指定区域内    ReleaseDC(pDC);}void CCamCalibrationDlg::OnBnClickedPictureOpen(){    // TODO:  在此添加控件通知处理程序代码    CFileDialog dlg(        TRUE, _T("*.bmp;*.jpg;*.jpeg"), NULL,        OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,        _T("image files (*.bmp; *.jpg;*.jpeg) |*.bmp; *.jpg; *.jpeg | All Files (*.*) |*.*||"), NULL        );                                        // 选项图片的约定    dlg.m_ofn.lpstrTitle = _T("Open Image");    // 打开文件对话框的标题名    if (dlg.DoModal() != IDOK)                    // 判断是否获得图片        return;    CString mPath = dlg.GetPathName();            // 获取图片路径    TheImage = cvLoadImage(mPath, 1);    // 读取图片、缓存到一个局部变量 ipl 中    // Mat TheImage = imread(mPath,1);    // drawpic(TheImage, IDC_PICTURE);    ShowMatImgToWnd(GetDlgItem(IDC_PICTURE), TheImage);      UpdateWindow();}//单幅图像信息熵计算double CCamCalibrationDlg::Entropy(Mat img){    double temp[256] = { 0.0 };    // 计算每个像素的累积值    for (int m = 0; m<img.rows; m++)    {// 有效访问行列的方式        const uchar* t = img.ptr<uchar>(m);        for (int n = 0; n<img.cols; n++)        {            int i = t[n];            temp[i] = temp[i] + 1;        }    }    // 计算每个像素的概率    for (int i = 0; i<256; i++)    {        temp[i] = temp[i] / (img.rows*img.cols);    }    double result = 0;    // 计算图像信息熵    for (int i = 0; i<256; i++)    {        if (temp[i] == 0.0)            result = result;        else            result = result - temp[i] * (log(temp[i]) / log(2.0));    }    return result;}// 两幅图像联合信息熵计算double CCamCalibrationDlg::ComEntropy(Mat img1, Mat img2, double img1_entropy, double img2_entropy){    double temp[256][256] = { 0.0 };    // 计算联合图像像素的累积值    for (int m1 = 0, m2 = 0; m1 < img1.rows, m2 < img2.rows; m1++, m2++)    {    // 有效访问行列的方式        const uchar* t1 = img1.ptr<uchar>(m1);        const uchar* t2 = img2.ptr<uchar>(m2);        for (int n1 = 0, n2 = 0; n1 < img1.cols, n2 < img2.cols; n1++, n2++)        {            int i = t1[n1], j = t2[n2];            temp[i][j] = temp[i][j] + 1;        }    }    // 计算每个联合像素的概率    for (int i = 0; i < 256; i++)    {        for (int j = 0; j < 256; j++)        {            temp[i][j] = temp[i][j] / (img1.rows*img1.cols);        }    }    double result = 0.0;    //计算图像联合信息熵    for (int i = 0; i < 256; i++)    {        for (int j = 0; j < 256; j++)        {            if (temp[i][j] == 0.0)                result = result;            else                result = result - temp[i][j] * (log(temp[i][j]) / log(2.0));        }    }    //得到两幅图像的互信息熵    img1_entropy = Entropy(img1);    img2_entropy = Entropy(img2);    result = img1_entropy + img2_entropy - result;    return result;}Point2f CCamCalibrationDlg::Refresh_MacthPoints(Point2f point1, Point2f point2){    int IMGSIDE = 10; //截取正方形子图的边长的一半    int ScanSide = 5; //搜索半径    Mat dst1;   // 视频帧图像上截取子图    Mat dst2;   // 全景图像上截取子图    Point2f CLSPoint, CRXPoint;    //截取视频帧图像子图     CLSPoint.x = point1.x - IMGSIDE;    CLSPoint.y = point1.y - IMGSIDE;    CRXPoint.x = point1.x + IMGSIDE;    CRXPoint.y = point1.y + IMGSIDE;    Rect ROI0(CLSPoint, CRXPoint);    CamImage(ROI0).copyTo(dst1);    Point2f TLSPoint, TRXPoint;    //截取全景图图像子图    TLSPoint.x = point2.x - IMGSIDE;    TLSPoint.y = point2.y - IMGSIDE;    TRXPoint.x = point2.x + IMGSIDE;    TRXPoint.y = point2.y + IMGSIDE;    Rect ROI00(TLSPoint, TRXPoint);    TheImage(ROI00).copyTo(dst2);    //计算对应图像子块的互信息熵    double OneImaEntropy1; //视频帧图像信息熵    double OneImaEntropy2; //全景图信息熵    double TwoImaEntropy; //初始互信息熵    OneImaEntropy1 = Entropy(dst1);    OneImaEntropy2 = Entropy(dst2);    TwoImaEntropy = ComEntropy(dst1, dst2, OneImaEntropy1, OneImaEntropy2);    Point2f NewPoints; // 更新的匹配点    Point2f Points;    Mat dst;    double OneImaEntropy;    double NewTwoEntropy;    double MAX = TwoImaEntropy;    for (int i = -ScanSide; i <= ScanSide; i++)    {        for (int j = -ScanSide; j <= ScanSide; j++)        {            NewPoints.x = point2.x + i;            NewPoints.y = point2.y + j;            Rect rect(NewPoints.x - IMGSIDE, NewPoints.y - IMGSIDE, 2 * IMGSIDE, 2 * IMGSIDE);            TheImage(rect).copyTo(dst);            OneImaEntropy = Entropy(dst);            NewTwoEntropy = ComEntropy(dst, dst1, OneImaEntropy, OneImaEntropy1);            if (NewTwoEntropy > MAX)   // 计算所有互信息熵,然后取最大值的坐标点            {                MAX = NewTwoEntropy;                //Points = NewPoints;                Points.x = point2.x + i;                Points.y = point2.y + j;            }        }    }    return Points;}// 鼠标点击坐标转换为实际图像坐标Point2f CCamCalibrationDlg::MousePointToImgPixel(Mat img, CRect rect, Point2f point){    Point2f imgpix;    imgpix.x = point.x / (rect.right - rect.left) * img.cols;    imgpix.y = point.y / (rect.bottom - rect.top) * img.rows;    return imgpix;}void CCamCalibrationDlg::OnBnClickedVideoPreview(){    switch (m_Radio)    {       case 1:           capture = cvCaptureFromAVI("51_20150115161213.avi");          break;      case 2:          capture = cvCaptureFromAVI("53_20150115161348.avi");          break;      case 3:          capture = cvCaptureFromAVI("54_20150115161515.avi");          break;      case 4:           capture = cvCaptureFromAVI("56_20150115161648.avi");          break;      case 5:          capture = cvCaptureFromAVI("58_20150115161823.avi");          break;      case 6:          capture = cvCaptureFromAVI("59_20150115161946.avi");          break;    default:        break;    }    CamImage = cvQueryFrame(capture); //获取一帧图片     //CamImage = imread("22.jpg");    ShowMatImgToWnd(GetDlgItem(IDC_VIDEO), CamImage);}void CCamCalibrationDlg::OnBnClickedVideoPlay(){    // TODO:  在此添加控件通知处理程序代码    //if (!capture)    //{    //  // capture = cvCaptureFromCAM(0);//调取摄像头    //  capture = cvCaptureFromAVI("53_20150115161348.avi");//调取视频    //}    Mat m_Frame;    // m_Frame = cvQueryFrame(capture);    g_m_run = 1;    while(g_m_run == 1)    {        m_Frame = cvQueryFrame(capture);        ShowMatImgToWnd(GetDlgItem(IDC_VIDEO), m_Frame);        char c = cvWaitKey(33);    }    // 设置计时器,每30ms触发一次事件      // SetTimer(1, 1000 / 25, NULL);}void CCamCalibrationDlg::OnBnClickedVideoPause(){    // TODO:  在此添加控件通知处理程序代码    g_m_run = 0;}//void CCamCalibrationDlg::OnTimer(UINT_PTR nIDEvent)//{//  // TODO:  在此添加消息处理程序代码和/或调用默认值//  Mat m_Frame;//  m_Frame = cvQueryFrame(capture);//////  if (g_m_run = 1)//  {//      ShowMatImgToWnd(GetDlgItem(IDC_VIDEO), m_Frame);//  }////  CDialogEx::OnTimer(nIDEvent);//}void CCamCalibrationDlg::OnBnClickedRadio1(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 1;}void CCamCalibrationDlg::OnBnClickedRadio2(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 2;}void CCamCalibrationDlg::OnBnClickedRadio3(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 3;}void CCamCalibrationDlg::OnBnClickedRadio4(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 4;}void CCamCalibrationDlg::OnBnClickedRadio5(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 5;}void CCamCalibrationDlg::OnBnClickedRadio6(){    // TODO:  在此添加控件通知处理程序代码    m_Radio = 6;}void CCamCalibrationDlg::OnBnClickedPictureChangeSize(){    // TODO:  在此添加控件通知处理程序代码    Mat tmpImage = TheImage; //将全景图赋给临时图像    Mat dstImage; //目标图像    UpdateData(true); // 将控件数据传递给变量,更新显示        //double    m_scale = 1.75;    //全景图缩放比率    resize(tmpImage, dstImage, Size(tmpImage.cols * m_scale, tmpImage.rows * m_scale), CV_INTER_LINEAR);    //pyrUp(tmpImage, dstImage, Size(tmpImage.cols * 0.5 , tmpImage.rows * 0.5));    imshow("1", dstImage);    TheImage = dstImage;}void CCamCalibrationDlg::OnBnClickedPicturePoints(){    // TODO:  在此添加控件通知处理程序代码         m_Pointdownflag = 1;    }void CCamCalibrationDlg::OnBnClickedVideoPoints(){    // TODO:  在此添加控件通知处理程序代码        m_Pointdownflag = 2;     }void CCamCalibrationDlg::OnBnClickedSavePoints(){    // TODO:  在此添加控件通知处理程序代码    m_Pointdownflag = 3;}void CCamCalibrationDlg::OnBnClickedAdjustPoints(){    // TODO:  在此添加控件通知处理程序代码    vector<Point2f> Newpoints;// 需要利用互信息子函数更新得到的匹配点集(要求四个)     vector<Point2f> Points1;// 视频帧图像匹配点集(要求四个)    vector<Point2f> Points2;// 全景图像匹配点集,初始匹配点集(要求四个)    for (int i = 0; i < m_points1.size(); i++)    {        Point2f CamImagePix = MousePointToImgPixel(CamImage, m_CamImageRect, m_points1[i]);        Points1.push_back(CamImagePix);    }    for (int i = 0; i < m_points2.size(); i++)    {        Point2f TheImagePix = MousePointToImgPixel(TheImage, m_TheImageRect, m_points2[i]);        Points2.push_back(TheImagePix);    }    for (int i = 0; i < Points2.size(); i++)    {        Point2f Newpoint = Refresh_MacthPoints(Points1[i], Points2[i]);//查找全景图上四个精确匹配点集        Newpoints.push_back(Newpoint);    }    m_newpoints = Newpoints;    //清空临时容器     Newpoints.clear();     Points1.clear();     Points2.clear();}void CCamCalibrationDlg::OnBnClickedQuit(){    // TODO:  在此添加控件通知处理程序代码    CDialogEx::OnCancel();}void CCamCalibrationDlg::OnBnClickedHomgtaphyFind(){    // TODO:  在此添加控件通知处理程序代码    vector <Point2f> TheImgPix;    vector <Point2f> CamImgPix;    for (int i = 0; i < m_points1.size(); i++)    {        Point2f CamImagePix = MousePointToImgPixel(CamImage, m_CamImageRect, m_points1[i]);        CamImgPix.push_back(CamImagePix);    }    //校准后匹配    //for (int i = 0; i < m_newpoints.size(); i++)    //{    //  Point2f TheImagePix = MousePointToImgPixel(TheImage, m_TheImageRect, m_newpoints[i]);    //  TheImgPix.push_back(TheImagePix);    //}     // TheImgPix = m_newpoints;    //校准前匹配    for (int i = 0; i < m_points2.size(); i++)    {        Point2f TheImagePix = MousePointToImgPixel(TheImage, m_TheImageRect, m_points2[i]);        TheImgPix.push_back(TheImagePix);    }     FileStorage fs1(".\\H1.xml", FileStorage::WRITE);     FileStorage fs2(".\\H2.xml", FileStorage::WRITE);     FileStorage fs3(".\\H3.xml", FileStorage::WRITE);     FileStorage fs4(".\\H4.xml", FileStorage::WRITE);     FileStorage fs5(".\\H5.xml", FileStorage::WRITE);     FileStorage fs6(".\\H6.xml", FileStorage::WRITE);     switch (m_Radio)     {     case 1:         H1 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs1 << "H1" << H1;         fs1.release();         break;     case 2:         H2 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs2 << "H2" << H2;         fs2.release();         break;     case 3:         H3 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs3 << "H3" << H3;         fs3.release();         break;     case 4:         H4 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs4 << "H4" << H4;         fs4.release();         break;     case 5:         H5 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs5 << "H5" << H5;         fs5.release();         break;     case 6:         H6 = getPerspectiveTransform(CamImgPix, TheImgPix);//src ,dst         fs6 << "H6" << H6;         fs6.release();         break;     default:         break;     }     TheImgPix.clear();//计算完H后清空     CamImgPix.clear();}void CCamCalibrationDlg::OnBnClickedCalibration(){    // TODO:  在此添加控件通知处理程序代码    //清空容器里的坐标点     m_newpoints.clear();      m_points1.clear();     m_points2.clear();    Mat CamImage1;    CamImage1 = cvQueryFrame(capture);    Mat logoWarped;    if (true)    {        CamImage1 = cvQueryFrame(capture);        switch (m_Radio)        {        case 1:            warpPerspective(CamImage1, logoWarped, H1, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        case 2:            warpPerspective(CamImage1, logoWarped, H2, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        case 3:            warpPerspective(CamImage1, logoWarped, H3, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        case 4:            warpPerspective(CamImage1, logoWarped, H4, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        case 5:            warpPerspective(CamImage1, logoWarped, H5, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        case 6:            warpPerspective(CamImage1, logoWarped, H6, TheImage.size(), INTER_LINEAR, BORDER_CONSTANT);            break;        default:            break;        }        Mat TheImage1;        TheImage1 = showFinal(TheImage, logoWarped);        TheImage = TheImage1;        //ShowMatImgToWnd(GetDlgItem(IDC_VIDEO), m_Frame);    }    // 设置计时器,每30ms触发一次事件      SetTimer(1, 1000 / 25, NULL);}Mat  CCamCalibrationDlg::showFinal(Mat src1, Mat src2){    Mat gray, gray_inv, src1final, src2final;    cvtColor(src2, gray, CV_BGR2GRAY);    threshold(gray, gray, 0, 255, CV_THRESH_BINARY);    //adaptiveThreshold(gray,gray,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,5,4);    bitwise_not(gray, gray_inv);    src1.copyTo(src1final, gray_inv);    src2.copyTo(src2final, gray);    Mat finalImage = src1final + src2final;    // src1 = finalImage;    ShowMatImgToWnd(GetDlgItem(IDC_PICTURE), finalImage);    return finalImage;}void CCamCalibrationDlg::OnMouseMove(UINT nFlags, CPoint point){    // TODO:  在此添加消息处理程序代码和/或调用默认值          switch (m_Pointdownflag)        {        case 1:            m_TheImage.GetWindowRect(m_TheImageRect);//获取显示全景图像所在矩形窗的坐标            ScreenToClient(m_TheImageRect);         //转换为对话框上的坐标            point.x -= m_TheImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和            point.y -= m_TheImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标            if (point.x>(m_TheImageRect.right - m_TheImageRect.left) || point.y>(m_TheImageRect.bottom - m_TheImageRect.top) || point.x<0 || point.y<0)                ;            else            {                               m_pt.Format("x=%f,y=%f", float(point.x), float(point.y));            }            UpdateData(false); // 将数据传给控件显示,更新显示            break;        case 2:            m_CamImage.GetWindowRect(m_CamImageRect);//获取显示视频帧图像所在矩形窗            ScreenToClient(m_CamImageRect);         //转换为对话框上的坐标            point.x -= m_CamImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和            point.y -= m_CamImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标            if (point.x>(m_CamImageRect.right - m_CamImageRect.left) || point.y>(m_CamImageRect.bottom - m_CamImageRect.top) || point.x < 0 || point.y < 0)                ;            else            {                               m_pt.Format("x=%f,y=%f", float(point.x), float(point.y));            }            UpdateData(false); // 将数据传给控件显示,更新显示            break;        case 3:            m_TheImage.GetWindowRect(m_TheImageRect);//获取显示全景图像所在矩形窗的坐标            ScreenToClient(m_TheImageRect);         //转换为对话框上的坐标            point.x -= m_TheImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和            point.y -= m_TheImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标            if (point.x>(m_TheImageRect.right - m_TheImageRect.left) || point.y>(m_TheImageRect.bottom - m_TheImageRect.top) || point.x<0 || point.y<0)                ;            else            {                m_pt.Format("x=%f,y=%f", float(point.x), float(point.y));            }            UpdateData(false); // 将数据传给控件显示,更新显示            break;        default:            break;        }    CDialogEx::OnMouseMove(nFlags, point);}void CCamCalibrationDlg::OnLButtonDown(UINT nFlags, CPoint point){    // TODO:  在此添加消息处理程序代码和/或调用默认值    switch (m_Pointdownflag)    {    case 1:        m_TheImage.GetWindowRect(m_TheImageRect);//获取显示全景图像所在矩形窗的坐标        ScreenToClient(m_TheImageRect);         //转换为对话框上的坐标        point.x -= m_TheImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和        point.y -= m_TheImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标        if (m_points2.size() <= 3)        {            m_points2.push_back(Point2f(float(point.x), float(point.y)));        }        else return;        break;    case 2:        m_CamImage.GetWindowRect(m_CamImageRect);//获取显示视频帧图像所在矩形窗        ScreenToClient(m_CamImageRect);         //转换为对话框上的坐标        point.x -= m_CamImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和        point.y -= m_CamImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标        if (m_points1.size() <= 3)        {            m_points1.push_back(Point2f(float(point.x), float(point.y)));        }        else return;        break;    case 3:        m_TheImage.GetWindowRect(m_TheImageRect);//获取显示全景图像所在矩形窗的坐标        ScreenToClient(m_TheImageRect);         //转换为对话框上的坐标        point.x -= m_TheImageRect.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和        point.y -= m_TheImageRect.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标        //Point2f imgpix;        //imgpix.x = point.x / (m_TheImageRect.right - m_TheImageRect.left) * TheImage.cols;        //imgpix.y = point.y / (m_TheImageRect.bottom - m_TheImageRect.top) * TheImage.rows;        switch (m_Radio)        {        case 1:            if (m_pointsh1.size() <= 3)            {                m_pointsh1.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        case 2:            if (m_pointsh2.size() <= 3)            {                m_pointsh2.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        case 3:            if (m_pointsh3.size() <= 3)            {                m_pointsh3.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        case 4:            if (m_pointsh4.size() <= 3)            {                m_pointsh4.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        case 5:            if (m_pointsh5.size() <= 3)            {                m_pointsh5.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        case 6:            if (m_pointsh6.size() <= 3)            {                m_pointsh6.push_back(Point2f(float(point.x), float(point.y)));            }            else return;            break;        /*default:            break;*/        }           //default:    //  break;    }    CDialogEx::OnLButtonDown(nFlags, point);}void CCamCalibrationDlg::OnLButtonDblClk(UINT nFlags, CPoint point){    // TODO:  在此添加消息处理程序代码和/或调用默认值    if (!bFullScreen)    {        bFullScreen = true;        //获取系统屏幕宽高        int g_iCurScreenWidth = GetSystemMetrics(SM_CXSCREEN);        int g_iCurScreenHeight = GetSystemMetrics(SM_CYSCREEN);        //用m_struOldWndpl得到当前窗口的显示状态和窗体位置,以供退出全屏后使用        GetWindowPlacement(&m_struOldWndpl);        GetDlgItem(IDC_PICTURE)->GetWindowPlacement(&m_struOldWndpPic);        //计算出窗口全屏显示客户端所应该设置的窗口大小,主要为了将不需要显示的窗体边框等部分排除在屏幕外        CRect rectWholeDlg;        CRect rectClient;        GetWindowRect(&rectWholeDlg);//得到当前窗体的总的相对于屏幕的坐标        RepositionBars(0, 0xffff, AFX_IDW_PANE_FIRST, reposQuery, &rectClient);//得到客户区窗口坐标        ClientToScreen(&rectClient);//将客户区相对窗体的坐标转为相对屏幕坐标        //GetDlgItem(IDC_STATIC_PICSHOW)->GetWindowRect(rectClient);//得到PICTURE控件坐标        rectFullScreen.left = rectWholeDlg.left - rectClient.left;        rectFullScreen.top = rectWholeDlg.top - rectClient.top;        rectFullScreen.right = rectWholeDlg.right + g_iCurScreenWidth - rectClient.right;        rectFullScreen.bottom = rectWholeDlg.bottom + g_iCurScreenHeight - rectClient.bottom;        //设置窗口对象参数,为全屏做好准备并进入全屏状态        WINDOWPLACEMENT struWndpl;        struWndpl.length = sizeof(WINDOWPLACEMENT);        struWndpl.flags = 0;        struWndpl.showCmd = SW_SHOWNORMAL;        struWndpl.rcNormalPosition = rectFullScreen;        SetWindowPlacement(&struWndpl);//该函数设置指定窗口的显示状态和显示大小位置等,是我们该程序最为重要的函数        //将PICTURE控件的坐标设为全屏大小        GetDlgItem(IDC_PICTURE)->MoveWindow(CRect(0, 0, g_iCurScreenWidth, g_iCurScreenHeight));    }    else    {        GetDlgItem(IDC_PICTURE)->SetWindowPlacement(&m_struOldWndpPic);        SetWindowPlacement(&m_struOldWndpl);        bFullScreen = false;    }    CDialogEx::OnLButtonDblClk(nFlags, point);}void CCamCalibrationDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI){    // TODO:  在此添加消息处理程序代码和/或调用默认值    if (bFullScreen)    {        lpMMI->ptMaxSize.x = rectFullScreen.Width();        lpMMI->ptMaxSize.y = rectFullScreen.Height();        lpMMI->ptMaxPosition.x = rectFullScreen.left;        lpMMI->ptMaxPosition.y = rectFullScreen.top;        lpMMI->ptMaxTrackSize.x = rectFullScreen.Width();        lpMMI->ptMaxTrackSize.y = rectFullScreen.Height();    }    CDialogEx::OnGetMinMaxInfo(lpMMI);}void CCamCalibrationDlg::drawpic(IplImage* img, unsigned int id)//CShowPictureInFullScreenDlg 为对话框类名{    BYTE *g_pBits;    HDC g_hMemDC;    HBITMAP g_hBmp, g_hOldBmp;    CDC *pDC;    CStatic *pic;    int width, height;    CRect rect;    pDC = GetDlgItem(id)->GetDC();    pic = (CStatic*)GetDlgItem(id);    pic->GetClientRect(&rect);    width = rect.Width();    height = rect.Height();    g_hMemDC = ::CreateCompatibleDC(pDC->m_hDC);//创建兼容DC    BYTE bmibuf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];    memset(bmibuf, 0, sizeof(bmibuf));    BITMAPINFO *pbmi = (BITMAPINFO*)bmibuf;    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);    pbmi->bmiHeader.biWidth = img->width;    pbmi->bmiHeader.biHeight = img->height;    pbmi->bmiHeader.biPlanes = 1;    pbmi->bmiHeader.biBitCount = 24;    pbmi->bmiHeader.biCompression = BI_RGB;    g_hBmp = ::CreateDIBSection(g_hMemDC, pbmi, DIB_RGB_COLORS, (void**)&g_pBits, 0, 0);//创建应用程序可以直接写入的、与设备无关的位图(DIB)    g_hOldBmp = (HBITMAP)::SelectObject(g_hMemDC, g_hBmp);//复原兼容DC数据    BitBlt(g_hMemDC, 0, 0, width, height, pDC->m_hDC, 0, 0, SRCCOPY);    //修改图像内容:g_pBits    int l_width = WIDTHBYTES(img->width* pbmi->bmiHeader.biBitCount);    for (int row = 0; row < img->height; row++)        memcpy(&g_pBits[row*l_width], &img->imageData[(img->height - row - 1)*l_width], l_width);    TransparentBlt(pDC->m_hDC, 0, 0, width, height, g_hMemDC, 0, 0, img->width, img->height, RGB(0, 0, 0));    SelectObject(g_hMemDC, g_hOldBmp);    //释放内存资源    ReleaseDC(pDC);    DeleteDC(g_hMemDC);    DeleteObject(pic);    DeleteObject(g_hBmp);    DeleteObject(g_hOldBmp);}
#ifndef CVVIMAGE_CLASS_DEF  #define CVVIMAGE_CLASS_DEF  #ifndef RC_OPENCV_2_1_0  #include <opencv/cv.h>  #include <opencv/highgui.h>  /* CvvImage class definition */class  CvvImage{public:    CvvImage();    virtual ~CvvImage();    /* Create image (BGR or grayscale) */    virtual bool  Create(int width, int height, int bits_per_pixel, int image_origin = 0);    /* Load image from specified file */    virtual bool  Load(const char* filename, int desired_color = 1);    /* Load rectangle from the file */    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      /* Save entire image to specified file. */    virtual bool  Save(const char* filename);    /* Get copy of input image ROI */    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);    /* width and height of ROI */    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);    /* draw to highgui window */    virtual void  Show(const char* window);#if defined WIN32 || defined _WIN32      /* draw part of image to the specified DC */    virtual void  Show(HDC dc, int x, int y, int width, int height,        int from_x = 0, int from_y = 0);    /* draw the current image ROI to the specified rectangle of the destination DC */    virtual void  DrawToHDC(HDC hDCDst, RECT* pDstRect);#endif  protected:    IplImage*  m_img;};typedef CvvImage CImage;#endif  #endif  
#include "StdAfx.h"#include "CvvImage.h"//////////////////////////////////////////////////////////////////////// Construction/Destruction//////////////////////////////////////////////////////////////////////CV_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();        /* prepare IPL header */        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;    }    /* truncate r to source image */    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,           // handle to device context                HALFTONE);        }        else        {            SetStretchBltMode(                hDCDst,           // handle to device context                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));}