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

发布时间:2016-12-9 21:27:53 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"使用三角形,矩形细分连通域轮廓多边形计算连通域重心",主要涉及到使用三角形,矩形细分连通域轮廓多边形计算连通域重心方面的内容,对于使用三角形,矩形细分连通域轮廓多边形计算连通域重心感兴趣的同学可以参考一下。

方法类似于网格思路,但是使用三角形和四边形等易于计算的基本基本图元可以达到更快速目的。大致思路就是将连通域轮廓分解为更多基本土元,然后计算出这些基本土元的重心和面积之后再将他们呢一一合并。 为了偷懒我直接载入了下面这张黑白图: 图-1 首先需要得到轮廓的原始数据,使用Opencv函数 cvFindContours(...) 来提取,得到轮廓如下: 图-2 虽然这个轮廓很完美,但是不能直接用于多边形细分,过多的棱边需要太多计算量。函数 cvApproxPoly(..) 可以对轮廓进行“简化”,得到一个更加稀疏新轮廓。cvApproxPoly(..) 倒数第二个参数2.45版本里叫做eps决定了函数的逼近精度,我使用5得到了下面这个逼近后的新轮廓: 图-3 这张图标出了新轮廓的点和起点: 图-4 简单的轮廓意味着更少的计算量和凸包缺陷,但是精度也更差。说道这里不得不提一下凸包缺陷(说白了就是苹果被啃了一口),一个没有凸缺陷的轮廓的重心和面积计算是相当简便的(轮廓内随便找个点就可以将N个元素的图轮廓细分为N个三角形,最后一一合并即可,参考这里),例如下边这个凸轮廓(红标)就是图-4去掉凸缺陷后得到新轮廓: 图-5 既然已经知道了结果那么过程也就不难猜了,再计算出那些凸缺陷处的轮廓并将所有轮廓再一一合并就可以了。 顺便值得一提的是图-5里正好有一处特殊情况,红标1和2之间实际上有数个凸缺陷,但是了计算简单在逼近轮廓生成凸轮廓的过程中我将它们看做一个凸缺陷,随后对这个缺陷进行细分的时候见那些位于图轮廓内部的基本土元的面积设为正,外部为负。 当 使用精度5计算逼近轮廓时的计算结果: C++项目,Opencv使用了2.45版本,代码写的比较糟糕,又长又烂,看完它肯定心灵和体力上的双重煎熬。 如果你使用了预编译头文件请将宏定义放到头文件中。 ////////////////////////////////////////////////////////////////////////////// // 例子测试:使用三角形,矩形细分连通域轮廓多边形计算连通域重心 // // !!图像以及轮廓点坐标以左上角为原点 // // [email protected] // // // ////////////////////////////////////////////////////////////////////////////// #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<CvPoint> &seq); // 逆序数组 void reverseVector(vector<contourPoint> &seq); void reverseVector(vector<CvPoint> &seq); // 点是否击中直线 int isPointHitLine(const contourPoint &point, const lineINT &l); int isPointHitLine(const CvPoint &point, const lineINT &l); // 计算三角形面积(2维) float calTrianArea(const CvPoint &pt0, const CvPoint &pt1, const CvPoint &pt2); // 计算矩形的面积(2维) float calRecArea(const vector<CvPoint> &rec); // 计算一个逆时针凸包轮廓的面积 bool calConvecContourCOG(contour &c); int main(int argc, char* argv[]) { dumpVersion(); // 以灰度图加载两幅原图像 IplImage *worm_gray = cvLoadImage("worm.bmp", CV_LOAD_IMAGE_GRAYSCALE); IplImage *circle_gray = cvLoadImage("circle.bmp", CV_LOAD_IMAGE_GRAYSCALE); IplImage *showimg = cvCreateImage(cvGetSize(worm_gray), 8, 3); cvZero(showimg); // 计算虫子的轮廓 CvSeq *worm_contours = nullptr; CvMemStorage *memB1 = cvCreateMemStorage(0); cvFindContours(worm_gray, memB1, &worm_contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE); // 计算逼近轮廓 CvMemStorage *memB2 = cvCreateMemStorage(0); CvSeq *worm_approxed = cvApproxPoly(worm_contours, sizeof(CvContour), memB2, CV_POLY_APPROX_DP, 5, 0/**/); // 绘制逼近后的轮廓 cvDrawContours(showimg, worm_approxed, cvScalar(100, 100, 100), cvScalar(100, 100, 100), 2); // 读取所有点数据 contour worm_contour_vector; worm_contour_vector.seq.reserve( worm_approxed->total ); for (int ite = 0; ite < worm_approxed->total; ++ite) { CvPoint *cvp = CV_GET_SEQ_ELEM(CvPoint, worm_approxed, ite); contourPoint newp = { false, false, -1, cvPoint(cvp->x, cvp->y) }; worm_contour_vector.seq.push_back(newp); } worm_contour_vector.seq.shrink_to_fit(); dumpTangentialInfo(worm_contour_vector.seq); // 如果轮廓不是逆时针排列的就反转它 worm_contour_vector.isCCW = checkContourCCW(worm_contour_vector.seq); if (!worm_contour_vector.isCCW) { reverseVector(worm_contour_vector.seq); worm_contour_vector.isCCW = true; } dumpTangentialInfo(worm_contour_vector.seq); //// 绘制轮廓点 //for (int ite = 0; ite < worm_approxed->total; ++ite) //{ // if (ite == worm_approxed->total-1) // { // CvPoint *point = nullptr; // point = CV_GET_SEQ_ELEM(CvPoint, worm_approxed, ite); // cvDrawCircle(showimg, *point, 4, cvScalar(0, 90, 62), 2); // continue; // } // CvPoint *point = nullptr; // point = CV_GET_SEQ_ELEM(CvPoint, worm_approxed, ite); // cvDrawCircle(showimg, *point, 4, cvScalar(62, 90, 62), 2); //} // 计算重心坐标 CvPoint cog = calCOG(worm_contour_vector); cout << "重心坐标:<" << cog.x << "," << cog.y << ">" << endl; // 绘制重心坐标 cvDrawCircle(showimg, cog, 6, cvScalar(10, 60, 10), 2); cvDrawCircle(showimg, cog, 1, cvScalar(20, 20, 100), 3); cvNamedWindow("测试"); cvShowImage("测试", showimg); cvWaitKey(); // 释放资源 (worm_gray) ? (cvReleaseImage(&worm_gray), worm_gray = nullptr) : (NULL); (circle_gray) ? (cvReleaseImage(&circle_gray), circle_gray = nullptr) : (NULL); (showimg) ? (cvReleaseImage(&showimg), showimg = nullptr) : (NULL); (worm_contours) ? (cvClearSeq(worm_contours), worm_contours = nullptr) : (NULL); (memB1) ? (cvClearMemStorage(memB1), memB1 = nullptr) : (NULL); (memB2) ? (cvClearMemStorage(memB2), memB2 = nullptr) : (NULL); (worm_approxed) ? (cvClearSeq(worm_approxed), worm_approxed = nullptr) : (NULL); return 0; } /* * 打印CV库版本号 */ void dumpVersion() { const char *version = nullptr; cvGetModuleInfo(nullptr, &version, nullptr); cout << version << endl; } /* * 打印相切的轮廓边(符合相切的条件,与轮廓有且只有两个交点) */ void dumpTangentialInfo(const vector<contourPoint> &seq) { cout << "相切的边有 " << ends; int count = 0; for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (checkTangential(ite, seq)) { cout << "<" << ite << "," << (ite + 1) % seq.size() << ">" << ends; ++count; } } cout << " 共" << count << "条" << endl; } /* * 计算重心 */ CvPoint calCOG(contour &c) { // 循环轮廓 c.seq 找出所有凸缺陷边界点 int defectCount = 0; // 凸包缺陷计数 int firstBoundaryIndex = 0; bool isFBIInited = false; int lastBoundaryIndex = 0; bool preState = checkTangential(0, c.seq); int preBoundaryIndex = 0; for (int ite = 1, strideCount = 0; ; ++ite) { ite %= c.seq.size(); if (isFBIInited && c.seq[ite].isHeadBoundary) { c.seq[preBoundaryIndex].nextBoundStride = strideCount%c.seq.size(); break; } ++strideCount; // 遇到了一个边界点 if (SAME_TRUE_DIFF_FALSE(preState, checkTangential(ite, c.seq)) == false) { (!isFBIInited) ? (firstBoundaryIndex = ite, isFBIInited = true) : (NULL); lastBoundaryIndex = ite; c.seq[ite].isHeadBoundary = preState; c.seq[ite].isFootBoundary = !preState; (c.seq[ite].isHeadBoundary) ? (++defectCount) : (NULL); if (c.seq[preBoundaryIndex].isHeadBoundary || c.seq[preBoundaryIndex].isFootBoundary) c.seq[preBoundaryIndex].nextBoundStride = strideCount; preState = !preState; preBoundaryIndex = ite; strideCount = 0; continue; } } // 设置最后一个边界到第一个边界的跨距 c.seq[lastBoundaryIndex].nextBoundStride = (firstBoundaryIndex + c.seq.size()) - lastBoundaryIndex; vector<contourPoint> newSeq = c.seq; // 用于存储去掉凸缺陷后的新轮廓 vector<roughStuff> stuffs; // 毛边们,用于纠正seq与newSeq的面积误差 roughStuff anyoneStuff; // 存储某个毛边 unsigned maxfor = 0; if ( defectCount > 0 ) // 如果缺陷数量比较多 { firstBoundaryIndex = 0; // 用于标识第一个凸缺陷的头边界 isFBIInited = false; // firstBoundaryIndex 是否已被初始化 for (int ite = 0;;++ite, ++maxfor) { if (maxfor > 2 * newSeq.size()) // 避免误差导致的死循环 break; ite %= newSeq.size(); if (newSeq[ite].isHeadBoundary) // 如果碰到了一个凸缺陷 { if ( !isFBIInited ) { firstBoundaryIndex = ite; isFBIInited = true; }else if( ite == firstBoundaryIndex ){ break; } int thisIndex = (ite + 1) % newSeq.size(); int thatIndex = (ite + 2) % newSeq.size(); int footIndex = (newSeq[ite].nextBoundStride + ite) % newSeq.size(); // 当前凸包缺陷尾边界的索引 lineSegment defecHead2Foot = createLine(newSeq[ite], newSeq[footIndex]); // 当前凸缺陷头边界和尾边界所在的直线 // 计算newseq轮廓重心位于直线(ite, footIndex)的哪个位置 // 上侧(左侧)为1,击中0,下侧(或右侧)为-1 int vDirectionValue = isPointHitLine(newSeq[(footIndex + 1) % newSeq.size()], defecHead2Foot); float v[2] = { ((float)newSeq[footIndex].coord.x - newSeq[ite].coord.x), ((float)newSeq[footIndex].coord.y - newSeq[ite].coord.y) }; float vlen = sqrtf(v[0] * v[0] + v[1] * v[1]); float v1[2] = { ((float)newSeq[thisIndex].coord.x - newSeq[ite].coord.x), ((float)newSeq[thisIndex].coord.y - newSeq[ite].coord.y) }; float v1len = sqrtf(v1[0] * v1[0] + v1[1] * v1[1]); float cosALPHA1 = (v1[0] * v[0] + v1[1] * v[1]) / (vlen*v1len); float projection1 = abs(v1len * cosALPHA1); // 线段 ite->(thisindex) 在线段 ite->footIndex 上的投影长度 float v2[2] = { 0.f }; float v2len = 0.f; float cosALPHA2 = 0.f; float projection2 = 0.f; // 线段 ite->(thatindex) 在线段 ite->footIndex 上的投影长度 // thisindex 在直线上的投影 CvPoint v1PointProjection; float factor = projection1 / (vlen - projection1); v1PointProjection.x = (int)(((float)newSeq[ite].coord.x + factor * newSeq[footIndex].coord.x) / (1.f + factor)); v1PointProjection.y = (int)(((float)newSeq[ite].coord.y + factor * newSeq[footIndex].coord.y) / (1.f + factor)); // thatindex 在直线上的投影 CvPoint v2PointProjection; // 手动添加当前凸包缺陷的第一个三角形(ite, v1PointProjection, thisindex) anyoneStuff.seq.push_back(cvPoint(newSeq[ite].coord.x, newSeq[ite].coord.y)); anyoneStuff.seq.push_back(v1PointProjection); anyoneStuff.seq.push_back(cvPoint(newSeq[thisIndex].coord.x, newSeq[thisIndex].coord.y)); if (!(anyoneStuff.isCCW = checkContourCCW(anyoneStuff.seq))) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.cog.x = (anyoneStuff.seq[0].x + anyoneStuff.seq[1].x + anyoneStuff.seq[2].x) / 3; anyoneStuff.cog.y = (anyoneStuff.seq[0].y + anyoneStuff.seq[1].y + anyoneStuff.seq[2].y) / 3; anyoneStuff.area = -1.f * calTrianArea(anyoneStuff.seq[0], anyoneStuff.seq[1], anyoneStuff.seq[2]); stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); for ( ; newSeq[ite].nextBoundStride > 0; ) { if ( 2 == newSeq[ite].nextBoundStride ) { // 添加当前凸缺陷的最后一个一个三角形 anyoneStuff.seq.push_back(cvPoint(newSeq[ite].coord.x, newSeq[ite].coord.y)); anyoneStuff.seq.push_back(v1PointProjection); anyoneStuff.seq.push_back(cvPoint(newSeq[footIndex].coord.x, newSeq[ite].coord.y)); if ( !(anyoneStuff.isCCW = checkContourCCW(anyoneStuff.seq)) ) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.cog.x = (anyoneStuff.seq[0].x + anyoneStuff.seq[1].x + anyoneStuff.seq[2].x) / 3; anyoneStuff.cog.y = (anyoneStuff.seq[0].y + anyoneStuff.seq[1].y + anyoneStuff.seq[2].y) / 3; anyoneStuff.area = -1.f * calTrianArea(anyoneStuff.seq[0], anyoneStuff.seq[1], anyoneStuff.seq[2]); stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); // 删掉点newSeq[thisindex] if (thisIndex == 0) // 越界了 { newSeq.erase(newSeq.begin()); --ite; }else{ newSeq.erase(newSeq.begin() + thisIndex); } --newSeq[ite].nextBoundStride; break; } v2[0] = (float)newSeq[thatIndex].coord.x - newSeq[ite].coord.x; v2[1] = (float)newSeq[thatIndex].coord.y - newSeq[ite].coord.y; v2len = sqrtf(v2[0] * v2[0] + v2[1] * v2[1]); cosALPHA2 = (v2[0] * v[0] + v2[1] * v[1]) / (v2len * vlen); projection2 = abs(v2len * cosALPHA2); if (projection1 < projection2) { // 计算点 thisindex 位于直线(ite, footIndex)的哪个位置 int v1DirectionValue = isPointHitLine(newSeq[thisIndex], defecHead2Foot); // 计算点 thatindex 位于直线(ite, footIndex)的哪个位置 int v2DirectionValue = isPointHitLine(newSeq[thatIndex], defecHead2Foot); // 计算ite+2 在直线上的投影 factor = projection1 / (vlen - projection2); v2PointProjection.x = (int)(((float)newSeq[ite].coord.x + factor * newSeq[footIndex].coord.x) / (1.f + factor)); v2PointProjection.y = (int)(((float)newSeq[ite].coord.y + factor * newSeq[footIndex].coord.y) / (1.f + factor)); if ( v1DirectionValue * v2DirectionValue == 0 ) // 如果某个点位于直线上 { if (0 == v1DirectionValue) // 如果点 thisindex 位于直线上 { // 基本不可能发生但还是考虑一下 // 。。。恩!!代码略去 zeroStuff(anyoneStuff); }else{ // 如果点 thatindex 位于直线上 anyoneStuff.seq.push_back(cvPoint(newSeq[thisIndex].coord.x, newSeq[thisIndex].coord.y)); anyoneStuff.seq.push_back(v2PointProjection); anyoneStuff.seq.push_back(cvPoint(newSeq[thatIndex].coord.x, newSeq[thatIndex].coord.y)); anyoneStuff.isCCW = checkTrianCCW(anyoneStuff.seq); if (!anyoneStuff.isCCW) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.area = ((vDirectionValue * v2DirectionValue > 0) ? (1.f) : (-1.f)) * calTrianArea(anyoneStuff.seq[0], anyoneStuff.seq[1], anyoneStuff.seq[2]); anyoneStuff.cog.x = (anyoneStuff.seq[0].x + anyoneStuff.seq[1].x + anyoneStuff.seq[2].x) / 3; anyoneStuff.cog.y = (anyoneStuff.seq[0].y + anyoneStuff.seq[1].y + anyoneStuff.seq[2].y) / 3; stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); } }else if (v1DirectionValue*v2DirectionValue > 0) { // 如果 thisindex 与 thatindex 位于同侧 anyoneStuff.seq.push_back(cvPoint(newSeq[thisIndex].coord.x, newSeq[thisIndex].coord.y)); anyoneStuff.seq.push_back(v1PointProjection); anyoneStuff.seq.push_back(v2PointProjection); anyoneStuff.seq.push_back(cvPoint(newSeq[thatIndex].coord.x, newSeq[thatIndex].coord.y)); anyoneStuff.isCCW = checkTrianCCW(anyoneStuff.seq); // 四边形的一半即是三角形,三角形的环绕方向即是四边形的环绕方向 if (!anyoneStuff.isCCW) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.area = ((vDirectionValue * v2DirectionValue > 0) ? (1.f) : (-1.f)) * calRecArea(anyoneStuff.seq); anyoneStuff.cog.x = (anyoneStuff.seq[0].x + anyoneStuff.seq[1].x + anyoneStuff.seq[2].x + anyoneStuff.seq[3].x) / 4; anyoneStuff.cog.y = (anyoneStuff.seq[0].y + anyoneStuff.seq[1].y + anyoneStuff.seq[2].y + anyoneStuff.seq[3].y) / 4; stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); }else { // 如果 thisindex 与 thatindex 位于异侧 CvPoint interPoint; // 两条直线的交点 // 相似三角形定理(http://blog.csdn.net/funte/article/details/19222995) factor = sqrtf(powf((float)newSeq[thisIndex].coord.x - v1PointProjection.x, 2) + powf((float)newSeq[thisIndex].coord.y - v1PointProjection.y, 2)) / sqrtf(powf((float)newSeq[thatIndex].coord.x - v2PointProjection.x, 2) + powf((float)newSeq[thatIndex].coord.y - v2PointProjection.y, 2)); interPoint.x = (int)(((float)newSeq[thisIndex].coord.x + factor*newSeq[thatIndex].coord.x) / (factor + 1.f)); interPoint.y = (int)(((float)newSeq[thisIndex].coord.y + factor*newSeq[thatIndex].coord.y) / (factor + 1.f)); anyoneStuff.seq.push_back(cvPoint(newSeq[thisIndex].coord.x, newSeq[thisIndex].coord.y)); anyoneStuff.seq.push_back(v1PointProjection); anyoneStuff.seq.push_back(interPoint); anyoneStuff.isCCW = checkTrianCCW(anyoneStuff.seq); if ( !anyoneStuff.isCCW ) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.area = ((vDirectionValue * v1DirectionValue > 0) ? (1.f) : (-1.f)) * calTrianArea(anyoneStuff.seq[0], anyoneStuff.seq[1], anyoneStuff.seq[2]); anyoneStuff.cog.x = (anyoneStuff.seq[0].x = anyoneStuff.seq[1].x + anyoneStuff.seq[2].x) / 3; anyoneStuff.cog.y = (anyoneStuff.seq[0].y = anyoneStuff.seq[1].y + anyoneStuff.seq[2].y) / 3; stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); anyoneStuff.seq.push_back(interPoint); anyoneStuff.seq.push_back(v2PointProjection); anyoneStuff.seq.push_back(cvPoint(newSeq[thatIndex].coord.x, newSeq[thatIndex].coord.y)); anyoneStuff.isCCW = checkTrianCCW(anyoneStuff.seq); if (!anyoneStuff.isCCW) { reverseVector(anyoneStuff.seq); anyoneStuff.isCCW = true; } anyoneStuff.area = ((vDirectionValue * v2DirectionValue > 0) ? (1.f) : (-1.f)) * calTrianArea(anyoneStuff.seq[0], anyoneStuff.seq[1], anyoneStuff.seq[2]); anyoneStuff.cog.x = (anyoneStuff.seq[0].x = anyoneStuff.seq[1].x + anyoneStuff.seq[2].x) / 3; anyoneStuff.cog.y = (anyoneStuff.seq[0].y = anyoneStuff.seq[1].y + anyoneStuff.seq[2].y) / 3; stuffs.push_back(anyoneStuff); zeroStuff(anyoneStuff); } } if (thisIndex == 0) // 越界了 { newSeq.erase(newSeq.begin()); --ite; }else{ newSeq.erase(newSeq.begin() + thisIndex); } --newSeq[ite].nextBoundStride; footIndex = (newSeq[ite].nextBoundStride + ite) % newSeq.size(); projection1 = projection2; v1PointProjection = v2PointProjection; } } } } // 计算轮廓重心 contour newContour = {true, cvPoint(0, 0), -1, newSeq}; newSeq.swap( vector<contourPoint>() ); // 释放newSeq calConvecContourCOG(newContour); // 根据每个 roughStuff 对 newContour 的重心进行微调 for (int ite = 0; (unsigned)ite < stuffs.size(); ++ite) { float factor = stuffs[ite].area / newContour.area; newContour.cog.x = (int)(((float)newContour.cog.x + factor * stuffs[ite].cog.x) / (1.f + factor)); newContour.cog.y = (int)(((float)newContour.cog.y + factor * stuffs[ite].cog.y) / (1.f + factor)); } return newContour.cog; } /* * 检查轮廓是否为逆时针 */ bool checkContourCCW(const vector<contourPoint> &seq) { for (int ite = 0; (unsigned)ite < seq.size();) { if (!checkTangential(ite, seq)) { ++ite; continue; }else{ if (!checkTangential((ite + 1) % seq.size(), seq)) { ite += 2; continue; }else{ vector<CvPoint> trian; trian.push_back(cvPoint(seq[ite].coord.x, seq[ite].coord.y)); trian.push_back(cvPoint(seq[(ite + 1) % seq.size()].coord.x, seq[(ite + 1) % seq.size()].coord.y)); trian.push_back(cvPoint(seq[(ite + 2) % seq.size()].coord.x, seq[(ite + 2) % seq.size()].coord.y)); return checkTrianCCW(trian); } } } // 极少见的情况,例如五角星 //!!特殊轮廓的处理代码!! return false; } bool checkContourCCW(const vector<CvPoint> &seq) { for (int ite = 0; (unsigned)ite < seq.size();) { if (!checkTangential(ite, seq)) { ++ite; continue; }else{ if (!checkTangential((ite + 1) % seq.size(), seq)) { ite += 2; continue; }else{ vector<CvPoint> trian; trian.push_back(cvPoint(seq[ite].x, seq[ite].y)); trian.push_back(cvPoint(seq[(ite + 1) % seq.size()].x, seq[(ite + 1) % seq.size()].y)); trian.push_back(cvPoint(seq[(ite + 2) % seq.size()].x, seq[(ite + 2) % seq.size()].y)); return checkTrianCCW(trian); } } } // 极少见的情况,例如五角星 //!!特殊轮廓的处理代码!! return false; } /* * 检查三角形轮廓是否为逆时针 */ bool checkTrianCCW(const vector<contourPoint> &trian) { float v1[2] = { (float)trian[1].coord.x - trian[0].coord.x, (float)trian[1].coord.y - trian[0].coord.y }; float v2[2] = { (float)trian[2].coord.x - trian[0].coord.x, (float)trian[2].coord.y - trian[0].coord.y }; if (((float)v1[0] * (float)v2[1] - (float)v2[0] * (float)v1[1]) > 0.f) return false; // 一个顺时针三角形 else return true; // 一个逆时针三角形 } bool checkTrianCCW(const vector<CvPoint> &trian) { float v1[2] = { (float)trian[1].x - trian[0].x, (float)trian[1].y - trian[0].y }; float v2[2] = { (float)trian[2].x - trian[0].x, (float)trian[2].y - trian[0].y }; if (((float)v1[0] * (float)v2[1] - (float)v2[0] * (float)v1[1]) > 0.f) return false; // 一个顺时针三角形 else return true; // 一个逆时针三角形 } /* * 计算轮廓的某条边所在直线是否与轮廓相切 * 两点确定一条直线,这两点是索引为 index 和 index+1 */ bool checkTangential(const int &index, const vector<contourPoint> &seq) { int la = index % seq.size(); int lb = (index + 1) % seq.size(); // 计算直线斜率和截距 float deltaY = seq[la].coord.y - (float)seq[lb].coord.y; float deltaX = seq[la].coord.x - (float)seq[lb].coord.x; float K = 0.f, C = 0.f; if (0.f == deltaY) { for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断点索引为 ite, ite+1 是否在直线(index,index+1)的同侧 if ((seq[ite].coord.y - seq[la].coord.y)*(seq[(ite + 1) % seq.size()].coord.y - seq[la].coord.y) < 0.) return false; } } }else if (0.f == deltaX) { for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断点索引为 ite, ite+1 是否在直线(index,index+1)的同侧 if ((seq[ite].coord.x - seq[la].coord.x)*(seq[(ite + 1) % seq.size()].coord.x - seq[la].coord.x) < 0) return false; } } }else{ float K = deltaY / deltaX; float C = (float)seq[la].coord.y - K * (float)seq[la].coord.x; // 判断直线(index,index+1)与轮廓是否相切 for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断点索引为 ite, ite+1 是否在直线(index,index+1)的同侧 if (((float)seq[ite].coord.y - (K*seq[ite].coord.x + C)) * ((float)seq[(ite + 1) % seq.size()].coord.y - (K*seq[(ite + 1) % seq.size()].coord.x + C)) < -0.1f) return false; // 如果不在同一侧 } } } return true; } bool checkTangential(const int &index, const vector<CvPoint> &seq) { int la = index % seq.size(); int lb = (index + 1) % seq.size(); // 计算直线斜率和截距 float deltaY = seq[la].y - (float)seq[lb].y; float deltaX = seq[la].x - (float)seq[lb].x; float K = 0.f, C = 0.f; if (0.f == deltaY) { for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断索引为 ite, ite+1 的点是否在直线(index,index+1)的同侧 if ((seq[ite].y - seq[la].y)*(seq[(ite + 1) % seq.size()].y - seq[la].y) < 0) return false; } } }else if (0.f == deltaX) { for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断索引为 ite, ite+1 的点是否在直线(index,index+1)的同侧 if ((seq[ite].x - seq[la].x)*(seq[(ite + 1) % seq.size()].x - seq[la].x) < 0) return false; } } }else{ float K = deltaY / deltaX; float C = (float)seq[la].y - K * (float)seq[la].x; // 判断直线(index,index+1)与轮廓是否相切 for (int ite = 0; (unsigned)ite < seq.size(); ++ite) { if (index != ite) { // 判断点索引为 ite, ite+1 是否在直线(index,index+1)的同侧 if (((float)seq[ite].y - (K*seq[ite].x + C)) * ((float)seq[(ite + 1) % seq.size()].y - (K*seq[(ite + 1) % seq.size()].x + C)) < -0.1f) return false; // 如果不在同一侧 } } } return true; } /* * 逆序数组 */ void reverseVector(vector<contourPoint> &seq) { for (int ite = 0; (unsigned)ite < seq.size() / 2; ++ite) { int index = seq.size() - 1 - ite; contourPoint temp; // seq[ite] -> temp temp.coord.x = seq[ite].coord.x; temp.coord.y = seq[ite].coord.y; temp.isHeadBoundary = seq[ite].isHeadBoundary; temp.isFootBoundary = seq[ite].isFootBoundary; temp.nextBoundStride = seq[ite].nextBoundStride; // seq[index] -> seq[ite] seq[ite].coord.x = seq[index].coord.x; seq[ite].coord.y = seq[index].coord.y; seq[ite].isHeadBoundary = seq[index].isHeadBoundary; seq[ite].isFootBoundary = seq[index].isFootBoundary; seq[ite].nextBoundStride = seq[index].nextBoundStride; // temp -> seq[index] seq[index].coord.x = temp.coord.x; seq[index].coord.y = temp.coord.y; seq[index].isHeadBoundary = temp.isHeadBoundary; seq[index].isFootBoundary = temp.isFootBoundary; seq[index].nextBoundStride = temp.nextBoundStride; } } void reverseVector(vector<CvPoint> &seq) { for (int ite = 0; (unsigned)ite < seq.size() / 2; ++ite) { int index = seq.size() - 1 - ite; CvPoint temp; // seq[ite] -> temp temp.x = seq[ite].x; temp.y = seq[ite].y; // seq[index] -> seq[ite] seq[ite].x = seq[index].x; seq[ite].y = seq[index].y; // temp -> seq[index] seq[index].x = temp.x; seq[index].y = temp.y; } } /* * 点是否击中直线 * 如果点在直线上侧(或左侧)返回1,击中返回0,下侧(或右侧)返回-1 */ int isPointHitLine(const contourPoint &point, const lineINT &l) { return isPointHitLine(cvPoint(point.coord.x, point.coord.y), l); } int isPointHitLine(const CvPoint &point, const lineINT &l) { float deltaY = (float)l.b.y - l.a.y; float deltaX = (float)l.b.x - l.a.x; float K = 0.f, C = 0.f; if (0.f == deltaY) // 如果直线平行于X轴 { if (point.y == l.a.y) return 0; else if (point.y > l.a.y) return 1; else return -1; }else if(0.f == deltaX){ // 如果直线平行于Y轴 if (point.x == l.a.x) return 0; else if (point.x < l.a.x) return 1; else return -1; }else{ K = deltaY / deltaX; C = (float)l.a.y - K * l.a.x; float ret = (float)point.y - (K*point.x + C); if (ret >= -0.1f && ret < 0.1f) return 0; else if (ret > -0.1f) return 1; else return -1; } } /* * 计算三角形面积(2维) */ float calTrianArea(const CvPoint &pt0, const CvPoint &pt1, const CvPoint &pt2) { CvPoint AB, BC; AB.x = pt1.x - pt0.x; AB.y = pt1.y - pt0.y; BC.x = pt2.x - pt1.x; BC.y = pt2.y - pt1.y; return fabs(((float)AB.x * BC.y - (float)AB.y * BC.x)) / 2.f; } /* * 计算矩形的面积(2维) * rec必须是逆时针排列的 */ float calRecArea(const vector<CvPoint> &rec) { // 一个矩形可以看做两个三角形 return calTrianArea(rec[0], rec[1], rec[2]) + calTrianArea(rec[1], rec[2], rec[3]); } /* * 计算一个逆时针凸包轮廓的面积 * contour 必须是一个逆时针凸包轮廓 */ bool calConvecContourCOG(contour &c) { if (!checkContourCCW(c.seq) || !c.isCCW || c.seq.size() < 4) return false; const CvPoint center = { (c.seq[0].coord.x + c.seq[c.seq.size() / 2].coord.x) / 2, (c.seq[0].coord.y + c.seq[c.seq.size() / 2].coord.y) / 2 }; CvPoint cog = { (center.x + c.seq[0].coord.x + c.seq[1].coord.x) / 3, (center.y + c.seq[0].coord.y + c.seq[1].coord.y) / 3 }; float area = calTrianArea(center, cvPoint(c.seq[0].coord.x, c.seq[0].coord.y), cvPoint(c.seq[1].coord.x, c.seq[1].coord.y)); roughStuff stuf; for (int ite = 1; (unsigned)ite < c.seq.size(); ++ite) { // 参考:http://blog.csdn.net/funte/article/details/19289605 int nextindex = (ite + 1) % c.seq.size(); stuf.cog.x = (center.x + c.seq[ite].coord.x + c.seq[nextindex].coord.x) / 3; stuf.cog.y = (center.y + c.seq[ite].coord.y + c.seq[nextindex].coord.y) / 3; stuf.area = calTrianArea(center, cvPoint(c.seq[ite].coord.x, c.seq[ite].coord.y), cvPoint(c.seq[nextindex].coord.x, c.seq[nextindex].coord.y) ); float factor = stuf.area / area; cog.x = (int)((cog.x + factor*stuf.cog.x) / (1.f + factor)); cog.y = (int)((cog.y + factor*stuf.cog.y) / (1.f + factor)); area += stuf.area; } c.cog = cog; c.area = area; return true; }

上一篇:数据库连接数异常增多
下一篇:org.hibernate.LazyInitializationException: could not initialize proxy - no Session

相关文章

相关评论