在圖像處理中,通過當前位置的鄰域像素計算新的像素值是很常見的操作。當鄰域包含圖像的上幾行和下幾行時,就需要同時掃描圖像的若干行,這就是圖像的鄰域操作了。至於模板操作是實現空間濾波的基礎,通常是使用一個模板(一個的矩形)滑過整幅圖像產生新的像素。下面介紹通過使用OpenCV2實現Laplace算子銳化圖像,來介紹OpenCV2中對鄰域和模板的操作。
銳化處理主要的目的是突出灰度的過渡部分,通常由微分來定義和實現銳化算子的各種方法。Laplace算子是最賤的各向同性微分算子,常用的Laplace模板如下:
使用Laplace算子銳化圖像時需要注意模板中心的系數,如果中心系數是負的,就需要將原圖像減去經過Laplace算子處理后的圖像,得到銳化后的結果。如果中心系數是正的,則相反。
銳化圖像時不能以In-Place的方式來完成,需要提供一個輸出圖像。在對圖像遍歷時使用兩個3個指針:一個指向當前行,一個指向當前行的上一行,一個指向當前行的下一行。而且,由於每個像素值的計算都需要它的上下左右四個相鄰像素,所以無法對圖像的邊界進行計算,需要另作處理。具體實現代碼如下:
1: /*
2: 0 -1 0
3: -1 4 -1
4: 0 -1 0
5: */
6: void sharpen(const Mat & image,Mat & result)
7: {
8: CV_Assert(image.depth() == CV_8U);
9:
10: result.create(image.size(),image.type());
11:
12: const int channels = image.channels() ;
13: for(int j = 1 ;j < image.rows - 1 ; j ++){
14: const uchar * previous = image.ptr<const uchar>(j - 1) ; // 當前行的上一行
15: const uchar * current = image.ptr<const uchar>(j) ; //當前行
16: const uchar * next = image.ptr<const uchar>(j + 1) ; //當前行的下一行
17:
18: uchar * output = result.ptr<uchar>(j) ; // 輸出行
19: for(int i = channels ; i < channels * (image.cols - 1) ; i ++) {
20: * output ++ = saturate_cast<uchar>( 4 * current[i] - previous[i] - next[i] - current[i - channels] - current[ i + channels]) ;
21: }
22: }
23:
24: //對圖像邊界進行處理
25: //邊界像素設置為0
26: result.row(0).setTo(Scalar(0));
27: result.row(result.rows-1).setTo(Scalar(0)) ;
28: result.col(0).setTo(Scalar(0));
29: result.col(result.cols-1).setTo(Scalar(0));
30: }
這里使用指針遍歷整個圖像,使用三個指針同時掃描圖像的三行,另外使用一個指針指向輸出行。在計算輸出像素時,使用模板函數saturate_cast<uchar>對計算結果進行調整。這是因為對像素值的計算有可能導致結果超出了像素允許的范圍,即小於0或者大於255,當計算結果是浮點數時,該函數會將結果取整至最近的整數。
由於邊界的像素沒有完整的鄰域,無法使用模板計算其值,需要單獨處理。這里只是簡單的將其值設為0.
測試代碼:
1: Mat image = imread("d:\\lenna.jpg") ;
2: Mat result ;
3: sharpen(image,result) ;
4: imwrite("d:\\lenna1.jpg",result) ;
5: imwrite("d:\\lenna2.jpg",image + result) ;
結果如下:
這里需要說明,在OpenCV2中對Mat進行了大量的運算符重載,例如上面,兩幅圖像相加直接使用image + result即可。另外,如位操作符:&,|,^,~;函數max,min,abs;比較操作符:<,<=,>,>=,比較操作符返回一個8位二進制圖像。另外矩陣乘法m1 * m2,矩陣求逆 m1.inv(),矩陣轉置m.t(),舉證的行列式m.determinate(),向量的模v.norm(),向量叉乘v.corss(v1),向量的點乘v.dot(v1)等。
由於使用模板實現空間濾波在圖像處理中非常的常用,在OpenCV中專門定義了一個特殊的函數來完成該處理:
filter2D.
函數原型:
1: void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )
參數:
src,輸入圖像
dst 輸出圖像,和輸入圖像有相同的大小和通道
ddepth 輸出圖像的depth,如果為負數,則和輸入圖像的depth相同
kernel 模板
anchor 進行卷積運算的中心位置,默認的是kernel的中心
delta 可選值,加到輸出像素上的值
bordertype 對輸出圖像邊界的處理。
使用filter2D實現Laplace算子
1: Mat kern = (Mat_<char> (3,3) << 1,1,1,
2: 1,-8,1,
3: 1,1,1) ;
4: filter2D(image,result,image.depth(),kern) ;
這里只需要定義好kernel調用filter2D即可,而且OpenCV對該函數進行了優化,其效率要比上面使用指針實現的要高。