OpenCV:判定曲线为弧线的简单方法
生活随笔
收集整理的這篇文章主要介紹了
OpenCV:判定曲线为弧线的简单方法
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
借鑒了一個前同事的一個代碼段(良心代碼),功能為判定曲線為弧線,并且給出擬合度;
代碼段:
//尋找所有canny 弧int findArcsOfAllCanny( cv::Mat &inMatCanny,std::vector<std::vector<std::pair< cv::Point, float> > > &circlesPupil, cv::RotatedRect &ecf, int method){//簡單方法,鏈接各個連通域的質(zhì)心,拼湊成一個連通域std::vector<std::vector<cv::Point> > all_contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(inMatCanny, all_contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);//連續(xù)點(diǎn)int minSize = 20;int maxSize =1000; #ifdef SHOW_TEMPcv::Mat canvas = cv::Mat::zeros(inMatCanny.rows, inMatCanny.cols, CV_8UC3);cv::bitwise_not(canvas, canvas);for (int i = 0; i < all_contours.size(); i++) {if (all_contours[i].size() > minSize) {cv::Mat canvas2 = canvas.clone();cv::drawContours(canvas, all_contours, i, cv::Scalar(0, 0, 255));cv::imshow("canvasEdgeSrc", canvas); cv::waitKey(1);}} #endif//在找出所有的弧,并計(jì)算出弧度int kSegLen = 6;//設(shè)定固定尋找弦長度if ( 2== method){kSegLen = 10;}else{kSegLen = 8;}std::vector<std::vector<cv::Point> > edgesValid(0);for (int i = 0; i < all_contours.size(); i++) {if (all_contours[i].size() > minSize && all_contours[i].size() < maxSize ) {//添加std::vector<std::vector<cv::Point> > curves = divideCurveByCurvity( all_contours[i], kSegLen );for ( int k=0;k< curves.size();++k ){if ( curves[k].size()>=5 ) {edgesValid.push_back(curves[k]); //}}}}//在找出所有的弧,并計(jì)算出弧度circlesPupil.resize(0);{int counter = edgesValid.size();std::vector<std::vector<cv::Point> > convex_contour(counter);//(all_contours.size() );//std::vector<cv::Point> approx_poly;//影響了后面的程序std::vector<int> apdNum(counter);//(all_contours.size() );std::vector<cv::RotatedRect > rects;std::vector<float> sigmas(counter);//(all_contours.size() );//float mv = 99999; int uind = 0;//1.發(fā)現(xiàn)凸邊緣int idx = 0;for (int i = 0; i < edgesValid.size(); i++){float mv = 99999; int uind = 0;std::vector<cv::Point> approx_poly;bool showTemp = true;if (showTemp) {cv::Mat canvas( 200, 200, CV_8UC3 );for (int k = 0; k < edgesValid[i].size(); ++k)canvas.at<cv::Vec3b>(edgesValid[i][k].y, edgesValid[i][k].x) = cv::Vec3b(255, 0, 0);cv::imshow("edgesValid", canvas); cv::waitKey(10);}cv::convexHull(edgesValid[i], convex_contour[i], false, true);//放大時插值圖片可否保持凸包情形if (showTemp) {cv::Mat canvas(200, 200, CV_8UC3);for (int k = 0; k < convex_contour[i].size(); ++k)canvas.at<cv::Vec3b>(convex_contour[i][k].y, convex_contour[i][k].x) = cv::Vec3b(255, 0, 0);cv::imshow("convex_contour", canvas); cv::waitKey(10);}float ts = isCircle(convex_contour[i]);sigmas[i] = ts;cv::RotatedRect rr = cv::minAreaRect(convex_contour[i]);rects.push_back(rr);cv::approxPolyDP(convex_contour[i], approx_poly, 1, true);apdNum[i] = approx_poly.size();if (mv > ts && apdNum[i] >= 10) {mv = ts;uind = i;}}//2.判定所有弧int minPs = 5;std::vector<std::vector<cv::Point2f> > arc_pts(0);std::vector<int> ulist, uInds;for (int i = 0; i < convex_contour.size(); ++i) {std::vector<cv::Point2f> arc_pt(0);bool isFind = findArc2(convex_contour[i], arc_pt, ulist);//函數(shù)有問題,對于一個點(diǎn)/兩個相同的點(diǎn) 也判斷為弧!if (isFind && arc_pt.size() >= minPs) {//if (isFind)arc_pts.push_back(arc_pt);uInds.push_back(i);}}///2.1 判斷弧的擬合度for (int i = 0; i < arc_pts.size(); ++i) {sortPoints(arc_pts[i]);sampleArc(arc_pts[i]);}circlesPupil.resize(uInds.size());if (circlesPupil.size() < 1) { return 0; }for (int i = 0; i < uInds.size(); ++i) {circlesPupil[i].resize(convex_contour[uInds[i]].size());for (int j = 0; j < convex_contour[uInds[i]].size(); ++j) {circlesPupil[i][j].first = convex_contour[uInds[i]][j];}} #ifdef SHOW_TEMPcv::Mat canvasV = cv::Mat::zeros(inMatCanny.rows, inMatCanny.cols, CV_8UC3);cv::bitwise_not(canvasV, canvasV);for (int i = 0; i < circlesPupil.size(); i++) {for (int j = 0; j < circlesPupil[i].size(); j++) {cv::circle(canvasV, circlesPupil[i][j].first, 1, (0, 0, 255), 1, 8, 0);}cv::imshow("canvasValid", canvasV); cv::waitKey(1);} #endif}return 1;}//findArcsOfAllCanny bool findArc2(const std::vector<cv::Point>& hpts, std::vector<cv::Point2f>& arc_pts, std::vector<int>& inds){arc_pts.clear();if (hpts.size() <= 2) { return false; }std::vector<edgePoint>epts(hpts.size());for (int i = 0; i < hpts.size(); i++) {cv::Vec3d v1, v2, v3;if (i == 0) {v1 = cv::Vec3d(hpts[0].x - hpts[hpts.size() - 1].x,hpts[0].y - hpts[hpts.size() - 1].y, 0);v2 = cv::Vec3d(hpts[1].x - hpts[0].x,hpts[1].y - hpts[0].y, 0);}else if (i == hpts.size() - 1) {v1 = cv::Vec3d(hpts[i].x - hpts[i - 1].x,hpts[i].y - hpts[i - 1].y, 0);v2 = cv::Vec3d(hpts[0].x - hpts[i].x,hpts[0].y - hpts[i].y, 0);}else {v1 = cv::Vec3d(hpts[i].x - hpts[i - 1].x,hpts[i].y - hpts[i - 1].y, 0);v2 = cv::Vec3d(hpts[i + 1].x - hpts[i].x,hpts[i + 1].y - hpts[i].y, 0);}float len1 = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);float len2 = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);v1[0] /= len1; v1[1] /= len1; v1[2] /= len1;v2[0] /= len2; v2[1] /= len2; v2[2] /= len2;v3 = VecCross(v1, v2);epts[i].angle = sqrt(v3[0] * v3[0] + v3[1] * v3[1] + v3[2] * v3[2]);epts[i].ind = i;epts[i].pos = hpts[i];}cv::sort(epts, cmp);inds.push_back(epts[0].ind);for (int i = 1; i < epts.size(); i++) {float len = epts[i] * epts[0];//運(yùn)算符重載失效,為何?//float len =0;//edgePoint a = epts[i]; const edgePoint b= epts[0];//len = sqrt((double)(a.pos.x - b.pos.x) * (a.pos.x - b.pos.x) +(a.pos.y - b.pos.y) * (a.pos.y - b.pos.y));if (len <= 10)continue;else {inds.push_back(epts[i].ind);break;}}if (inds.size() <= 1) { return false; }//inds.push_back(epts[1].ind);int leftlen = std::max(inds[0], inds[1]) - std::min(inds[0], inds[1]);int rightlen = hpts.size() - leftlen;if (inds[0] < hpts.size() && inds[1] < hpts.size())//附件!wishchin!!!排除大下標(biāo){if (leftlen > rightlen) {for (int i = 0; i < hpts.size(); i++) {if (i >= std::min(inds[0], inds[1]) && i <= std::max(inds[0], inds[1])) {if (!cvWish::line::isLinear(hpts[inds[0]], hpts[inds[1]], hpts[i])) {arc_pts.push_back(hpts[i]);}}}}else {for (int i = 0; i < hpts.size(); i++) {if (i > std::min(inds[0], inds[1]) && i < std::max(inds[0], inds[1])) {//arc_pts.push_back(hpts[i]);}else {//std::cout<<"id = "<<i<<endl;if (!cvWish::line::isLinear(hpts[inds[0]], hpts[inds[1]], hpts[i])) {arc_pts.push_back(hpts[i]);}}}}arc_pts.push_back(hpts[inds[0]]);arc_pts.push_back(hpts[inds[1]]);return true;}else return false;}//findArc2 inline cv::Vec3d VecCross(const cv::Vec3d& p1, const cv::Vec3d& p2) {return cv::Vec3d(p1[1]*p2[2]-p1[2]*p2[1],p1[2]*p2[0]-p1[0]*p2[2],p1[0]*p2[1]-p1[1]*p2[0]); } //判斷是否在一條線上bool isLinear(const cv::Point2f& a, const cv::Point2f& b, const cv::Point2f& c){cv::Point2f a1 = b - a;float alen = b * a;if (alen <= 0.5)return true;a1.x /= alen; a1.y /= alen;cv::Point2f a2 = c - a;float alen2 = c * a;if (alen2 <= 0.5)return true;a2.x /= alen2; a2.y /= alen2;cv::Vec3d cc = VecCross(cv::Vec3d(a1.x, a1.y, 0), cv::Vec3d(a2.x, a2.y, 0));float len = abs(cc[2]);//std::cout<<"len = "<<len<<endl;if (len <= 0.02)return true;return false;}}; //判斷是否是圓//方法不怎么地!float isCircle(const std::vector<cv::Point>& hull){cv::RotatedRect r = cv::minAreaRect(hull);std::vector<cv::Point2f>pts;getRectBottomLine(r, pts);cv::Point2f center = cv::Point2f((pts[0].x + pts[1].x) / 2.0, (pts[0].y + pts[1].y) / 2.0);float mean_v = 0;std::vector<float>lens(hull.size());for (int i = 0; i < hull.size(); i++) {float tlen = sqrt((hull[i].x - center.x) * (hull[i].x - center.x) +(hull[i].y - center.y) * (hull[i].y - center.y));lens[i] = tlen;mean_v += tlen;}mean_v /= hull.size();float sigma = 0.0;for (int i = 0; i < hull.size(); i++) {sigma += (lens[i] - mean_v) * (lens[i] - mean_v);}sigma = sqrt(sigma);sigma /= hull.size();return sigma;}//isCircle void getRectBottomLine(const cv::RotatedRect& r, std::vector<cv::Point2f>& pts){pts.resize(2);//std::vector<cv::Point2f>pts;//cv::Point2f * pp = new cv::Point2f(4);//r.points(pp);//std::vector<cv::Point2f> pp(4);// = new cv::Point2f(4);cv::Point2f pp[4];r.points(pp);float mid_x = 0;for (int i = 0; i < 4; i++) {mid_x += pp[i].x;}mid_x /= 4;float lmax = -99999, rmax = -99999;int lu = 0, ru = 0;for (int i = 0; i < 4; i++) {if (pp[i].x < mid_x) {if (lmax < pp[i].y) {lmax = pp[i].y;lu = i;}}else if (pp[i].x >= mid_x) {if (rmax < pp[i].y) {rmax = pp[i].y;ru = i;}}}//cv::Point2f pSpl;pts[0].x = pp[lu].x; pts[0].y = pp[lu].y;//cv::Point2f pSpr =pp[ru];pts[1].x = pp[ru].x; pts[1].y = pp[ru].y;//pts.push_back(pp[lu]);//pts.push_back(pp[ru]);//pts.push_back(pSpl );//pts.push_back(pSpr );//delete [] pp;return;}//getRectBottomLine //排序點(diǎn)集;不使用qsort//僅用于尋找 合適的 弧 void sortPoints( std::vector<cv::Point2f>& pts ) {int pSize = pts.size();cv::Point2f LeftPoint = pts[pSize-1];cv::Point2f RightPoint = pts[pSize-2];if(LeftPoint.x > RightPoint.x){swapPoint(LeftPoint , RightPoint);}int mSize = pSize / 2;cv::Point2f mPoint = pts[mSize];int caseFlag = 1;if( mPoint.y < std::min( LeftPoint.y , RightPoint.y ) ){caseFlag = 2;}std::vector<cv::Point2f> newpts;std::vector<int>visited(pSize-2, 0);if(caseFlag == 1){newpts.push_back(RightPoint);}else{newpts.push_back(LeftPoint);}for(int i=0;i<pSize-2;i++){float dis = 9999.0;int uind = 0;cv::Point2f pTop = newpts[newpts.size()-1];for(int j=0;j<pSize-2;j++){if(!visited[j]){float dis_j = pts[j] * pTop;if(dis > dis_j){dis = dis_j;uind = j;}}}newpts.push_back(pts[uind]);visited[uind] = 1;}if(caseFlag == 1){newpts.push_back(LeftPoint);}else{newpts.push_back(RightPoint);}pts.clear();pts = newpts;return; } inline void swapPoint(cv::Point2f& a, cv::Point2f& b) {cv::Point2f c;c = b; b= a; a = c; } //確定弧的弧的相似性質(zhì)void sampleArc(std::vector<cv::Point2f>& arc_points){std::vector<cv::Point2f> new_arc;std::vector<float> arc_dis;float max_dis = 99999; int uind = 0;for (int i = 1; i < arc_points.size(); i++) {float tdis = arc_points[i] * arc_points[i - 1];arc_dis.push_back(tdis);if (max_dis > tdis) {max_dis = tdis;uind = i;}}for (int i = 0; i < arc_dis.size(); i++) {int sampleNum = floor(arc_dis[i] / max_dis);cv::Point2f n = arc_points[i + 1] - arc_points[i];float nLen = arc_points[i + 1] * arc_points[i];n.x /= nLen; n.y /= nLen;for (int j = 0; j < sampleNum; j++) {cv::Point2f tp = arc_points[i];tp.x += j * max_dis * n.x;tp.y += j * max_dis * n.y;new_arc.push_back(tp);}cv::Point2f ltp = arc_points[i];ltp.x += sampleNum * max_dis * n.x;ltp.y += sampleNum * max_dis * n.y;float tLen = ltp * arc_points[i + 1];if (tLen < 0.5 * max_dis) {}else {new_arc.push_back(ltp);}}float lastDis = arc_points[arc_points.size() - 1] * new_arc[new_arc.size() - 1];if (lastDis < 0.5 * max_dis) {new_arc[new_arc.size() - 1] = arc_points[arc_points.size() - 1];}else {new_arc.push_back(arc_points[arc_points.size() - 1]);}arc_points.clear();arc_points = new_arc;}分割弧線的方法:
//根據(jù)曲率斷開曲線-在曲線首尾處也需要判斷是否連接!std::vector<std::vector<cv::Point> > divideCurveByCurvity( std::vector<cv::Point> &curve, const int k){std::vector<std::vector<cv::Point> > curves;//擬合橢圓的效果不好,不能貼合邊,而是每個點(diǎn)最小二乘//cv::RotatedRect ec;//if ( curve.size()>5 ){// ec = cv::fitEllipse(curve);// cv::Mat canvas(80, 80, CV_8UC3);// cv::ellipse(canvas, ec, cv::Scalar(0, 0, 255), 1, 8);// cv::imshow("ellipse", canvas); cv::waitKey(100);//}bool showTemp = true;if ( showTemp && curve.size() >= 5 ) {cv::Mat canvas( 200, 200, CV_8UC3 );for ( int i = 0; i < curve.size(); ++i ) {canvas.at<cv::Vec3b>(curve[i].y, curve[i].x) = cv::Vec3b(255, 0, 0);cv::imshow("curveDiscre", canvas); cv::waitKey(10);}cv::imshow("curveDiscre", canvas); cv::waitKey(10);}int numCurve = 0;int L = curve.size();std::vector<double> curvitys;std::vector<bool > isArcs;std::vector<double> dis2ps;bool useCurvity = true;//const int k = 9;//選取計(jì)算曲率的曲線長度,原始圖選擇6可以//使用曲率半徑或許更好if ( curve.size()>5 ){curve[0]; curve[curve.size()-1];int i = 0;//for (auto ptr = curve.begin(); ptr != curve.end(),i< curve.size(); ++ptr,++i)for ( ; i< curve.size(); ++i ){std::vector<cv::Point> curveSeg;if ( useCurvity ){//curveSeg.push_back(cv::Point(curve[(i) % L]));//curveSeg.push_back(cv::Point(curve[(1 + i) % L]));//curveSeg.push_back(cv::Point(curve[(2 + i) % L]));//curveSeg.push_back(cv::Point(curve[(3 + i) % L]));//curveSeg.push_back(cv::Point(curve[(4 + i) % L]));for (int j = 0; j < k; ++j){curveSeg.push_back(cv::Point(curve[(i+j) % L]));}double curvity = 0;// bool isArc = false;curvity = getCurvity( curveSeg, isArc);//curvity = getCurvityR(curveSeg);//使用曲率半徑//效果也不好,必須有負(fù)值!curvitys.push_back(curvity);isArcs.push_back( isArc );} else{//使用距離截?cái)郿ouble dis2p = cvWish::disCv(cv::Point(curve[(i) % L]), cv::Point(curve[(1+i) % L]));dis2ps.push_back(dis2p);}}if ( useCurvity ){//根據(jù)曲率判斷是否斷開,這與曲線的曲率均值密切相關(guān)double disCrThres = 0.12;//與R半徑相關(guān),在大于0.1的時候斷開,即表示為彎道//判斷是否為弧在下一步//bool isChange = false;//bool isExcess = false;bool isDown = false;//可能有冗余,或者多不過比少一個安全for ( i = 0; i < curvitys.size(); ++i ) {if (curvitys[i] < disCrThres ) {if ( !isDown){isDown = true;numCurve += 1;//只數(shù)分開的段數(shù),同時表示有效的段數(shù)}else{//isChange = false;}}else {isDown = false;}}//forint pos = -1;//分段if (numCurve > 0) {curves.resize(numCurve);}else {curves.resize(1);}isDown = false;//需要重新初始化?for ( i = 0; i < curvitys.size(); ++i ) {if ( curvitys[i] < disCrThres ) {if (!isDown) {isDown = true;pos += 1;//注意0和1}else {//isDown = false;}curves[pos].push_back(cv::Point(curve[(i) % L]));}else {isDown = false;}}//for} else{//根據(jù)距離判斷是否斷開double disThres = 8;//與R半徑相關(guān),為了避免45度時候斷開for (i = 0; i < dis2ps.size(); ++i) {if (dis2ps[i] > disThres) {numCurve += 1;}}int pos = 0;if (numCurve > 0) {curves.resize(numCurve);}else {curves.resize(1);}for (i = 0; i < dis2ps.size(); ++i) {if (dis2ps[i] > disThres) {pos += 1;pos %= numCurve;}curves[pos].push_back(cv::Point(curve[(i) % L]));}}}showTemp = true;if ( showTemp ) {for (int n = 0; n < curves.size(); ++n) {cv::Mat canvas(200, 200, CV_8UC3);for (int i = 0; i < curves[n].size(); ++i)canvas.at<cv::Vec3b>(curves[n][i].y, curves[n][i].x) = cv::Vec3b(255, 0, 0);cv::imshow("curveDiscreSeg", canvas); cv::waitKey(10);}}return curves;}//divideCurveByCurvitycurvity = getCurvity( curveSeg, isArc);函數(shù)在上一篇文章中:https://blog.csdn.net/wishchin/article/details/83447712
圖片檢測結(jié)果:
勘誤:數(shù)據(jù)結(jié)構(gòu)
struct edgePoint{int ind;cv::Point2f pos;float angle;public:edgePoint(const int& _ind , const cv::Point2f& _pos, const double& _angle): ind(_ind),pos(_pos) , angle(_angle){}edgePoint(){} };cmp函數(shù):
inline bool cmp(edgePoint a, edgePoint b) {return a.angle > b.angle; }inline cv::Vec3d VecCross(const cv::Vec3d& p1, const cv::Vec3d& p2) {return cv::Vec3d(p1[1]*p2[2]-p1[2]*p2[1],p1[2]*p2[0]-p1[0]*p2[2],p1[0]*p2[1]-p1[1]*p2[0]); }?
?
?
總結(jié)
以上是生活随笔為你收集整理的OpenCV:判定曲线为弧线的简单方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 剑与远征多少章可以刷出红装
- 下一篇: dnf谋略战异次元裂缝怎么打 地下城与勇