1.15【OpenCV圖像處理】自定義線性濾波
卷積filter2D()、銳化、邊緣、Robert運算元、Sobel運算元、Laplace運算元 、
1.15.1 卷積概念
卷積:是在每一個圖像塊與某個運算元(核)之間進行的運算。
卷積核(Kernel)本質上是一個固定大小的矩陣數組,其中心點稱為錨點(anchor point)。通常這些卷積都是線性操作,所以又叫線性濾波。(見1.9平滑模糊濾波)
作用:可以做模糊,降低雜訊,增強原信號特徵。
如何用核實現卷積:
把kernel放到像素數組之上,求錨點周圍覆蓋的像素乘積之和(包括錨點)(圖像里要取平均值),用來替換錨點覆蓋下像素點值稱為卷積處理。數學表達如下:
假如你想得到圖像的某個特定位置的卷積值,可用下列方法計算:
1. 將核的錨點放在該特定位置的像素上,同時,核內的其他值與該像素鄰域的各像素重合;
2. 將核內各值與相應像素值相乘,並將乘積相加;
Sum = 8x1+6x1+6x1+2x1+8x1+6x1+2x1+2x1+8x1 各乘積相加
New pixel = sum / (m*n) 取平均值
3. 將所得結果放到與錨點對應的像素上;
4. 再對每個像素依次做卷積,重複上述過程:
幸運的是我們不必自己去實現這些運算,OpenCV為我們提供了函數 filter2D。
1.15.2 常見運算元-邊緣提取、銳化處理
圖像銳化(image sharpening):就是補償圖像的輪廓,增強圖像的邊緣及灰度跳變的部分,使圖像的邊緣、輪廓線以及圖像的細節變的清晰,亦有空域(微分法)處理和頻域(高通濾波)處理兩類方法。微分運算是求信號的變化率,由傅立葉變換的微分性質可知,微分運算具有較強高頻分量作用。從頻率域來考慮,圖像模糊的實質是因為其高頻分量被衰減,因此可以用高通濾波器來使圖像清晰。但要注意能夠進行銳化處理的圖像必須有較高的性噪比,否則銳化後圖像性噪比反而更低,從而使得雜訊增加的比信號還要多,因此一般是先去除或減輕雜訊後再進行銳化處理。
高通濾波 :圖像的邊緣或線條的細節(邊緣)部分與圖像頻譜的高頻分量相對應,因此採用高通濾波讓高頻分量順利通過,並適當抑制中低頻分量,是圖像的細節變得清楚,實現圖像的銳化。
空域微分法 :一階微分主要指梯度模運算,圖像的梯度模值包含了邊界及細節信息。梯度模運算元用於計算梯度模值,通常認為它是邊界提取運算元,具有極值性、位移不變性和旋轉不變性。
由以上梯度的計算可知,在圖像灰度變化較大的邊沿區域其梯度值大,在灰度變化平緩的區域梯度值較小,而在灰度均勻的區域其梯度值為零。
根據梯度值,進而對像素的處理一般有兩種方式:銳化-加強梯度值大的像素灰度值,突出細節(邊界),所以要對邊緣的像素加強(比如直接用梯度值作為像素的灰度或者RGB的分量);邊緣檢測-根據設置的閥值,超過閥值的像素灰度設為0,否則設為255。
根據圖像邊界(細節,邊緣)的拓撲結構,一階微分銳化具體又分為單方向的一階微分銳化和無方向的微分銳化
單方向的一階微分銳化:(水平方向、垂直方向、Kirsch運算元)是指對某個特定方向上的邊緣(細節)信息的進行加強。對於人工設計製造的具有矩形特徵物體(例如:樓房、漢字等)的邊緣的提取很有效。但是,對於不規則形狀(如:人物)的邊緣提取,則存在信息的缺損。
無方向的微分銳化 :(Roberts運算元,Sobel運算元,Prewitt運算元,Laplacian運算元(二元微分))對任何方向上的邊緣信息均敏感的銳化演算法,這類銳化方法要求對邊緣的方向沒有選擇。
圖像邊緣(image border):是圖像中特性(如像素灰度、紋理等)分布的不連續處,圖像周圍特性有階躍變化或屋脊狀變化的那些像素集合,是圖像的重要特徵。即是指其周圍像素灰度變化不連續的那些像素的集合,在圖像合成中單一物體的輪廓叫做邊緣,通常一個邊緣圖像是一個二值圖像。圖像的邊緣部分集中了圖像的大部分信息,一幅圖像的邊緣結構與特點往往是決定圖像特質的重要部分。邊緣廣泛存在於物體與背景之間、物體與物體之間,因此,邊緣是圖像分割、圖像理解及圖像識別的重要特徵。
邊緣檢測:是圖形圖像處理、計算機視覺和機器視覺中的一個基本工具,通常用於特徵提取和特徵檢測,旨在檢測一張數字圖像中有明顯變化的邊緣或者不連續的區域,在一維空間中,類似的操作被稱作步長檢測(step detection)。主要用於增強圖像中的輪廓邊緣、細節以及灰度跳變部分,形成完整的物體邊界,達到將物體從圖像中分離出來或將表示同一物體表面的區域檢測出來的目的。目前為止最通用的方法是檢測亮度值的不連續性,用一階和二階導數檢測的。邊緣檢測的目的是捕捉亮度急劇變化的區域,在一幅圖像中亮度不連續的區域通常有:圖像深度不連續處、圖像(梯度)朝向不連續處、圖像光照(強度)不連續處、紋理變化處。
紅色的kernel稱為卷積核,推導出的很經典的卷積核稱作運算元,以下3種運算元都可用來提取邊緣特徵、梯度計算、銳化處理(原圖與卷積圖混合相加)。
(1)Robert運算元:無方向一階銳化-交叉微分運算元,最常見的運算元,梯度運算元,尋找邊緣。
左圖正對角線的為X方向的運算元,提取圖像正對角線上的邊緣特徵;
右圖斜對角線的為Y方向的運算元,提取圖像副對角線上的邊緣特徵 。
/*自定義法Robert運算元*/nMat kernel_x = (Mat_<char>(2, 2) << 1, 0, 0, -1); //定義Robert的x主對角線方向運算元nfilter2D(src, dst, -1, kernel_x, Point(-1, -1), 0, 0); //濾波/卷積nimshow("Robert_x image", dst); nMat kernel_y = (Mat_<char>(2, 2) << 0, 1, -1, 0); //Robert的y副對角線方向運算元nfilter2D(src, dst2, -1, kernel_y, Point(-1, -1), 0, 0);nimshow("Robert_y image", dst2);n
(2)Sobel運算元:無方向一階銳化,常用在邊緣檢測,獲取的邊緣比Robert強,因為Sobel運算元中間是用2乘,使差異化更大了。(如Canny邊緣檢測最中間的一步,通過Sobel運算元求得梯度)
左圖是X水平方向的運算元,提取圖像水平方向上的邊緣特徵,更體現出X方向上的差異;
右圖是Y垂直方向的運算元,提取圖像垂直方向上的邊緣特徵,更體現出Y方向上的差異 。
/*自定義法Sobel運算元*/nMat Sobel_x = (Mat_<char>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1); //Sobel的x水平方向運算元nfilter2D(src, dst3, -1, Sobel_x, Point(-1, -1), 0, 0);nimshow("Sobel_x image", dst3);nMat Sobel_y = (Mat_<char>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1); //Sobel的y垂直方向運算元nfilter2D(src, dst4, -1, Sobel_y, Point(-1, -1), 0, 0);nimshow("Sobel_y image", dst4);n
(3)拉普拉斯(Laplacian)運算元:二階微分,尋找梯度,尋找邊緣,邊緣檢測運算元,提取的是整體的差異,可獲得圖像主要的輪廓特徵和信息。(跟銳化運算元(銳化掩膜見1.3)的區別是中間的值為5)Laplacian運算元對雜訊比較敏感,Laplacian運算元有一個缺點是它對圖像中的某些邊緣產生雙重響應。所以圖像一般先經過平滑處理,通常把Laplacian運算元和平滑運算元結合起來生成一個新的模板。
左圖是4鄰域的Laplacian運算元,右圖是8鄰域的Laplacian運算元,可獲取整體邊緣(可以進一步處理小於一定值的給去掉,對邊緣再進行加強),:
/*自定義法拉普拉斯運算元*/nMat Laplacian = (Mat_<char>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0); //Laplace的整體方向運算元nfilter2D(src, dst5, -1, Laplace, Point(-1, -1), 0, 0);nimshow("Laplacianimage", dst5);n
銳化:將原圖像和拉普拉斯圖像疊加在一起的簡單方法,可以復原背景特性並保持拉普拉斯銳化處理的效果:
addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst); //圖像混合API,權重相加n
(4)Prewitt運算元
Priwitt銳化:無方向一階銳化在一個方向求微分,而在另一個方向求平均,因而對雜訊相對不敏感,有抑制雜訊作用。但是像素平均相當於對圖像的低通濾波,所以Prewitt運算元對邊緣的定位不如Roberts運算元。
特點:與Sobel相比,有一定的抗干擾性,圖像效果比較乾淨。
1.15.3 自定義卷積模糊
二維線性濾波filter2D,對圖像做卷積:
filter2D(
Mat src, //輸入圖像
Mat dst, // 模糊圖像
int depth, // 圖像深度32/8,直接寫-1表示與輸入圖深度一致
Mat kernel, // 卷積核/模板/運算元 ,模板定義時大小要是奇數
Point anchor, // 錨點位置,寫Point(-1, -1)自動找到中心位置
double delta // 計算出來的像素+delta
)
如下步驟自定義濾波器:
第一行代碼設置核的大小:為[5,13]範圍內的奇數。
第二行代碼歸一化濾波器的核:把1填充進矩陣,並執行歸一化(除以矩陣元素數),以構造出所用的核。
第三行代碼使用函數filter2D生成濾波器。
// 更新歸一化塊濾波器的核大小nksize = 4 + (index % 5)* 2 + 1; //設置核大小,index取余運算,5、7、9、11、13循環,模糊程度逐漸加深nMat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize*ksize); //定義全是1的32位卷積核n// 使用濾波器nfilter2D(src, dst6, -1, kernel, Point(-1, -1),0 ,0);n
其中 kernel是可以自定義的卷積核,對圖像執行歸一化塊濾波器 。舉例來說,如果該濾波器核的大小為 size=3 ,則它會像下面這樣:
程序將執行核的大小分別為5、7、9、11、13的濾波器運算,該濾波器每一種核的輸出將在屏幕上顯示500毫秒,動態漸進模糊例子見完整程序。
完整程序:
/*1.15 自定義線性濾波*/n#include <opencv2/opencv.hpp>n#include <iostream> n#include <math.h>nusing namespace cv; //使用cv命名空間nnint main(int argc, char** argv) { //argc 表示命令行輸入參數的個數(以空白符分隔),argv中存儲了所有的命令行參數nMat src, dst, dst2, dst3, dst4, dst5, dst6;n src = imread("E:/OpenCV/testimage/test7.jpg");nif (src.empty()) {n printf("could not load image...n");nreturn -1;n }n namedWindow("input image", CV_WINDOW_AUTOSIZE);n imshow("input image", src);nn/*Robert運算元*/nMat kernel_x = (Mat_<char>(2, 2) << 1, 0, 0, -1); //Robert的x主對角線方向運算元n filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0, 0); //濾波/卷積n imshow("Robert_x image", dst); nMat kernel_y = (Mat_<char>(2, 2) << 0, 1, -1, 0); //Robert的y副對角線方向運算元n filter2D(src, dst2, -1, kernel_y, Point(-1, -1), 0, 0);n imshow("Robert_y image", dst2);nn/*Sobel運算元*/nMat Sobel_x = (Mat_<char>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1); //Sobel的x水平方向運算元n filter2D(src, dst3, -1, Sobel_x, Point(-1, -1), 0, 0);n imshow("Sobel_x image", dst3);nMat Sobel_y = (Mat_<char>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1); //Sobel的y垂直方向運算元n filter2D(src, dst4, -1, Sobel_y, Point(-1, -1), 0, 0);n imshow("Sobel_y image", dst4);nn/*拉普拉斯運算元*/nMat Laplace = (Mat_<char>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0); //Laplace的整體方向運算元n filter2D(src, dst5, -1, Laplace, Point(-1, -1), 0, 0);n imshow("Laplace image", dst5);nn/*自定義卷積模糊,漸進模糊例子*/n // 初始化濾波器參數nint c = 0;nint index = 0;nint ksize = 3;n // 循環 - 每隔0.5秒,用一個不同的核來對圖像進行濾波nwhile (true) {nc = waitKey(500); //500ms,每0.5s模糊一次n if ((char)c == 27){ //轉型,27代表ESC退出鍵nbreak; //退出n }n// 更新歸一化塊濾波器的核大小n ksize = 4 + (index % 5)* 2 + 1; //設置核大小,index取余運算,5、7、9、11、13循環,模糊程度逐漸加深nMat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize*ksize); //定義全是1的32位卷積核n // 使用濾波器n filter2D(src, dst6, -1, kernel, Point(-1, -1), 0, 0);n index++; //每0.5s加一次,模糊程度加深一次n imshow("custom blur image", dst6);n }nn waitKey(0);nreturn 0;n}n
運行結果:
推薦閱讀:
※[轉自github]基於深度學習的醫療影像論文匯總(Deep Learning Papers on Medical Image Analysis)
※《SegNet: A Deep ConvolutionalnEncoder-Decoder Architecture for ImagenSegmentation》論文筆記
※FDDB和LFW數據集淺析及刷分心得
※ImageNet冠軍領隊帶你入門計算機視覺:監督學習與神經網路的簡單實現