一、项目需求
确定网络摄像机的监控视场在全景地图上的精确位置,并采用简单而高效的方法将其所在视场标定并投影到全景地图上,项目要求能够将多路网络摄像机的视场标定在全景地图上,并实现这样的功能:当调取当前摄像机视频播放时,其他摄像机处于暂停状态。
二、项目分析
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); //绘图到 MFC 的 Picture Control 控件相关函数 //参数一为 OpenCV 的图像数据结构类,参数二为 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));}