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中使用itertools模块中的组合函数的教程
Apr 13 Python
python实现清屏的方法
Apr 30 Python
Python实现生成随机日期字符串的方法示例
Dec 25 Python
python实现视频分帧效果
May 31 Python
pycharm设置鼠标悬停查看方法设置
Jul 29 Python
python rsa实现数据加密和解密、签名加密和验签功能
Sep 18 Python
Python内置数据类型list各方法的性能测试过程解析
Jan 07 Python
Python continue语句实例用法
Feb 06 Python
Matplotlib使用字符串代替变量绘制散点图的方法
Feb 17 Python
Python爬虫实现模拟点击动态页面
Mar 05 Python
pycharm部署、配置anaconda环境的教程
Mar 24 Python
Python自动重新加载模块详解(autoreload module)
Apr 01 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指定函数参数默认值示例代码
2013/12/04 PHP
yii2安装详细流程
2018/05/23 PHP
一个多次搜索+多次传值的解决方案
2007/01/20 Javascript
Javascript验证上传图片大小[前台处理]
2014/07/18 Javascript
自己动手手写jQuery插件总结
2015/01/20 Javascript
Node.js和MongoDB实现简单日志分析系统
2015/04/25 Javascript
真正好用的js验证上传文件大小的简单方法
2016/10/27 Javascript
Bootstrap3 datetimepicker控件使用实例
2016/12/13 Javascript
javascript中toFixed()四舍五入使用方法详解
2018/09/28 Javascript
vant(ZanUi)结合async-validator实现表单验证的方法
2018/12/06 Javascript
vue中利用simplemde实现markdown编辑器(增加图片上传功能)
2019/04/29 Javascript
JavaScript实现美化滑块效果
2019/05/17 Javascript
js实现for循环跳过undefined值示例
2019/07/02 Javascript
微信小程序通过一个json实现分享朋友圈图片
2019/09/03 Javascript
通过Kettle自定义jar包供javascript使用
2020/01/29 Javascript
[04:03]2014DOTA2西雅图国际邀请赛 LGD战队巡礼
2014/07/07 DOTA
python字符串str和字节数组相互转化方法
2017/03/18 Python
Python基于分水岭算法解决走迷宫游戏示例
2017/09/26 Python
Python3.6连接Oracle数据库的方法详解
2018/05/18 Python
Python简单实现网页内容抓取功能示例
2018/06/07 Python
Django框架会话技术实例分析【Cookie与Session】
2019/05/24 Python
使用Python和OpenCV检测图像中的物体并将物体裁剪下来
2019/10/30 Python
Pygame的程序开始示例代码
2020/05/07 Python
Django models文件模型变更错误解决
2020/05/11 Python
详解Pytorch显存动态分配规律探索
2020/11/17 Python
最新个人职业生涯规划书
2014/01/22 职场文书
幼儿教师师德演讲稿
2014/05/06 职场文书
推荐信怎么写
2014/05/09 职场文书
企业宣传工作方案
2014/06/02 职场文书
土建工程师岗位职责
2014/06/10 职场文书
我的大学四年规划书范文2014
2014/09/26 职场文书
工会2014法制宣传日活动总结
2014/11/01 职场文书
横店影视城导游词
2015/02/06 职场文书
企业宣传语大全
2015/07/13 职场文书
2015小学音乐教师个人工作总结
2015/07/21 职场文书
公司晚宴祝酒词
2015/08/11 职场文书