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();
}

 

posted @ 2020-05-17 00:29  KenSporger  阅读(1763)  评论(0编辑  收藏  举报