MFC中使用OpenCV动态绘制图像ROI区域(矩形+多边形)
- [1 环境](#1 环境)
- [2 绘制矩形](#2 绘制矩形)
- [3 绘制多边形](#3 绘制多边形)
- [4 参考文章](#4 参考文章)
在MFC中,使用Opencv的鼠标事件实现图像的矩形、多边形ROI区域选择。
通过设置鼠标回调函数,监听鼠标的点击、移动、拖拽等操作,动态的在图像上绘制矩形或多边形。并利用选择的矩形或多边形所形成的Mask,提取图像上的ROI区域。
1 环境
OpenCV 4.8
Visual Studio 2015
2 绘制矩形
(1)左键按下时,鼠标移动,在临时图像上实时显示鼠标拖动时形成的矩形。
(2)左键松开时,根据初始点和结束点,将矩形绘制到图像上。
(3)鼠标移动时,实时显示当前点坐标。
cpp
void on_mouse_rect(int event, int x, int y, int flags, void *ustc)-
{
char temp[16];
if (event == CV_EVENT_LBUTTONDOWN)
{
m_show.copyTo(img);
pre_pt = cv::Point(x, y);
circle(img, pre_pt, 2, cv::Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
cv::imshow("view", img);
}
else if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))
{
img.copyTo(tmp);
sprintf(temp,"(%d,%d)",x,y);
cur_pt = cv::Point(x, y);
putText(tmp,temp,cur_pt,cv::FONT_HERSHEY_SIMPLEX,0.5,cv::Scalar(0,0,0,255),2);
cv::imshow("view", tmp);
}
else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))
{
//-- 为了打开图像时不展示鼠标操作
if (pre_pt.x == -1 && pre_pt.y == -1) {
return;
}
m_show.copyTo(tmp);
sprintf(temp, "(%d,%d)", x, y);
cur_pt = cv::Point(x, y);
putText(tmp, temp, cur_pt, cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0, 255),2);
rectangle(tmp, pre_pt, cur_pt, cv::Scalar(0, 0, 0, 255), 2, 8, 0);
cv::imshow("view", tmp);
}
else if (event == CV_EVENT_LBUTTONUP)
{
if (pre_pt.x == -1 && pre_pt.y == -1) {
return;
}
//-- show view
m_show.copyTo(img);
cur_pt = cv::Point(x, y);
circle(img, pre_pt, 2, cv::Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
rectangle(img, pre_pt, cur_pt, cv::Scalar(0, 0, 0, 255), 2, 8, 0);
cv::imshow("view", img);
//-- get rect
int width = abs(pre_pt.x - cur_pt.x);
int height = abs(pre_pt.y - cur_pt.y);
if (width == 0 || height == 0)
{
printf("width == 0 || height == 0");
return;
}
//-- show roi
cv::Rect roiRect = cv::Rect(
(int)std::min(cur_pt.x, pre_pt.x),
(int)std::min(cur_pt.y, pre_pt.y),
width,
height);
roiRect = roiRect & cv::Rect(0, 0, m_show.cols, m_show.rows);
cv::Mat showRoi = m_show(roiRect);
cv::resize(showRoi, showRoi, cv::Size(m_rectRoi.Width(), m_rectRoi.Height()));
HWND hWndRoi = (HWND)cvGetWindowHandle("roi");
::ShowWindow(hWndRoi, SW_SHOWNORMAL);
cv::imshow("roi", showRoi);
}
}
3 绘制多边形
(1)左键按下时,选择点,并与上一次选择的点连成线实时显示。
(2)右键按下时,取消最近一次选择的点。
(3)中间键按下时,绘制由所选择的点组成的多边形的轮廓。
(4)鼠标移动时,实时显示当前点坐标。
cpp
void on_mouse_polygon(int event, int x, int y, int flags, void *ustc)//event鼠标事件代号,x,y鼠标坐标,flags拖拽和键盘操作的代号
{
m_show.copyTo(img);
char temp[16];
if (event == CV_EVENT_LBUTTONDOWN)
{
if (m_bPolygonFinished) {
pts.clear();
}
m_bPolygonFinished = false;
pts.push_back(cv::Point(x, y));
}
if (event == CV_EVENT_RBUTTONDOWN)
{
m_bPolygonFinished = false;
pts.pop_back();
}
if (event == CV_EVENT_MBUTTONDOWN)
{
//-- roi选完
m_bPolygonFinished = true;
//-- 展示区 mask提取
cv::Mat mask = cv::Mat::zeros(m_show.size(), CV_8UC1);
cv::fillPoly(mask, pts, cv::Scalar(255), 8, 0);
cv::Mat dst;
cv::bitwise_and(img, img, dst, mask);
//-- 展示区 roi mask外接矩形展示
cv::Rect rt = cv::boundingRect(mask);
cv::resize(dst(rt), dst, cv::Size(m_rectRoi.Width(), m_rectRoi.Height()));
HWND hWndRoi = (HWND)cvGetWindowHandle("roi");
::ShowWindow(hWndRoi, SW_SHOWNORMAL);
cv::imshow("roi", dst);
}
if(event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))
{
sprintf(temp, "(%d,%d)", x, y);
cur_pt = cv::Point(x, y);
putText(img, temp, cur_pt, cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0, 255), 2);
cv::imshow("view", img);
}
int num = pts.size();
if (num > 0)
circle(img, pts[num - 1], 2, cv::Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
if (num > 1) {
for (int i = 0; i < num - 1; i++) {
cv::circle(img, pts[i], 2, cv::Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
cv::line(img, pts[i], pts[i + 1], cv::Scalar(255, 0, 0), 2);
}
if (m_bPolygonFinished)
cv::line(img, pts[0], pts[num - 1], cv::Scalar(255, 0, 0), 2);
}
cv::imshow("view", img);
}