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 相关文章推荐
pycharm 使用心得(八)如何调用另一文件中的函数
Jun 06 Python
Python实现输出程序执行进度百分比的方法
Sep 16 Python
Python实现简单的HttpServer服务器示例
Sep 25 Python
Python实现按特定格式对文件进行读写的方法示例
Nov 30 Python
python将一个英文语句以单词为单位逆序排放的方法
Dec 20 Python
深入理解Python异常处理的哲学
Feb 01 Python
python分数表示方式和写法
Jun 26 Python
python flask web服务实现更换默认端口和IP的方法
Jul 26 Python
基于spring boot 日志(logback)报错的解决方式
Feb 20 Python
Python 剪绳子的多种思路实现(动态规划和贪心)
Feb 24 Python
Python 改变数组类型为uint8的实现
Apr 09 Python
python 监控logcat关键字功能
Sep 04 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字符转义相关函数小结(php下的转义字符串)
2007/04/12 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十四)
2014/06/26 PHP
PHP和javascript常用正则表达式及用法实例
2014/07/01 PHP
dedecms集成财付通支付接口
2014/12/28 PHP
PHP 返回数组后处理方法(开户成功后弹窗提示)
2017/07/03 PHP
tp5.1 框架查询表达式用法详解
2020/05/25 PHP
IE和Firefox在JavaScript应用中的兼容性探讨
2008/04/01 Javascript
风吟的小型JavaScirpt库 (FY.JS).
2010/03/09 Javascript
window.location.href中url中数据量太大时的解决方法
2013/12/23 Javascript
js如何判断用户是在PC端和还是移动端访问
2014/04/24 Javascript
JQuery右键菜单插件ContextMenu使用指南
2014/12/19 Javascript
学习使用jquery iScroll.js移动端滚动条插件
2020/03/24 Javascript
jQuery 限制输入字符串长度
2016/06/20 Javascript
jQuery多选框选择数量限制方法
2017/02/08 Javascript
Vue 创建组件的两种方法小结(必看)
2018/02/23 Javascript
React中使用async validator进行表单验证的实例代码
2018/08/17 Javascript
对angular4子路由&amp;辅助路由详解
2018/10/09 Javascript
Jquery实现无缝向上循环滚动列表的特效
2019/02/13 jQuery
[01:07:41]IG vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python抓取网页中的图片示例
2014/02/28 Python
python通过pil将图片转换成黑白效果的方法
2015/03/16 Python
Python迭代器与生成器用法实例分析
2018/07/09 Python
详解python 中in 的 用法
2019/12/12 Python
对Pytorch中Tensor的各种池化操作解析
2020/01/03 Python
基于Python下载网络图片方法汇总代码实例
2020/06/24 Python
Python描述数据结构学习之哈夫曼树篇
2020/09/07 Python
html5使用canvas绘制一张图片
2014/12/15 HTML / CSS
英国百安居装饰建材网上超市:B&Q
2016/09/13 全球购物
美国网上鞋子零售商:Dr. Scholl’s Shoes
2017/11/17 全球购物
幼师求职自荐信
2014/05/31 职场文书
计算机专业毕业生自荐书
2014/06/02 职场文书
乡镇党委书记个人整改措施
2014/09/15 职场文书
个人收入证明模板
2014/09/18 职场文书
2014年行风建设工作总结
2014/12/01 职场文书
python实现高效的遗传算法
2021/04/07 Python
Nginx如何获取自定义请求header头和URL参数详解
2022/07/23 Servers