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爬虫之网页图片抓取的方法
Jul 16 Python
Python基础之函数的定义与使用示例
Mar 23 Python
Python实现查找字符串数组最长公共前缀示例
Mar 27 Python
Python使用POP3和SMTP协议收发邮件的示例代码
Apr 16 Python
python selenium登录豆瓣网过程解析
Aug 10 Python
Django 导出项目依赖库到 requirements.txt过程解析
Aug 23 Python
Python 使用多属性来进行排序
Sep 01 Python
Python实现投影法分割图像示例(一)
Jan 17 Python
django使用F方法更新一个对象多个对象字段的实现
Mar 28 Python
Python 为什么推荐蛇形命名法原因浅析
Jun 18 Python
python模拟点击玩游戏的实例讲解
Nov 26 Python
详解在OpenCV中如何使用图像像素
Mar 03 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句法规则详解 入门学习
2011/11/09 PHP
php使用cookie保存用户登录的用户名实例
2015/01/26 PHP
php实现微信扫码支付
2017/03/26 PHP
PHP实现的激活用户注册验证邮箱功能示例
2017/06/06 PHP
php 利用socket发送GET,POST请求的实例代码
2020/07/04 PHP
jQuery Animation实现CSS3动画示例介绍
2013/08/14 Javascript
Javascript执行效率全面总结
2013/11/04 Javascript
ie下$.getJSON出现问题的解决方法
2014/02/12 Javascript
jQuery性能优化技巧分析
2015/02/20 Javascript
JS实现网页滚动条感应鼠标变色的方法
2015/02/26 Javascript
iframe里使用JavaScript控制主页转向的方法
2015/04/03 Javascript
jQuery选择器_动力节点Java学院整理
2017/07/05 jQuery
解决vue单页使用keep-alive页面返回不刷新的问题
2018/03/13 Javascript
浅谈Vue内置component组件的应用场景
2018/03/27 Javascript
Angular中sweetalert弹框的基本使用教程
2018/07/22 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
2018/09/13 Javascript
JS中使用cavas截图网页并解决跨域及模糊问题
2018/11/13 Javascript
基于JS开发微信网页录音功能的实例代码
2019/04/30 Javascript
js JSON.stringify()基础详解
2019/06/19 Javascript
微信小程序点击生成朋友圈分享图(遇到的坑)
2020/06/17 Javascript
通过滑动翻页效果实现和移动端click事件问题
2021/01/26 Javascript
关于Js中new操作符的作用详解
2021/02/21 Javascript
Python中文件遍历的两种方法
2014/06/16 Python
python中__call__方法示例分析
2014/10/11 Python
Python 中导入csv数据的三种方法
2018/11/01 Python
使用python绘制温度变化雷达图
2019/10/18 Python
Python3 pickle对象串行化代码实例解析
2020/03/23 Python
详解移动端html5页面长按实现高亮全选文本内容的兼容解决方案
2016/12/03 HTML / CSS
GAP阿联酋官网:GAP UAE
2017/11/30 全球购物
便利店投资的创业计划书
2014/01/12 职场文书
社区母亲节活动记录
2014/03/06 职场文书
新闻编辑求职信
2014/04/09 职场文书
小学教师年度个人总结
2015/02/05 职场文书
重温入党誓词主持词
2015/06/29 职场文书
公司晚会主持词
2019/04/17 职场文书
uniapp 微信小程序 自定义tabBar 导航
2022/04/22 Javascript