傅里叶变换【3】:卷积与傅里叶的关系

//利用快速傅里叶变换计算卷积,I 为浮点型矩阵
Mat fft2Conv(Mat I, Mat kernel, int borderType = BORDER_DEFAULT,Scalar value = Scalar())
{
	// I 的宽高
	int R = I.rows;
	int C = I.cols;
	// 卷积核 kernel 的宽高,均为奇数
	int r = kernel.rows;
	int c = kernel.cols;
	// 卷积核的半径 
	int tb = (r - 1) / 2;
	int lr = (c - 1) / 2;
	/* 第一步:边界扩充 */
	Mat I_padded;
	copyMakeBorder(I, I_padded, tb, tb, lr, lr, borderType, value);
	/* 第二步:对 I_padded 和 kernel 右侧和下侧补零,以满足快速傅里叶变换的行数和列数 */
	//满足二维快速傅里叶变换的行数、列数
	int rows = getOptimalDFTSize(I_padded.rows + r -1);
	int cols = getOptimalDFTSize(I_padded.cols + c - 1);
	//补零
	Mat I_padded_zeros, kernel_zeros;
	copyMakeBorder(I_padded, I_padded_zeros, 0, rows - I_padded.rows, 0, cols - I_padded.cols,BORDER_CONSTANT, Scalar(0,0,0,0));
	copyMakeBorder(kernel, kernel_zeros, 0, rows - kernel.rows, 0, cols - kernel.cols, BORDER_CONSTANT, Scalar(0,0,0,0));
	/* 第三步:快速傅里叶变换 */
	Mat fft2_Ipz,fft2_kz;
	dft(I_padded_zeros, fft2_Ipz, DFT_COMPLEX_OUTPUT);
	dft(kernel_zeros, fft2_kz, DFT_COMPLEX_OUTPUT);
	/* 第四步 :两个傅里叶变换点乘 */
	Mat Ipz_kz;
	mulSpectrums(fft2_Ipz, fft2_kz, Ipz_kz, DFT_ROWS);
	/* 第五步:傅里叶逆变换,并只取实部 */
	Mat ifft2;
	dft(Ipz_kz, ifft2, DFT_INVERSE + DFT_SCALE + DFT_REAL_OUTPUT);
	/* 第六步:裁剪,同输入矩阵同样的尺寸 */
	Mat sameConv = ifft2(Rect(c - 1, r - 1, C + c - 1, R + r - 1));
	return sameConv;
}
int main(int argc, char*argv[])
{
	Mat img = imread("lena.jpg", 0);
	if (!img.data)
		return -1;
	//将图像转换为浮点型
	Mat fImg;
	img.convertTo(fImg, CV_32FC1, 1.0, 0);
	//卷积核
	Mat kernel = (Mat_(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	//通过傅里叶变换计算卷积
	Mat sameConv = fft2Conv(fImg, kernel, BORDER_DEFAULT);
	return 0;
}

原图:

傅里叶变换【3】:卷积与傅里叶的关系_第1张图片

卷积核:

傅里叶变换【3】:卷积与傅里叶的关系_第2张图片

结果:

傅里叶变换【3】:卷积与傅里叶的关系_第3张图片

你可能感兴趣的:(Opencv,视觉算法)