Opencv求取连通区域重心实例


Posted in Python onJune 04, 2020

我们有时候需要求取某一个物体重心,这里一般将图像二值化,得出该物体的轮廓,然后根据灰度重心法,计算出每一个物体的中心。

步骤如下:

1)合适的阈值二值化

2)求取轮廓

3)计算重心

otsu算法求取最佳阈值

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

计算轮廓

opencv中函数findContours函数

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

灰度重心法

利用灰度重心法计算中心,灰度重心法将区域内每一像素位置处的灰度值当做该点的“质量”,其求区域中心的公式为:

Opencv求取连通区域重心实例

其中,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);
}

原图:

Opencv求取连通区域重心实例

二值化:

Opencv求取连通区域重心实例

重心点:

Opencv求取连通区域重心实例

补充知识:opencv 根据模板凸包求阈值化后的轮廓组合

图像处理中,要求特征与背景的对比度高,同时,合适的图像分割也是解决问题的关键。

博主以前的方法,默认为特征必然是最大的连通域,所以阈值化后,查找轮廓,直接提取面积最大的轮廓即可。

但可能会存在另一种情况,不论怎么阈值化和膨胀,想要的特征被分成好几块,也即断开了。此时,再加上一些不可预测的干扰和噪声,findcontours之后,会得到很多轮廓。

那么问题来了,我们需要的是哪个轮廓,或者是哪几个轮廓组合的区域?

本文的意义也在于此。

根据模板的凸包,求出图像中最相似的轮廓组合。

本方法,主要用到matchshapes函数,并基于这样一个前提:模板凸包的2/3部分,与模板凸包的相似度,大于模板凸包的1/2部分。

话不多说,上代码。

void getAlikeContours(std::vector<cv::Point> Inputlist, cv::Mat InputImage, std::vector<cv::Point> &Outputlist)
{
 Mat image;
 InputImage.copyTo(image);
 vector<vector<Point> > contours;
 findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);//查找最外层轮廓
 for (int idx = contours.size() - 1; idx >= 0; idx--)
  {
	for (int i = contours[idx].size() - 1; i >= 0; i--)
	{
		if (contours[idx][i].x == 1 || contours[idx][i].y == 1 || contours[idx][i].x == image.cols - 2 || contours[idx][i].y == image.rows - 2)
		{
			swap(contours[idx][i], contours[idx][contours[idx].size() - 1]);
			contours[idx].pop_back();
			
		}
	}
	//可能会存在空的轮廓,把他们删除
	for (int idx = contours.size() - 1; idx >= 0; idx--)
	{
		if (contours[idx].size() == 0) contours.erase(contours.begin() + idx);
	}
 
	while (true)
	{
		if (contours.size() == 0) break;
		if (contours.size() == 1)
		{
			vector<Point> finalList;
			finalList.assign(contours[0].begin(), contours[0].end());
			convexHull(Mat(finalList), Outputlist, true);
			break;
		}
 
		int maxContourIdx = 0;
		int maxContourPtNum = 0;
		for (int index = contours.size() - 1; index >= 0; index--)
		{
			if (contours[index].size() > maxContourPtNum)
			{
				maxContourPtNum = contours[index].size();
				maxContourIdx = index;
			}
		}
		//第二大轮廓
		int secondContourIdx = 0;
		int secondContourPtNum = 0;
		for (int index = contours.size() - 1; index >= 0; index--)
		{
			if (index == maxContourIdx) continue;
			if (contours[index].size() > secondContourPtNum)
			{
				secondContourPtNum = contours[index].size();
				secondContourIdx = index;
			}
		}
		vector<Point> maxlist;
		vector<Point> maxAndseclist;
		vector<Point> maxlistHull;
		vector<Point> maxAndseclistHull;
		maxlist.insert(maxlist.end(), contours[maxContourIdx].begin(), contours[maxContourIdx].end());
		maxAndseclist.insert(maxAndseclist.end(), contours[maxContourIdx].begin(), contours[maxContourIdx].end());
		maxAndseclist.insert(maxAndseclist.end(), contours[secondContourIdx].begin(), contours[secondContourIdx].end());
		convexHull(Mat(maxlist), maxlistHull, true);
		convexHull(Mat(maxAndseclist), maxAndseclistHull, true);
		double maxcontourScore = matchShapes(Inputlist, maxlistHull, CV_CONTOURS_MATCH_I1, 0);
		double maxandseccontourScore = matchShapes(Inputlist, maxAndseclistHull, CV_CONTOURS_MATCH_I1, 0);
		if (maxcontourScore>maxandseccontourScore)
		{
			contours[maxContourIdx].insert(contours[maxContourIdx].end(), contours[secondContourIdx].begin(), contours[secondContourIdx].end());
		}
		contours.erase(contours.begin() + secondContourIdx);
	}
}

以上这篇Opencv求取连通区域重心实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
用python代码做configure文件
Jul 20 Python
Python实现爬取知乎神回复简单爬虫代码分享
Jan 04 Python
浅析使用Python操作文件
Jul 31 Python
关于python列表增加元素的三种操作方法
Aug 22 Python
Django2.1集成xadmin管理后台所遇到的错误集锦(填坑)
Dec 20 Python
python实现在函数中修改变量值的方法
Jul 16 Python
Pytorch抽取网络层的Feature Map(Vgg)实例
Aug 20 Python
利用python实现.dcm格式图像转为.jpg格式
Jan 13 Python
python爬虫基础知识点整理
Jun 02 Python
解决Keras 自定义层时遇到版本的问题
Jun 16 Python
Python2手动安装更新pip过程实例解析
Jul 16 Python
Python中的socket网络模块介绍
Jul 23 Python
Python中zip函数如何使用
Jun 04 #Python
Python中有几个关键字
Jun 04 #Python
Python如何转换字符串大小写
Jun 04 #Python
如何在Python对Excel进行读取
Jun 04 #Python
opencv 查找连通区域 最大面积实例
Jun 04 #Python
Python中的Cookie模块如何使用
Jun 04 #Python
Python爬虫获取页面所有URL链接过程详解
Jun 04 #Python
You might like
PHP原理之异常机制深入分析
2010/08/08 PHP
网上抓的一个特效
2007/05/11 Javascript
来自qq的javascript面试题
2010/07/24 Javascript
对xmlHttp对象方法和属性的理解
2011/01/17 Javascript
基于jquery的15款幻灯片插件
2011/04/10 Javascript
理解JSON:3分钟课程
2011/10/28 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
js中parseInt函数浅谈
2013/07/31 Javascript
了解了这些才能开始发挥jQuery的威力
2013/10/10 Javascript
JavaScript中instanceof与typeof运算符的用法及区别详细解析
2013/11/19 Javascript
node.js中的console.info方法使用说明
2014/12/09 Javascript
jquery动画效果学习笔记(8种效果)
2015/11/13 Javascript
最细致的vue.js基础语法 值得收藏!
2016/11/03 Javascript
angularjs点击图片放大实现上传图片预览
2017/02/24 Javascript
bootstrap paginator分页插件的两种使用方式实例详解
2017/11/14 Javascript
Mac中安装nvm的教程分享
2017/12/11 Javascript
解决vue单页使用keep-alive页面返回不刷新的问题
2018/03/13 Javascript
原生nodejs使用websocket代码分享
2018/04/07 NodeJs
js与jQuery实现获取table中的数据并拼成json字符串操作示例
2018/07/12 jQuery
JS基于Location实现访问Url、重定向及刷新页面的方法分析
2018/12/03 Javascript
JavaScript函数的特性与应用实践深入详解
2018/12/30 Javascript
JavaScript数组、json对象、eval()函数用法实例分析
2019/02/21 Javascript
JavaScript简易计算器制作
2020/01/17 Javascript
通过实例了解JS执行上下文运行原理
2020/06/17 Javascript
[44:51]2018DOTA2亚洲邀请赛 4.4 淘汰赛 VP vs Liquid 第二场
2018/04/05 DOTA
python使用any判断一个对象是否为空的方法
2014/11/19 Python
python实现各进制转换的总结大全
2017/06/18 Python
《Python学习手册》学习总结
2018/01/17 Python
在Python中表示一个对象的方法
2019/06/25 Python
详解Python 多线程 Timer定时器/延迟执行、Event事件
2019/06/27 Python
详解pycharm的python包opencv(cv2)无代码提示问题的解决
2021/01/29 Python
京东国际站:JOYBUY
2017/11/23 全球购物
策划创业计划书
2014/02/06 职场文书
终止劳动合同证明书样本
2014/11/19 职场文书
企业工会工作总结2015
2015/05/13 职场文书
python实现简单反弹球游戏
2021/04/12 Python