使用三角形,矩形细分连通域轮廓多边形计算连通域重心

本文介绍了如何利用三角形和矩形等基本几何图元细分连通域轮廓,以高效计算连通域的重心。通过OpenCV的cvFindContours和cvApproxPoly函数处理图像轮廓,减少计算量并处理凸包缺陷。通过细分和合并图元,即便存在凸缺陷,也能准确计算重心。代码使用C++和OpenCV 2.45版,虽然较长且复杂,但提供了实用的方法。
摘要由CSDN通过智能技术生成

方法类似于网格思路,但是使用三角形和四边形等易于计算的基本基本图元可以达到更快速目的。大致思路就是将连通域轮廓分解为更多基本土元,然后计算出这些基本土元的重心和面积之后再将他们呢一一合并。

为了偷懒我直接载入了下面这张黑白图:

图-1

首先需要得到轮廓的原始数据,使用Opencv函数 cvFindContours(...) 来提取,得到轮廓如下:

图-2

虽然这个轮廓很完美,但是不能直接用于多边形细分,过多的棱边需要太多计算量。函数 cvApproxPoly(..) 可以对轮廓进行“简化”,得到一个更加稀疏新轮廓。cvApproxPoly(..) 倒数第二个参数2.45版本里叫做eps决定了函数的逼近精度,我使用5得到了下面这个逼近后的新轮廓:


图-3

这张图标出了新轮廓的点和起点:

图-4

简单的轮廓意味着更少的计算量和凸包缺陷,但是精度也更差。说道这里不得不提一下凸包缺陷(说白了就是苹果被啃了一口),一个没有凸缺陷的轮廓的重心和面积计算是相当简便的(轮廓内随便找个点就可以将N个元素的图轮廓细分为N个三角形,最后一一合并即可,参考这里),例如下边这个凸轮廓(红标)就是图-4去掉凸缺陷后得到新轮廓:


图-5

既然已经知道了结果那么过程也就不难猜了,再计算出那些凸缺陷处的轮廓并将所有轮廓再一一合并就可以了。

顺便值得一提的是图-5里正好有一处特殊情况,红标1和2之间实际上有数个凸缺陷,但是了计算简单在逼近轮廓生成凸轮廓的过程中我将它们看做一个凸缺陷,随后对这个缺陷进行细分的时候见那些位于图轮廓内部的基本土元的面积设为正,外部为负。

当 使用精度5计算逼近轮廓时的计算结果:


C++项目,Opencv使用了2.45版本,代码写的比较糟糕,又长又烂,看完它肯定心灵和体力上的双重煎熬。

如果你使用了预编译头文件请将宏定义放到头文件中。

//
// 例子测试:使用三角形,矩形细分连通域轮廓多边形计算连通域重心				//
// !!图像以及轮廓点坐标以左上角为原点										//
// 高国文@2014-2-11															//
//																			//
//
#define _CRT_SECURE_NO_WARNINGS
#define SAME_TRUE_DIFF_FALSE(p,q) (((p) == (q))? (true):(false))	// 同真异假


C++头文件/
#include <iostream>															//
#include <vector>															//
#include <math.h>															//
#include <algorithm>														//
using namespace std;														//
OpenCV 2.45///
#include <opencv\cv.h>														//
#include <opencv\cxcore.h>													//
#include <opencv\highgui.h>													//
#pragma comment(lib, "opencv_calib3d245.lib")								//
#pragma comment(lib, "opencv_core245.lib")									//
#pragma comment(lib, "opencv_highgui245.lib")								//
#pragma comment(lib, "opencv_imgproc245.lib")								//
using namespace cv;															//
//


// 轮廓点
typedef struct _contourPoint{
	bool	isHeadBoundary;		// 是否是凸包缺陷头边界
	bool	isFootBoundary;		// 是否是凸包缺陷尾边界
	int		nextBoundStride;	// 下一个边界的跨距
	CvPoint coord;				// 坐标数据
}contourPoint;
inline contourPoint createContourPoint(const int &x, const int &y) { return{ false, false, -1, cvPoint(x, y) }; }

// 轮廓
typedef struct _contour{
	bool isCCW;					// 轮廓上的点是否为逆时针排列
	CvPoint cog;				// 轮廓重心
	float area;					// 轮廓面积
	vector<contourPoint> seq;	// 轮廓数据
}contour;

// 毛边
typedef struct _roughStuff{
	bool isCCW;					// 轮廓上的点是否为逆时针排列
	CvPoint cog;				// 三角形或四边形的重心
	float area;					// 轮廓面积
	vector<CvPoint> seq;		// 轮廓数据
}roughStuff;
void zeroStuff(roughStuff &stuff) { stuff.isCCW = false; stuff.cog = { 0, 0 }; stuff.area = 0; stuff.seq.clear(); }

// 线段
typedef struct _lineSegment{
	CvPoint a, b;
}lineSegment, lineINT;
inline lineSegment createLine(const CvPoint &p1, const CvPoint &p2) { return{ p1, p2 }; }
inline lineSegment createLine(const contourPoint &p1, const contourPoint &p2) { return{ cvPoint(p1.coord.x, p1.coord.y), cvPoint(p2.coord.x, p2.coord.y) }; }


// 打印版本号
void dumpVersion();
// 打印轮廓每条边是否和轮廓相切(符合相切的条件,与轮廓有且只有两个焦点)
void dumpTangentialInfo(const vector<contourPoint> &seq);
// 计算重心
CvPoint calCOG(contour &c);
// 检查轮廓是否为逆时针
bool checkContourCCW(const vector<contourPoint> &seq);
bool checkContourCCW(const vector<CvPoint> &seq);
// 检查三角形轮廓是否为逆时针
bool checkTrianCCW(const vector<contourPoint> &trian);
bool checkTrianCCW(const vector<CvPoint> &trian);
// 计算轮廓的某条边所在直线是否与轮廓相切
bool checkTangential(const int &index, const vector<contourPoint> &seq);
bool checkTangential(const int &index, const vector
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值