【OpenCV第一篇】安裝OpenCV本篇主要介紹如何下載OpenCV安裝程序,如何在VS2008下安裝配置OpenCV,文章最后還介紹了一個(gè)使用OpenCV的簡單小例子。 一.OpenCV的下載可以到http://www./index.php/Download,然后選一個(gè)較新版本下載。我下的是V2.3.1版本,下載地址是: http://www./download/OpenCV-2.3.1-win-superpack.exe 下載完成后,雙擊運(yùn)行exe,選擇輸出目錄,我選擇的是D:\opencv。然后解壓縮就完成了安裝。安裝過程如下圖所示: 二.在我的電腦中配置OpenCV在“我的電腦”右擊彈出“系統(tǒng)屬性”對話框,選擇“高級”再點(diǎn)擊“環(huán)境變量”,然后再編輯path,在“編輯用戶變量”對話框的變量值輸入以下三條,注意用“;”來分開。 D:\opencv\opencv\build\x86\vc9\bin; D:\opencv\opencv\build\x86\mingw\bin; D:\opencv\opencv\build\common\tbb\ia32\vc9; 三.在VS2008中加入OpenCV在VS2008中點(diǎn)擊“工具”,再點(diǎn)擊“選項(xiàng)”,然后選擇“項(xiàng)目和解決方案”,手動加入庫文件,引用文件和包含文件即可完成OpenCV在VS2008的導(dǎo)入。 四.第一個(gè)OpenCV程序下面是最簡單的OpenCV使用例子,功能也很簡單——加載圖像文件并顯示出來。代碼如下: //顯示圖像文件 #include <opencv2/opencv.hpp>
using namespace std; #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
int main() { const char *pstrImageName = "沖浪大師賽001.jpg"; const char *pstrWindowsTitle = "OpenCV第一個(gè)程序(http://blog.csdn.net/MoreWindows)"; //從文件中讀取圖像 IplImage *pImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_UNCHANGED); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsTitle, CV_WINDOW_AUTOSIZE); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsTitle, pImage); //等待按鍵事件 cvWaitKey(); cvDestroyWindow(pstrWindowsTitle); cvReleaseImage(&pImage); return 0; }
//顯示圖像文件 #include <opencv2/opencv.hpp> using namespace std; #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") int main() { const char *pstrImageName = "沖浪大師賽001.jpg"; const char *pstrWindowsTitle = "OpenCV第一個(gè)程序(http://blog.csdn.net/MoreWindows)"; //從文件中讀取圖像 IplImage *pImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_UNCHANGED); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsTitle, CV_WINDOW_AUTOSIZE); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsTitle, pImage); //等待按鍵事件 cvWaitKey(); cvDestroyWindow(pstrWindowsTitle); cvReleaseImage(&pImage); return 0; }
對代碼中的主要函數(shù)進(jìn)行下講解: 1.創(chuàng)建窗口 cvNamedWindow 函數(shù)名稱:cvNamedWindow 函數(shù)功能:創(chuàng)建窗口 函數(shù)原型: int cvNamedWindow( const char* name, int flags=CV_WINDOW_AUTOSIZE ); 參數(shù)說明: 第一個(gè)參數(shù)表示窗口的名字,它被用來區(qū)分不同的窗口,并被顯示為窗口標(biāo)題。被創(chuàng)建的窗口可以通過它們的名字被引用。 第二個(gè)參數(shù)表示窗口屬性標(biāo)志。目前唯一支持的標(biāo)志是CV_WINDOW_AUTOSIZE。當(dāng)這個(gè)標(biāo)志被設(shè)置后,用戶不能手動改變窗口大小,窗口大小會自動調(diào)整以適合被顯示圖像。 函數(shù)cvNamedWindow創(chuàng)建一個(gè)可以放置圖像和trackbar的窗口。 注意: 如果已經(jīng)存在這個(gè)名字的窗口,這個(gè)函數(shù)將不做任何事情。 2.在指定窗口中顯示圖像 cvShowImage 函數(shù)名稱:cvShowImage 函數(shù)功能:在指定窗口中顯示圖像 函數(shù)原型: void cvShowImage( const char* name, const CvArr*
image ); 參數(shù)說明: 第一個(gè)參數(shù):窗口的名字。 第二個(gè)參數(shù):被顯示的圖像。
3.等待按鍵事件 cvWaitKey 函數(shù)名稱:cvWaitKey 函數(shù)功能:等待按鍵事件 函數(shù)原型: int cvWaitKey( int delay=0 ); 參數(shù)說明: 第一個(gè)參數(shù):延遲的毫秒數(shù),當(dāng)delay<=0時(shí)表示無限等待。 函數(shù)返回值: 如果超過指定時(shí)間則返回-1,否則返回被按鍵的值。 編譯,居然出錯(cuò)了?。″e(cuò)誤信息如下: 1>正在鏈接... 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvReleaseImage,該符號在函數(shù)_main中被引用 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvDestroyWindow,該符號在函數(shù)_main中被引用 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvWaitKey,該符號在函數(shù)_main中被引用 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvShowImage,該符號在函數(shù)_main中被引用 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvNamedWindow,該符號在函數(shù)_main中被引用 1>opencv_test1.obj : error LNK2019:無法解析的外部符號_cvLoadImage,該符號在函數(shù)_main中被引用 現(xiàn)在如何解決這個(gè)問題了?既然是在鏈接時(shí)出的錯(cuò),說明肯定有靜態(tài)庫沒有被編譯器找到。因此我們在VS2008中點(diǎn)擊“項(xiàng)目”,再點(diǎn)擊“屬性”,依次展開“配置屬性”-->“鏈接器”-->“輸入”在“附加依賴項(xiàng)中”添加如下靜態(tài)文件。 opencv_calib3d231d.lib opencv_contrib231d.lib opencv_core231d.lib opencv_features2d231d.lib opencv_flann231d.lib opencv_gpu231d.lib opencv_highgui231d.lib opencv_imgproc231d.lib
opencv_legacy231d.lib opencv_ml231d.lib opencv_objdetect231d.lib opencv_ts231d.lib opencv_video231d.lib 再編譯,成功。程序運(yùn)行結(jié)果如下圖所示: 呵呵,很酷的沖浪~~ 【OpenCV入門指南】第二篇 縮放圖像 一. 主要函數(shù)介紹1.1 cvResize函數(shù)功能:圖像大小變換 函數(shù)原型: voidcvResize( const CvArr*src, CvArr*dst, intinterpolation=CV_INTER_LINEAR ); 函數(shù)說明: 第一個(gè)參數(shù)表示輸入圖像。 第二個(gè)參數(shù)表示輸出圖像。 第三個(gè)參數(shù)表示插值方法,可以有以下四種: CV_INTER_NN -最近鄰插值, CV_INTER_LINEAR -雙線性插值 (缺省使用) CV_INTER_AREA -使用象素關(guān)系重采樣。當(dāng)圖像縮小時(shí)候,該方法可以避免波紋出現(xiàn)。當(dāng)圖像放大時(shí),類似于 CV_INTER_NN方法.. CV_INTER_CUBIC -立方插值. 這個(gè)函數(shù)在功能上與Win32 API中的StretchBlt()函數(shù)類似。 1.2 cvCreateImage函數(shù)功能:創(chuàng)建圖像 函數(shù)原型: IplImage*cvCreateImage(CvSizesize, intdepth,intchannels); 函數(shù)說明: 第一個(gè)參數(shù)表示圖像的大小。 第二個(gè)參數(shù)表示圖像的深度,可以為IPL_DEPTH_8U,IPL_DEPTH_16U等等。 第三個(gè)參數(shù)表示圖像的通道數(shù)。 二. 示例程序代碼有了這二個(gè)函數(shù)后,不難寫出代碼: //縮放圖像文件 //By MoreWindows (http://blog.csdn.net/MoreWindows)
#include <opencv2/opencv.hpp>
using namespace std; //隱藏控制臺窗口 #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
int main() { const char *pstrImageName = "沖浪大師賽001.jpg"; const char *pstrSaveImageName = "沖浪大師賽001縮放圖.jpg"; const char *pstrWindowsSrcTitle = "原圖 (http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsDstTitle = "縮放圖 (http://blog.csdn.net/MoreWindows)"; double fScale = 0.314; //縮放倍數(shù)
CvSize czSize; //目標(biāo)圖像尺寸
//從文件中讀取圖像 IplImage *pSrcImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_UNCHANGED); IplImage *pDstImage = NULL; //計(jì)算目標(biāo)圖像大小 czSize.width = pSrcImage->width * fScale; czSize.height = pSrcImage->height * fScale; //創(chuàng)建圖像并縮放 pDstImage = cvCreateImage(czSize, pSrcImage->depth, pSrcImage->nChannels); cvResize(pSrcImage, pDstImage, CV_INTER_AREA); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvNamedWindow(pstrWindowsDstTitle, CV_WINDOW_AUTOSIZE); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsSrcTitle, pSrcImage); cvShowImage(pstrWindowsDstTitle, pDstImage); //等待按鍵事件 cvWaitKey(); //保存圖片 cvSaveImage(pstrSaveImageName, pDstImage); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsDstTitle); cvReleaseImage(&pSrcImage); cvReleaseImage(&pDstImage); return 0; }
//縮放圖像文件 //By MoreWindows (http://blog.csdn.net/MoreWindows) #include <opencv2/opencv.hpp> using namespace std; //隱藏控制臺窗口 #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") int main() { const char *pstrImageName = "沖浪大師賽001.jpg"; const char *pstrSaveImageName = "沖浪大師賽001縮放圖.jpg"; const char *pstrWindowsSrcTitle = "原圖 (http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsDstTitle = "縮放圖 (http://blog.csdn.net/MoreWindows)"; double fScale = 0.314; //縮放倍數(shù) CvSize czSize; //目標(biāo)圖像尺寸 //從文件中讀取圖像 IplImage *pSrcImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_UNCHANGED); IplImage *pDstImage = NULL; //計(jì)算目標(biāo)圖像大小 czSize.width = pSrcImage->width * fScale; czSize.height = pSrcImage->height * fScale; //創(chuàng)建圖像并縮放 pDstImage = cvCreateImage(czSize, pSrcImage->depth, pSrcImage->nChannels); cvResize(pSrcImage, pDstImage, CV_INTER_AREA); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvNamedWindow(pstrWindowsDstTitle, CV_WINDOW_AUTOSIZE); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsSrcTitle, pSrcImage); cvShowImage(pstrWindowsDstTitle, pDstImage); //等待按鍵事件 cvWaitKey(); //保存圖片 cvSaveImage(pstrSaveImageName, pDstImage); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsDstTitle); cvReleaseImage(&pSrcImage); cvReleaseImage(&pDstImage); return 0; }
程序運(yùn)行結(jié)果如下: 【OpenCV入門指南】第三篇Canny邊緣檢測 圖像的邊緣檢測的原理是檢測出圖像中所有灰度值變化較大的點(diǎn),而且這些點(diǎn)連接起來就構(gòu)成了若干線條,這些線條就可以稱為圖像的邊緣。 Canny邊緣檢測算子是John F. Canny于 1986年開發(fā)出來的一個(gè)多級邊緣檢測算法。Canny 邊緣檢測的數(shù)學(xué)原理和算法實(shí)現(xiàn)這里就不再了,有興趣的讀者可以查閱專業(yè)書籍,本文主要介紹如何在OpenCV中對圖像進(jìn)行Canny邊緣檢測,下面就來看看這個(gè)函數(shù)的原型。 一. 主要函數(shù)1.1 cvCanny函數(shù)功能:采用Canny方法對圖像進(jìn)行邊緣檢測 函數(shù)原型: voidcvCanny( constCvArr* image, CvArr*edges, doublethreshold1,doublethreshold2, intaperture_size=3 ); 函數(shù)說明: 第一個(gè)參數(shù)表示輸入圖像,必須為單通道灰度圖。 第二個(gè)參數(shù)表示輸出的邊緣圖像,為單通道黑白圖。 第三個(gè)參數(shù)和第四個(gè)參數(shù)表示閾值,這二個(gè)閾值中當(dāng)中的小閾值用來控制邊緣連接,大的閾值用來控制強(qiáng)邊緣的初始分割即如果一個(gè)像素的梯度大與上限值,則被認(rèn)為是邊緣像素,如果小于下限閾值,則被拋棄。如果該點(diǎn)的梯度在兩者之間則當(dāng)這個(gè)點(diǎn)與高于上限值的像素點(diǎn)連接時(shí)我們才保留,否則刪除。 第五個(gè)參數(shù)表示Sobel算子大小,默認(rèn)為3即表示一個(gè)3*3的矩陣。Sobel算子與高斯拉普拉斯算子都是常用的邊緣算子,詳細(xì)的數(shù)學(xué)原理可以查閱專業(yè)書籍。 為了更好的使用cvCanny()函數(shù),下面再介紹二個(gè)實(shí)用的函數(shù),這二個(gè)函數(shù)對后面的程序?qū)崿F(xiàn)非常有幫助。 1.2 cvCreateTrackbar函數(shù)功能:創(chuàng)建trackbar并添加到指定窗口 函數(shù)原型: intcvCreateTrackbar( constchar* trackbar_name, constchar* window_name, int*value, intcount, CvTrackbarCallbackon_change ); 函數(shù)說明: 第一個(gè)參數(shù)表示該trackbar的名稱。 第二個(gè)參數(shù)表示窗口名稱,該trackbar將顯示在這個(gè)窗口內(nèi)。 第三個(gè)參數(shù)表示創(chuàng)建時(shí)滑塊的位置。 第四個(gè)參數(shù)表示滑塊位置的最大值,最小值固定為0。 第五個(gè)參數(shù)表示回調(diào)函數(shù)。當(dāng)滑塊位置有變化時(shí),系統(tǒng)會調(diào)用該回調(diào)函數(shù)。 注:被創(chuàng)建的trackbar默認(rèn)顯示在指定窗口的頂端,可以通過函數(shù)cvGetTrackbarPos()來獲取trackbar顯示的位置信息,以及通過函數(shù)cvSetTrackbarPos()來重新設(shè)置trackbar的顯示位置。 1.3 CvTrackbarCallback函數(shù)功能:cvCreateTrackbar()函數(shù)所使用的回調(diào)函數(shù) 函數(shù)定義: typedef void (CV_CDECL *CvTrackbarCallback)(int pos) 函數(shù)說明: 當(dāng)trackbar位置被改變的時(shí),系統(tǒng)會調(diào)用這個(gè)回調(diào)函數(shù),并將參數(shù)pos設(shè)置為表示trackbar位置的數(shù)值。 二. 示例程序代碼下面就給出在OpenCV中使用Canny 邊緣檢測的程序代碼: //圖像的Canny邊緣檢測 //By MoreWindows (http://blog.csdn.net/MoreWindows)
#include <opencv2/opencv.hpp>
using namespace std; #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
IplImage *g_pSrcImage, *g_pCannyImg; const char *pstrWindowsCannyTitle = "邊緣檢測圖(http://blog.csdn.net/MoreWindows)"; //cvCreateTrackbar的回調(diào)函數(shù) void on_trackbar(int threshold) { //canny邊緣檢測 cvCanny(g_pSrcImage, g_pCannyImg, threshold, threshold * 3, 3); cvShowImage(pstrWindowsCannyTitle, g_pCannyImg); } int main() { const char *pstrImageName = "001.jpg"; const char *pstrWindowsSrcTitle = "原圖(http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsToolBar = "Threshold"; //從文件中載入圖像的灰度圖CV_LOAD_IMAGE_GRAYSCALE - 灰度圖
g_pSrcImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_GRAYSCALE); g_pCannyImg = cvCreateImage(cvGetSize(g_pSrcImage), IPL_DEPTH_8U, 1); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvNamedWindow(pstrWindowsCannyTitle, CV_WINDOW_AUTOSIZE); //創(chuàng)建滑動條 int nThresholdEdge = 1; cvCreateTrackbar(pstrWindowsToolBar, pstrWindowsCannyTitle, &nThresholdEdge, 100, on_trackbar); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsSrcTitle, g_pSrcImage); on_trackbar(1); //等待按鍵事件 cvWaitKey(); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsCannyTitle); cvReleaseImage(&g_pSrcImage); cvReleaseImage(&g_pCannyImg); return 0; }
//圖像的Canny邊緣檢測 //By MoreWindows (http://blog.csdn.net/MoreWindows) #include <opencv2/opencv.hpp> using namespace std; #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") IplImage *g_pSrcImage, *g_pCannyImg; const char *pstrWindowsCannyTitle = "邊緣檢測圖(http://blog.csdn.net/MoreWindows)"; //cvCreateTrackbar的回調(diào)函數(shù) void on_trackbar(int threshold) { //canny邊緣檢測 cvCanny(g_pSrcImage, g_pCannyImg, threshold, threshold * 3, 3); cvShowImage(pstrWindowsCannyTitle, g_pCannyImg); } int main() { const char *pstrImageName = "001.jpg"; const char *pstrWindowsSrcTitle = "原圖(http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsToolBar = "Threshold"; //從文件中載入圖像的灰度圖CV_LOAD_IMAGE_GRAYSCALE - 灰度圖 g_pSrcImage = cvLoadImage(pstrImageName, CV_LOAD_IMAGE_GRAYSCALE); g_pCannyImg = cvCreateImage(cvGetSize(g_pSrcImage), IPL_DEPTH_8U, 1); //創(chuàng)建窗口 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvNamedWindow(pstrWindowsCannyTitle, CV_WINDOW_AUTOSIZE); //創(chuàng)建滑動條 int nThresholdEdge = 1; cvCreateTrackbar(pstrWindowsToolBar, pstrWindowsCannyTitle, &nThresholdEdge, 100, on_trackbar); //在指定窗口中顯示圖像 cvShowImage(pstrWindowsSrcTitle, g_pSrcImage); on_trackbar(1); //等待按鍵事件 cvWaitKey(); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsCannyTitle); cvReleaseImage(&g_pSrcImage); cvReleaseImage(&g_pCannyImg); return 0; }
運(yùn)行效果如圖所示: 本篇介紹了Canny邊緣檢測,這種方法能有效的找出圖像中的所有邊緣。 【OpenCV入門指南】第四篇 圖像的二值化 在上一篇《【OpenCV入門指南】第三篇Canny邊緣檢測》中介紹了使用Canny算子對圖像進(jìn)行邊緣檢測。與邊緣檢測相比,輪廓檢測有時(shí)能更好的反映圖像的內(nèi)容。而要對圖像進(jìn)行輪廓檢測,則必須要先對圖像進(jìn)行二值化,圖像的二值化就是將圖像上的像素點(diǎn)的灰度值設(shè)置為0或255,這樣將使整個(gè)圖像呈現(xiàn)出明顯的黑白效果。在數(shù)字圖像處理中,二值圖像占有非常重要的地位,圖像的二值化使圖像中數(shù)據(jù)量大為減少,從而能凸顯出目標(biāo)的輪廓。 一. 關(guān)鍵函數(shù)介紹下面就介紹OpenCV中對圖像進(jìn)行二值化的關(guān)鍵函數(shù)——cvThreshold()。 函數(shù)功能:采用Canny方法對圖像進(jìn)行邊緣檢測 函數(shù)原型: voidcvThreshold( constCvArr* src, CvArr*dst, doublethreshold, doublemax_value, intthreshold_type ); 函數(shù)說明: 第一個(gè)參數(shù)表示輸入圖像,必須為單通道灰度圖。 第二個(gè)參數(shù)表示輸出的邊緣圖像,為單通道黑白圖。 第三個(gè)參數(shù)表示閾值 第四個(gè)參數(shù)表示最大值。 第五個(gè)參數(shù)表示運(yùn)算方法。 在OpenCV的imgproc\types_c.h中可以找到運(yùn)算方法的定義。 /* Threshold types */ enum { CV_THRESH_BINARY =0, /* value = value > threshold ? max_value : 0 */ CV_THRESH_BINARY_INV =1, /* value = value > threshold ? 0 : max_value */ CV_THRESH_TRUNC =2, /* value = value > threshold ? threshold : value */ CV_THRESH_TOZERO =3, /* value = value > threshold ? value : 0 */ CV_THRESH_TOZERO_INV =4, /* value = value > threshold ? 0 : value */ CV_THRESH_MASK =7, CV_THRESH_OTSU =8 /* use Otsu algorithm to choose the optimal threshold value; combine the flag with one of the above CV_THRESH_* values */ }; 注釋已經(jīng)寫的很清楚了,因此不再用中文來表達(dá)了。 二. 示例程序代碼下面給出對圖像進(jìn)行二值化的完整的源代碼: //圖像的二值化 //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; IplImage *g_pBinaryImage = NULL; const char *pstrWindowsBinaryTitle = "二值圖(http://blog.csdn.net/MoreWindows)"; void on_trackbar(int pos) { // 轉(zhuǎn)為二值圖 cvThreshold(g_pGrayImage, g_pBinaryImage, pos, 255, CV_THRESH_BINARY); // 顯示二值圖 cvShowImage(pstrWindowsBinaryTitle, g_pBinaryImage); } int main( int argc, char** argv ) { const char *pstrWindowsSrcTitle = "原圖(http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsToolBarName = "二值圖閾值"; // 從文件中加載原圖 IplImage *pSrcImage = cvLoadImage("002.jpg", CV_LOAD_IMAGE_UNCHANGED); // 轉(zhuǎn)為灰度圖 g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY); // 創(chuàng)建二值圖 g_pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1); // 顯示原圖 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvShowImage(pstrWindowsSrcTitle, pSrcImage); // 創(chuàng)建二值圖窗口 cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE); // 滑動條 int nThreshold = 0; cvCreateTrackbar(pstrWindowsToolBarName, pstrWindowsBinaryTitle, &nThreshold, 254, on_trackbar); on_trackbar(1); cvWaitKey(0); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsBinaryTitle); cvReleaseImage(&pSrcImage); cvReleaseImage(&g_pGrayImage); cvReleaseImage(&g_pBinaryImage); return 0; }
//圖像的二值化 //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; IplImage *g_pBinaryImage = NULL; const char *pstrWindowsBinaryTitle = "二值圖(http://blog.csdn.net/MoreWindows)"; void on_trackbar(int pos) { // 轉(zhuǎn)為二值圖 cvThreshold(g_pGrayImage, g_pBinaryImage, pos, 255, CV_THRESH_BINARY); // 顯示二值圖 cvShowImage(pstrWindowsBinaryTitle, g_pBinaryImage); } int main( int argc, char** argv ) { const char *pstrWindowsSrcTitle = "原圖(http://blog.csdn.net/MoreWindows)"; const char *pstrWindowsToolBarName = "二值圖閾值"; // 從文件中加載原圖 IplImage *pSrcImage = cvLoadImage("002.jpg", CV_LOAD_IMAGE_UNCHANGED); // 轉(zhuǎn)為灰度圖 g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY); // 創(chuàng)建二值圖 g_pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1); // 顯示原圖 cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE); cvShowImage(pstrWindowsSrcTitle, pSrcImage); // 創(chuàng)建二值圖窗口 cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE); // 滑動條 int nThreshold = 0; cvCreateTrackbar(pstrWindowsToolBarName, pstrWindowsBinaryTitle, &nThreshold, 254, on_trackbar); on_trackbar(1); cvWaitKey(0); cvDestroyWindow(pstrWindowsSrcTitle); cvDestroyWindow(pstrWindowsBinaryTitle); cvReleaseImage(&pSrcImage); cvReleaseImage(&g_pGrayImage); cvReleaseImage(&g_pBinaryImage); return 0; }
運(yùn)行結(jié)果如下所示,讀者可以到下載源文件和程序(Release版本,不用安裝OpenCV也能使用),自己動手調(diào)試下閾值大小,看看生成的二值圖有什么變化。 OpenCV還有個(gè)cvAdaptiveThreshold()函數(shù),這個(gè)函數(shù)會使用Otsu算法(大律法或最大類間方差法)(注1)來計(jì)算出一個(gè)全局閾值,然后根據(jù)這個(gè)閾值進(jìn)行二值化。當(dāng)然直接使用上一篇《【OpenCV入門指南】第三篇Canny邊緣檢測》中的cvCanny()函數(shù)也可以對圖像進(jìn)行二值化(想到怎么傳參數(shù)了嗎?)。 注1.調(diào)用cvThreshold()時(shí)傳入?yún)?shù)CV_THRESH_OTSU也是使用Otsu算法來自動生成一個(gè)閾值。 【OpenCV入門指南】第五篇 輪廓檢測 上《【OpenCV入門指南】第三篇Canny邊緣檢測》中介紹了邊緣檢測,本篇介紹輪廓檢測,輪廓檢測的原理通俗的說就是掏空內(nèi)部點(diǎn),比如原圖中有3*3的矩形點(diǎn)。那么就可以將中間的那一點(diǎn)去掉。 在OpenCV中使用輪廓檢測是非常方便。直接使用cvFindContours函數(shù)就能完成對圖像輪廓的檢測。下面就來看看這個(gè)函數(shù)的用法。 一.關(guān)鍵函數(shù)1.1 cvFindContours函數(shù)功能:對圖像進(jìn)行輪廓檢測,這個(gè)函數(shù)將生成一條鏈表以保存檢測出的各個(gè)輪廓信息,并傳出指向這條鏈表表頭的指針。 函數(shù)原型: intcvFindContours( CvArr*image, CvMemStorage*storage, CvSeq**first_contour, intheader_size=sizeof(CvContour), intmode=CV_RETR_LIST, intmethod=CV_CHAIN_APPROX_SIMPLE, CvPointoffset=cvPoint(0,0) ); 函數(shù)說明: 第一個(gè)參數(shù)表示輸入圖像,必須為一個(gè)8位的二值圖像。圖像的二值化請參見《【OpenCV入門指南】第四篇圖像的二值化》。 第二參數(shù)表示存儲輪廓的容器。為CvMemStorage類型,定義在OpenCV的\core\types_c.h中。 第三個(gè)參數(shù)為輸出參數(shù),這個(gè)參數(shù)將指向用來存儲輪廓信息的鏈表表頭。 第四個(gè)參數(shù)表示存儲輪廓鏈表的表頭大小,當(dāng)?shù)诹鶄€(gè)參數(shù)傳入CV_CHAIN_CODE時(shí),要設(shè)置成sizeof(CvChain),其它情況統(tǒng)一設(shè)置成sizeof(CvContour)。 第五個(gè)參數(shù)為輪廓檢測的模式,有如下取值: CV_RETR_EXTERNAL:只檢索最外面的輪廓; CV_RETR_LIST:檢索所有的輪廓,并將其保存到一條鏈表當(dāng)中; CV_RETR_CCOMP:檢索所有的輪廓,并將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界; CV_RETR_TREE:檢索所有的輪廓,并重構(gòu)嵌套輪廓的整個(gè)層次,可以參見下圖。
第六個(gè)參數(shù)用來表示輪廓邊緣的近似方法的,常用值如下所示: CV_CHAIN_CODE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點(diǎn)的序列)。 CV_CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數(shù)只保留他們的終點(diǎn)部分。 第七個(gè)參數(shù)表示偏移量,比如你要從圖像的(100, 0)開始進(jìn)行輪廓檢測,那么就傳入(100,
0)。 使用cvFindContours函數(shù)能檢測出圖像的輪廓,將輪廓繪制出來則需要另一函數(shù)——cvDrawContours來配合了。下面介紹cvDrawContours函數(shù)。 1.2 cvDrawContours函數(shù)功能:在圖像上繪制外部和內(nèi)部輪廓 函數(shù)原型: voidcvDrawContours( CvArr *img, CvSeq*contour, CvScalarexternal_color, CvScalarhole_color, intmax_level, intthickness=1, intline_type=8, CvPointoffset=cvPoint(0,0) ); 第一個(gè)參數(shù)表示輸入圖像,函數(shù)將在這張圖像上繪制輪廓。 第二個(gè)參數(shù)表示指向輪廓鏈表的指針。 第三個(gè)參數(shù)和第四個(gè)參數(shù)表示顏色,繪制時(shí)會根據(jù)輪廓的層次來交替使用這二種顏色。 第五個(gè)參數(shù)表示繪制輪廓的最大層數(shù),如果是0,只繪制contour;如果是1,追加繪制和contour同層的所有輪廓;如果是2,追加繪制比contour低一層的輪廓,以此類推;如果值是負(fù)值,則函數(shù)并不繪制contour后的輪廓,但是將畫出其子輪廓,一直到abs(max_level)
- 1層。 第六個(gè)參數(shù)表示輪廓線的寬度,如果為CV_FILLED則會填充輪廓內(nèi)部。 第七個(gè)參數(shù)表示輪廓線的類型。 第八個(gè)參數(shù)表示偏移量,如果傳入(10,20),那繪制將從圖像的(10,20)處開始。 二.示例程序代碼下面用一個(gè)非常簡單的例子展示如何使用輪廓檢測。 //圖像的輪廓檢測上 //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; // 創(chuàng)建圖像 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); // 轉(zhuǎn)為灰度圖 IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY); // 轉(zhuǎn)為二值圖 IplImage *pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1); cvThreshold(pGrayImage, pBinaryImage, 250, 255, CV_THRESH_BINARY); // 檢索輪廓并返回檢測到的輪廓的個(gè)數(shù)
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; }
//圖像的輪廓檢測上 //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; // 創(chuàng)建圖像 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); // 轉(zhuǎn)為灰度圖 IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY); // 轉(zhuǎn)為二值圖 IplImage *pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1); cvThreshold(pGrayImage, pBinaryImage, 250, 255, CV_THRESH_BINARY); // 檢索輪廓并返回檢測到的輪廓的個(gè)數(shù) 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; }
運(yùn)行結(jié)果如下圖所示:
由圖可以看出,輪廓線已經(jīng)按層次交替的繪制成功了,讀者可以修改程序中的cvDrawContours中的nLevels參數(shù),看看圖形會有什么變化。 OpenCV入門指南】第六篇 輪廓檢測 下 上一篇《【OpenCV入門指南】第五篇輪廓檢測上》介紹了cvFindContours函數(shù)和cvDrawContours函數(shù),并作了一個(gè)簡單的使用示范。本篇將展示一個(gè)實(shí)例,讓大家對輪廓檢測有個(gè)更加深入的認(rèn)識。 代碼如下: //圖像的輪廓檢測下 //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) { // 轉(zhuǎn)為二值圖 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(); // 檢索輪廓并返回檢測到的輪廓的個(gè)數(shù) 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); // 轉(zhuǎn)為灰度圖 g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY); // 創(chuàng)建二值圖和輪廓圖窗口 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; }
//圖像的輪廓檢測下 //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) { // 轉(zhuǎn)為二值圖 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(); // 檢索輪廓并返回檢測到的輪廓的個(gè)數(shù) 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); // 轉(zhuǎn)為灰度圖 g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1); cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY); // 創(chuàng)建二值圖和輪廓圖窗口 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; }
程序運(yùn)行結(jié)果如下所示:
可以明顯看出,由于圖像的二值化處理只是根據(jù)圖像中單個(gè)像素的值來決定,因此這種最簡單輪廓檢測所產(chǎn)生的結(jié)果和人眼觀測的實(shí)際感覺肯定是有比較大的區(qū)別的。 輪廓檢測另外還有一個(gè)重要的函數(shù)cvApproxPoly,它的函數(shù)原型如下所示 CVAPI(CvSeq*) cvApproxPoly( constvoid* src_seq, intheader_size, CvMemStorage*storage, intmethod, doubleparameter, intparameter2 CV_DEFAULT(0) ); 這是一個(gè)輪廓的多邊形輪廓逼近函數(shù),這個(gè)函數(shù)用指定精度逼近一個(gè)或多個(gè) 曲線,并返回逼近結(jié)果。一開始覺得這個(gè)函數(shù)沒什么必要,因?yàn)槿绻皇且唵蔚奶崛『惋@示圖像輪廓的話只要cvFindContours函數(shù)就夠了,那么為什么還要這個(gè)函數(shù)呢?后來百度了下,在opencv論壇上找到了答案。 首先,輪廓的多邊形逼近指的是:使用多邊形來近似表示一個(gè)輪廓。其次,多邊形逼近的目的是為了減少輪廓的頂點(diǎn)數(shù)目。但多邊形逼近的結(jié)果依然是一個(gè)輪廓,只是這個(gè)輪廓相對要粗曠一些。 下面看下測試代碼: #include<cv.h> #include<highgui.h> int main() { IplImage* src = NULL; IplImage* img = NULL; IplImage* dst = NULL; CvMemStorage* storage = cvCreateMemStorage (0); CvMemStorage* storage1 = cvCreateMemStorage (0); CvSeq* contour = 0; CvSeq* cont; CvSeq* mcont; src = cvLoadImage ("8.jpg", 1); img = cvCreateImage (cvGetSize(src), IPL_DEPTH_8U, 1); dst = cvCreateImage (cvGetSize(src), src->depth, src->nChannels); cvCvtColor (src, img, CV_BGR2GRAY); cvThreshold (img, img, 100, 200, CV_THRESH_BINARY); cvFindContours (img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); if (contour) { CvTreeNodeIterator iterator; cvInitTreeNodeIterator (&iterator, contour, 1); while (0 != (cont = (CvSeq*)cvNextTreeNode (&iterator))) { mcont = cvApproxPoly (cont, sizeof(CvContour), storage1, CV_POLY_APPROX_DP, cvContourPerimeter(cont)*0.02,0); cvDrawContours (dst, mcont, CV_RGB(255,0,0),CV_RGB(0,0,100),1,2,8,cvPoint(0,0)); } } cvNamedWindow ("Contour", 1); cvShowImage ("Contour", dst); cvWaitKey (0); cvReleaseMemStorage (&storage); cvReleaseImage (&src); cvReleaseImage (&img); cvReleaseImage (&dst); return 0; }
#include<cv.h> #include<highgui.h> int main() { IplImage* src = NULL; IplImage* img = NULL; IplImage* dst = NULL; CvMemStorage* storage = cvCreateMemStorage (0); CvMemStorage* storage1 = cvCreateMemStorage (0); CvSeq* contour = 0; CvSeq* cont; CvSeq* mcont; src = cvLoadImage ("8.jpg", 1); img = cvCreateImage (cvGetSize(src), IPL_DEPTH_8U, 1); dst = cvCreateImage (cvGetSize(src), src->depth, src->nChannels); cvCvtColor (src, img, CV_BGR2GRAY); cvThreshold (img, img, 100, 200, CV_THRESH_BINARY); cvFindContours (img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); if (contour) { CvTreeNodeIterator iterator; cvInitTreeNodeIterator (&iterator, contour, 1); while (0 != (cont = (CvSeq*)cvNextTreeNode (&iterator))) { mcont = cvApproxPoly (cont, sizeof(CvContour), storage1, CV_POLY_APPROX_DP, cvContourPerimeter(cont)*0.02,0); cvDrawContours (dst, mcont, CV_RGB(255,0,0),CV_RGB(0,0,100),1,2,8,cvPoint(0,0)); } } cvNamedWindow ("Contour", 1); cvShowImage ("Contour", dst); cvWaitKey (0); cvReleaseMemStorage (&storage); cvReleaseImage (&src); cvReleaseImage (&img); cvReleaseImage (&dst); return 0; }
源圖: 多邊形逼近的結(jié)果圖: 直接用cvFindContours得到的輪廓圖
對比兩個(gè)圖(輪廓顏色不用管),可以發(fā)現(xiàn)多邊形逼近的結(jié)果確實(shí)相對粗曠點(diǎn)。
|