ROI多区域选择
学习图像处理时,为了快速验证处理效果,经常需要手动选取ROI区域。其中,多边形区域是最具普适性的,而有时候我们可能还有一次性提取多个区域的需求。本文实现了该过程,先上效果图。
鼠标交互
鼠标交互使用OpenCV函数setMouseCallback:
void cv::setMouseCallback(const cv::String &winname, cv::MouseCallback onMouse, void *userdata = (void *)0)
主要实现的就是回调函数onMouse,以下是我实现的回调,用于监听鼠标滑动和左键点击事件。在左键点击后在图上绘制出该角点以及末两点的连线;在鼠标滑动时绘制当前位置点和轮廓始末点的两条连线,作为效果预估。
//鼠标交互相应 void polygonCallback(int EVENT, int x, int y, int flags, void* userdata) { //获取窗口显示图像 Mat img = *(Mat*) userdata; Mat dst; img.copyTo(dst); //获取坐标 Point pos(x,y); switch (EVENT) { //鼠标左键点击确认角点 case CV_EVENT_LBUTTONDOWN: { mousePoints.push_back(pos); //绘制点以反馈 circle(img, pos, 4, Scalar(255, 0, 0), -1); size_t end = mousePoints.size(); if (end > 1) { //将轮廓的最后两个点进行连线 line(img, mousePoints[end - 2], mousePoints[end - 1], Scalar(255, 0, 0)); line(dst, mousePoints[end - 2], mousePoints[end - 1], Scalar(255, 0, 0)); } break; } //鼠标滑动时可以进行预估 case CV_EVENT_MOUSEMOVE: { size_t end = mousePoints.size(); if (end > 1) { //当前位置点和轮廓始末点的连线 line(dst, mousePoints[end - 1], pos, Scalar(255, 0, 0)); line(dst, mousePoints[0], pos, Scalar(255, 0, 0)); } } } //展示dst,而img不含预估线 imshow(windowName, dst); }
掩码获取
这个步骤主要是用一个二维的vector存储多个历史轮廓,然后使用OpenCV函数drawContours绘制掩码区域:
void cv::drawContours(cv::InputOutputArray image, cv::InputArrayOfArrays contours, int contourIdx, const cv::Scalar &color, int thickness = 1, int lineType = 8, cv::InputArray hierarchy = noArray(), int maxLevel = 2147483647, cv::Point offset = cv::Point())
具体函数如下:
//ROI多边形区域选择 void selectPolygon(const Mat &srcMat, Mat &dstMat) { if (srcMat.empty()) { std::cerr << "srcMat is empty!" << std::endl; return; } imshow(windowName, srcMat); Mat selectMat; char key; srcMat.copyTo(selectMat); std::vector<std::vector<Point>> contours; do{ //鼠标左键选择角点,任意非q按键新建选区,q按键退出ROI选择 setMouseCallback(windowName, polygonCallback, &selectMat); key = waitKey(0); //判断是否能构成多边形,不能则忽略本次选择 if (mousePoints.size() < 3) { std::cout << "points are too little!:" << std::endl; mousePoints.clear(); } else { //补出轮廓始末点连线 line(selectMat, mousePoints[0], mousePoints[mousePoints.size() - 1], Scalar(255, 0, 0)); //存储边界 contours.push_back(mousePoints); //清空本次轮廓点,准备接收下一个区域 mousePoints.clear(); } }while (key != 'q'); destroyAllWindows(); //实心roi掩码 //掩码图像 Mat mask(srcMat.rows, srcMat.cols, CV_8UC1, Scalar(0)); for (size_t i = 0; i < contours.size(); i++) { drawContours(mask, contours, i, Scalar(255), -1); } mask.copyTo(dstMat); mousePoints.clear(); }