我们有时候需要求取某一个物体重心,这里一般将图像二值化,得出该物体的轮廓,然后根据灰度重心法,计算出每一个物体的中心。
步骤如下:
1)合适的阈值二值化
2)求取轮廓
3)计算重心
otsu算法求取最佳阈值
otsu法(最大类间方差法,有时也称之为大津算法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分,使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分,otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。
计算轮廓
opencv中函数findContours函数
findContours(二值化图像,轮廓,hierarchy,轮廓检索模式,轮廓近似办法,offset)
灰度重心法
利用灰度重心法计算中心,灰度重心法将区域内每一像素位置处的灰度值当做该点的“质量”,其求区域中心的公式为:
其中,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<Pointcontours; 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 根据模板凸包求阈值化后的轮廓组合
图像处理中,要求特征与背景的对比度高,同时,合适的图像分割也是解决问题的关键。
博主以前的方法,默认为特征必然是最大的连通域,所以阈值化后,查找轮廓,直接提取面积最大的轮廓即可。
但可能会存在另一种情况,不论怎么阈值化和膨胀,想要的特征被分成好几块,也即断开了。此时,再加上一些不可预测的干扰和噪声,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求取连通区域重心实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
Opencv,连通区域,重心
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。