一、概要
本文首先给出图像的维纳滤波器的推导过程用以补充书中缺失的证明,而后在C++的编译环境下,利用opencv构造维纳滤波器实现运动模糊图像的复原,并结合代码给出算法流程,最后在本文末给出全部代码。
二、图像的维纳滤波原理
三、构造退化函数
这部分冈萨雷斯所著的数字信号处理第三版已经给出了建模过程,因此这里直接引用书本上给出的运动模糊函数的推导过程。如下所示:
在这里应当注意:实际操作中往往会将图像的频谱中心化,因此进行图像模拟退化时所使用的退化函数也应当中心化。设原始图像大小为M·N,经过扩充后的大小为(2M)·(2N)。此时退化函数的尺寸也应当为(2M)·(2N),且退化函数应当改写成(中心化):
四、算法流程
1、对原始图像进行傅里叶变换,计算出原始图像f(x,y)的幅度谱和相位谱。即:
该部分代码如下所示:
Mat src = imread("T.jpg");
Mat gray_src;
cvtColor(src, gray_src, CV_BGR2GRAY);
Mat padded = Mat::zeros(2 * gray_src.rows, 2 * gray_src.cols, CV_32FC1);
//图像扩充
for (int row = 0; row < gray_src.rows; row++)
{
for (int col = 0; col < gray_src.cols; col++)
{
padded.at<float>(row, col) = gray_src.at<uchar>(row, col);
}
}
//中心化
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
padded.at<float>(row, col) *= pow(-1, row + col);
}
}
//傅里叶变换
Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg);
//求原始图像的幅度谱和相位谱
Mat temp1[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(complexImg, temp1);
Mat amplitude1, angle1;
amplitude1 = Magnitude(temp1[0], temp1[1]);
angle1 = Phase(temp1[0], temp1[1]);
2、构造退化函数h(x,y),并计算其幅度谱和相位谱,即:
该部分代码如下所示:
//定义退化函数
Mat degenerate[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < degenerate[0].rows; row++)
{
for (int col = 0; col < degenerate[0].cols; col++)
{
float v = pi*(0.1*(row - 262) + 0.1*(col - 175));
if (v == 0)
{
degenerate[0].at<float>(row, col) = 1;
degenerate[1].at<float>(row, col) = 0;
}
else
{
degenerate[0].at<float>(row, col) = sin(v) / v*cos(v);
degenerate[1].at<float>(row, col) = -sin(v) / v*sin(v);
}
}
}
//求退化函数的幅度谱和相位谱
Mat amplitude2, angle2;
amplitude2 = Magnitude(degenerate[0], degenerate[1]);
angle2 = Phase(degenerate[0], degenerate[1]);
3、实现图像的退化
代码实现的原理为:
随后将上式分离出来的实部与虚部分别赋予两个通道,在这里是用photo[0]存储实部,photo[1]存储虚部。
代码如下所示:
//图像退化
Mat photo[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
photo[0].at<float>(row, col) = amplitude1.at<float>(row, col)*amplitude2.at<float>(row, col)*cos(angle1.at<float>(row, col) + angle2.at<float>(row, col));
photo[1].at<float>(row, col) = amplitude1.at<float>(row, col)*amplitude2.at<float>(row, col)*sin(angle1.at<float>(row, col) + angle2.at<float>(row, col));
}
}
4、得到运动模糊图像
以上都是在频域中进行操作,现将上一步得到的结果进行傅里叶逆变换,然后进行显示。运动模糊的效果如下:
该部分代码如下:
//空域中显示退化图像
Mat complex;
merge(photo, 2, complex);
idft(complex, complex);
Mat temp2[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(complex, temp2);
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
temp2[0].at<float>(row, col) *= pow(-1, row + col);
}
}
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
if (temp2[0].at<float>(row, col) < 0)
temp2[0].at<float>(row, col) = 0;
}
}
normalize(temp2[0], temp2[0], 0, 255, CV_MINMAX);
temp2[0].convertTo(temp2[0], gray_src.type());
imshow("退化图像", temp2[0]);
5、对运动模糊图像添加高斯白噪声
这里添加的是方差为4的高斯白噪声,图像添加后的效果如下:
该部分的代码如下所示:
//添加高斯白噪声
temp2[0] = addGaussianNoise(temp2[0]);
imshow("添加了高斯白噪声的退化图像", temp2[0]);
temp2[0].convertTo(temp2[0], CV_32FC1);
6、将上一步得到的图像进行傅里叶变换,得到添加了高斯白噪声的运动模糊图像的频谱:
代码如下所示:
//中心化
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
temp2[0].at<float>(row, col) *= pow(-1, row + col);
}
}
//求退化图像的傅里叶变换
Mat picture[] = { Mat_<float>(temp2[0]),Mat::zeros(temp2[0].size(),CV_32FC1) };
Mat img;
merge(picture, 2, img);
dft(img, img);
//求退化图像的幅度谱和相位谱
Mat temp3[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(img, temp3);
Mat amplitude3, angle3;
amplitude3 = Magnitude(temp3[0], temp3[1]);
angle3 = Phase(temp3[0], temp3[1]);
7、构造维纳滤波函数,函数解析式为:
其中K是常数,根据图像复原的实际情况进行调整。该部分代码如下所示:
//定义维纳滤波函数
Mat filter[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < filter[0].rows; row++)
{
for (int col = 0; col < filter[0].cols; col++)
{
float v = pi*(0.1*(row - 262) + 0.1*(col - 175));
if (v == 0)
{
filter[0].at<float>(row, col) = amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
filter[1].at<float>(row, col) = 0;
}
else
{
filter[0].at<float>(row, col) = v / sin(v)*cos(v)*amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
filter[1].at<float>(row, col) = v*amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
}
}
}
8、计算维纳滤波函数的幅度谱及相位谱,即:
代码如下所示:
//求维纳滤波函数的幅度谱和相位谱
Mat amplitude4, angle4;
amplitude4 = Magnitude(filter[0], filter[1]);
angle4 = Phase(filter[0], filter[1]);
9、将被污染的图像g(x,y)的频谱G(u,v)乘以维纳滤波函数的频谱,即:
然后将上式分离出来的实部与虚部分别赋予两个通道,在这里是用result[0]存储实部,result[1]存储虚部。
代码如下所示:
//退化图像乘以维纳滤波函数
Mat result[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
result[0].at<float>(row, col) = amplitude3.at<float>(row, col)*amplitude4.at<float>(row, col)*cos(angle3.at<float>(row, col) + angle4.at<float>(row, col));
result[1].at<float>(row, col) = amplitude3.at<float>(row, col)*amplitude4.at<float>(row, col)*sin(angle3.at<float>(row, col) + angle4.at<float>(row, col));
}
}
10、图像复原
对上一步处理得到的频谱进行傅里叶逆变换即可。图像复原结果如下所示:
该部分的代码如下所示:
//图像复原
Mat rst;
merge(result, 2, rst);
idft(rst, rst);
Mat temp4[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(rst, temp4);
for (int row = 0; row < temp4[0].rows; row++)
{
for (int col = 0; col < temp4[0].cols; col++)
{
temp4[0].at<float>(row, col) *= pow(-1, row + col);
}
}
for (int row = 0; row < temp4[0].rows; row++)
{
for (int col = 0; col < temp4[0].cols; col++)
{
if (temp4[0].at<float>(row, col) < 0)
temp4[0].at<float>(row, col) = 0;
}
}
normalize(temp4[0], temp4[0], 0, 1, CV_MINMAX);
imshow("图像复原", temp4[0]);
对于未加高斯白噪声的运动模糊图像复原结果如下所示:
五、全部代码
全部代码如下所示:
#include
#include
#include
using namespace std;
using namespace cv;
#define pi 3.1415926
Mat Magnitude(Mat Re, Mat Im);
Mat Phase(Mat Re, Mat Im);
double generateGaussianNoise(double m, double sigma);
Mat addGaussianNoise(Mat &srcImag);
int main()
{
Mat src = imread("T.jpg");
Mat gray_src;
cvtColor(src, gray_src, CV_BGR2GRAY);
Mat padded = Mat::zeros(2 * gray_src.rows, 2 * gray_src.cols, CV_32FC1);
//图像扩充
for (int row = 0; row < gray_src.rows; row++)
{
for (int col = 0; col < gray_src.cols; col++)
{
padded.at<float>(row, col) = gray_src.at<uchar>(row, col);
}
}
//中心化
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
padded.at<float>(row, col) *= pow(-1, row + col);
}
}
//傅里叶变换
Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
Mat complexImg;
merge(planes, 2, complexImg);
dft(complexImg, complexImg);
//求原始图像的幅度谱和相位谱
Mat temp1[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(complexImg, temp1);
Mat amplitude1, angle1;
amplitude1 = Magnitude(temp1[0], temp1[1]);
angle1 = Phase(temp1[0], temp1[1]);
//定义退化函数
Mat degenerate[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < degenerate[0].rows; row++)
{
for (int col = 0; col < degenerate[0].cols; col++)
{
float v = pi*(0.1*(row - 262) + 0.1*(col - 175));
if (v == 0)
{
degenerate[0].at<float>(row, col) = 1;
degenerate[1].at<float>(row, col) = 0;
}
else
{
degenerate[0].at<float>(row, col) = sin(v) / v*cos(v);
degenerate[1].at<float>(row, col) = -sin(v) / v*sin(v);
}
}
}
//求退化函数的幅度谱和相位谱
Mat amplitude2, angle2;
amplitude2 = Magnitude(degenerate[0], degenerate[1]);
angle2 = Phase(degenerate[0], degenerate[1]);
//图像退化
Mat photo[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
photo[0].at<float>(row, col) = amplitude1.at<float>(row, col)*amplitude2.at<float>(row, col)*cos(angle1.at<float>(row, col) + angle2.at<float>(row, col));
photo[1].at<float>(row, col) = amplitude1.at<float>(row, col)*amplitude2.at<float>(row, col)*sin(angle1.at<float>(row, col) + angle2.at<float>(row, col));
}
}
//空域中显示退化图像
Mat complex;
merge(photo, 2, complex);
idft(complex, complex);
Mat temp2[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(complex, temp2);
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
temp2[0].at<float>(row, col) *= pow(-1, row + col);
}
}
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
if (temp2[0].at<float>(row, col) < 0)
temp2[0].at<float>(row, col) = 0;
}
}
normalize(temp2[0], temp2[0], 0, 255, CV_MINMAX);
temp2[0].convertTo(temp2[0], gray_src.type());
imshow("退化图像", temp2[0]);
//添加高斯白噪声
temp2[0] = addGaussianNoise(temp2[0]);
imshow("添加了高斯白噪声的退化图像", temp2[0]);
temp2[0].convertTo(temp2[0], CV_32FC1);
//中心化
for (int row = 0; row < temp2[0].rows; row++)
{
for (int col = 0; col < temp2[0].cols; col++)
{
temp2[0].at<float>(row, col) *= pow(-1, row + col);
}
}
//求退化图像的傅里叶变换
Mat picture[] = { Mat_<float>(temp2[0]),Mat::zeros(temp2[0].size(),CV_32FC1) };
Mat img;
merge(picture, 2, img);
dft(img, img);
//求退化图像的幅度谱和相位谱
Mat temp3[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(img, temp3);
Mat amplitude3, angle3;
amplitude3 = Magnitude(temp3[0], temp3[1]);
angle3 = Phase(temp3[0], temp3[1]);
//定义维纳滤波函数
Mat filter[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < filter[0].rows; row++)
{
for (int col = 0; col < filter[0].cols; col++)
{
float v = pi*(0.1*(row - 262) + 0.1*(col - 175));
if (v == 0)
{
filter[0].at<float>(row, col) = amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
filter[1].at<float>(row, col) = 0;
}
else
{
filter[0].at<float>(row, col) = v / sin(v)*cos(v)*amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
filter[1].at<float>(row, col) = v*amplitude2.at<float>(row, col) / (amplitude2.at<float>(row, col) + 0.08);
}
}
}
//求维纳滤波函数的幅度谱和相位谱
Mat amplitude4, angle4;
amplitude4 = Magnitude(filter[0], filter[1]);
angle4 = Phase(filter[0], filter[1]);
//退化图像乘以维纳滤波函数
Mat result[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
for (int row = 0; row < padded.rows; row++)
{
for (int col = 0; col < padded.cols; col++)
{
result[0].at<float>(row, col) = amplitude3.at<float>(row, col)*amplitude4.at<float>(row, col)*cos(angle3.at<float>(row, col) + angle4.at<float>(row, col));
result[1].at<float>(row, col) = amplitude3.at<float>(row, col)*amplitude4.at<float>(row, col)*sin(angle3.at<float>(row, col) + angle4.at<float>(row, col));
}
}
//图像复原
Mat rst;
merge(result, 2, rst);
idft(rst, rst);
Mat temp4[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32FC1) };
split(rst, temp4);
for (int row = 0; row < temp4[0].rows; row++)
{
for (int col = 0; col < temp4[0].cols; col++)
{
temp4[0].at<float>(row, col) *= pow(-1, row + col);
}
}
for (int row = 0; row < temp4[0].rows; row++)
{
for (int col = 0; col < temp4[0].cols; col++)
{
if (temp4[0].at<float>(row, col) < 0)
temp4[0].at<float>(row, col) = 0;
}
}
normalize(temp4[0], temp4[0], 0, 1, CV_MINMAX);
imshow("图像复原", temp4[0]);
waitKey(0);
system("pause");
return 0;
}
Mat Phase(Mat Re, Mat Im)
{
Mat rst = Mat::zeros(Re.rows, Re.cols, CV_32F);
for (int row = 0; row < Re.rows; row++)
{
for (int col = 0; col < Re.cols; col++)
{
rst.at<float>(row, col) = atan2(Im.at<float>(row, col), Re.at<float>(row, col));
}
}
return rst;
}
Mat Magnitude(Mat Re, Mat Im)
{
Mat rst = Mat::zeros(Re.rows, Re.cols, CV_32F);
for (int row = 0; row < Re.rows; row++)
{
for (int col = 0; col < Re.cols; col++)
{
rst.at<float>(row, col) = sqrt(pow(Re.at<float>(row, col), 2) + pow(Im.at<float>(row, col), 2));
}
}
return rst;
}
//生成高斯噪声
double generateGaussianNoise(double mu, double sigma)
{
//定义小值
const double epsilon = numeric_limits<double>::min();
static double z0, z1;
static bool flag = false;
flag = !flag;
//flag为假构造高斯随机变量X
if (!flag)
return z1 * sigma + mu;
double u1, u2;
//构造随机变量
do
{
u1 = rand() * (1.0 / RAND_MAX);
u2 = rand() * (1.0 / RAND_MAX);
} while (u1 <= epsilon);
//flag为真构造高斯随机变量
z0 = sqrt(-2.0*log(u1))*cos(2 * CV_PI*u2);
z1 = sqrt(-2.0*log(u1))*sin(2 * CV_PI*u2);
return z0*sigma + mu;
}
//为图像添加高斯噪声
Mat addGaussianNoise(Mat &srcImag)
{
Mat dstImage = srcImag.clone();
int channels = dstImage.channels();
int rowsNumber = dstImage.rows;
int colsNumber = dstImage.cols*channels;
//判断图像的连续性
if (dstImage.isContinuous())
{
colsNumber *= rowsNumber;
rowsNumber = 1;
}
for (int i = 0; i < rowsNumber; i++)
{
for (int j = 0; j < colsNumber; j++)
{
//添加高斯噪声
int val = dstImage.ptr<uchar>(i)[j] +
generateGaussianNoise(0, 2);
if (val < 0)
val = 0;
if (val>255)
val = 255;
dstImage.ptr<uchar>(i)[j] = (uchar)val;
}
}
return dstImage;
}