1. 程式人生 > >理解影象空間域操作

理解影象空間域操作

暑假期間我排除一些干擾,終於得以出來實習。實習時間只有兩個月,但是希望能夠有所收穫吧。真正專心地學習影象知識和C++只有四個月左右的時間,正好利用這次實習的機會來感受一下影象處理行業。學習影象時,基本的練習都是用書本提供的圖片、程式碼,去驗證書上的處理效果,當然結果都是差不多的。在熟悉演算法的同時,卻往往會忘了思考為什麼要用這樣的方法處理。

實習階段,我感受到對影象知識數學原理理解的重要性,以及在此原理上的方法應用的場合。首先,加深了我對空間濾波的理解。灰度變換和空間濾波是空間域處理的兩種主要方法,灰度變換是特殊的空間濾波處理,它的鄰域大小為1*1大小,所以灰度變換也稱為點處理技術,岡薩雷斯的《數字影象處理》一書中用灰度變換實現了影象增強和影象分割兩種應用,目前個人學的還比較淺。灰度變換主要用在影象全域性顯示效果改善上,如冪律變換,直方圖拉伸等,還有就是閾值分割了,這個大家都應該很熟悉。

空間濾波器是影象處理的重點,是影象預處理的重要組成部分。空間濾波器的機理主要是(1)一個鄰域(2)在該鄰域上預定義的操作(一般是濾波器係數和畫素的乘積和)。濾波器產生一個新畫素取代濾波器中心點的畫素值。濾波的本質是加權,即選擇性的提取影象中某些方面的內容,這些內容能夠滿足特定的應用場合。opencv中低通濾波基本的函式有均值濾波blur,高斯濾波GaussianBlur。

均值濾波函式一般只是簡單的用鄰域畫素均值取代中心的畫素值,其模糊效果與鄰域尺寸直接相關,尺寸越大影象越模糊,對細節的保留越少,它適用於提取全域性影象目標中的主要目標,對感興趣物體進行粗略的描述。

高斯濾波屬於加權平均濾波,它的濾波效果與鄰域尺寸和加權係數分佈有關,離中心越近的點加權係數越高,因此高斯模糊可以在過度平滑和保留細節之間折衷,加權係數的獲取可以用對應的sigma值呼叫函式getGaussianKernal。不同的sigma值對應不同的係數分佈,高斯函式影象如下,可見sigma越小,中心畫素值佔的比重越大。


opencv中高斯函式還有一個使用技巧是,可以只設置sigma的值(第四個引數)由opencv決定size的值(第三個引數設為0)。反過來也可以,即只提供尺寸的數值,sigma設為0,由函式自行判斷最適合的sigma值。

       在本次實習的專案中相機標定部分使用自定義的標定板,獲取標定板的角點之前先對影象經行了高斯模糊,這樣可以保證獲取的角點位於兩條直線的交點。可見,高斯濾波適合提取全域性影象細節中的主要細節(自己的理解),所以高斯濾波在許多opencv函式中作為去除噪聲的首選函式。

            

這裡順便提一下中值濾波,中值濾波是非線性濾波器,基於它的濾波原理,其非常適合消除椒鹽噪聲,椒鹽噪聲一般是在訊號通訊過程中受到干擾而產生的。中值濾波有利於儲存邊緣,但會洗去勻質區域的紋理

影象模糊處理類似於積分,相應的有影象的空間微分,微分處理主要是突出影象上灰度的過渡部分,即邊緣。關於影象微分《數字影象處理》講解的非常好,我這裡簡單描述一下一階微分和二階微分的差異,首先,它們的公式還是要記得的,然後 性質也就很清楚了。

一階微分:(1)恆定灰度區域為0,(2)灰度臺階或斜坡開始處不為0,            (3)灰度斜坡點處不為0

二階微分:(1)恆定灰度區域為0,(2)灰度臺階或斜坡開始和結束處不為0,(3)灰度斜坡點出為0

一階微分在影象中主要是用梯度幅值來實現的,所以也可以稱為是幅梯度圖。數字影象的在邊緣上的灰度常常類似於斜坡過渡,這樣導致一階微分產生較粗的邊緣,opencv中常用的一階邊緣函式是sobel,也可以利用自定義的核+filter2D實現影象一階微分運算或者特定方向邊緣檢測的運算。

二階微分在影象處理中使用 拉普拉斯運算元實現,拉普拉斯運算元產生的邊緣較sobel運算元要細,同時很難保證邊緣的連續性,在質量較好,背景單一的影象中可以考慮使用二階微分來計算邊緣。此外,二階微分對孤立點和細線也有較強的響應,具體例項可參考《數字影象和處理》第十章。

以下給出sobel,laplacian運算元處理lena圖的邊緣結果:


程式碼如下:

	void main()
	{
		Mat dst, dst1,dst2,dst3,dst4, dst5;
		Mat src = imread("F:\\程式設計學習\\影象處理\\圖片庫\\影象處理練習圖片庫\\lena.png", 0);
		resize(src, src, Size(600, 600));
		imshow("原圖", src);

		Scharr(src, dst2, CV_16S, 1,0);
		Scharr(src, dst3, CV_16S, 0,1);
		addWeighted(abs(dst2), 0.5,abs(dst3), 0.5, 0, dst4);

		double max = 0;
		minMaxLoc(dst4, 0, &max);  //提取最大值用於歸一化
		cout << max << endl;
		dst4.convertTo(dst5, CV_8U, 255./max);  //轉變影象資料型別,並歸一化
		threshold(dst5, dst5, 40, 255, THRESH_BINARY);//閾值化
		imshow("Scharr合成sobel", dst5);

		Mat dst1_1;
		Laplacian(src, dst1, CV_16S, 3);
		double max2 = 0;
		minMaxLoc(dst1, 0, &max2);
		cout << max << endl;
		dst1.convertTo(dst1_1, CV_8U, 255. / max2);
		threshold(dst1_1, dst1_1, 40, 255, THRESH_BINARY);
		imshow("拉普拉斯圖", dst1_1);
		waitKey(0);
	}

從影象可看出,一階邊緣雖然較粗,但是比較完整、連續的顯示出了影象邊緣,可以很方便的用findContours函式對這些邊緣進行提取。二階微分產生的邊緣較細,斷裂處較多無法很好的進行輪廓提取。實際使用中,梯度閾值法也是邊緣獲取的一種選擇。此外,還有優秀的canny邊緣檢測運算元,它提取的邊緣質量很高,但是也會出現邊緣斷裂的問題,需要自己後續處理。