opencv學習--輪廓檢測
輪廓檢測,是影象處理中比較重要的一個部分。findContourhe和DrawContours
一、輪廓概述
輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
• 為了更加準確,要使用二值化影象。在尋找輪廓之前,要進行閾值化處理或者 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表示在邊界上。
五、輪廓匹配