当前位置:   article > 正文

MFC OpenCV4.1创建ROI_mfc roi 拖拽

mfc 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     //允许画的最多的矩形个数

在头文件中添加相关的变量和函数声明,

  1. //用于创建ROI相关的 S
  2. CRectTracker m_rctCurTracker;   //当前选中的矩形区域
  3. CRectTracker m_rctTracker[MAX_RECT_NUM]; //用于存储已画的矩形区域
  4. bool m_IsChose;  //标记是否被选中
  5. bool m_IsDraw;   //标记“绘制”按钮是否按下
  6. int m_rectNum;   //当前实际已经画的矩形的个数
  7. int m_rctChoseNum;//当前选中的矩形的编号
  8. int m_FlaMoveStep;//键盘方向键每响应一次的图像移动的像素单位上的步长
  9. int dirct;     //用于标记那个方向键按下。1:左,2:右,3:上,4:下,5:delete(删除)
  10. //鼠标在ROI区域时光标变换
  11. afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
  12. //绘制ROI 触发按键
  13. afx_msg void OnBnClickedBtnDrawroi();
  14. // 键盘消息时的移动方向
  15. virtual BOOL PreTranslateMessage(MSG* pMsg);
  16. //移动+删除操作,改变区域
  17. void ChangeRectPt(int ChangeDirct);
  18. 用于创建ROI相关的 E

//键盘消息时的移动方向

 

ROI相关变量在InitDialog对话框中初始化

  1. BOOL CXXXDlg::OnInitDialog()
  2. {
  3.          CDialogEx::OnInitDialog();
  4.          // 将“关于...”菜单项添加到系统菜单中  // IDM_ABOUTBOX 必须在系统命令范围内。
  5.          ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
  6.          ASSERT(IDM_ABOUTBOX < 0xF000);
  7.          CMenu* pSysMenu = GetSystemMenu(FALSE);
  8.          if (pSysMenu != nullptr)
  9.          {
  10.                    BOOL bNameValid;
  11.                    CString strAboutMenu;
  12.                    bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
  13.                    ASSERT(bNameValid);
  14.                    if (!strAboutMenu.IsEmpty())
  15.                    {
  16.                             pSysMenu->AppendMenu(MF_SEPARATOR);
  17.                             pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  18.                    }
  19.          }
  20.          // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动         //  执行此操作
  21.          SetIcon(m_hIcon, TRUE);                            // 设置大图标
  22.          SetIcon(m_hIcon, FALSE);                  // 设置小图标
  23.          // TODO: 在此添加额外的初始化代码
  24.          /ROI相关的初始化 S
  25.          m_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小
  26.          m_rctCurTracker.m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;
  27.          m_rctCurTracker.m_nHandleSize = 6;
  28.          for (int i = 0; i < MAX_RECT_NUM; i++)
  29.          {
  30.                    m_rctTracker[i].m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小
  31.                    m_rctTracker[i].m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;
  32.                    m_rctTracker[i].m_nHandleSize = 6;
  33.          }
  34.          m_IsChose = FALSE;//表示未选中
  35.          m_IsDraw = false;
  36.          m_rectNum = 0;
  37.          m_rctChoseNum = 0;
  38.          m_FlaMoveStep = 2;
  39.          dirct = 0;
  40.          /ROI相关的初始化 E        
  41.          return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
  42. }

在Paint函数中绘制当前ROI

  1. void CXXXDlg::OnPaint()
  2. {
  3.          CPaintDC dc(this); // 用于绘制的设备上下文
  4.          if (IsIconic())
  5.          {
  6.                    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  7.                    // 使图标在工作区矩形中居中
  8.                    int cxIcon = GetSystemMetrics(SM_CXICON);
  9.                    int cyIcon = GetSystemMetrics(SM_CYICON);
  10.                    CRect rect;
  11.                    GetClientRect(&rect);
  12.                    int x = (rect.Width() - cxIcon + 1) / 2;
  13.                    int y = (rect.Height() - cyIcon + 1) / 2;
  14.                    // 绘制图标
  15.                    dc.DrawIcon(x, y, m_hIcon);            
  16.          }
  17.          else
  18.          {                
  19.                    if (m_IsChose)
  20.                    {
  21.                             //若选择了该区域,则显示边框以及8个调整点
  22.                             m_rctCurTracker.Draw(&dc);
  23.                             //输出坐标信息
  24.                             CRect rect = m_rctCurTracker.m_rect;
  25.                             CString strLT = _T("");
  26.                             strLT.Format(_T("%d,%d"),rect.left,rect.top);
  27.                             SetDlgItemTextA(IDC_EDIT_CURROILT, strLT);
  28.                             CString strRB = _T("");
  29.                             strRB.Format(_T("%d,%d"), rect.right, rect.bottom);
  30.                             SetDlgItemTextA(IDC_EDIT_CURROIRB, strRB);
  31.                             //输出当前ROI的长宽信息
  32.                             CString strWidth = _T("");
  33.                             strWidth.Format(_T("%d"), rect.Width());                        
  34.                             SetDlgItemTextA(IDC_EDIT_ROIWIDTH, strWidth);
  35.                             CString strHeight = _T("");
  36.                             strHeight.Format(_T("%d"), rect.Height());
  37.                             SetDlgItemTextA(IDC_EDIT_ROIHEIGHT, strHeight);
  38.                    }
  39.                            
  40.                    CPen pen(PS_SOLID, 1, RGB(100, 255, 200));
  41.                    dc.SelectObject(&pen);
  42.                    CBrush *pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
  43.                    dc.SelectObject(pbrush);
  44.                    CRect rect;
  45.                    m_rctCurTracker.GetTrueRect(&rect);//得到矩形区域的大小
  46.                    dc.Rectangle(&rect);//画出矩形
  47.                    CSize rct_size;
  48.                    for (int i = 0; i < MAX_RECT_NUM; i++)
  49.                    {
  50.                             m_rctTracker[i].GetTrueRect(&rect);//得到矩形区域的大小
  51.                             rct_size = m_rctTracker[i].m_rect.Size();
  52.                             if (rct_size.cx * rct_size.cy == 0 || i == m_rctChoseNum)
  53.                             {
  54.                                      continue;
  55.                             }
  56.                             dc.Rectangle(&rect);//画出矩形
  57.                    }
  58.                    //CRect rect1;
  59.                    /*rect1.top=rect.top+3;
  60.                    rect1.bottom=rect.bottom-3;
  61.                    rect1.left=rect.left+3;
  62.                    rect1.right=rect.right-3;
  63.                    pbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
  64.                    dc.SelectObject(&brush);
  65.                    dc.Rectangle(&rect1);*/
  66.                    CDialogEx::OnPaint();
  67.          }
  68. }

鼠标左键按下后,开始绘制ROI

  1. void CXXXDlg::OnLButtonDown(UINT nFlags, CPoint point)
  2. {       
  3.          // TODO:在此添加消息处理程序代码和/或调用默认值
  4.          bool IsInRct = false;
  5.          int i = 0;
  6.          do
  7.          {
  8.                    if (m_rctTracker[i].HitTest(point) < 0)
  9.                    {
  10.                             IsInRct = false;
  11.                    }
  12.                    else
  13.                    {
  14.                             IsInRct = true;
  15.                             m_rctChoseNum = i;
  16.                             m_rctCurTracker = m_rctTracker[m_rctChoseNum];
  17.                             m_IsChose = true;
  18.                             break;
  19.                    }
  20.                    i++;
  21.          } while (i < m_rectNum);
  22.          if (!IsInRct)
  23.          {
  24.                    CRectTracker tempRectTracker;
  25.                    CRect rect;
  26.                    tempRectTracker.TrackRubberBand(this, point);
  27.                    tempRectTracker.m_rect.NormalizeRect();
  28.                    if (rect.IntersectRect(tempRectTracker.m_rect, m_rctCurTracker.m_rect))
  29.                             m_IsChose = TRUE;
  30.                    else
  31.                    {
  32.                             m_IsChose = false;
  33.                             if (m_IsDraw)
  34.                             {
  35.                                      //m_IsChose=FALSE;
  36.                                      m_rctTracker[m_rectNum].m_rect = tempRectTracker.m_rect;
  37.                                      m_rctCurTracker.m_rect = m_rctTracker[m_rectNum].m_rect;
  38.                                      CClientDC dc(this);
  39.                                      m_rctCurTracker.Draw(&dc);
  40.                                      //注意!!在这里一定要调用绘制边框的程序,否则单凭onpaint中绘制,不能显示出来
  41.                                      m_rctChoseNum = m_rectNum;
  42.                                      m_rectNum++;
  43.                                      if (m_rectNum >= MAX_RECT_NUM)
  44.                                      {
  45.                                                m_rectNum = MAX_RECT_NUM;
  46.                                                MessageBoxA("已画矩形超过上限,不能再画矩形区域", "警告", MB_OK);
  47.                                      }
  48.                                      m_IsChose = TRUE;
  49.                                      m_IsDraw = false;
  50.                                      Invalidate();
  51.                             }                          
  52.                    }
  53.                    Invalidate();
  54.          }
  55.          else
  56.          {
  57.                    CClientDC dc(this);
  58.                    m_rctCurTracker.Draw(&dc);
  59.                    m_rctCurTracker.Track(this, point);
  60.                    m_rctCurTracker.m_rect.NormalizeRect();
  61.                    m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
  62.                    m_IsChose = TRUE;
  63.                    Invalidate();
  64.          }
  65.          CDialogEx::OnLButtonDown(nFlags, point);
  66. }

绘制ROI指令入口

  1. void CXXXDlg::OnBnClickedBtnDrawroi()
  2. {
  3.          // TODO: 在此添加控件通知处理程序代码
  4.          m_IsDraw = true;   
  5. }

鼠标在ROI区域时,光标变换

  1. BOOL CXXXDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  2. {
  3.          //TODO: 在此添加消息处理程序代码和/或调用默认值
  4.          if (pWnd == this && m_rctCurTracker.SetCursor(this, nHitTest))
  5.          {
  6.                    return TRUE;
  7.          }
  8.          return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
  9. }

ROI 移动删除 通过键盘来实现

  1. BOOL CAOITestDemoDlg::PreTranslateMessage(MSG* pMsg)
  2. {
  3.          // TODO: 在此添加专用代码和/或调用基类
  4.          if (pMsg->message == WM_KEYDOWN)
  5.          {
  6.                    switch (pMsg->wParam)
  7.                    {
  8.                    case VK_LEFT:
  9.                             dirct = 1;
  10.                             break;
  11.                    case VK_RIGHT:
  12.                             dirct = 2;
  13.                             break;
  14.                    case VK_UP:
  15.                             dirct = 3;
  16.                             break;
  17.                    case VK_DOWN:
  18.                             dirct = 4;
  19.                             break;
  20.                    case VK_DELETE:
  21.                             dirct = 5;
  22.                             break;
  23.                    default:
  24.                             dirct = 0;
  25.                    }
  26.          }
  27.          ChangeRectPt(dirct);
  28.          return CDialogEx::PreTranslateMessage(pMsg);
  29. }

ROI区域改变后需重新绘制,函数定义如下,

  1. void CXXXDlg::ChangeRectPt(int ChangeDirct)
  2. {
  3.          CRect rct;
  4.          rct = m_rctCurTracker.m_rect;
  5.          switch (ChangeDirct)
  6.          {
  7.          case 1://左移
  8.                    rct.TopLeft().x -= m_FlaMoveStep;
  9.                    rct.BottomRight().x -= m_FlaMoveStep;
  10.                    break;
  11.          case 2://右移
  12.                    rct.TopLeft().x += m_FlaMoveStep;
  13.                    rct.BottomRight().x += m_FlaMoveStep;
  14.                    break;
  15.          case 3://下移
  16.                    rct.TopLeft().y -= m_FlaMoveStep;
  17.                    rct.BottomRight().y -= m_FlaMoveStep;
  18.                    break;
  19.          case 4://上移
  20.                    rct.TopLeft().y += m_FlaMoveStep;
  21.                    rct.BottomRight().y += m_FlaMoveStep;
  22.                    break;
  23.          case 5://删除
  24.                    m_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);
  25.                    m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
  26.                    dirct = 0;
  27.                    Invalidate();
  28.                    return;
  29.          }
  30.          m_rctCurTracker.m_rect.SetRect(rct.TopLeft(), rct.BottomRight());
  31.          m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
  32.          if (ChangeDirct != 0)
  33.          {
  34.                    Invalidate();
  35.          }
  36.          dirct = 0;
  37. }

以上功能添加完成后,效果如下,

到这一步,ROI绘制相关的需求已完成。这里ROI不是绘制在Picture控件上,而是整个窗体,离我们项目中的需求还有一段距离。待继续实现:

1.ROI的坐标改为相对Picture控件左上顶点为原点;

2.在Picture控件上绘制,移动ROI。

待续。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/144206
推荐阅读
相关标签
  

闽ICP备14008679号