本文主要是介绍MFC OpenCV4.1创建ROI,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
需求
1.消息触发,用户从界面获取响应消息;
2.鼠标创建初始的矩形ROI;
3.支持多ROI;
4.支持选择,拉伸,移动和删除;
5.开发环境,MFC 对话框程序
橡皮筋类(CRectTracker)
要想实现图形的拉伸功能,可以借用vs函数库中封装的橡皮筋类(CRectTracker),达到事半功倍的效果。
首先,简要介绍一下CRectTracker这个类:
Windows自带的画图软件中可以用虚线框选择图像的某个区域,之后便可以拖动、放大、缩小该区域,这是通过橡皮筋类(CRectTracker)来实现的,它将实现用线框选中一个区域,并可以拖动、放大、缩小该区域。
CRectTracker类允许一个项被显示,移动,以不同的方式改变大小。虽然CRectTracker类是设计来支持用户以图形化界面与OLE项交互的,但是它的使用不仅限于支持OLE的应用程序。它可以使用在任何需要用户界面的地方。
CRectTracker的边框可以是实线,也可以是点线。可给予项一种阴影式边框或用一种阴影样式覆盖项,用来指示项的不同状态。你可以在项的外界或内部放置八个调整大小把手。(有关八个调整大小把手的解释,参见GetHandleMask。)最后,一个CRectTracker允许你在调整项的大小时改变项的方向。
要使用CRectTracker,首先要构造一个CRectTracker对象,并指定用哪种显示状态来初始化。然后,应用程序就可以使用这个界面,提供给用户有关与CRectTracker对象相关联的OLE项当前状态的直观反馈了。
#include <afxext.h>
请参阅:
COleResizeBar, CRect, CRectTracker::GetHandleMask
CRectTracker类成员
数据成员
m_nHandleSize | 确定调整大小把手的尺寸 |
m_rect | 矩形的以像素表示的当前位置 |
m_sizeMin | 确定矩形宽度和高度的最小值 |
m_nStyle | 跟踪器的当前风格 |
构造
CRectTracker | 构造一个CRectTracker对象 |
操作
Draw | 显示矩形,并显示八个调整把手(调用时若要正常显示调整把手,不能只是在重绘函数中调用,还需要在程序当前位置调用一次) |
GetTrueRect | 返回矩形的宽度和高度,包括改变大小句柄 |
HitTest | 返回与CRectTracker对象关联的光标的当前位置(返回值>=0:在矩形内,小于0:矩形外) |
NormalizeHit | 规范化一个单击测试代码 |
SetCursor | 根据光标在矩形上方的位置来设置光标 |
Track | 支持用户操作矩形(会捕捉鼠标左键弹起消息,一直到鼠标左键弹起才会执行其下面的程序;双击鼠标左键,将会当做响应Track完毕,并执行一次左键按下一次,左键弹起一起) |
TrackRubberBand | 支持用户“橡皮筋”似的拉伸选择(此函数主要用于拉取矩形,用于选择区域内的矩形区域) |
可重载
AdjustRect | 当矩形被改变大小时此函数被调用 |
DrawTrackerRect | 当画一个CRectTracker对象的边框时此函数被调用 |
OnChangedRect | 当矩形被改变大小或被移动时,此函数被调用 |
GetHandleMask | 调用此函数来获得一个CRectTracker项的调整大小把手的掩码 |
☆需要注意的是:CRectTracker类并不是用来画矩形区域的,而是用来选择区域的!!!如果要实现画区域边框,还得利用其它绘制函数~
下面是实现的源代码:
首先,在stdafx.h头文件中加入
#define MAX_RECT_NUM 100 //允许画的最多的矩形个数
在头文件中添加相关的变量和函数声明,
//用于创建ROI相关的 SCRectTracker m_rctCurTracker; //当前选中的矩形区域CRectTracker m_rctTracker[MAX_RECT_NUM]; //用于存储已画的矩形区域bool m_IsChose; //标记是否被选中bool m_IsDraw; //标记“绘制”按钮是否按下int m_rectNum; //当前实际已经画的矩形的个数int m_rctChoseNum;//当前选中的矩形的编号int m_FlaMoveStep;//键盘方向键每响应一次的图像移动的像素单位上的步长int dirct; //用于标记那个方向键按下。1:左,2:右,3:上,4:下,5:delete(删除)//鼠标在ROI区域时光标变换afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);//绘制ROI 触发按键afx_msg void OnBnClickedBtnDrawroi();// 键盘消息时的移动方向virtual BOOL PreTranslateMessage(MSG* pMsg);//移动+删除操作,改变区域void ChangeRectPt(int ChangeDirct);用于创建ROI相关的 E
//键盘消息时的移动方向
ROI相关变量在InitDialog对话框中初始化
BOOL CXXXDlg::OnInitDialog(){CDialogEx::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中 // IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != nullptr){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); // 设置小图标// TODO: 在此添加额外的初始化代码/ROI相关的初始化 Sm_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小m_rctCurTracker.m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;m_rctCurTracker.m_nHandleSize = 6;for (int i = 0; i < MAX_RECT_NUM; i++){m_rctTracker[i].m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小m_rctTracker[i].m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;m_rctTracker[i].m_nHandleSize = 6;}m_IsChose = FALSE;//表示未选中m_IsDraw = false;m_rectNum = 0;m_rctChoseNum = 0;m_FlaMoveStep = 2;dirct = 0;/ROI相关的初始化 E return TRUE; // 除非将焦点设置到控件,否则返回 TRUE}
在Paint函数中绘制当前ROI
void CXXXDlg::OnPaint(){CPaintDC dc(this); // 用于绘制的设备上下文if (IsIconic()){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{ if (m_IsChose){//若选择了该区域,则显示边框以及8个调整点m_rctCurTracker.Draw(&dc);//输出坐标信息CRect rect = m_rctCurTracker.m_rect;CString strLT = _T("");strLT.Format(_T("%d,%d"),rect.left,rect.top);SetDlgItemTextA(IDC_EDIT_CURROILT, strLT);CString strRB = _T("");strRB.Format(_T("%d,%d"), rect.right, rect.bottom);SetDlgItemTextA(IDC_EDIT_CURROIRB, strRB);//输出当前ROI的长宽信息CString strWidth = _T("");strWidth.Format(_T("%d"), rect.Width()); SetDlgItemTextA(IDC_EDIT_ROIWIDTH, strWidth);CString strHeight = _T("");strHeight.Format(_T("%d"), rect.Height());SetDlgItemTextA(IDC_EDIT_ROIHEIGHT, strHeight);}CPen pen(PS_SOLID, 1, RGB(100, 255, 200));dc.SelectObject(&pen);CBrush *pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));dc.SelectObject(pbrush);CRect rect;m_rctCurTracker.GetTrueRect(&rect);//得到矩形区域的大小dc.Rectangle(&rect);//画出矩形CSize rct_size;for (int i = 0; i < MAX_RECT_NUM; i++){m_rctTracker[i].GetTrueRect(&rect);//得到矩形区域的大小rct_size = m_rctTracker[i].m_rect.Size();if (rct_size.cx * rct_size.cy == 0 || i == m_rctChoseNum){continue;}dc.Rectangle(&rect);//画出矩形}//CRect rect1;/*rect1.top=rect.top+3;rect1.bottom=rect.bottom-3;rect1.left=rect.left+3;rect1.right=rect.right-3;pbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));dc.SelectObject(&brush);dc.Rectangle(&rect1);*/CDialogEx::OnPaint();}}
鼠标左键按下后,开始绘制ROI
void CXXXDlg::OnLButtonDown(UINT nFlags, CPoint point){ // TODO:在此添加消息处理程序代码和/或调用默认值bool IsInRct = false;int i = 0;do{if (m_rctTracker[i].HitTest(point) < 0){IsInRct = false;}else{IsInRct = true;m_rctChoseNum = i;m_rctCurTracker = m_rctTracker[m_rctChoseNum];m_IsChose = true;break;}i++;} while (i < m_rectNum);if (!IsInRct){CRectTracker tempRectTracker;CRect rect;tempRectTracker.TrackRubberBand(this, point);tempRectTracker.m_rect.NormalizeRect();if (rect.IntersectRect(tempRectTracker.m_rect, m_rctCurTracker.m_rect))m_IsChose = TRUE;else{m_IsChose = false;if (m_IsDraw){//m_IsChose=FALSE;m_rctTracker[m_rectNum].m_rect = tempRectTracker.m_rect;m_rctCurTracker.m_rect = m_rctTracker[m_rectNum].m_rect;CClientDC dc(this);m_rctCurTracker.Draw(&dc);//注意!!在这里一定要调用绘制边框的程序,否则单凭onpaint中绘制,不能显示出来m_rctChoseNum = m_rectNum;m_rectNum++;if (m_rectNum >= MAX_RECT_NUM){m_rectNum = MAX_RECT_NUM;MessageBoxA("已画矩形超过上限,不能再画矩形区域", "警告", MB_OK);}m_IsChose = TRUE;m_IsDraw = false;Invalidate();} }Invalidate();}else{CClientDC dc(this);m_rctCurTracker.Draw(&dc);m_rctCurTracker.Track(this, point);m_rctCurTracker.m_rect.NormalizeRect();m_rctTracker[m_rctChoseNum] = m_rctCurTracker;m_IsChose = TRUE;Invalidate();}CDialogEx::OnLButtonDown(nFlags, point);}
绘制ROI指令入口
void CXXXDlg::OnBnClickedBtnDrawroi(){// TODO: 在此添加控件通知处理程序代码m_IsDraw = true; }
鼠标在ROI区域时,光标变换
BOOL CXXXDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message){//TODO: 在此添加消息处理程序代码和/或调用默认值if (pWnd == this && m_rctCurTracker.SetCursor(this, nHitTest)){return TRUE;}return CDialogEx::OnSetCursor(pWnd, nHitTest, message);}
ROI 移动删除 通过键盘来实现
BOOL CAOITestDemoDlg::PreTranslateMessage(MSG* pMsg){// TODO: 在此添加专用代码和/或调用基类if (pMsg->message == WM_KEYDOWN){switch (pMsg->wParam){case VK_LEFT:dirct = 1;break;case VK_RIGHT:dirct = 2;break;case VK_UP:dirct = 3;break;case VK_DOWN:dirct = 4;break;case VK_DELETE:dirct = 5;break;default:dirct = 0;}}ChangeRectPt(dirct);return CDialogEx::PreTranslateMessage(pMsg);}
ROI区域改变后需重新绘制,函数定义如下,
void CXXXDlg::ChangeRectPt(int ChangeDirct){CRect rct;rct = m_rctCurTracker.m_rect;switch (ChangeDirct){case 1://左移rct.TopLeft().x -= m_FlaMoveStep;rct.BottomRight().x -= m_FlaMoveStep;break;case 2://右移rct.TopLeft().x += m_FlaMoveStep;rct.BottomRight().x += m_FlaMoveStep;break;case 3://下移rct.TopLeft().y -= m_FlaMoveStep;rct.BottomRight().y -= m_FlaMoveStep;break;case 4://上移rct.TopLeft().y += m_FlaMoveStep;rct.BottomRight().y += m_FlaMoveStep;break;case 5://删除m_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);m_rctTracker[m_rctChoseNum] = m_rctCurTracker;dirct = 0;Invalidate();return;}m_rctCurTracker.m_rect.SetRect(rct.TopLeft(), rct.BottomRight());m_rctTracker[m_rctChoseNum] = m_rctCurTracker;if (ChangeDirct != 0){Invalidate();}dirct = 0;}
以上功能添加完成后,效果如下,
到这一步,ROI绘制相关的需求已完成。这里ROI不是绘制在Picture控件上,而是整个窗体,离我们项目中的需求还有一段距离。待继续实现:
1.ROI的坐标改为相对Picture控件左上顶点为原点;
2.在Picture控件上绘制,移动ROI。
待续。
这篇关于MFC OpenCV4.1创建ROI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!