在對數字圖像進行處理時,我們一般都會在空間域(spatial domain)或者頻域(frequency domain)中進行。所謂“空間域”,實際上指的是圖像本身,在空間域上的操作常常是改變像素點的值,也就是經過一個映射(我們所做的變換,如濾波等),將原來的f(x,y)變換為新的g(x,y)。而“頻域”,它的數學基礎是法國學者傅里葉提出的傅里葉級數和隨后發展起來的傅里葉變換。在這其中起到重要作用的,就是電子計算機的不斷完善和快速傅里葉變換(FFT)算法的提出。這些使得傅里葉變換成為了一中有力的分析和變換工具。就像一列波,我們在時間上觀察,每個時刻的幅值是一個時間的函數。而當我們變換角度,從頻率域上去看,又會發現它是一系列正弦波的疊加,而這些正弦波的頻率都會是某個基波頻率的整數倍。可謂“橫看成嶺側成峰”!
在空間域的操作主要可以分為兩類:第一類是所謂的“圖像強度變換”(Intensity Transform),另一類是所謂的“空間域圖像濾波”(Spatial Filtering)。這兩者的區別主要是處理方法的不同。前者對單個像素點進行操作,例如通過閾值函數實現圖形的二值化,實現灰度平均等。而后者建立在鄰域(neighborhood)的概念上,講究的是利用一個矩陣核(Kernel)對一個小區域進行操作。今天這篇文章主要介紹的是后者,以及如何用OpenCV中的函數去實現。
我們先來看下面的這個公式,以及它的矩陣表示形式:
這個公式到它的矩陣表示形式的轉換相信並不復雜,稍有線性代數知識就可以看懂。而這種操作可以用來進行圖像增強。那么我們如何來使用OpenCV中的函數進行操作呢?我們要用到filter2D這個函數。可以在幫助文檔中查到,filter2D函數的功能是:Convolves an image with the kernel,即如何實現圖像的卷積運算。其函數原型如下:
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT ),可以用下面這個公式來說明:
讓我們用代碼來說明:
#include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/core/core.hpp> #include <opencv/cv.hpp> using namespace std; using namespace cv; int main() { string picName="lena.jpg"; Mat A=imread (picName,CV_LOAD_IMAGE_COLOR); Mat mask=(Mat_<char>(3,3)<<0,-1,0, -1,5,-1, 0,-1,0); Mat B; filter2D (A,B,A.depth (),mask); imshow("A的圖像",A); imshow("B的圖像",B); waitKey (); return 0; }
比較上面這兩幅圖片,是否感覺處理后的圖像對比度更加強烈了呢?
接下來讓我們用這個函數實現一個平滑濾波的功能,我們只要把矩陣核寫成下面這樣:
所以代碼改成了:
#include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/core/core.hpp> #include <opencv/cv.hpp> using namespace std; using namespace cv; int main() { string picName="board.jpg"; Mat A=imread (picName,CV_LOAD_IMAGE_COLOR); //構造矩陣核 Mat mask=Mat::ones (3,3,CV_32FC1); mask/=9; Mat B; filter2D (A,B,A.depth (),mask); //使用了OpenCV提供的平滑濾波函數 Mat C; Size s=Size(3,3); blur(A,C,s); imshow("A的圖像",A); imshow("B的圖像",B); imshow("C的圖像",C); waitKey (); return 0; }
運行結果為:
可以看出,圖像的邊緣變得模糊不清,左上角芯片上的字跡已經不易識別了。