1. 程式人生 > >opencv學習--輪廓檢測

opencv學習--輪廓檢測

      輪廓檢測,是影象處理中比較重要的一個部分。findContourheDrawContours

一、輪廓概述

       輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。 
  • 為了更加準確,要使用二值化影象。在尋找輪廓之前,要進行閾值化處理或者 Canny 邊界檢測。 
  • 查詢輪廓的函式會修改原始影象。如果你在找到輪廓之後還想使用原始影象的話,你應該將原始影象儲存到其他變數中。 
  • 在 OpenCV 中,查詢輪廓就像在黑色背景中超白色物體,要找的物體應該是白色而背景應該是黑色。

二、 尋找輪廓

       函式cv::findContour是從二值影象中來計算輪廓的,它可以使用cv::Canny()函式處理的影象,因為這樣的影象含有邊緣畫素;也可以使用cv::threshold()或者cv::adaptiveThreshold()處理後的影象,其邊緣隱含在正負區域的交界處。

int cvFindContours(

  CvArr* image,

  CvMemStorage* storage,

  CvSeq** first_contour,   

  int header_size=sizeof(CvContour),

  int mode=CV_RETR_LIST,   

  int method=CV_CHAIN_APPROX_SIMPLE,

  CvPoint offset=cvPoint(0,0)

);

findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

       第一個引數:image,單通道影象矩陣,可以是灰度圖等,此處為threshold_output是一個二值影象;

      第二個引數:contours,定義為“vector<vector<Point>> contours”,是一個雙重向量,向量內每個元素儲存了一組由連續的Point點構成的點的集合的向量,每一組Point點集就是一個輪廓。有多少輪廓,向量contours就有多少元素。

      第三個引數:hierarchy,為輸出引數,這個引數將指向用來儲存輪廓資訊的連結串列表頭。hierarchy向量內每一個元素的4個int型變數——hierarchy[i][0] ~hierarchy[i][3],分別表示第i個輪廓的後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號。如果當前輪廓沒有對應的後一個輪廓、前一個輪廓、父輪廓或內嵌輪廓的話,則hierarchy[i][0] ~hierarchy[i][3]的相應位被設定為預設值-1。

     第四個引數   表示儲存輪廓連結串列的表頭大小,當第六個引數傳入CV_CHAIN_CODE時,要設定成sizeof(CvChain),其它情況統一設定成sizeof(CvContour)。

     第五個引數:int型的mode,定義輪廓的檢索模式:

           取值一:CV_RETR_EXTERNAL只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略。

           取值二:CV_RETR_LIST   檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關

                  系,彼此之間獨立,沒有等級關係,這就意味著這個檢索模式下不存在父輪廓或內嵌輪廓,

                  所以hierarchy向量內所有元素的第3、第4個分量都會被置為-1。

           取值三:CV_RETR_CCOMP  檢測所有的輪廓,但所有輪廓只建立兩個等級關係,外圍為頂層,若外圍

                  內的內圍輪廓還包含了其他的輪廓資訊,則內圍內的所有輪廓均歸屬於頂層

           取值四:CV_RETR_TREE, 檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內

                   層輪廓還可以繼續包含內嵌輪廓。

    第六個引數:int型的method,定義輪廓的近似方法:

           取值一:CV_CHAIN_APPROX_NONE 儲存物體邊界上所有連續的輪廓點到contours向量內

           取值二:CV_CHAIN_APPROX_SIMPLE 僅儲存輪廓的拐點資訊,把所有輪廓拐點處的點儲存入contours

                   向量內,拐點與拐點之間直線段上的資訊點不予保留

           取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似演算法

    第七個引數   表示偏移量,比如你要從影象的(100, 0)開始進行輪廓檢測,那麼就傳入(100, 0)。

三、輪廓繪製

 cvDrawContours函式功能:在影象上繪製外部和內部輪廓

函式原型:

void cvDrawContours(

  CvArr *img,

  CvSeq* contour,

  CvScalar external_color,

  CvScalar hole_color,

  int max_level,

  int thickness=1,

  int line_type=8,

  CvPoint offset=cvPoint(0,0)

);

        第一個引數表示輸入影象,函式將在這張影象上繪製輪廓。

        第二個引數表示指向輪廓連結串列的指標。

        第三個引數和第四個引數表示顏色,繪製時會根據輪廓的層次來交替使用這二種顏色。

       第五個引數表示繪製輪廓的最大層數,如果是0,只繪製contour;如果是1,追加繪製和contour同層的所有輪廓;如果是2,追加繪製比contour低一層的輪廓,以此類推;如果值是負值,則函式並不繪製contour後的輪廓,但是將畫出其子輪廓,一直到abs(max_level) - 1層。

      第六個引數表示輪廓線的寬度,如果為CV_FILLED則會填充輪廓內部。

      第七個引數表示輪廓線的型別。

      第八個引數表示偏移量,如果傳入(10,20),那繪製將從影象的(10,20)處開始。

程式:對普通影象進行檢測,繪製輪廓。


 
#include "stdafx.h"
#include <opencv2/opencv.hpp>  
using namespace std;  
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
IplImage *g_pGrayImage = NULL;  
const char *pstrWindowsBinaryTitle = "二值圖";  
const char *pstrWindowsOutLineTitle = "輪廓圖";  
CvSeq *g_pcvSeq = NULL;  
  
void on_trackbar(int pos)  
{  
    // 轉為二值圖  
    IplImage *pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1);  
    cvThreshold(g_pGrayImage, pBinaryImage, pos, 255, CV_THRESH_BINARY);  
    // 顯示二值圖  
    cvShowImage(pstrWindowsBinaryTitle, pBinaryImage);  
  
    CvMemStorage* cvMStorage = cvCreateMemStorage();  
    // 檢索輪廓並返回檢測到的輪廓的個數  
    cvFindContours(pBinaryImage,cvMStorage, &g_pcvSeq);  
  
    IplImage *pOutlineImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 3);  
	//輪廓最大層數
    int _levels = 5;  
    cvZero(pOutlineImage);  
    cvDrawContours(pOutlineImage, g_pcvSeq, CV_RGB(255,0,0), CV_RGB(0,255,0), _levels);  
    cvShowImage(pstrWindowsOutLineTitle, pOutlineImage);  
  
    cvReleaseMemStorage(&cvMStorage);  
    cvReleaseImage(&pBinaryImage);  
    cvReleaseImage(&pOutlineImage);  
}  
  
int main( int argc, char** argv )  
{     
    const char *pstrWindowsSrcTitle = "原圖";  
    const char *pstrWindowsToolBarName = "二值化";  
  
    // 從檔案中載入原圖  
    IplImage *pSrcImage = cvLoadImage("beautiful.jpg", CV_LOAD_IMAGE_UNCHANGED);  
    // 顯示原圖  
    cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsSrcTitle, pSrcImage);  
  
    // 轉為灰度圖  
    g_pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);  
    cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);  
  
    // 建立二值圖和輪廓圖視窗  
    cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE);  
    cvNamedWindow(pstrWindowsOutLineTitle, CV_WINDOW_AUTOSIZE);  
  
  
    // 滑動條    
    int nThreshold = 0;  
    cvCreateTrackbar(pstrWindowsToolBarName, pstrWindowsBinaryTitle, &nThreshold, 254, on_trackbar);  
  
    on_trackbar(1);  
  
    cvWaitKey(0);  
  
    cvDestroyWindow(pstrWindowsSrcTitle);  
    cvDestroyWindow(pstrWindowsBinaryTitle);  
    cvDestroyWindow(pstrWindowsOutLineTitle);  
    cvReleaseImage(&pSrcImage);  
    cvReleaseImage(&g_pGrayImage);  
    return 0;  
}  

四 其他輪廓相關

           findContours後會對輸入的2值影象改變,所以如果不想改變該2值影象,需建立新mat來存放,findContours後的輪廓資訊contours可能過於複雜不平滑,可以用approxPolyDP函式對該多邊形曲線做適當近似

           contourArea函式可以得到當前輪廓包含區域的大小,方便輪廓的篩選

         findContours經常與drawContours配合使用,用來將輪廓繪製出來。其中第一個引數image表示目標影象,第二個引數contours表示輸入的輪廓組,每一組輪廓由點vector構成,第三個引數contourIdx指明畫第幾個輪廓,如果該引數為負值,則畫全部輪廓,第四個引數color為輪廓的顏色,第五個引數thickness為輪廓的線寬,如果為負值或CV_FILLED表示填充輪廓內部,第六個引數lineType為線型,第七個引數為輪廓結構資訊,第八個引數為maxLevel

        得到了複雜輪廓往往不適合特徵的檢測,這裡再介紹一個點集凸包絡的提取函式convexHull,輸入引數就可以是contours組中的一個輪廓,返回外凸包絡的點集

       還可以得到輪廓的外包絡矩形,使用函式boundingRect,如果想得到旋轉的外包絡矩形,使用函式minAreaRect,返回值為RotatedRect;也可以得到輪廓的外包絡圓,對應的函式為minEnclosingCircle;想得到輪廓的外包絡橢圓,對應的函式為fitEllipse,返回值也是RotatedRect,可以用ellipse函式畫出對應的橢圓

       如果想根據多邊形的輪廓資訊得到多邊形的多階矩,可以使用類moments,這個類可以得到多邊形和光柵形狀的3階以內的所有矩,類內有變數m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,比如多邊形的質心為 x = m10 / m00,y = m01 / m00。

       如果想獲得一點與多邊形封閉輪廓的資訊,可以呼叫pointPolygonTest函式,這個函式返回值為該點距離輪廓最近邊界的距離,為正值為在輪廓內部,負值為在輪廓外部,0表示在邊界上。

五、輪廓匹配