图像处理之获取闭合轮廓内的所有坐标点

现在需要用到这个功能,然而opencv没有函数可以直接拿来用,看到一位网友说可以用邻域连通的算法尝试解决。大意是先建一个背景灰度值为0的图片,再把轮廓点集的灰度值设置成a,从轮廓外找一种子点P,以4邻域的方式扩充直到遇到轮廓点。按照这个思路我写了个demo程序。

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <queue>

int main()
{
    cv::Mat image = cv::imread("test.png", cv::IMREAD_GRAYSCALE);

    //我的测试图片只有一个轮廓,直接获取最外侧轮廓
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(image,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);

    /*获取轮廓外接矩形,这个矩形和轮廓保持1个像素的距离,这样矩形的顶点必定在轮廓外侧,
    方便后面直接将矩形顶点作为连通算法的起始种子点*/
    cv::Rect rect = cv::boundingRect(contours[0]);
    rect.x -=1;
    rect.y -= 1;
    rect.width +=2;
    rect.height += 2;

    /*lamda表达式判断坐标点是否在外接矩形内,连通算法的计算区域控制在外接矩形内,
    减少不必要的计算量
    */
    auto fun_in_rect = [&rect](int x,int y)
    {
        return (x>=rect.x&&x<=rect.x+rect.width && y>=rect.y&&y<=rect.y+rect.height);
    };

    //队列里保存当前种子点4邻域内可扩展的点作为后续种子点
    std::queue<std::pair<int,int>> neighbor_queue;
    neighbor_queue.push(std::pair<int,int>(rect.x,rect.y));
    //标记种子点,防止重复计算
    image.at<uchar>(rect.y,rect.x) = 128;
    while (!neighbor_queue.empty())
    {
        //从队列取出种子点,获取其4邻域坐标点
        auto seed = neighbor_queue.front();
        neighbor_queue.pop();

        std::vector<std::pair<int,int>> pts;
        pts.emplace_back(seed.first,seed.second-1);
        pts.emplace_back(seed.first,seed.second+1);
        pts.emplace_back(seed.first-1,seed.second);
        pts.emplace_back(seed.first+1,seed.second);

        for(auto& pt : pts)
        {
            if(fun_in_rect(pt.first,pt.second) && image.at<uchar>(pt.second,pt.first)==0)
            {
                //将矩形范围内且灰度值为0的可连通坐标点添加到队列
                neighbor_queue.push(pt);
                image.at<uchar>(pt.second,pt.first)=128;
            }
        }
    }

    //在外接矩形内搜索所有灰度值为0的坐标点即为轮廓内的坐标点
    std::vector<cv::Point> ret;
    for(int i=rect.y; i<rect.y+rect.height; i++)
    {
        for(int j=rect.x; j<rect.x+rect.width; j++)
        {
            if(image.at<uchar>(i,j) == 0)
            {
                ret.emplace_back(j,i);
            }
        }
    }

    //测试一下吧,将轮廓内的坐标点全部填充为红色
    cv::Mat showImg = cv::Mat::zeros(image.rows,image.cols,CV_8UC3);
    for(auto& pt : ret)
    {
        cv::circle(showImg,pt,0,cv::Scalar(0,0,255));
    }

    cv::imshow("show", showImg);

    cv::waitKey();
    return 0;
}

测试图片如下:

从已获取到轮廓开始到获得轮廓内的点集耗时93ms,测试效果如下:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值