1. 程式人生 > >OpenCV輪廓檢測

OpenCV輪廓檢測

邊緣 因此 view window pan 組織 hang otto 技術

《【OpenCV入門指南】第三篇Canny邊緣檢測》中介紹了邊緣檢測,本篇介紹輪廓檢測,輪廓檢測的原理通俗的說就是掏空內部點,比如原圖中有3*3的矩形點。那麽就可以將中間的那一點去掉。

在OpenCV中使用輪廓檢測是非常方便。直接使用cvFindContours函數就能完成對圖像輪廓的檢測。下面就來看看這個函數的用法。

《OpenCV入門指南》系列文章地址:http://blog.csdn.net/morewindows/article/category/863841


一.關鍵函數
1.1 cvFindContours
函數功能:對圖像進行輪廓檢測,這個函數將生成一條鏈表以保存檢測出的各個輪廓信息,並傳出指向這條鏈表表頭的指針。


函數原型:
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));

函數說明:
第一個參數表示輸入圖像,必須為一個8位的二值圖像。圖像的二值化請參見《【OpenCV入門指南】第四篇圖像的二值化》。
第二參數表示存儲輪廓的容器。為CvMemStorage類型,定義在OpenCV的\core\types_c.h中。
第三個參數為輸出參數,這個參數將指向用來存儲輪廓信息的鏈表表頭。
第四個參數表示存儲輪廓鏈表的表頭大小,當第六個參數傳入CV_CHAIN_CODE時,要設置成sizeof(CvChain),其它情況統一設置成sizeof(CvContour)。
第五個參數為輪廓檢測的模式,有如下取值:

CV_RETR_EXTERNAL:只檢索最外面的輪廓;
CV_RETR_LIST:檢索所有的輪廓,並將其保存到一條鏈表當中;
CV_RETR_CCOMP:檢索所有的輪廓,並將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
CV_RETR_TREE:檢索所有的輪廓,並重構嵌套輪廓的整個層次,可以參見下圖。

技術分享圖片

第六個參數用來表示輪廓邊緣的近似方法的,常用值如下所示:

CV_CHAIN_CODE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點的序列)。

CV_CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數只保留他們的終點部分。

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

使用cvFindContours函數能檢測出圖像的輪廓,將輪廓繪制出來則需要另一函數——cvDrawContours來配合了。下面介紹cvDrawContours函數。

1.2 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)處開始。

二.示例程序代碼
下面用一個非常簡單的例子展示如何使用輪廓檢測。
//圖像的輪廓檢測上  
//By MoreWindows (http://blog.csdn.net/MoreWindows)  
#include <opencv2/opencv.hpp>  
using namespace std;  
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
int main( int argc, char** argv )  
{     
    const char *pstrWindowsSrcTitle = "原圖(http://blog.csdn.net/MoreWindows)";  
    const char *pstrWindowsOutLineTitle = "輪廓圖(http://blog.csdn.net/MoreWindows)";  
      
    const int IMAGE_WIDTH = 400;  
    const int IMAGE_HEIGHT = 200;  
  
    // 創建圖像  
    IplImage *pSrcImage = cvCreateImage(cvSize(IMAGE_WIDTH, IMAGE_HEIGHT), IPL_DEPTH_8U, 3);  
    // 填充成白色  
    cvRectangle(pSrcImage, cvPoint(0, 0), cvPoint(pSrcImage->width, pSrcImage->height), CV_RGB(255, 255, 255), CV_FILLED);  
    // 畫圓  
    CvPoint ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);  
    int nRadius = 80;  
    cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 0), CV_FILLED);  
    ptCircleCenter = cvPoint(IMAGE_WIDTH / 4, IMAGE_HEIGHT / 2);  
    nRadius = 30;  
    cvCircle(pSrcImage, ptCircleCenter, nRadius, CV_RGB(255, 255, 255), CV_FILLED);  
    // 畫矩形  
    CvPoint ptLeftTop = cvPoint(IMAGE_WIDTH / 2 + 20, 20);  
    CvPoint ptRightBottom = cvPoint(IMAGE_WIDTH - 20, IMAGE_HEIGHT - 20);  
    cvRectangle(pSrcImage, ptLeftTop, ptRightBottom, CV_RGB(0, 255, 255), CV_FILLED);  
    ptLeftTop = cvPoint(IMAGE_WIDTH / 2 + 60, 40);  
    ptRightBottom = cvPoint(IMAGE_WIDTH - 60, IMAGE_HEIGHT - 40);  
    cvRectangle(pSrcImage, ptLeftTop, ptRightBottom, CV_RGB(255, 255, 255), CV_FILLED);  
    // 顯示原圖  
    cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsSrcTitle, pSrcImage);  
  
  
    // 轉為灰度圖  
    IplImage *pGrayImage =  cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);  
    cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);  
    // 轉為二值圖  
    IplImage *pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1);  
    cvThreshold(pGrayImage, pBinaryImage, 250, 255, CV_THRESH_BINARY);  
  
  
    // 檢索輪廓並返回檢測到的輪廓的個數  
    CvMemStorage *pcvMStorage = cvCreateMemStorage();  
    CvSeq *pcvSeq = NULL;  
    cvFindContours(pBinaryImage, pcvMStorage, &pcvSeq, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));  
      
    // 畫輪廓圖  
    IplImage *pOutlineImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);  
    int nLevels = 5;  
    // 填充成白色  
    cvRectangle(pOutlineImage, cvPoint(0, 0), cvPoint(pOutlineImage->width, pOutlineImage->height), CV_RGB(255, 255, 255), CV_FILLED);  
    cvDrawContours(pOutlineImage, pcvSeq, CV_RGB(255,0,0), CV_RGB(0,255,0), nLevels, 2);  
    // 顯示輪廓圖  
    cvNamedWindow(pstrWindowsOutLineTitle, CV_WINDOW_AUTOSIZE);  
    cvShowImage(pstrWindowsOutLineTitle, pOutlineImage);  
  
    cvWaitKey(0);  
  
    cvReleaseMemStorage(&pcvMStorage);  
  
    cvDestroyWindow(pstrWindowsSrcTitle);  
    cvDestroyWindow(pstrWindowsOutLineTitle);  
    cvReleaseImage(&pSrcImage);  
    cvReleaseImage(&pGrayImage);  
    cvReleaseImage(&pBinaryImage);  
    cvReleaseImage(&pOutlineImage);  
    return 0;  
}

運行結果如下圖所示:

技術分享圖片

由圖可以看出,輪廓線已經按層次交替的繪制成功了,讀者可以修改程序中的cvDrawContours中的nLevels參數,看看圖形會有什麽變化。

上一篇《【OpenCV入門指南】第五篇輪廓檢測上》介紹了cvFindContours函數和cvDrawContours函數,並作了一個簡單的使用示範。本篇將展示一個實例,讓大家對輪廓檢測有個更加深入的認識。

代碼如下:

//圖像的輪廓檢測下
//By MoreWindows (http://blog.csdn.net/MoreWindows)
#include <opencv2/opencv.hpp>
using namespace std;
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
IplImage *g_pGrayImage = NULL;
const char *pstrWindowsBinaryTitle = "二值圖(http://blog.csdn.net/MoreWindows)";
const char *pstrWindowsOutLineTitle = "輪廓圖(http://blog.csdn.net/MoreWindows)";
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 = "原圖(http://blog.csdn.net/MoreWindows)";
	const char *pstrWindowsToolBarName = "二值化";

	// 從文件中加載原圖
	IplImage *pSrcImage = cvLoadImage("003.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;
}

程序運行結果如下所示:

技術分享圖片

可以明顯看出,由於圖像的二值化處理只是根據圖像中單個像素的值來決定,因此這種最簡單輪廓檢測所產生的結果和人眼觀測的實際感覺肯定是有比較大的區別的。

輪廓檢測另外還有一個重要的函數cvApproxPoly,它的函數原型如下所示

CVAPI(CvSeq*) cvApproxPoly(
const void* src_seq, int header_size, CvMemStorage* storage, int method, double parameter, int parameter2 CV_DEFAULT(0));

由於篇幅問題,對於cvApproxPoly函數就不細講了,有興趣的讀者可以參考下面這一篇博客:http://blog.csdn.net/bruce_zeng/article/details/

OpenCV入門指南》系列文章地址:http://blog.csdn.net/morewindows/article/category/863841

轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/8253137

歡迎關註微博:http://weibo.com/MoreWindows

再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://www.cnblogs.com/captainbed

OpenCV輪廓檢測