OpenCV学习笔记——寻找连通域的几何中心(不规则图形也适用),及筛选最大连通域中心

参考https://blog.csdn.net/qq_34914551/article/details/78916084

Opencv寻找连通域的几何中心

其中连通域的轮廓选取用到了OTSU算法

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv; 
using namespace std;
//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 matSrc = imread("1.png", 0);
	GaussianBlur(matSrc, matSrc, Size(5, 5), 0);
	vector<vector<Point> > contours;//contours的类型,双重的vector
	vector<Vec4i> hierarchy;//Vec4i是指每一个vector元素中有四个int型数据。
	//阈值
	threshold(matSrc, matSrc, 100, 255, THRESH_BINARY);
	imshow("threshold", matSrc);
	//寻找轮廓,这里注意,findContours的输入参数要求是二值图像,二值图像的来源大致有两种,第一种用threshold,第二种用canny
	findContours(matSrc.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	/// 计算矩
	vector<Moments> mu(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mu[i] = moments(contours[i], false);
	}
	///  计算中心矩:
	vector<Point2f> mc(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
	}
	/// 绘制轮廓
	Mat drawing = Mat::zeros(matSrc.size(), CV_8UC1);
	for (int i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(255);
		drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
		circle(drawing, mc[i], 4, Scalar(128), -1, 8, 0);	//中心用灰点表示
	}
	imshow("outImage",drawing);
	waitKey();
	return 0;
 
}

筛选最大连通域中心

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv; 
using namespace std;
//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 matSrc = imread("1.png", 0);
	GaussianBlur(matSrc, matSrc, Size(5, 5), 0);
	vector<vector<Point> > contours;//contours的类型,双重的vector
	vector<Vec4i> hierarchy;//Vec4i是指每一个vector元素中有四个int型数据。
	//阈值
	threshold(matSrc, matSrc, 100, 255, THRESH_BINARY);
	imshow("threshold", matSrc);
	//寻找轮廓,这里注意,findContours的输入参数要求是二值图像,二值图像的来源大致有两种,第一种用threshold,第二种用canny
	findContours(matSrc.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//计算最大连通域中心点
	int index;
	double area, maxArea(0);
	for (int i = 0; i < contours.size(); i++)
	{
		area = contourArea(Mat(contours[i]));
		if (area > maxArea)
		{
			maxArea = area;
			index = i;
		}
	}
	// 计算矩
	vector<Moments> mu(contours.size());
	mu[index] = moments(contours[index], false);
	//  计算中心矩:
	vector<Point2f> mc(contours.size());
	mc[index] = Point2f(mu[index].m10 / mu[index].m00, mu[index].m01 / mu[index].m00);
	// 绘制轮廓
	Mat drawing = Mat::zeros(matSrc.size(), CV_8UC1);
	Scalar color = Scalar(255);
	drawContours(drawing, contours, index, color, 2, 8, hierarchy, 0, Point());
	circle(drawing, mc[index], 4, Scalar(128), -1, 8, 0);	//中心用灰点表示
	imshow("outImage",drawing);
	waitKey();
	return 0;
 
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值