影象處理中的一階偏導數和二階偏導數
Laplace運算元和Sobel運算元一樣,屬於空間銳化濾波操作。起本質與前面的Spatial Filter操作大同小異,下面就通過Laplace運算元來介紹一下空間銳化濾波,並對OpenCV中提供的Laplacian函式進行一些說明。
- 數學原理
離散函式導數
離散函式的導數退化成了差分,一維一階差分公式和二階差分公式分別為,
Laplace運算元的差分形式
分別對Laplace運算元x,y兩個方向的二階導數進行差分就得到了離散函式的Laplace運算元。
在一個二維函式f(x,y)中,x,y兩個方向的二階差分分別為,
所以Laplace運算元的差分形式為,
寫成filter mask的形式如下,
0 | 1 | 0 |
1 | -4 | 1 |
0 | 1 | 0 |
1 | 1 | 1 |
1 | -8 | 1 |
1 | 1 | 1 |
注:
有時我們也會見到不同於上述結果的Laplace運算元的filter mask,
0 | -1 | 0 |
-1 | 4 | -1 |
0 | -1 | 0 |
-1 | -1 | -1 |
-1 | 8 | -1 |
-1 | -1 | -1 |
其原因是在定義二階導數的時候採用了相反的定義,這個無關緊要,但是要注意,當用Laplace運算元濾波後的影象與原圖疊加時,混合操作是加還是減因上述的定義而異。
影象的Laplace操作
如同本文開始時說的那樣,將Laplace運算元寫成filter mask後,其操作大同小異於其他的空間濾波操作。將filter mask在原圖上逐行移動,然後mask中數值與其重合的畫素相乘後求和,賦給與mask中心重合的畫素,對影象的第一,和最後的行和列無法做上述操作的畫素賦值零,就得到了拉普拉斯操作結果。
拉普拉斯操作結果與原圖的混合
因為Laplace運算元是二階導數操作,其在強調影象素中灰度不連續的部分的同時也不在強調灰度值連續的部分。這樣會產生一個具有很明顯的灰度邊界,但是沒有足夠特徵的黑色背景。背景特徵可以通過原影象與Laplace運算元操作後的影象混合恢復。用公式,
其中的引數c的取值和上面的兩種mask定義有關,當mask中心的數值取正時c=-1,相反c=1;
- 基於OpenCV的Laplace運算元的計算
OpenCV中Laplacian函式可以實現對影象的Laplace操作,具體用法如下,
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
引數意義為,
- src_gray,輸入影象
- dst,Laplace操作結果
- ddepth,輸出影象深度,因為輸入影象一般為CV_8U,為了避免資料溢位,輸出影象深度應該設定為CV_16S
- kernel_size,filter mask的規模,我們的mask時3x3的,所以這裡應該設定為3
- scale,delta,BORDER_DEFAULT,預設設定就好
基於OpenCV的Laplace運算元模擬程式碼段如下,
//load the Original Image and get some informations Mat src = imread("012.jpg",0); namedWindow("OriginalImage"); imshow("OriginalImage",src); CV_Assert(src.depth() == CV_8U); //OpenCV solution - Laplacian Mat dst,abs_dst_laplace; Laplacian(src,dst,CV_16S,3); convertScaleAbs(dst,abs_dst_laplace); //show the result namedWindow("result_laplacian"); imshow("result_laplacian",abs_dst_laplace);
其中convertScaleAbs函式功能是將CV_16S型的輸出影象轉變成CV_8U型的影象。
模擬結果:
原圖:
Laplace操作結果:
- 基於mask operation原理模擬
Laplace運算元濾波模擬
根據數學原理中介紹的演算法,編寫相應程式碼,進行相關模擬。其中對Laplace操作結果進行了影象拉伸顯示,因為Laplace操作結果的畫素值範圍可能落在了[0,255]之外,而計算機在顯示的時候將賦值全部置為0,大於255的畫素全部顯示成255。
程式碼段如下,
//get some informations of original image int nr = src.rows; int nc = src.cols; int n = nr*nc; int arr[9] = {0}; //scan the whole pixels of original image //and do Laplacian Operation int* table_lap = new int[n]; int* table_orig = new int[n]; int l; for (int i=0;i<n;i++) { table_lap[i] = 0; table_orig[i] = 0; } for (int i=1;i<nr-1;i++) { const uchar* previous = src.ptr<uchar>(i-1); const uchar* current = src.ptr<uchar>(i); const uchar* next = src.ptr<uchar>(i+1); for (int j=1;j<nc-1;j++) { for (int k=0;k<3;k++) { arr[k] = previous[j+k-1]; arr[k+3] = current[j+k-1]; arr[k+6] = next[j+k-1]; } l = nc*i+j; //calculate the location in the table of current pixel Lmaskoperation(table_lap,arr,l); table_orig[l] = arr[4]; } } //pixels scale uchar* La_scaled = new uchar[n]; table_scale(table_lap,La_scaled,n); //padding values Mat LaResult_own; LaResult_own.create(src.size(),src.type()); uchar* p = NULL; for (int i=0;i<nr;i++) { p = LaResult_own.ptr<uchar>(i); for (int j=0;j<nc;j++) { l = nc*i+j; p[j] = La_scaled[l]; } } //show results namedWindow("LaResult_own"); imshow("LaResult_own",LaResult_own);
其中Lmaskoperation是我寫的mask為Laplace mask的mask operation操作函式,函式段如下,
//**********************// //Laplacian mask operation //**********************// void Lmaskoperation(int* table,int* arr,int l) { int tmp[9] = {-1,-1,-1,-1,8,-1,-1,-1,-1}; for (int i=0;i<9;i++) { table[l] = table[l] + tmp[i]*arr[i]; } }
tabel_scale函式就是我寫的影象拉伸函式,將Laplace操作結果拉伸到[0,255],具體函式段如下,
//*****************************// //scale the pixels to [0 255] //*****************************// void table_scale(int* table,uchar* result,int n) { int min = table[0]; int max = table[0]; for (int i=0;i<n;i++) { if(min>table[i]) { min = table[i]; } if(max<table[i]) { max = table[i]; } } for (int i=0;i<n;i++) { result[i] = (uchar)(255*(table[i]-min)/(max-min)); } }
模擬結果,拉伸後Laplace運算元的操作結果
以灰色為主色調的顯示結果就是Laplace運算元操作拉伸後顯示的一大特點。
Laplace濾波影象與原影象的混合
我使用的mask中心值為正,所以混合操作需要原圖減去Laplace濾波影象,程式碼段如下,
//blending with the original image using Eq g(x,y)=f(x,y)+c*Lap(x,y) int* table_blend = new int[n]; for(int i=0;i<n;i++) { table_blend[i] = table_orig[i] - table_lap[i]; if(table_blend[i]<0) { table_blend[i] = 0; } else if (table_blend[i]>255) { table_blend[i] = 255; } } //padding values to blending result Mat Blresult; Blresult.create(src.size(),src.type()); for (int i=0;i<nr;i++) { p = Blresult.ptr<uchar>(i); for(int j=0;j<nc;j++) { l = nc*i+j; p[j] = table_blend[l]; } } //show blending result namedWindow("blending result_laplacian"); imshow("blending result_laplacian",Blresult);
模擬結果:
最後給出岡薩雷斯在介紹Laplacian時所給素材的模擬結果