MFC OpenCV4.1创建ROI

2023-12-10 12:48
文章标签 创建 mfc roi opencv4.1

本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/477152

相关文章

使用Python和Pyecharts创建交互式地图

《使用Python和Pyecharts创建交互式地图》在数据可视化领域,创建交互式地图是一种强大的方式,可以使受众能够以引人入胜且信息丰富的方式探索地理数据,下面我们看看如何使用Python和Pyec... 目录简介Pyecharts 简介创建上海地图代码说明运行结果总结简介在数据可视化领域,创建交互式地

idea中创建新类时自动添加注释的实现

《idea中创建新类时自动添加注释的实现》在每次使用idea创建一个新类时,过了一段时间发现看不懂这个类是用来干嘛的,为了解决这个问题,我们可以设置在创建一个新类时自动添加注释,帮助我们理解这个类的用... 目录前言:详细操作:步骤一:点击上方的 文件(File),点击&nbmyHIgsp;设置(Setti

Spring 中使用反射创建 Bean 实例的几种方式

《Spring中使用反射创建Bean实例的几种方式》文章介绍了在Spring框架中如何使用反射来创建Bean实例,包括使用Class.newInstance()、Constructor.newI... 目录1. 使用 Class.newInstance() (仅限无参构造函数):2. 使用 Construc

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Python中conda虚拟环境创建及使用小结

《Python中conda虚拟环境创建及使用小结》本文主要介绍了Python中conda虚拟环境创建及使用小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录0.前言1.Miniconda安装2.conda本地基本操作3.创建conda虚拟环境4.激活c

使用Python创建一个能够筛选文件的PDF合并工具

《使用Python创建一个能够筛选文件的PDF合并工具》这篇文章主要为大家详细介绍了如何使用Python创建一个能够筛选文件的PDF合并工具,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录背景主要功能全部代码代码解析1. 初始化 wx.Frame 窗口2. 创建工具栏3. 创建布局和界面控件4

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c

使用Python在Excel中创建和取消数据分组

《使用Python在Excel中创建和取消数据分组》Excel中的分组是一种通过添加层级结构将相邻行或列组织在一起的功能,当分组完成后,用户可以通过折叠或展开数据组来简化数据视图,这篇博客将介绍如何使... 目录引言使用工具python在Excel中创建行和列分组Python在Excel中创建嵌套分组Pyt