求解向量和輪廓的交點


在“學習OpenCV3"的QQ群眾,網友且行且珍惜針對前期博客(https://www.cnblogs.com/jsxyhelu/p/9345590.html)中的內容提出了以下問題:

比如這張圖,利用PCA求出了特征向量之后,我想要求解與輪廓的交點,不知道有沒有簡單的方法@禾老師 
 
非常好的問題!在尋找到輪廓的”主方向“后,往往下一個動作就是尋找向量和輪廓的交點,因為往往這才是我們更關心的地方。為了解決這個問題,我認為的思路應該是這樣的:
1、首先要界定范圍。對於交點來說,肯定是在這個輪廓的“最小外接矩形”中的,所以先求出外接矩形作為限定;
2、向量只是一個方向,要將其變成一條直線(如果在“最小外接矩形”中就是線段),推薦使用LineIterator來表示直線;
3、最后,判斷這條線段上的點是否在輪廓上,可以使用pointpolytest函數。
 
結合代碼具體講解。為了凸顯本文知識重點,本文采用以下一幅圖像來說明算法
最后得到的結果是這樣的,其中黃點為主方向向量和外界矩形交點,紅點為和輪廓交點。
 
全部代碼為:
/************************************************************************/
// 求解向量和輪廓的交點
// by jsxyhelu(jsxyhelu.cnblogs.com)
// 2018/10/05
/************************************************************************/
# include  "stdafx.h"
# include  "opencv2/imgcodecs.hpp"
# include  "opencv2/highgui.hpp"
# include  "opencv2/imgproc.hpp"
# include  "opencv2/photo.hpp"
using  namespace std;
using  namespace cv;
//尋找最大外接輪廓
vector <Point > FindBigestContour(Mat src){
     int max_area_contour_idx  =  0;
     double max_area  =  - 1;
    vector <vector <Point >  >contours;
    findContours(src,contours,RETR_LIST,CHAIN_APPROX_SIMPLE);
     //handle case if no contours are detected
    CV_Assert( 0  != contours.size());
     for (uint i = 0;i <contours.size();i ++){
         double temp_area  = contourArea(contours[i]);
         if (max_area  < temp_area ){
            max_area_contour_idx  = i;
            max_area  = temp_area;
        }
    }
     return contours[max_area_contour_idx];
}
//程序主要部分
int main(  int argc,  char * * argv )
{
     //讀入圖像,轉換為灰度
    Mat src  = imread( "E:/sandbox/cloud.png");
    Mat src_gray;
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
     //閾值處理
    Mat threshold_output;
    cv : :threshold(src_gray,threshold_output, 150, 255,THRESH_OTSU |THRESH_BINARY_INV);
     //輪廓分析
    vector <vector <Point >  > contours;
    vector <Vec4i > hierarchy;
    vector <Point > biggestContour  =  FindBigestContour(threshold_output); //尋找最大輪廓
    Rect boundRect     = boundingRect( Mat(biggestContour) );  //獲得輪廓最小外接矩形
    cv : :rectangle(src,boundRect,Scalar( 0, 0, 255));
     //pca分析,求出斜率和經過的一點
    Mat data_pts  = Mat(biggestContour.size(),  2, CV_64FC1); //Construct a buffer used by the pca analysis
     for ( int i  =  0; i  < data_pts.rows;  ++i)
    {
        data_pts.at < double >(i,  0= biggestContour[i].x;
        data_pts.at < double >(i,  1= biggestContour[i].y;
    }
    PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); //執行PCA運算
    Point pos  = Point2f(pca_analysis.mean.at < double >( 00),
        pca_analysis.mean.at < double >( 01));     //主方向直線經過的一點
    vector <Point2d > eigen_vecs( 2);     //保存PCA分析結果,其中0組為主方向,1組為垂直方向
    vector < double > eigen_val( 2);
     for ( int i  =  0; i  <  2++i)
    {
        eigen_vecs[i]  = Point2d(pca_analysis.eigenvectors.at < double >(i,  0),
            pca_analysis.eigenvectors.at < double >(i,  1));
        eigen_val[i]  = pca_analysis.eigenvalues.at < double >(i, 0);
    }
    line(src, pos  -  0. 02  * Point(eigen_vecs[ 0].x  * eigen_val[ 0],eigen_vecs[ 0].y  * eigen_val[ 0]),
        pos + 0. 02  * Point(eigen_vecs[ 0].x  * eigen_val[ 0],eigen_vecs[ 0].y  * eigen_val[ 0]) , Scalar( 2552550)); //繪制概略主方向
     //求出主方向直線和外接矩形的交點,
     float k  = eigen_vecs[ 0].y /eigen_vecs[ 0].x;  //斜率
    Point2f pt1  = Point2f(boundRect.x,k *(boundRect.x  - pos.x) +pos.y);
    Point2f pt2  = Point2f((boundRect.x +boundRect.width),k *((boundRect.x +boundRect.width) -pos.x) +pos.y);
    circle(src,pt1, 5,Scalar( 0, 255, 255), - 1);
    circle(src,pt2, 5,Scalar( 0, 255, 255), - 1);
     //遍歷兩個交點之間的線段,得出和輪廓的交點
    LineIterator it(src, pt1, pt2,  8);
     for( int i  =  0; i  < it.count; i ++++it)
    {
         Point pt(it.pos()); //獲得線段上的點
          if (abs(pointPolygonTest(biggestContour,pt, true))  <  1)
                circle(src,pt, 5,Scalar( 0, 0, 255), - 1);
    }
    waitKey();
     return  0;
}
知識重點:
 
1、FindBigestContour為尋找輪廓中最大輪廓的函數,目前這個函數還沒有merge到OpenCV中,下一步有這個計划,注意這個函數的命名規則是按照OpenCV的方法定 義的;
 
2、我們采用Rect boundRect    = boundingRect( Mat(biggestContour) );
來獲得輪廓的最小外接矩形。為什么要首先獲得這個外接矩形了,因為我們這里來所有要求的點肯定都在這個矩形中,我們做這一步就能夠降低算法的計算復雜程度;
 
3、PCA分析的具體原來和細節,請查看《如何獲得物體的主要方向?》 https://www.cnblogs.com/jsxyhelu/p/7690699.html
     我們這里使用,主要是獲得兩個數據,一個是該輪廓的重心,這個點是我們最后要求的那條直線肯定經過的;二個是求出直線的斜率。那么對於一條直線,已經知道 斜率和經過的一點,就已經能夠被定義出來
 
4、最后在求該直線和輪廓的交點的時候,采用了LineIterator 和pointPolygonTest,前者是OpenCV中專門用來遍歷直線的;后者是專門用來計算點和輪廓的關系的,應該說這里的應用還是非常高效的。
 
感謝閱讀至此,希望有所幫助。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM