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相关的 S

CRectTracker 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相关的初始化 S

         m_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。

待续。

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法哥2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值