Opencv求取连通区域重心

我们有时候需要求取某一个物体重心,这里一般将图像二值化,得出该物体的轮廓,然后根据灰度重心法,计算出每一个物体的中心。
步骤如下:
1)合适的阈值二值化
2)求取轮廓
3)计算重心

otsu算法求取最佳阈值
otsu法(最大类间方差法,有时也称之为大津算法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分,otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。

计算轮廓
opencv中函数findContours函数

findContours(二值化图像,轮廓,hierarchy,轮廓检索模式,轮廓近似办法,offset

灰度重心法
利用灰度重心法计算中心,灰度重心法将区域内每一像素位置处的灰度值当做该点的“质量”,其求区域中心的公式为:
这里写图片描述
这里写图片描述
其中,f(u,v)是坐标为(u,v)的像素点的灰度值, 是目标区域集合, 是区域中心坐标,灰度重心法提取的是区域的能量中心。

//otsu算法实现函数
int Otsu(Mat &image)
{
    int width = image.cols;
    int height = image.rows;
    int x = 0, y = 0;
    int pixelCount[256];
    float pixelPro[256];
    int i, j, pixelSum = width * height, threshold = 0;

    uchar* data = (uchar*)image.data;

    //初始化  
    for (i = 0; i < 256; i++)
    {
        pixelCount[i] = 0;
        pixelPro[i] = 0;
    }

    //统计灰度级中每个像素在整幅图像中的个数  
    for (i = y; i < height; i++)
    {
        for (j = x; j<width; j++)
        {
            pixelCount[data[i * image.step + j]]++;
        }
    }


    //计算每个像素在整幅图像中的比例  
    for (i = 0; i < 256; i++)
    {
        pixelPro[i] = (float)(pixelCount[i]) / (float)(pixelSum);
    }

    //经典ostu算法,得到前景和背景的分割  
    //遍历灰度级[0,255],计算出方差最大的灰度值,为最佳阈值  
    float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0;
    for (i = 0; i < 256; i++)
    {
        w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0;

        for (j = 0; j < 256; j++)
        {
            if (j <= i) //背景部分  
            {
                //以i为阈值分类,第一类总的概率  
                w0 += pixelPro[j];
                u0tmp += j * pixelPro[j];
            }
            else       //前景部分  
            {
                //以i为阈值分类,第二类总的概率  
                w1 += pixelPro[j];
                u1tmp += j * pixelPro[j];
            }
        }

        u0 = u0tmp / w0;        //第一类的平均灰度  
        u1 = u1tmp / w1;        //第二类的平均灰度  
        u = u0tmp + u1tmp;      //整幅图像的平均灰度  
                                //计算类间方差  
        deltaTmp = w0 * (u0 - u)*(u0 - u) + w1 * (u1 - u)*(u1 - u);
        //找出最大类间方差以及对应的阈值  
        if (deltaTmp > deltaMax)
        {
            deltaMax = deltaTmp;
            threshold = i;
        }
    }
    //返回最佳阈值;  
    return threshold;
}

int main()
{
    Mat White=imread("white.tif");//读取图像
    int threshold_white = otsu(White);//阈值计算,利用otsu
    cout << "最佳阈值:" << threshold_white << endl;
    Mat thresholded = Mat::zeros(White.size(), White.type());
    threshold(White, thresholded, threshold_white, 255, CV_THRESH_BINARY);//二值化
    vector<vector<Point>>contours;
    vector<Vec4i>hierarchy;
    findContours(thresholded, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//查找轮廓

    int i = 0;
    int count = 0;
    Point pt[10];//假设有三个连通区域
    Moments moment;//矩
    vector<Point>Center;//创建一个向量保存重心坐标
    for (; i >= 0; i = hierarchy[i][0])//读取每一个轮廓求取重心
    {
        Mat temp(contours.at(i));
        Scalar color(0, 0, 255);
        moment = moments(temp, false);
        if (moment.m00 != 0)//除数不能为0
        {
            pt[i].x = cvRound(moment.m10 / moment.m00);//计算重心横坐标
            pt[i].y = cvRound(moment.m01 / moment.m00);//计算重心纵坐标

        }
            Point p = Point(pt[i].x, pt[i].y);//重心坐标
            circle(White, p, 1, color, 1, 8);//原图画出重心坐标
            count++;//重心点数或者是连通区域数
            Center.push_back(p);//将重心坐标保存到Center向量中
        }
    }
    cout << "重心点个数:" << Center.size() << endl;
    cout << "轮廓数量:" << contours.size() << endl;
    imwrite("Center.tif", White);
}

原图:

这里写图片描述

二值化:

这里写图片描述

重心点:

这里写图片描述

  • 7
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值