opencv6.5-imgproc圖像處理模塊之輪廓


opencv6.4-imgproc圖像處理模塊之直方圖與模板

這部分的《opencv_tutorial》上都是直接上代碼,沒有原理部分的解釋的。

十一、輪廓

1、圖像中找輪廓


  /// 轉成灰度並模糊化降噪
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );
Mat canny_output;//找到輪廓的圖
  vector<vector<Point> > contours;//裝載曲線的點
  vector<Vec4i> hierarchy;

  /// 用Canny算子檢測邊緣
  Canny( src_gray, canny_output, thresh, thresh*2, 3 );
  /// 尋找輪廓
  findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 繪出輪廓
  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )//通過對contours.size(),就知道有幾個分離的輪廓了
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );//畫出輪廓
     }

  /// 在窗體中顯示結果
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
查找輪廓的函數原型:void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
                                         void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())

參數列表:image:一個8-bit的單通道原圖像。非零的像素值被當作1來處理(類似bool類型)。0的像素值仍舊還是0,所以這個image是被當作二值處理的。你可以使用compare(),inRange(),threshold(),adaptiveThreshold(),Canny(),和其他的從一個灰度或者彩色圖中生成一個二值圖。該函數會在提出輪廓的時候修改image。如果mode等於CV_RETR_CCOMP或者CV_RETR_FLOODFILL,那么這個輸入同樣可以是標簽的32-bit整數圖(CV_32SC1)。

                  contours:檢測到的輪廓,每個輪廓都是存儲為點向量;

                  hierarchy:可選的輸出向量,包含圖像拓撲信息。它有着和輪廓數量一樣多的元素。對於第i 個輪廓contours[i],元素hierarchy【i】【0】,hiearchy【i】【1】,hiearchy【i】【2】和hiearchy【i】【3】是在contours中基於0索引的,分別表示在同一個層次級別上的下一個輪廓上一個輪廓,第一個孩子輪廓父親輪廓。如果輪廓 i 沒有下一個、上一個、父親或者嵌套的輪廓,對應的hierarchy【i】的元素就是負的。(這里其實就是個樹結構,來進行索引不同的輪廓)

                  mode:輪廓索引模型

                 – CV_RETR_EXTERNAL     只檢索最外面的輪廓。它會對所有的輪廓設置 hierarchy[i][2] = hierarchy[i][3] = -1 .
                 – CV_RETR_LIST                 不建立任何層次關系來進行索引所有的輪廓.
                 – CV_RETR_CCOMP       索引所有的輪廓並將它們組織成一個two-level的hierarchy(兩個層次的層級關系):在頂層上,有着成分的外部邊界;在第二層是孔洞的邊界。如果有另一個輪廓在一個連接起來的成分內部,那么就將它放在頂層上。
                – CV_RETR_TREE        檢索所有的輪廓並重構一個全層次的嵌套輪廓結構,在contours.c的例子中你更可以看到全層次建立的代碼。
                 method :輪廓逼近的方法

                   – CV_CHAIN_APPROX_NONE   存儲所有輪廓點的絕對值。也就是說任何的輪廓的2子序列點 (x1,y1) 和 (x2,y2) 就是表示水平的,豎直的或者對角線鄰居,也就是: max(abs(x1-x2),abs(y2-y1))==1;
                – CV_CHAIN_APPROX_SIMPLE    壓縮水平的,豎直的和對角線線段,只保留他們的終端。例如:對於一個up-right 的矩形輪廓是由4個點進行編碼的。

                 – CV_CHAIN_APPROX_TC89_L1,  CV_CHAIN_APPROX_TC89_KCOS     使用 Teh-Chin 鏈逼近算法中主流之一的算法。

                offset:可選的偏移量,通過這個值可以平移每一個輪廓。當輪廓是從一個圖像的ROI中提取而你需要在整個圖像上下文中分析的時候會變得很有用。

該函數使用算法從二值圖像中索引輪廓。輪廓對於形狀分析和對象的檢測識別是很有用的,在squares.c中有例子代碼。

Notes:image會被該函數所修改,同樣的該函數不考慮圖像的1像素邊界(它會被0填充然后用來作為鄰居分析),因此接觸圖像邊界的輪廓會被修剪(clip,夾)。

畫出輪廓的函數原型:void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=LINE_8, InputArray hierarchy= noArray(), int maxLevel=INT_MAX, Point offset=Point() );

參數列表:目標圖像、輸入的輪廓、輪廓的索引id、輪廓的顏色、粗度、線類型、可選的層次信息、最大級別、偏移量;

   第一個參數:目標圖像,即畫布;

   第二個參數:所有的輸入輪廓,每個輪廓存儲成點向量的形式;

   第三個參數:用來索引需要畫的輪廓的參數,如果是負的,那么就畫所有的輪廓;

   第四個參數:輪廓的顏色;

   第五個參數:畫的輪廓的線的粗細程度,如果是負的(例如:thickness = CV_FILLED),輪廓內部也會畫出來;

   第六個參數:線連接類型,line()可以有更詳細的說明。

   第七個參數:可選的關於層次的信息。當你只想畫某些輪廓的時候才是需要的(見maxLevel);

   第八個參數:畫輪廓的最大等級。如果為0,那么只畫指定的輪廓。如果為1,該函數會畫輪廓及所有的嵌套輪廓。如果為2,該函數會畫輪廓、所有的嵌套輪廓、所有的嵌套-to-嵌套的輪廓,以此類推。該參數當hierarchy可用的時候才被考慮;

   第九個參數:可選的輪廓平移參數。通過制定的平移量offset = (dx,dy)來平移所有的畫的輪廓。

該函數但thickness >=0的時候會畫輪廓的外部線條,或者當thickness <0的時候會填充輪廓的框起來的區域。下面的代碼是展示如何從二值圖像中索引聯通的成分,並且標記他們:

Mat src;
     src = imread(name,0);
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
     src = src > 1;

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( src, contours, hierarchy,RETR_CCOMP, CHAIN_APPROX_SIMPLE );
//通過對top-level的輪廓進行迭代來畫出每個聯通的成分,使用隨機顏色

int idx = 0;
   for( ; idx >= 0; idx = hierarchy[idx][0] ){
     Scalar color( rand()&255, rand()&255, rand()&255 );
     drawContours( dst, contours, idx, color, FILLED, 8, hierarchy );
}
     namedWindow( "Components", 1 );
     imshow( "Components", dst );
     waitKey(0);
2、計算物體的凸包

凸包就是將對象進行外部輪廓的包含,而且是凸圖形的:


/// 轉成灰度圖並進行模糊降噪
   cvtColor( src, src_gray, CV_BGR2GRAY );
   blur( src_gray, src_gray, Size(3,3) );
Mat src_copy = src.clone();
   Mat threshold_output;
   vector<vector<Point> > contours;//存儲輪廓的點集合
   vector<Vec4i> hierarchy;//構建輪廓的層次結構

   /// 對圖像進行二值化
int thresh = 100;
 int max_thresh = 255;
 RNG rng(12345);
threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY ); /// 尋找輪廓 findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) ); /// 對每個輪廓計算其凸包 vector<vector<Point> >hull( contours.size() ); for( int i = 0; i < contours.size(); i++ ) {  
     convexHull( Mat(contours[i]), hull[i], false );//凸包計算
     }

   /// 繪出輪廓及其凸包
   Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
   for( int i = 0; i< contours.size(); i++ )
      {
        Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );//畫輪廓
        drawContours( drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point() );//畫凸包
      }

   /// 把結果顯示在窗體
   namedWindow( "Hull demo", CV_WINDOW_AUTOSIZE );
   imshow( "Hull demo", drawing );
凸包函數原型:void convexHull(InputArray points, OutputArray hull, bool clockwise=false, bool returnPoints=true);

參數列表:2d點集合、輸出的凸包、定向標識、操作標識;

第一個參數:輸入的2D點集合,存儲在vector或者Mat中。

第二個參數:輸出的凸包,是一個包含索引的整數向量或者是點向量。在前者中,這個hull元素是在原始數組中的凸包點上基於0索引的;后者中,hull元素自身就是凸包點。(也就是說該參數要不是索引到原始圖像找凸包點,或者是直接提取凸包點);

第三個參數:定向標識,如果為true,那么輸出的凸包就是順時針方向的;不然就是逆時針方向的。假設的坐標系的x 軸指向右邊,y 軸指向上面;

第四個參數:操作標識,在矩陣Mat的情況中,當這個參數為true,該函數返回凸包點;否則返回指向凸包點的索引值;當在第二個參數是數組vector的情況中,該標識被忽略,輸出是依賴vector的類型指定的,vector<int> 暗示returnPoints= true ,vector<Point>暗示 returnPoints= false。

    該函數使用Sklansky的算法來查找一個2d點集的凸包,在當前的執行情況下的復雜度是O(NlogN),可以參見convexhull.cpp中驗證不同的函數變量的結果。

3、給輪廓加上矩形或者圓形邊界框


這個還是挺有用的,有時候識別一個物體,想先用簡單的數字圖像處理方法得到更少的區域然后可以提取從而接着使用模式識別的方法進行訓練和建立分類器。

使用OpenCV函數 boundingRect 來計算包圍輪廓的矩形框.

使用OpenCV函數 minEnclosingCircle 來計算完全包圍已有輪廓最小圓.

int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
/// 轉化成灰度圖像並進行平滑 用來減少噪音點
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );
Mat threshold_output;
  vector<vector<Point> > contours;//存儲輪廓點
  vector<Vec4i> hierarchy;//構建不同輪廓的層次結構

  /// 使用Threshold檢測邊緣
  threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
  /// 找到輪廓
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 多邊形逼近輪廓 + 獲取矩形和圓形邊界框
  vector<vector<Point> > contours_poly( contours.size() );//創建contours.size()個空的多邊形
  vector<Rect> boundRect( contours.size() );//創建contours.size()個矩形框
  vector<Point2f>center( contours.size() );//創建contours.size()個圓心
  vector<float>radius( contours.size() );//創建contours.size()個半徑

  for( int i = 0; i < contours.size(); i++ )
     { 
       approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );//多邊形逼近
       boundRect[i] = boundingRect( Mat(contours_poly[i]) );//獲取某個輪廓的矩形框
       minEnclosingCircle( contours_poly[i], center[i], radius[i] );//生成某個輪廓的最小包含圓的圓心和半徑
     }


  /// 畫多邊形輪廓 + 包圍的矩形框 + 圓形框
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );//隨機顏色
       drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );//畫輪廓-多邊形
       rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );//畫矩形
       circle( drawing, center[i], (int)radius[i], color, 2, 8, 0 );//畫圓形
     }

  /// 顯示在一個窗口
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
多邊形逼近的函數原型:void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

參數列表:輸入數組、逼近的結果、逼近的精度、是否閉合的標識;

第一個參數:一個2d點的輸入向量可以存儲在:

                          – std::vector or Mat    (C++ interface)
                         – Nx2 numpy array      (Python interface)
                        – CvSeq or ‘‘ CvMat      (C interface)

第二個參數:逼近的結果,該參數的類型應該匹配輸入曲線的類型。(在c接口中,逼近的曲線存儲在內存存儲區中,返回的是指向該內存的指針,我列出來的都是cpp接口,所以該句可忽略);

第三個參數:指定逼近精度的參數。這是介於原始曲線與它的逼近之間的最大的距離;

第四個參數:如果為真,逼近的曲線是閉合的(即,將首尾連起來);否則就不是閉合的。

該函數使用一個更少頂點的曲線/多邊形來逼近另一個曲線或多邊形,使得介於他們之間的距離小於或者等於指定的精度。該函數使用的是Douglas-Peucker算法。http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm


計算一個點集合的up-right邊界矩形的函數原型:Rect boundingRect(InputArray points);

參數列表:輸入點;

第一個參數:輸入的2D點集合,存儲在vector或者Mat中。

該函數對指定的點集合計算並返回最小的up-right矩形。


計算一個2d點集合的最小區域圓的函數原型:void minEnclosingCircle(InputArray points, Point2f& center, float& radius)

參數列表:輸入的點集合、輸出的圓心坐標、輸出的半徑;

第一個參數:2D點的輸入向量,存儲在:

                        – std::vector<> or Mat (C++ interface)
                         – CvSeq* or CvMat* (C interface)
                        – Nx2 numpy array (Python interface)

第二個參數:圓的輸出的圓心;

第三個參數:圓的輸出的半徑

該函數在一個2D點集合中使用一個迭代算法來查找最接近的圓,見minarea.cpp中有例子。

4、給輪廓加上傾斜的邊界框和橢圓

該部分與上面部分很相似,不過采用的函數是不同的。


int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
 /// 轉為灰度圖並模糊化 來減小噪音點
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );
 Mat threshold_output;
  vector<vector<Point> > contours;//存儲輪廓的向量
  vector<Vec4i> hierarchy;//輪廓的層次結構

  /// 閾值化檢測邊界
  threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
  /// 尋找輪廓
  findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 對每個找到的輪廓創建可傾斜的邊界框和橢圓
  vector<RotatedRect> minRect( contours.size() );//存儲contours.size()個旋轉的邊界框***************此處是重點,RotateRect
  vector<RotatedRect> minEllipse( contours.size() );////存儲contours.size()個旋轉的橢圓*************

  for( int i = 0; i < contours.size(); i++ )
     { 
        minRect[i] = minAreaRect( Mat(contours[i]) );//獲取包含最小區域的矩形
       if( contours[i].size() > 5 )
         { 
         minEllipse[i] = fitEllipse( Mat(contours[i]) );//獲取橢圓所需的信息
         }
     }

  /// 繪出輪廓及其可傾斜的邊界框和邊界橢圓
  Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       // contour
       drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );//畫對象的輪廓
       // ellipse
       ellipse( drawing, minEllipse[i], color, 2, 8 );//畫橢圓
       // rotated rectangle
       Point2f rect_points[4]; 
       minRect[i].points( rect_points );//將旋轉矩形的頂點賦值給實參;
       for( int j = 0; j < 4; j++ )
          line( drawing, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );//畫出傾斜矩形,采用的是畫四條線的形式實現的
     }

  /// 結果在窗體中顯示
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
在輸入的2D點集合中找到一個最小區域的旋轉后的矩形的函數原型:RotatedRect minAreaRect(InputArray points)
參數列表:輸入的2D點集合
第一個參數:輸入的2D點的向量,存儲在:
                               – std::vector<> or Mat (C++ interface)

                              – CvSeq* or CvMat* (C interface)
                              – Nx2 numpy array (Python interface)

該函數計算並返回一個指定的點集合的最小區域邊界的矩形,在minarea.cpp中有例子,開發者應該注意當數據是接近包含的矩陣元素邊界的時候 返回的旋轉矩形可以包含負指數。


沿着一個2D點集合進行擬合一個橢圓的函數原型:RotatedRect fitEllipse(InputArray points)

第一個參數:輸入的2D點集合,可以存儲在:

                      – std::vector<> or Mat (C++ interface)
                     – CvSeq* or CvMat* (C interface)
                     – Nx2 numpy array (Python interface)

該函數計算在對一個2D點集合擬合的最好的橢圓(最小二乘作為損失函數來判別)。它返回旋轉的矩形中內含的橢圓,使用的算法是[FItzgibbon95].開發者應該注意當數據點很靠近包含的矩陣元素的邊界的時候,返回的橢圓/旋轉矩形數據包含負指數例子代碼在fitellipse.cpp中。

5、計算輪廓的矩

使用OpenCV函數 moments 計算圖像所有的矩(最高到3階)

使用OpenCV函數 contourArea 來計算輪廓面積

使用OpenCV函數 arcLength 來計算輪廓或曲線長度

int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
 /// 把原圖像轉化成灰度圖像並進行平滑
  cvtColor( src, src_gray, CV_BGR2GRAY );
  blur( src_gray, src_gray, Size(3,3) );
Mat canny_output;
  vector<vector<Point> > contours;//存儲輪廓
  vector<Vec4i> hierarchy;//存儲輪廓的層次結構

  /// 使用Canndy檢測邊緣
  Canny( src_gray, canny_output, thresh, thresh*2, 3 );
  /// 找到輪廓
  findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

  /// 計算矩
  vector<Moments> mu(contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { 
         mu[i] = moments( contours[i], false );//計算輪廓的矩
      }

  ///  計算中心矩:
  vector<Point2f> mc( contours.size() );
  for( int i = 0; i < contours.size(); i++ )
     { 
          mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); 
      }

  /// 繪制輪廓
  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
  for( int i = 0; i< contours.size(); i++ )
     {
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
       circle( drawing, mc[i], 4, color, -1, 8, 0 );
     }

  /// 顯示到窗口中
  namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );

  /// 通過m00計算輪廓面積並且和OpenCV函數比較
  printf("\t Info: Area and Contour Length \n");
  for( int i = 0; i< contours.size(); i++ )
     {
       printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", 
              i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) //第幾個輪廓,輪廓的空間矩,輪廓的面積,輪廓的周長
               );//計算輪廓面積和 輪廓或曲線的長度
       Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
       drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
       circle( drawing, mc[i], 4, color, -1, 8, 0 );
 

計算一個多邊形或者光柵形狀所有的矩最高到3階的函數原型:Moments moments(InputArray array, bool binaryImage=false )

參數列表:輸入數組、標識

第一個參數:光柵圖像(單通道,8-bit或者浮點2D數組);或者一個2D點(Point 或者 Point2f)的數組(1xN或者Nx1);

第二個參數:如果為真,所有的非0圖像像素被視為 1 ;該參數只用在圖像上。

該函數計算一個向量形狀或者光柵形狀的矩到3階。結果返回在結構體Moments。

class Moments
{
public:
<span style="white-space:pre">		</span>Moments();
<span style="white-space:pre">		</span>Moments(double m00, double m10, double m01, double m20, double m11,
<span style="white-space:pre">		</span>double m02, double m30, double m21, double m12, double m03 );
<span style="white-space:pre">		</span>Moments( const CvMoments& moments );
<span style="white-space:pre">		</span>operator CvMoments() const;
// spatial moments
<span style="white-space:pre">		</span>double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
// central moments
<span style="white-space:pre">		</span>double mu20, mu11, mu02, mu30, mu21, mu12, mu03;
// central normalized moments
<span style="white-space:pre">		</span>double nu20, nu11, nu02, nu30, nu21, nu12, nu03;
}
在光柵圖像的時候,空間矩 以下面的方式計算:

 

中心矩以下面的方式計算:

這里是團塊中心:

歸一化的中心矩按照下面的方式計算:

notes:
輪廓的矩以同樣的方式定義,不過是使用Green(http://en.wikipedia.org/wiki/Green_theorem).)的公式計算的。所以因為一個受限的光柵分辨率,輪廓的矩的計算輕微的不同於同樣的光柵輪廓的矩計算。

因為輪廓矩是使用Green公式計算的,所以可能看到奇怪的與自相交的輪廓結果,例如:一個蝴蝶形狀輪廓的0區域(m00)。


計算一個輪廓的面積的函數原型:double contourArea(InputArray contour, bool oriented=false )

第一個參數:輸入的2D點的向量(輪廓索引),存儲在vector或者Mat中;

第二個參數:定向區域標識。如果為true,該函數返回一個有符號的面積值,依賴於輪廓的方向(順時針或者逆時針)。使用這個特征,你可以通過使用一個區域的符號決定一個輪廓的方向。默認情況下,該參數是false的,這意味着會返回絕對值。

該函數計算一個輪廓的面積,相似於moment()。該面積是使用Green公式計算的。所以,如果你通過使用drawContours()或者fillPoly()畫這個輪廓,返回的面積和非0像素的個數,會不一樣。同樣的該函數差不多會在一個有着自相交的輪廓上給出一個錯誤的結果。(也就是這時候該函數不靠譜)。

例子代碼:

   vector<Point> contour;
contour.push_back(Point2f(0, 0));
contour.push_back(Point2f(10, 0));
contour.push_back(Point2f(10, 10));
contour.push_back(Point2f(5, 4));
   double area0 = contourArea(contour);
vector<Point> approx;
   approxPolyDP(contour, approx, 5, true);
   double area1 = contourArea(approx);
cout << "area0 =" << area0 << endl <<
<span style="white-space:pre">	</span>"area1 =" << area1 << endl <<
<span style="white-space:pre">	</span>"approx poly vertices" << approx.size() << endl;

計算一個輪廓的周邊或者一個曲線長度的函數原型:double arcLength(InputArray curve, bool closed)

參數列表:

第一個參數:輸入的2D點向量,存儲在vector或者Mat中;

第二個參數:表示該曲線是否閉合。

該函數計算一個曲線的長度或者一個閉合輪廓的周長。

6、多邊形測試


是執行一個輪廓中點的測試。

 /// 創建一個圖形     const int r = 100;
  Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8UC1 );//創建畫布

  /// 繪制一系列點創建一個輪廓:
  vector<Point2f> vert(6);//一個有6個點的輪廓

  vert[0] = Point( 1.5*r, 1.34*r );
  vert[1] = Point( 1*r, 2*r );
  vert[2] = Point( 1.5*r, 2.866*r );
  vert[3] = Point( 2.5*r, 2.866*r );
  vert[4] = Point( 3*r, 2*r );
  vert[5] = Point( 2.5*r, 1.34*r );//自己定輪廓的6各點

  /// 在src內部繪制
  for( int j = 0; j < 6; j++ ){ 
      line( src, vert[j],  vert[(j+1)%6], Scalar( 255 ), 3, 8 ); //將該輪廓畫出來
      }

  /// 得到輪廓
  vector<vector<Point> > contours; vector<Vec4i> hierarchy;
  Mat src_copy = src.clone();

  findContours( src_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);//查找輪廓

  /// 計算到輪廓的距離
  Mat raw_dist( src.size(), CV_32FC1 );

  for( int j = 0; j < src.rows; j++ ){ 
    for( int i = 0; i < src.cols; i++ ){ 
           raw_dist.at<float>(j,i) = pointPolygonTest( contours[0], Point2f(i,j), true ); 整個畫布,逐點測試到第0個輪廓的距離
          }
     }

  double minVal; double maxVal;
  minMaxLoc( raw_dist, &minVal, &maxVal, 0, 0, Mat() );//查找全局最大最小
  minVal = abs(minVal); maxVal = abs(maxVal);

  /// 圖形化的顯示距離
  Mat drawing = Mat::zeros( src.size(), CV_8UC3 );

  for( int j = 0; j < src.rows; j++ ){ 
      for( int i = 0; i < src.cols; i++ ) {
            if( raw_dist.at<float>(j,i) < 0 ){ 
               drawing.at<Vec3b>(j,i)[0] = 255 - (int) abs(raw_dist.at<float>(j,i))*255/minVal;
             }
            else if( raw_dist.at<float>(j,i) > 0 ){
               drawing.at<Vec3b>(j,i)[2] = 255 - (int) raw_dist.at<float>(j,i)*255/maxVal;
              }
            else{ 
              drawing.at<Vec3b>(j,i)[0] = 255; drawing.at<Vec3b>(j,i)[1] = 255; drawing.at<Vec3b>(j,i)[2] = 255;
              }
          }
     }

  /// 創建窗口顯示結果
  char* source_window = "Source";
  namedWindow( source_window, CV_WINDOW_AUTOSIZE );
  imshow( source_window, src );
  namedWindow( "Distance", CV_WINDOW_AUTOSIZE );
  imshow( "Distance", drawing );
執行一個輪廓中點的測試的函數原型:double pointPolygonTest(InputArray contour, Point2f pt, bool measureDist)
參數列表:輸入的輪廓、測試的點、標識;

第一個參數:輸入的輪廓;

第二個參數:針對某個的測試的點;

第三個參數:如果為真,該函數將評估一個點到最近的輪廓的邊界的有符號距離;否則只檢查該點是否在輪廓內部。

該函數決策着該點在一個輪廓的內部,外部,或者剛好在輪廓的邊界上(或者剛好在輪廓的頂點上)。該函數返回為正(內部),負(外部),0(在邊界上)。當measureDist = false,該函數返回+1,-1,0;不然返回的值是一個點到最近輪廓邊的有符號距離值。


圖像處理部分的例子算是都過了一遍了,接下來是ml部分


免責聲明!

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



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