本節爲opencv數字圖像處理(12):圖像復原與重建的第三小節,逆濾波、維納濾波、約束最小二乘方濾波和幾何均值濾波,主要包括:四種濾波復原圖像的數學推導以及維納濾波的C++實現。
1. 逆濾波器
emsp; 若退化函數已知或可以得到一個估計,最簡單的圖像復原方法就是直接做逆濾波,用退化函數除退化圖像的傅立葉變換來計算原始圖形的傅立葉變換的估計即:
展開計算爲:
如果退化噪聲爲0或很小,噪聲就會支配估計值,這時候經常需要限制濾波的頻率,使其接近原點。
1. 最小均方誤差濾波/維納濾波
這種濾波方法建立在圖像和噪聲都是隨機變量的基礎上,目標是找出未污染圖像的一個估計,使它們之間的均方誤差最小,誤差度量由下式給出:
假設噪聲和圖像不相關,二者中有一個有零均值且估計中的灰度級是退化圖像中灰度級的線性函數,則誤差函數的最小值在頻率與中由下式給出:
這個結果就是維納濾波,方括號中的項組成的濾波器稱爲最小均方差濾波器或最小二乘誤差濾波器。式子中:退化函數;=的複共軛;;噪聲的功率譜。
如果噪聲爲0,那麼噪聲功率譜消失,維納濾波簡化爲逆濾波。
信噪比是一個基於噪聲和未退化圖像的功率譜爲基礎的,頻率域可用下式近似:
攜帶低噪聲的圖像SNR較高,是表徵復原算法的性能的一個重要量度。
均方誤差也可以這樣描述:
而如果把復原圖像考慮爲“信號”,復原圖像和原圖像的差考慮爲噪聲,則空間域中信噪比可定義如下:
和越接近,這個比值越大。【這個定量與感覺上的圖像質量沒有很好的必然關係】
當處理白噪聲時是一個常數,但是未退化圖像和噪聲的功率譜通常未知或不能估計,則可用下式近似:
C++實現來自這篇博文:
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta);
void fftshift(const Mat& inputImg, Mat& outputImg);
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr);
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma = 5.0, double beta = 0.2);
int LEN = 50;
int THETA = 360;
int snr = 8000;
Mat imgIn;
Rect roi;
static void onChange(int pos, void* userInput);
int main(int argc, char* argv[])
{
string strInFileName = "1.JPG";
imgIn = imread(strInFileName, IMREAD_GRAYSCALE);
if (imgIn.empty()) //check whether the image is loaded or not
{
cout << "ERROR : Image cannot be loaded..!!" << endl;
return -1;
}
imshow("src", imgIn);
// it needs to process even image only
roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
imgIn = imgIn(roi);
cv::namedWindow("inverse");
createTrackbar("LEN", "inverse", &LEN, 200, onChange, &imgIn);
onChange(0, 0);
createTrackbar("THETA", "inverse", &THETA, 360, onChange, &imgIn);
onChange(0, 0);
createTrackbar("snr", "inverse", &snr, 10000, onChange, &imgIn);
onChange(0, 0);
imshow("inverse", imgIn);
cv::waitKey(0);
return 0;
}
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta)
{
Mat h(filterSize, CV_32F, Scalar(0));
Point point(filterSize.width / 2, filterSize.height / 2);
ellipse(h, point, Size(0, cvRound(float(len) / 2.0)), 90.0 - theta,
0, 360, Scalar(255), FILLED);
Scalar summa = sum(h);
outputImg = h / summa[0];
Mat tmp;
normalize(outputImg, tmp, 1, 0, NORM_MINMAX);
imshow("psf", tmp);
}
void fftshift(const Mat& inputImg, Mat& outputImg)
{
outputImg = inputImg.clone();
int cx = outputImg.cols / 2;
int cy = outputImg.rows / 2;
Mat q0(outputImg, Rect(0, 0, cx, cy));
Mat q1(outputImg, Rect(cx, 0, cx, cy));
Mat q2(outputImg, Rect(0, cy, cx, cy));
Mat q3(outputImg, Rect(cx, cy, cx, cy));
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
}
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
{
Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
dft(complexI, complexI, DFT_SCALE);
Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
Mat complexH;
merge(planesH, 2, complexH);
Mat complexIH;
mulSpectrums(complexI, complexH, complexIH, 0);
idft(complexIH, complexIH);
split(complexIH, planes);
outputImg = planes[0];
}
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr)
{
Mat h_PSF_shifted;
fftshift(input_h_PSF, h_PSF_shifted);
Mat planes[2] = { Mat_<float>(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
dft(complexI, complexI);
split(complexI, planes);
Mat denom;
pow(abs(planes[0]), 2, denom);
denom += nsr;
divide(planes[0], denom, output_G);
}
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma, double beta)
{
int Nx = inputImg.cols;
int Ny = inputImg.rows;
Mat w1(1, Nx, CV_32F, Scalar(0));
Mat w2(Ny, 1, CV_32F, Scalar(0));
float* p1 = w1.ptr<float>(0);
float* p2 = w2.ptr<float>(0);
float dx = float(2.0 * CV_PI / Nx);
float x = float(-CV_PI);
for (int i = 0; i < Nx; i++)
{
p1[i] = float(0.5 * (tanh((x + gamma / 2) / beta) - tanh((x - gamma / 2) / beta)));
x += dx;
}
float dy = float(2.0 * CV_PI / Ny);
float y = float(-CV_PI);
for (int i = 0; i < Ny; i++)
{
p2[i] = float(0.5 * (tanh((y + gamma / 2) / beta) - tanh((y - gamma / 2) / beta)));
y += dy;
}
Mat w = w2 * w1;
multiply(inputImg, w, outputImg);
}
// Trackbar call back function
static void onChange(int, void* userInput)
{
Mat imgOut;
//Hw calculation (start)
Mat Hw, h;
calcPSF(h, roi.size(), LEN, (double)THETA);
calcWnrFilter(h, Hw, 1.0 / double(snr));
//Hw calculation (stop)
imgIn.convertTo(imgIn, CV_32F);
edgetaper(imgIn, imgIn);
// filtering (start)
filter2DFreq(imgIn(roi), imgOut, Hw);
// filtering (stop)
imgOut.convertTo(imgOut, CV_8U);
normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
// imwrite("result.jpg", imgOut);
imshow("inverse", imgOut);
}
2. 約束最小二乘方濾波
雖然維納濾波中可以根據上式來進行估計,但是很少得到合適的解,我們將圖像受噪聲污染表達爲如下的矩陣形式:
考慮一個帶約束的最小準則函數,定義如下:
約束爲:
在頻率域這個最佳化問題的解決方案由如下公式給出:
是一個參數,調整以滿足上2式,是函數:
的傅立葉變換,當時最小二乘方濾波退化爲直接逆濾波。
可以交互地手動調整,當然也可以迭代計算,步驟如下。
定義一個殘差向量爲:
由上3式知,是的函數,所以是該參數的函數,可以證明:
是的單調遞增函數,我們需要調整使得:
若,那麼可以嚴格滿足上述約束,具體地,尋找一個值得方法:
- 指定的初始值
- 計算
- 滿足上1式則停止;否則,若,增大,若,則減少。然後返回步驟2。重新計算最佳估計
上面步驟中,爲了計算,由上式3【從此處向上數第三個列出的公式】有:
計算的傅里葉反變換得,有:
計算的話,首先考慮正負圖像的噪聲方差:
其中:
爲樣本均值。注意到上2式【從此處向上數第二個列出的公式】的雙重求和即爲,給出如下表達式:
也就是說,僅僅使用噪聲的均值和方差的知識,就可以實現一個最佳復原算法,但必須假設噪聲和圖像灰度值不相關。但是需要指出,約束最小二乘方意義小的最佳復原並不是視覺效果上最好。
3. 幾何均值濾波
幾何均值濾波是維納濾波的推廣:
當,退化爲直接逆濾波,當,參數維納濾波器,參數維納濾波器在時還原爲標準的維納濾波器。如果,則濾波器變成相同冪次的兩個量的積,也就是幾何均值的定義。當,隨着減小到以下,濾波器性能越來越接近逆濾波器,增大到以上,越來越接近維納濾波器。當且時,稱爲譜均衡濾波器。
歡迎掃描二維碼關注微信公衆號 深度學習與數學 [每天獲取免費的大數據、AI等相關的學習資源、經典和最新的深度學習相關的論文研讀,算法和其他互聯網技能的學習,概率論、線性代數等高等數學知識的回顧]