1. 程式人生 > >opencv輪廓及點在輪廓內判斷

opencv輪廓及點在輪廓內判斷

查詢輪廓

輪廓到底是什麼?一個輪廓一般對應一系列的點,也就是影象中的一條曲線.表示的方法可能根據不同情況而有所不同.有多重方法可以表示曲線.在openCV中一般用序列來儲存輪廓資訊.序列中的每一個元素是曲線中一個點的位置.關於序列表示的輪廓細節將在後面討論,現在只要簡單把輪廓想象為使用CvSeq表示的一系列的點就可以了.

函式cvFindContours()從二值影象中尋找輪廓.cvFindContours()處理的影象可以是從cvCanny()函式得到的有邊緣畫素的影象,或者是從cvThreshold()及cvAdaptiveThreshold()得到的影象,這時的邊緣是正和負區域之間的邊界.


圖8-2描述了cvFindContours的函式功能,影象的上半部分是神色背景和白色區域(被從A到E標記)的測試影象.下半部分是使用cvFindCountours()函式後會得到的輪廓的說明.這些輪廓被標記為cX或hx,"c"表示"輪廓(contour)","h"表示"孔(hole)","X表述數字".其中一些輪廓用虛劃線表示;表明他們是白色區域的外部邊界(例如,非0區域).孔(hole)的外部邊界(例如,非0區域)即白色區域的內部邊界.在圖中是用電線表示外部邊界的.OpenCV的cvFindContours()函式可區分內部和外部邊界.

包含的概念在很多應用中都非常重要.因此.OpenCV允許得到的輪廓被聚合成一個輪廓樹,從而把包含關係編碼到樹結構中.這個測試圖的輪廓樹在根節點的輪廓叫c0,孔h00和h01是它的字子節點.這些輪廓中直接包含輪廓稱為他們的子節點,以此類推.

現在來看cvFindContours()函式

  1. int  cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,    
  2.                             int header_size CV_DEFAULT(sizeof(CvContour)),    
  3.                             int mode CV_DEFAULT(CV_RETR_LIST),    
  4.                             int
     method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
  5.                             CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
第一個引數 image是輸入影象,影象必須是8位單通道影象,並且應該被轉化成二值的(例如,所有非0畫素的值都是一個定值).cvFindContours()執行的時候,這個影象會被直接塗改,因此如果是將來還有用的影象,應該複製之後再傳給cvFindContours().

storage 是記憶體儲存器,cvFindContours()找到的輪廓記錄在此記憶體裡.正如之前所說,這個儲存器的空間應該由cvCreateMemStorage()分配.

first_contour 是指向CvSeq*的一個指標firstContour.無需動手,cvFindContours()會自動分配該指標.實際上,只要在這裡傳一個指標就可以了函式會自動設定.不需要分配和釋放(new/delete或者malloc/free).就是這個指標(例如,*firstContour)指向輪廓樹的首地址(head).cvFindContours()返回值是,找到的所有輪廓的個數

cvSeq* firstContout = NULL;

cvFindContours(..., &firstContour, ...);

headerSize告訴cvFindContours()更多有關物件分配的資訊,它可以被設定為sizeof(CvContour)或者sizeof(CvChain)(當近似方法引數method被設定為CV_ChAIN_CODE時使用後者).最後是mode和method引數,他們分別指定計算方法和如何計算.

mode變數可以被設定為以下四個選項之一: CV_RETR_ExTERNAL, CV_RETR_LIST, CV_RETR_CCOMP或CV_RETR_TREE.mode的值向cvFindeContours()說明需要的輪廓型別,和希望的放回值形式.具體說來,mode的值決定把找到的輪廓如何掛到輪廓樹節點變數(h_prev,h_next,v_prev和v_next)上,圖8-3展示了四種可能的mode值所得到的結果的拓撲結構.


每中情況下,結構都可以看成是被"橫向"連線(h_next和h_prev)聯絡和被"縱向"連線(v_next和v_prev)不同的"層次".

CV_RETR_EXTERNAL 只檢測出最外的輪廓.圖8-2中,只有一個最外輪廓,因此圖8-3中第一個輪廓指向最外的序列,除此之外沒有別的連線

CV_RETR_LIST 檢測所有的輪廓並將他們儲存到表(list)中.圖8-3描繪了從圖8-2樣圖中得到的表.在這個例子中,有8條輪廓被找到,他們相互之間有h_prev和h_next連線(這裡並沒有使用v_prev和v_next)

CV_RETR_CCOMP 檢出所有的輪廓並將他們組織成雙層結構(two-level hierarchy),頂層邊界是所有成份的外界邊界,第二層邊界是空的邊界.圖8-3中,我們能看到5個外部邊界,其中3個包含孔.孔被v_next和v_prev可以只包括一個值,此節點可以只有一個子節點.c0中有兩個孔,因為v_next可以值包括一個值,次節點可以只有一個子節點.c0之內的所有孔相互間有h_prev和h_next指標連線.

CV_RETR_TREE 檢出所有輪廓並且重新建立網狀的輪廓結構.在我們給出的例子中(圖8-2和8-3中),這意味著根節點是最外的輪廓c0.c0之下是空h00,在同一層次中與另一個孔h01相連線.同理,每個孔都有子節點(相對應的是c000和c010),這些子節點與父節點被垂直連線起來.這個步驟一直持續到影象最內層的輪廓,這些輪廓會成為樹葉節點.

以下的五個值與方法相關(例如輪廓會如何被近似).

 CV_CHAIN_CODE 用freeman鏈碼輸出輪廓,其他方法輸出多邊形(頂點的序列)

CV_CHAIN_APPROX_NONE 將鏈碼編碼中的所有點轉換為點

CV_CHAIN_APPROX_SIMPLE 壓縮水平,垂直或斜的部分,只儲存最後一個點

CV_CHAIN_APPROX_TC89_L1或CV_CHAIN_APPROX_TC89_KCOS使用Ten-Chin鏈逼近演算法中的一個

CV_LINK_RUNS 與上述演算法完全不同的演算法,連線所有水平層次的輪廓.此方法只可與Cv_RETR_LIST搭配使用.

使用序列表示輪廓

當呼叫cvFindContours函式的時候,返回多個序列.序列的型別依賴與呼叫cvFindContours時 所傳遞的引數.預設情況下使用CV_RETR_LIST和CV_CHAIN_APPROX_SIMPLE引數.

序列中儲存一系列的點,這些點構成輪廓,輪廓是本章的重點.輪廓只是序列所能表示物體的一種.輪廓的點的序列,可以用來表示影象空間中的曲線.這種點的序列很常用,所有需要有專門的函式來幫助我們對他進行處理.下面是一組這樣的處理函式.

  1. int  cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,    
  2.                             int header_size CV_DEFAULT(sizeof(CvContour)),    
  3.                             int mode CV_DEFAULT(CV_RETR_LIST),    
  4.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
  5.                             CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
  1. CvContourScanner  cvStartFindContours( CvArr* image, CvMemStorage* storage,    
  2.                             int header_size CV_DEFAULT(sizeof(CvContour)),    
  3.                             int mode CV_DEFAULT(CV_RETR_LIST),    
  4.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
  5.                             CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
  1. CvSeq*  cvFindNextContour( CvContourScanner scanner );    
  1. void  cvSubstituteContour( CvContourScanner scanner, CvSeq* new_contour );    
  1. /* Releases contour scanner and returns pointer to the first outer contour */
  2. CvSeq*  cvEndFindContours( CvContourScanner* scanner );    
  1. /* Approximates a single Freeman chain or a tree of chains to polygonal curves */
  2. CvSeq* cvApproxChains( CvSeq* src_seq, CvMemStorage* storage,    
  3.                             int method CV_DEFAULT(CV_CHAIN_APPROX_SIMPLE),    
  4.                             double parameter CV_DEFAULT(0),    
  5.                             int  minimal_perimeter CV_DEFAULT(0),    
  6.                             int  recursive CV_DEFAULT(0));    
第一函式是cvFindContours(),在前面已經提到.接著是cvStartFindContiors()函式,它和cvFin的Contours()功能類似.但是cvStartFindContours()每次放回一個輪廓,而不像cvFinContours()那樣一次查詢所有輪廓然後統一返回.呼叫cvStartFindContours()函式後,返回一個CvSequenceScanner結構.CvSequenceScanner結構中包含一些狀態資訊,這些資訊不可讀.你可以通過在cvSequenceScabber結構上依次呼叫cvFinNextContour()來查詢剩餘的輪廓.當全部輪廓都被找完之後,cvFindNextContour()將放回NULL

cvSubstituteContour()函式用於替換scanner指向的輪廓.該函式的一個特性是,如果引數 new_contour為NULL,那麼當前的輪廓將被從Scanner指定的樹或連結串列中刪除(受影響的序列會作適當更新,來保證不會有指標指向不存在的物體).

函式cvEndFindContour()結束輪廓查詢,並且將scanner設定為結束狀態.注意,scanner並沒有被刪除,實際上該函式返回的是指標所指序列的第一個元素.

最後一個函式cvApproxChains()函式.該函式將Freeman鏈轉換為多邊形表示(精確轉換或者近似擬合).

Freeman鏈碼

一般情況下,通過cvFindCountours獲取的輪廓是一系列頂點的序列.另一種不同的表達是設定method引數為CV_CHAIN_CODE,然後生成輪廓.當選者CV_CHAIN_CODE標誌的時候,檢測的輪廓通過Freemain鏈碼[Freeman67](圖8-4)的方式返回.在Freeman鏈碼中,多邊形被表示為一系列的位移,每一個位移有8個方向,這8個方向使用整數0到7表示.Freeman鏈碼對於識別一些形狀的物體很有幫助.如果得到的是Freeman鏈碼,可以通過以下兩個函式讀出每個點

  1. void cvStartReadChainPoints( CvChain* chain, CvChainPtReader* reader );    
  1. CvPoint cvReadChainPoint( CvChainPtReader* reader );    

第一個函式用來初始化Freeman鏈CvChainPtReader結構,第二個函式通過CvChainptReader來讀每個點,CvChainPtReader對應當前狀態.結構CvChain從CvSeq擴充套件得來.和CvContourScanner從多個輪廓間迭代一樣,CvChainPtReader用於迭代一個使用Freemain鏈碼錶示輪廓中的每個點.CvChainPtReader和CvSeqReader的用法類似.如您所期望,當所有點都讀完後,返回CvChainPtReader值為NULL.

繪製輪廓

一個經常使用的功能是在螢幕上繪製檢測到的輪廓.繪製可以用cvDrawContours函式完成

  1. /* Draws contour outlines or filled interiors on the image */
  2. void  cvDrawContours( CvArr *img, CvSeq* contour,    
  3.                              CvScalar external_color, CvScalar hole_color,    
  4.                              int max_level, int thickness CV_DEFAULT(1),    
  5.                              int line_type CV_DEFAULT(8),    
  6.                              CvPoint offset CV_DEFAULT(cvPoint(0,0)));    
第一個引數為要繪製輪廓的影象.第二個引數是要繪製的輪廓,他不像乍看上去那麼簡單,他是輪廓樹的根節點.其他的引數(主要是max_level)將會控制如何繪製輪廓樹.下一個引數很容易理解,是繪製輪廓所用的顏色.但是hole_color那?請回憶輪廓的分類,有外輪廓,也有"洞"(圖8-2中的虛劃線和點線).無論繪製單個輪廓還是輪廓樹中的所有輪廓,標記為"洞"的輪廓都會使用hole_color指定的顏色繪製.

通過max_level變數可以告訴cvDrawConturs() 如何處理通過節點樹變數連結到一個輪廓上的其他任何輪廓.此變數可以被設定為遍歷輪廓的最大深度.因此max_level = 0表示與輸入輪廓屬於同意等級的所有輪廓(更具體的說,輸入輪廓和與其相鄰的輪廓被畫出),max_level = 1表示與輸入輪廓屬於同一登記的所有輪廓與其子節點被畫出,以此類推.如果項要畫的輪廓是由cvFindContous()的CV_RETR_CCOMP或CV_RETR_TREE模式得到的話,max_level的負值也是被支援的.在這種情況下,max_level=-1表示只有輸入輪廓被畫出,以此類推,max_level = -2 表示輸入輪廓與其直系(僅直接相連的)子節點會被畫出,以此類推.

引數thickness和line_type就如其字面含義所示.最後,我們可以給繪圖程式一個偏移量,這樣輪廓可以被畫在指定的精確座標上.當輪廓座標被轉換成質心座標或其他區域性座標系的時候,這個特性非常有用.
如果在影象上的不同感興趣的區域多次執行cvFindContour(),然後又想將所有結果在原來大影象上顯示出來,便宜量offset也很有用.相反,可以先從大圖提取出一個輪廓,然後在用offset和填充,在小影象上形成和輪廓對應的蒙板(mask);

輪廓例子

首先建立一個視窗用於顯示影象,滑動條(trackbar)用於設定閾值,然後對採二值化後的影象提取輪廓並繪製輪廓.當控制引數的滑動條變換時,影象被更新.

  1. #include "stdafx.h"  
  2. #include <cv.h>  
  3. #include <highgui.h>  
  4. IplImage* g_image = NULL;    
  5. IplImage* g_gray = NULL;    
  6. int g_thresh = 100;    
  7. CvMemStorage* g_storage = nullptr;    
  8. void on_trackbar(int)    
  9. {    
  10.     if (g_storage == nullptr)    
  11.     {    
  12.         g_gray = cvCreateImage(cvGetSize(g_image),8,1);    
  13.         g_storage = cvCreateMemStorage(0);    
  14.     }    
  15.     else
  16.     {    
  17.         cvClearMemStorage(g_storage);    
  18.     }    
  19.     CvSeq* contours = NULL;    
  20.     cvCvtColor(g_image,g_gray,CV_BGR2GRAY);    
  21.     cvThreshold(g_gray,g_gray,g_thresh,255,CV_THRESH_BINARY);    
  22.     cvFindContours(g_gray,g_storage,&contours);    
  23.     cvZero(g_gray);    
  24.     if (contours)    
  25.     {    
  26.         cvDrawContours(g_gray,contours,cvScalarAll(255),cvScalarAll(255),100);    
  27.     }    
  28.     cvShowImage("Contours",g_gray);    
  29. }    
  30. int _tmain(int argc, _TCHAR* argv[])    
  31. {    
  32.     g_image = cvLoadImage("C:\\Users\\chenchao\\Desktop\\細胞圖象\\正常的紅細胞\\5.bmp");    
  33.     cvNamedWindow("Contours",1);    
  34.     cvCreateTrackbar("Threshold","Contours",&g_thresh,300,on_trackbar);    
  35.     on_trackbar(0);    
  36.     cvWaitKey(0);    
  37.     printf("HELLO");    
  38.     return 0;    
  39. }    
如果全域性引數g_storage為NULL的話,則用cvCreateMemSotrage(0)建立一個記憶體儲存器.g_gray被初始化為和g_image同樣大小的黑色影象,但是為單通道影象.如果g_storage非空的話,則先用cvClearMemStorage清空記憶體儲存器的中間,這樣以便重複利用記憶體儲存器中的資源.然後建立一個CvSeq*指標,該指標用來儲存cvFindCountours()檢測到的輪廓.

然後g_image被轉換為灰度影象,接著用g_thresh為引數進行二值化處理,得到的二值影象儲存在g_gray中.cvFindContours從二值影象g_gray查詢輪廓,然後將得到的輪廓用cvDrawContours()函式繪製為白色到灰度影象.最終影象在視窗中顯示出來,並將在回撥函式開始處申請的結構釋放.

另一個輪廓的例子

在上例中,我們檢測出輸入影象的輪廓,然後逐個繪製沒格輪廓.從這個例子中,我們可以瞭解到輪廓檢測方法(如程式碼中是CV_RETR_LIST)以及max_depth(程式碼中是0)等引數的細節.如果設定的max_depth是一個比較大的值,你可以發現cvFindCountours()返回的輪廓是通過h_next連線被遍歷.對於其他一些拓撲結構(CV_RETR_TREE,CV_REER_CCOMP等),你會發現有些輪廓被畫過不只一次

例8-3 在輸入影象上尋找並繪製輪廓

  1. int _tmain(int argc, _TCHAR* argv[])    
  2. {    
  3. 相關推薦

    opencv輪廓輪廓判斷

    查詢輪廓 輪廓到底是什麼?一個輪廓一般對應一系列的點,也就是影象中的一條曲線.表示的方法可能根據不同情況而有所不同.有多重方法可以表示曲線.在openCV中一般用序列來儲存輪廓資訊.序列中的每一個元素是曲線中一個點的位置.關於序列表示的輪廓細節將在後

    Opencv中提取影象中的輪廓中心

    // contourStudy.cpp : 定義控制檯應用程式的入口點。 //本文所提取的輪廓具有的特徵: 只有一個無孔洞的內連通 #include "stdafx.h" #include <opencv2/opencv.hpp> #include <iostream> #includ

    OpenCV用cvFindContours求得輪廓,存放於seq中,取出各個輪廓

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    OpenCV學習筆記 010】提取直線、輪廓連通區域

    一、Canny運算元檢測輪廓   (http://blog.csdn.net/davebobo/article/details/52583167) 1.概念及原理 (1)之前我們是對梯度大小進行閾值化以得到二值的邊緣影象。但是這樣做有兩個缺點。其一是檢測到的邊緣過粗,難以實

    我的OpenCV學習筆記(20):提取元素的輪廓形狀描述子

    先看提取輪廓的程式碼: Mat image = imread("D:/picture/images/binaryGroup.bmp",0); if(!image.data) return -1; imshow("源影象",image); //獲取輪廓 std

    opencv學習心得八----提取輪廓座標

    輪廓提取後,它是用關鍵點組成的,下面提取出這些關鍵點。 1.先輸出所有關鍵點的個數cout<<"elements"<<contour->total<<endl; 2.for(int i=0;i<contour->tota

    OpenCV——使用多邊形包圍輪廓

    分享圖片 bubuko inf mage http nbsp png enc 圖片 OpenCV——使用多邊形包圍輪廓

    OpenCV——圖像的矩(計算矩、輪廓面積、輪廓或曲線長度)

    inf 分享圖片 open 分享 面積 image http 技術 技術分享 OpenCV——圖像的矩(計算矩、輪廓面積、輪廓或曲線長度)

    0035-OpenCV環境下繪製輪廓的外接多邊形、最小立式矩形、最小外接圓

    OpenCV提供了函式approxPolyDP()、boundRect()、minEnclosingCircle()分別計算給定點集的外接多邊形,最小立式矩形和最小外接圓,下面分別給出這三個函式的原型和引數意義。approxPolyDP函式(計算外接多邊形): C++: void approxPol

    0034-使用OpenCV的函式findContours提取輪廓並繪製輪廓

    OpenCV裡提取目標輪廓的函式是findContours,它的輸入影象是一幅二值影象,輸出的是每一個連通區域的輪廓點的集合。 下面介紹相關函式的用法:findContours函式:原型如下: C++: void findContours(InputOutputArray image, Outp

    OpenCv-C++下的輪廓周圍繪製矩形框和圓形框

    目前我正在學習OpenCv下的輪廓周圍繪製矩形框和圓形框,將個人學習記錄放到這裡。 參考連結:https://blog.csdn.net/lanyuelvyun/article/details/76614872 https://blog.csdn.net/qq_31647835/arti

    如何判斷輪廓是否為圓 如何判斷輪廓是否為圓?

    如何判斷輪廓是否為圓? 如何判斷輪廓是否為圓?     判斷一個輪廓是否為圓?這看似簡單的問題,在opencv中並沒有現成的函式。當我真正想運用的時候,卻發現還是有許多內容在裡面的。     &nb

    Dlib+opencv實時提取人臉輪廓(windows環境下//Dlib配置入門)

    本文主要演示Dlib在windows環境下利用VS配置環境執行成功呼叫攝像頭的實時人臉特徵點提取以及輪廓的描繪。 ///////////////////////////////軟體資源//////////////////////////////// /

    UVA 10256 The Great Divide(凸包應用 即凸包+線段相交判定+是否在凸包判斷 模板)

    UVA 10256 The Great Divide(凸包應用) 題意:        有n個紅點和m個藍點,問你是否存在一條直線,使得任取任取一個紅點和一個藍點,都在直線的兩邊?這條直線不能穿過紅點或藍點. 分析:        先求出紅點的凸包和藍點的凸包,則

    OpenCv-C++-下的輪廓周圍繪製矩形框和圓形框

    目前我正在學習OpenCv下的輪廓周圍繪製矩形框和圓形框,將個人學習記錄放到這裡。 下面介紹幾個相關函式: findContours():不用說了,找到影象的輪廓點 approxPolyDP():減少輪廓點集裡的個數 boundingRect():得到包覆此輪

    OpenCv學習筆記2--輪廓檢測,多邊形 直線 圓檢測

    此opencv系列部落格只是為了記錄小編對<<opencv3計算機視覺-pyhton語言實現>>的學習筆記,所有程式碼可以在我的github主頁https://github.co

    OpenCV圖像的輪廓

    dma nbsp pen get 控制 inline alt external side 查找輪廓 輪廓到底是什麽?一個輪廓一般對應一系列的點,也就是圖像中的一條曲線.表示的方法可能根據不同情況而有所不同.有多重方法可以表示曲線.在openCV中一般用序列來存儲輪廓信息.

    opencv】影象和輪廓的匹配(hu矩)

    影象的hu矩具有平移不變、尺度不變和旋轉不變性。 (1)中心矩:構造平移不變性。利用質心座標構造影象的中心矩。 (2)歸一化中心矩:構造尺度不變性。 (3)hu矩:構造旋轉不變性。 opencv的實現計算hu矩: (1)普通矩和中心矩的計算 void cvMoments(c

    提取元素的輪廓形狀描述子

    先看提取輪廓的程式碼: Mat image = imread("D:/picture/images/binaryGroup.bmp",0);   if(!image.data)       return -1;   imshow("源影象",image);   //獲取輪廓 std::vector&

    openCV實現影象的輪廓檢測以及外接矩形

    前兩篇博文分別介紹了影象的邊緣檢測和輪廓檢測,本文接著介紹影象的輪廓檢測和輪廓外接矩形: 一、程式碼部分: // extract_contours.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include&