点击上方↑↑↑“OpenCV学堂”关注我
基于OpenCV4演示,关注我们,一起学习!
觉得不错,请点【在看】支持!
基本原理
图像退化模型在频率域的表示如下:
其中
S表示退化(模糊)图像频谱
H表示角点扩散功能(PSF)的频谱响应
U 表示原真实图像的频谱
N表示叠加的频谱噪声
圆形的PSF因为只有一个半径参数R,是一个非常好的失焦畸变近似,所以算法采用圆形的PSF。
模糊恢复,模板恢复本质是获得一个对原图的近似估算图像,在频率域可以表示如下:
其中SNR表示信噪比,因此可以基于维纳滤波恢复离焦图像,实现图像反模糊。这个过程最终重要的两个参数,分别是半径R与信噪比SNR,在反模糊图像时候,要先尝试调整R,然后再尝试调整SNR。
代码实现
计算PSF的代码如下:
void calcPSF(Mat& outputImg, Size filterSize, int R)
{
Mat h(filterSize, CV_32F, Scalar(0));
Point point(filterSize.width / 2, filterSize.height / 2);
circle(h, point, R, 255, -1, 8);
Scalar summa = sum(h);
outputImg = h / summa[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 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 adjust_filter(int, void*) {
Mat imgOut;
// 偶数处理,神级操作
Rect roi = Rect(0, 0, src.cols & -2, src.rows & -2);
printf("roi.x=%d, y=%d, w=%d, h=%d", roi.x, roi.y, roi.width, roi.height);
// 生成PSF与维纳滤波器
Mat Hw, h;
calcPSF(h, roi.size(), adjust_r);
calcWnrFilter(h, Hw, 1.0 / double(snr));
// 反模糊
filter2DFreq(src(roi), imgOut, Hw);
// 归一化显示
imgOut.convertTo(imgOut, CV_8U);
normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
imwrite("D:/deblur_result.jpg", imgOut);
imshow("deblur_result", imgOut);
}
图像傅里叶变换
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);
}
运行效果
原图: 肉眼无法辨识
R=10, SNR=40时候的运行效果:基本肉眼可以辨识!
欢迎扫码加入【OpenCV研习社】
往期精选
二值图像分析案例精选
OpenCV4 视频教程来了....
OpenCV视频分析背景提取与前景提取
|