1. 程式人生 > >【OpenCV學習筆記 004】 影象的縮放、Canny邊緣檢測和影象的二值化

【OpenCV學習筆記 004】 影象的縮放、Canny邊緣檢測和影象的二值化

一、影象的縮放

本篇將介紹使用OpenCV來縮放圖片。首先介紹幾個關鍵函式——cvResize和cvCreateImage

1.主要函式介紹

1.1 cvResize

函式功能:影象大小變換

函式原型:

voidcvResize(

  const CvArr* src,

  CvArr* dst,

  intinterpolation=CV_INTER_LINEAR

);

函式說明:

第一個引數表示輸入影象。

第二個引數表示輸出影象。

第三個引數表示插值方法,可以有以下四種:

CV_INTER_NN - 最近鄰插值,

CV_INTER_LINEAR - 雙線性插值 (預設使用)

CV_INTER_AREA - 使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現。當影象放大時,類似於 CV_INTER_NN 方法..

CV_INTER_CUBIC - 立方插值.

這個函式在功能上與Win32 API中的StretchBlt()函式類似。

1.2 cvCreateImage

函式功能:建立影象

函式原型:

IplImage* cvCreateImage(CvSize size, intdepth,intchannels);

函式說明:

第一個引數表示影象的大小。

第二個引數表示影象的深度,可以為IPL_DEPTH_8U,IPL_DEPTH_16U等等。

第三個引數表示影象的通道數。

2. 示例程式程式碼

有了這二個函式後,不難寫出程式碼:

//縮放影象檔案
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
//隱藏控制檯視窗
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  

int main(){

	const char* psrcImageName = "monster.jpg";	
	const char* pdstImageName = "smallMonster.jpg";
	const char* psrcImageTitle = "大怪獸";	
	const char* pdstImageTitle = "小怪獸";

	double fScale = 0.18;	//縮放倍數
	CvSize czSize;	//目標影象尺寸

	//從檔案中讀取影象
	IplImage *psrcImage = cvLoadImage(psrcImageName, CV_LOAD_IMAGE_UNCHANGED);
	IplImage *pdstImage = NULL;

	//計算目標影象大小  
	czSize.width = psrcImage->width * fScale;
	czSize.height = psrcImage->height * fScale;

	//建立影象並縮放  
	pdstImage = cvCreateImage(czSize, psrcImage->depth, psrcImage->nChannels);
	cvResize(psrcImage, pdstImage, CV_INTER_AREA);

	//建立視窗  
	cvNamedWindow(psrcImageTitle, CV_WINDOW_AUTOSIZE);
	cvNamedWindow(pdstImageTitle, CV_WINDOW_AUTOSIZE);

	//在指定視窗中顯示影象  
	cvShowImage(psrcImageTitle, psrcImage);
	cvShowImage(pdstImageTitle, pdstImage);

	//儲存圖片  
	cvSaveImage(pdstImageName, pdstImage);
	//等待按鍵事件  
	cvWaitKey();

	cvDestroyWindow(psrcImageTitle);
	cvDestroyWindow(pdstImageTitle);
	cvReleaseImage(&psrcImage);
	cvReleaseImage(&pdstImage);
	return 0;
		
}
程式執行結果如下:

二、Canny邊緣檢測

影象的邊緣檢測的原理是檢測出影象中所有灰度值變化較大的點,而且這些點連線起來就構成了若干線條,這些線條就可以稱為影象的邊緣。
    Canny邊緣檢測運算元是John F. Canny於 1986 年開發出來的一個多級邊緣檢測演算法。Canny 邊緣檢測的數學原理和演算法實現這裡就不說了,有興趣的讀者可以查閱專業書籍,本文主要介紹如何在OpenCV中對影象進行Canny 邊緣檢測,下面就來看看這個函式的原型。

1.主要函式

1.1 cvCanny
函式功能:採用Canny方法對影象進行邊緣檢測
函式原型:

/* Runs canny edge detector */
CVAPI(void)  cvCanny( const CvArr* image, CvArr* edges, double threshold1,
                      double threshold2, int  aperture_size CV_DEFAULT(3) );
函式說明:
第一個引數表示輸入影象,必須為單通道灰度圖。
第二個引數表示輸出的邊緣影象,為單通道黑白圖。
第三個引數和第四個引數表示閾值,這二個閾值中當中的小閾值用來控制邊緣連線,大的閾值用來控制強邊緣的初始分割即如果一個畫素的梯度大與上限值,則被認為是邊緣畫素,如果小於下限閾值,則被拋棄。如果該點的梯度在兩者之間則當這個點與高於上限值的畫素點連線時我們才保留,否則刪除。
第五個引數表示Sobel 運算元大小,預設為3即表示一個3*3的矩陣。Sobel 運算元與高斯拉普拉斯運算元都是常用的邊緣運算元,詳細的數學原理可以查閱專業書籍。
為了更好的使用cvCanny()函式,下面再介紹二個實用的函式,這二個函式對後面的程式實現非常有幫助。
1.2 cvCreateTrackbar
函式功能:建立trackbar並新增到指定視窗
函式原型:
/* create trackbar and display it on top of given window, set callback */
CVAPI(int) cvCreateTrackbar( const char* trackbar_name, const char* window_name,
                             int* value, int count, CvTrackbarCallback on_change CV_DEFAULT(NULL));
函式說明:
第一個引數表示該trackbar的名稱。
第二個引數表示視窗名稱,該trackbar將顯示在這個視窗內。
第三個引數表示建立時滑塊的位置。
第四個引數表示滑塊位置的最大值,最小值固定為0。
第五個引數表示回撥函式。當滑塊位置有變化時,系統會呼叫該回調函式。
注:被建立的trackbar預設顯示在指定視窗的頂端,可以通過函式cvGetTrackbarPos()來獲取trackbar顯示的位置資訊,以及通過函式cvSetTrackbarPos()來重新設定trackbar的顯示位置。
1.3  CvTrackbarCallback
函式功能:cvCreateTrackbar()函式所使用的回撥函式
函式定義:
typedef void (CV_CDECL *CvTrackbarCallback)(int pos)
函式說明:
當trackbar位置被改變的時,系統會呼叫這個回撥函式,並將引數pos設定為表示trackbar位置的數值。

2.示例程式程式碼

//影象的Canny邊緣檢測  
#include <opencv2/opencv.hpp>  
using namespace std;
IplImage *g_pSrcImage, *g_pCannyImg;
const char *pstrWindowsCannyTitle = "邊緣檢測圖";
//cvCreateTrackbar的回撥函式  
void on_trackbar(int threshold)
{
	//canny邊緣檢測  
	cvCanny(g_pSrcImage, g_pCannyImg, threshold, threshold * 3, 3);
	cvShowImage(pstrWindowsCannyTitle, g_pCannyImg);
}
int main()
{
	const char *pstrImageName = "girl.jpg";
	const char *pstrWindowsSrcTitle = "原圖";
	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);

	//建立視窗  
	cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
	cvNamedWindow(pstrWindowsCannyTitle, CV_WINDOW_AUTOSIZE);

	//建立滑動條  
	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運算元對影象進行邊緣檢測。與邊緣檢測相比,輪廓檢測有時能更好的反映影象的內容。而要對影象進行輪廓檢測,則必須要先對影象進行二值化,影象的二值化就是將影象上的畫素點的灰度值設定為0或255,這樣將使整個影象呈現出明顯的黑白效果。在數字影象處理中,二值影象佔有非常重要的地位,影象的二值化使影象中資料量大為減少,從而能凸顯出目標的輪廓。

1.關鍵函式介紹

下面就介紹OpenCV中對影象進行二值化的關鍵函式——cvThreshold()。
函式功能:採用Canny方法對影象進行邊緣檢測函式原型:

CVAPI(double)  cvThreshold( const CvArr*  src, CvArr*  dst,
                            double  threshold, double  max_value,
                            int threshold_type );
函式說明:
第一個引數表示輸入影象,必須為單通道灰度圖。
第二個引數表示輸出的邊緣影象,為單通道黑白圖。
第三個引數表示閾值
第四個引數表示最大值。
第五個引數表示運算方法。
/* 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 */
};
2.示例程式碼
#include <opencv2/opencv.hpp>  
using namespace std;


IplImage *g_pGrayImage = NULL;
IplImage *g_pBinaryImage = NULL;
const char *pstrWindowsBinaryTitle = "二值圖";

void on_trackbar(int pos)
{
	// 轉為二值圖  
	cvThreshold(g_pGrayImage, g_pBinaryImage, pos, 255, CV_THRESH_BINARY);
	// 顯示二值圖  
	cvShowImage(pstrWindowsBinaryTitle, g_pBinaryImage);
}

int main(int argc, char** argv)
{
	const char *pstrWindowsSrcTitle = "原圖";
	const char *pstrWindowsToolBarName = "二值圖閾值";

	// 從檔案中載入原圖  
	IplImage *pSrcImage = cvLoadImage("Baboon.jpg", CV_LOAD_IMAGE_UNCHANGED);

	// 轉為灰度圖  
	g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
	cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);

	// 建立二值圖  
	g_pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1);

	// 顯示原圖  
	cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
	cvShowImage(pstrWindowsSrcTitle, pSrcImage);

	// 建立二值圖視窗  
	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;
}
執行結果如下所示,自己動手除錯下閾值大小,看看生成的二值圖有什麼變化。

OpenCV還有個cvAdaptiveThreshold()函式,這個函式會使用Otsu演算法(大律法或最大類間方差法)(注1)來計算出一個全域性閾值,然後根據這個閾值進行二值化。當然直接使用cvCanny()函式也可以對影象進行二值化(想到怎麼傳引數了嗎?)。

注1.呼叫cvThreshold()時傳入引數CV_THRESH_OTSU也是使用Otsu演算法來自動生成一個閾值。

Reference:

http://blog.csdn.net/morewindows/article/details/8239560

http://blog.csdn.net/morewindows/article/details/8239625

http://blog.csdn.net/morewindows/article/details/8239678