【數字影象處理】線性濾波、最大值濾波,最小值濾波、中值濾波、高頻補償濾波(vs2017+openCV)
一、實驗原理
1、線性濾波
① 不管是低通線性濾波還是高通線性濾波原理都是一樣的,用圖一所示的濾波器模板進行加權處理,將最終得到的R值賦給w5對應的畫素。 ②低通線性濾波和高通線性濾波不同之處就在於:
- 低通線性濾波w1+w2+…+w9 = 1,且w1~w9全都>=0
- 高通線性濾波w1+w2+…+w9 =0.二階導數濾波(拉普拉斯)屬於線性濾波。
③高頻補償濾波 原影象減去模糊處理後的影象,再將相減得到的模板乘以一個權重加給原影象。公式表式如下:
2、非線性濾波
①中值濾波
- 沒有模板引數,但有模板大小,用模板去濾波,每次不再是求加權和,而是將模板內的灰度值進行排序,取中間位置的灰度值賦給w5.這種濾波器對處理椒鹽噪聲非常有用
②最大值濾波
- 和中值濾波的不同,最大值濾波器取模板內最大的灰度值賦給w5.並且不需要排序,可以直接求最大值。
③最小值濾波
- 和最大值濾波器相反,取最小灰度值賦給w5
④一階導數濾波
- 首先進行線性濾波再將濾波得到的結果進行模和操作(非線性操作)
3、標定方式
在影象進行銳化處理的過程中,有可能會出現影象灰度值大於255或者小於0的情況,所以遇到這種情況我們需要將影象的灰度值通過一些處理控制在0-255之間。在這次實驗中我原本使用了課本中的標定方法,但是結果卻很讓人失望,所以最後直接改用截斷的方法處理,即:大於255都置為255,小於0的都置為0。進一步說明在結果分析中。
二、設計思路
1、線性濾波器
從實驗原理我們可以知道,線性濾波原理都一樣,也就是說均值濾波器、基本高通濾波器、拉普拉斯濾波器、高頻補償濾波器原理都一樣,但是我們可以只用一個函式來實現這幾個濾波器嗎?當然不行請看下面的分析:
-
①不管是低通還是高通,線性濾波都是一個原理為什麼使用了兩個函式分別去實現低通和高通? 答: 低通濾波器的模板各系數之和為1,且他們的取值都在0-1之間,所以處理後得到的加權平均值不會發生溢位的狀況,得到結果後可以直接以Mat的形式儲存。 而高通濾波器不一樣,它的模板各個係數之和為0,有正有負,雖然也是線性濾波,但是處理後可能發生溢位的狀況。比如下面的情形:
左上為影象,右上為濾波器,濾波後-4x255,發生溢位。如果是線性(均值)濾波器則255/9,不會發生溢位。 說了這些其實就是說高通濾波器要多做一步溢位的處理。由於Mat中如果發生了溢位,那麼儲存的就不在是計算出的那個負數或者超過255的數。所以我們必須用一個int型別的陣列(源程式中用名為imageMat)來儲存計算後發生溢位的影象,然後在用演算法(源程式中函式名為scale)處理這個發生溢位的影象,讓他的所有值在0-255內。
-
②這樣一來:低通線性濾波器一個函式,基本高通濾波器和拉普拉斯濾波器可以使用同一個函式,高頻補償濾波器一個函式。其實在真正程式碼實現的時候基本高通濾波器和拉普拉斯濾波器也分別使用了一個函式,因為拉普拉斯變換後的影象,加上原影象,這樣拉普拉斯處理的效果能夠得到很好的體現。
-
③高頻補償濾波器:是對基本線性濾波器的一個線性使用,所以另寫函式,呼叫已經寫好的一些函式。
2、 邊緣處理的映象演算法是如何實現的?
答:這個問題很有價值嗎?我認為還是值得一提的,因為我在寫程式的時候,這部分還是挺費力的。
- ①首先新建影象長和寬都要加上濾波器大小減一(按照所有濾波器都是奇數長度來處理)。
- ②在處理四個角落,因為他們最好處理,直接賦值128;然後中間部分也好處理把原影象搬過來就可以。最麻煩的就是四個邊緣。特別是右邊緣和下邊緣,下面直說這兩個邊緣的處理: 我們要清楚的是,輸入是一個正常影象,輸出是一個經過邊緣對稱的影象,現在我們已經有了這個輸出影象的框架,接下來只需要填滿這個框架。 【處理前影象】 就下邊緣來說,我們現在的目的是根據眼前這個影象中的座標,求出原影象中對應的映象後的座標。首先綠色那塊是搬過來的原影象,下邊緣是藍色箭頭所指的那條線開始的。假如說我們現在要求藍色箭頭指向的位置的畫素值。首先我們減去邊緣寬度的2倍,那麼座標現在把這個座標放在原影象中對應的應該是橙色箭頭所在位置,如果我們把這個位置的畫素值賦給藍色箭頭處的畫素,那麼我們最後得到的影象將不是映象處理的,而是直接平移原影象邊緣一部分到新影象邊緣。所以為了得到映象結果。我們進行如下處理:把我們得到的目前的這個座標沿著原影象中從下邊緣開始寬度為新加邊緣寬度的對稱軸,做對稱,這樣我們就到達了黑色箭頭指向的位置,剛好就是我們需要的映象位置。
- ③求對稱點的方法: 首先算出對稱軸的座標,然後用對稱軸座標減去該點座標,再將得到的結果加到對稱軸座標上去。
3、 關於Mat傳值後處理,會影響原影象的說明?
- 在做給影象加椒鹽噪聲的時候,遇到了一個問題,就是將原影象以傳值的方 式傳進函式,然後加上椒鹽噪聲返回出來,發現後面使用的所用影象都加上了椒鹽噪聲。但是我是按值傳遞進去的啊,不應該影響原影象,百度了一下,opencv按值傳遞也會影響原影象,解決辦法是,使用原影象的拷貝時,需要用clone()或者copyTo()函式,而不能直接“=”賦值。
三、實驗過程
1、椒鹽噪聲
為了體現出中值濾波器處理椒鹽噪聲的強大功能,另寫了一個函式Mat addSaltAndPepperNoise(Mat image_in, float rate = 0.2);
來給影象imag_in加上椒鹽噪聲,然後返回出來。
2、特別注意
- 為了方便使用不同的模板進行拉普拉斯處理,使用瞭如下方法:
需要傳入將要進行Laplacian處理的圖片,以及選擇要用哪個模板處理,具體如下:
Mat Spafilt::laplacian(Mat &image_in, int id);
Image_in
是需要傳進去的待處理圖片,id
是選擇用哪個模板處理(在1-4中選),具體模板選擇如下:
3、設計檔案
spatial_filtering.cpp檔案500多行放在這裡不是很好,有需要的同學可以到這裡下載,其實有了設計檔案以及詳細的註釋,自己寫實現部分也不是很難。 【spatial_filtering.hpp】
#pragma once
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
class Spafilt {
public:
Spafilt();
~Spafilt();
/*
線性低通濾波器
@image_in:將要處理的圖片。
@filter_in[]:使用的濾波器模板。
@sizeOfFilter_in:濾波器大小
@return:處理後的圖片
*/
Mat linearFilter(Mat &image_in, float filter_in[], int sizeOfFilter_in);
/*
給圖片加上椒鹽噪聲
@Image_in:將要處理的圖片
@rate:噪聲程度,建議(0-0.5)
@return: 加上噪聲後的圖片
*/
Mat addSaltAndPepperNoise(Mat image_in, float rate = 0.2);
/*
中值濾波器
@image_in:將要處理的圖片。
@sizeOfFiter_in;濾波器大小
@return:處理後的圖片。
*/
Mat medianFilter(Mat &image_in, int sizeOfFilter_in = 3);
/*
最大值濾波器
@image_in:將要處理的圖片。
@sizeOfFiter_in;濾波器大小
@return:處理後的圖片。
*/
Mat maxFilter(Mat &image_in, int sizeOfFilter_in = 3);
/*
最小值濾波器
@image_in:將要處理的圖片。
@sizeOfFiter_in;濾波器大小
@return:處理後的圖片。
*/
Mat minFilter(Mat &image_in, int sizeOfFilter_in = 3);
/*
基本高通濾波器
@image_in:將要處理的圖片。
@filter_in[]:使用的濾波器模板。
@sizeOfFilter_in:濾波器大小
@return:處理後的圖片
*/
Mat basicHighPassFilter(Mat &image_in, float filter_in[], int sizeOfFilter_in);
/*
羅伯特運算元
@image_in:將要處理的圖片
@return:處理後的圖片
*/
Mat roberts(Mat &image_in);
/*
prewitt運算元
@image_in:將要處理的圖片
@return:處理後的圖片
*/
Mat prewitt(Mat &image_in);
/*
Sobel運算元
*/
Mat sobel(Mat &image_in);
/*
@image_in:將要處理的圖片
@id:選擇使用哪個模板處理,id應該是1-4中的某一整數,詳見實驗報告。
@return: Laplacian處理後的圖片
*/
Mat laplacian(Mat &image_in, int id);
/*
高頻補償濾波器
@k;高頻補償加模板的權重,預設為1
*/
Mat highBoostFilter(Mat &image_in, float k = 1);
/*
@原本是一個協助我除錯程式的函式
@但是最終它擔任了使用截斷方式標定的任務
@在Mat scale(int k = 255);中呼叫了
*/
Mat Debug();
private:
Mat imageSource;//待處理的圖片
Mat imageAfterBorderProcess;//對邊緣進行處理後的圖片。
int* imageMat;//存放經過某些處理後,灰度值超過0-255區間的圖片,等待後續處理或應用
int filterSize;//濾波器大小
float *filter;//濾波器模板
/*
設定濾波器大小及模板,這裡只考慮方形的濾波器(長寬相等)
@size_i:濾波器大小,只能是方形濾波器,傳入任一邊長度
@fil:濾波器模板
*/
void setFilter(int size_i, float *fil);
/*
設定濾波器大小這裡只考慮方形的濾波器(長寬相等)
@size_i:濾波器大小,只能是方形濾波器,傳入任一邊長度
*/
void setFilterSize(int size_i);
/*
設定將要處理的圖片
@image_i:將要處理的圖片
*/
void setImage(Mat &image_i);
/*
對影象的邊緣進行處理
* 處理的是imageSource圖片
* 處理後的結果存放在imageAfterBorderProcess中
* 採用映象處理辦法,角落處的值設為128
*/
void borderProcessing();
/*
* 用來求兩幅影象的帶權的差或者和,
* 可以在影象和影象或影象和陣列之間運算
* 用一個係數來控制加或者減或者其他係數
* 結果放在陣列imageMat中
@coefficient:係數,決定兩幅影象之間進行怎麼樣的操作
@image_in: 若插入該引數,則進行imageSource和image_in之間的運算,
否則進行imageSource和imageMat之間運算
*/
void addOrSubtractOfTwoImage(float coefficient, Mat* image_in = NULL);
/*
* 使用線性濾波器對圖片進行濾波
* 由於高通濾波器模板處理後灰度值有可能溢位,所以結果放在imageMat中,待進一步處理
* 使用私有變數中的filter對imageSource進行濾波
*/
void preHighPassFilter();
/*
用來將陣列內的所有的灰度值標定到指定灰度區間並以Mat的形式返回出來
不修改陣列內容
@引數k:表示標定的上界
下界為零
@return:返回標定後的影象
*/
Mat scale(int k = 255);
/*
非線性高通3x3濾波器
@filterR:大小為3x3的濾波器模板
@filterC:大小為3x3的濾波器模板
@return:處理後經過標定的影象
*/
Mat nonLinearHighPass3x3filter(float* filterR, float* filterC);
};
【main.cpp】
#include "spatial_filtering.h"
int main()
{
Mat image = imread("pic.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//被圖片讀入折磨的情況。
//驗證邊緣處理的正確性
//驗證加和標定的正確性。
//資料型別
imshow("source_image", image);
Spafilt spt;
//線性低通濾波器
float filter[9];
for (int counter = 0; counter < 9; counter++)
filter[counter] = (float)1 / 9;
imshow("linearFilter",spt.linearFilter(image, filter, 3));
//中值濾波器
Mat image_noise = spt.addSaltAndPepperNoise(image, 0.2);
imshow("beforeMedaiFiltering", image_noise);
imshow("afterMedaiFiltering", spt.medianFilter(image_noise, 3));
//最大值濾波器
imshow("maxFilter", spt.maxFilter(image,3));
//最小值濾波器
imshow("mixFilter", spt.minFilter(image, 3));
//基本高通濾波器
float highFilter[9] = { 1, 1, 1, 1, -8, 1, 1, 1, 1};
imshow("BasicHighPassFilter", spt.basicHighPassFilter(image, highFilter, 3));
//Roberts交叉運算元
imshow("Roberts", spt.roberts(image));
//prewitt運算元
imshow("Prewitt", spt.prewitt(image));
//Sobel運算元
imshow("Sobel", spt.sobel(image));
//Laplacian銳化處理
imshow("Laplacian", spt.laplacian(image, 3));
//高頻補償濾波器
imshow("highBoost", spt.highBoostFilter(image, 1));
waitKey(0);
return 0;
}
四、結果分析
1、可疑問題
剛開始標定使用了是課本中介紹的方法,原理如下: 就是找出最大值和最小值, min(f)就是最小值,max(fm)就是極差,當f為最大值的時候,fm = max(fm).這樣fs = k. 當f = min(f)的時候fs = 0.這樣整個影象的灰度值就控制在0-k之間了。 但是我處理後所有用了標定處理的影象顯得非常灰濛濛的,效果不是很好,像下面這樣: 上為原圖,下為高頻補償處理後的影象 仔細分析,出現這個現象的原因很可能是,上式中max(fm)非常大,然後f-fm能夠達到max(fm)的畫素個數又是極個別的。所以導致影象處理以後灰度值集中在了128附近。導致上述結果。所以使用了在影象標定部分說的截斷的方法。
2、結果展示
【原影象】 【均值濾波】 【椒鹽噪聲】 【中值濾波器】 【最大值濾波器】 【最小值濾波】 【基本高通濾波】