C++ 手动执行频域卷积,但在CPP/Opencv中获得错误的输出图像

C++ 手动执行频域卷积,但在CPP/Opencv中获得错误的输出图像,c++,opencv,image-processing,C++,Opencv,Image Processing,我遵循以下步骤:- 1.计算图像的dft 2.计算内核的dft(但第一次将其填充到图像大小) 3.分别乘以dft的实部和虚部 4.计算逆dft 我试图在每个中间步骤中显示图像,但最终的图像几乎是黑色的,除了角落。 在此处输入代码 #包括 #包括 #包括 #包括 int r=100; #定义SIGMA_CLIP 6.0f 使用名称空间cv; 使用名称空间std; 无效更新结果(Mat复合体) { 垫子; idft(综合体、工程); Mat平面[]={Mat::zeros(complex.siz

我遵循以下步骤:- 1.计算图像的dft 2.计算内核的dft(但第一次将其填充到图像大小) 3.分别乘以dft的实部和虚部 4.计算逆dft 我试图在每个中间步骤中显示图像,但最终的图像几乎是黑色的,除了角落。

在此处输入代码
#包括
#包括
#包括
#包括
int r=100;
#定义SIGMA_CLIP 6.0f
使用名称空间cv;
使用名称空间std;
无效更新结果(Mat复合体)
{
垫子;
idft(综合体、工程);
Mat平面[]={Mat::zeros(complex.size(),CV_32F),Mat::zeros(complex.size(),CV_32F)};
拆分(功,平面);//平面[0]=Re(DFT(I)),平面[1]=Im(DFT(I))
震级(平面[0],平面[1],功);//==sqrt(Re(DFT(I))^2+Im(DFT(I))^2)
正常化(工作,工作,0,1,标准值_最小值);
imshow(“结果”,工作);
}
无效移位(Mat magI){
//如果行数或列数为奇数,则进行裁剪
magI=magI(Rect(0,0,magI.cols&-2,magI.rows&-2));
int cx=magI.cols/2;
int cy=magI.rows/2;
Mat q0(magI,Rect(0,0,cx,cy));//左上角-为每个象限创建一个ROI
Mat q1(magI,Rect(cx,0,cx,cy));//右上角
Mat q2(magI,Rect(0,cy,cx,cy));//左下角
Mat q3(magI,Rect(cx,cy,cx,cy));//右下角
Mat tmp;//交换象限(左上角与右下角)
q0.copyTo(tmp);
q3.复制到(q0);
tmp.copyTo(第三季度);
q1.copyTo(tmp);//交换象限(右上角与左下角)
q2.抄袭(q1);
tmp.copyTo(第2季度);
}
Mat更新标记(Mat复合体)
{
Mat magI;
Mat平面[]={Mat::zeros(complex.size(),CV_32F),Mat::zeros(complex.size(),CV_32F)};
分裂(复数,平面);//平面[0]=Re(DFT(I)),平面[1]=Im(DFT(I))
震级(平面[0],平面[1],magI);//sqrt(Re(DFT(I))^2+Im(DFT(I))^2)
//切换到对数刻度:对数(1+量级)
magI+=Scalar::all(1);
日志(magI,magI);
轮班(magI);
normalize(magI,magI,1,0,NORM_INF);//将带有浮点值的矩阵转换为
return magI;//可查看的图像形式(在值0和1之间浮动)。
//imshow(“光谱”,magI);
}
Mat createGausFilterMask(大小imsize,整数半径){
//调用openCV高斯核生成器
双西格玛=(r/sigma_片段+0.5f);
Mat kernelX=getGaussianKernel(2*半径+1,σ,CV_32F);
Mat kernelY=getGaussianKernel(2*半径+1,σ,CV_32F);
//创建二维高斯
Mat kernel=kernelX*kernelY.t();
int w=imsize.width-kernel.cols;
int h=imsize.height-kernel.rows;
int r=w/2;
int l=imsize.width-kernel.cols-r;
int b=h/2;
int t=imsize.height-kernel.rows-b;
Mat-ret;
copyMakeBorder(内核,ret,t,b,l,r,BORDER_常量,标量::all(0));
返回ret;
}
//代码引用https://docs.opencv.org/2.4/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html
int main(int argc,字符**argv)
{ 
字符串文件;
file=“lena.png”;
Mat image=imread(文件、CV\u加载\u图像\u灰度);
垫垫;
int m=getOptimalDFTSize(image.rows);
int n=getoptimizedftsize(image.cols);
copyMakeBorder(image,padded,0,m-image.rows,0,n-image.cols,BORDER_常量,Scalar::all(0));//将输入图像扩展到最佳大小,在边框上添加零值
Mat平面[]={Mat_u(padded),Mat::zeros(padded.size(),CV_32F)};
席状复合体;
合并(平面2,复杂);
dft(复数,复数);//计算dft
拆分(复数,平面);//此处将图像转换为复数和实dft
Mat mask=createGausFilterMask(padded.size(),r);//形成高斯滤波器
Mat-mplane[]={Mat_u(mask),Mat::zeros(mask.size(),CV_32F)};
垫核复合体;
合并(mplane,2,kernelcomplex);
dft(kernelcomplex,kernelcomplex);
split(kernelcomplex,mplane);//将内核的dft分解为实和复
mplane[1]=mplane[0];//用内核dft的实值覆盖虚值
Mat-kernel_-spec;
合并(mplane,2,内核规范);
多光谱(复杂、内核规格、复杂、DFT行);
Mat magI=更新标记(复杂度);
namedWindow(“图像傅立叶”,CV_窗口_自动调整大小);
imshow(“光谱幅度”,magI);
updateResult(completi);//转换为可视形式,计算idft
等待键(0);
返回0;
}
哪一步出错了?还是我缺少了一些概念



在Cris的帮助下编辑了代码,现在它可以完美地工作。

有两个明显的问题:

  • 高斯函数是实值且对称的。它的傅里叶变换也应该如此。如果内核的DFT有一个非零虚部,那么您就做错了

    很可能,你所做的错误是你的内核起源于图像的中间,而不是左上角的样本。这与中的问题相同。解决方案是使用MATLAB的等价物ifftshift,其实现如所示

  • 要应用卷积,需要将两个DFT相乘,而不是DFT的实部和虚部。将两个复数
    a+ib
    c+id
    相乘,结果是
    ac-bd+iad+ibc
    ,而不是
    ac+ibd

    但是,由于内核的DFT应该仅为实值,因此可以简单地将内核的实部与映像的实部和虚部相乘:
    (a+ib)c=ac+ibc


  • 它似乎非常迂回,你正在做什么与复值图像。为什么不让OpenCV为您处理所有这些?你或许可以这样做:

    Mat image = imread(file, CV_LOAD_IMAGE_GRAYSCALE);
    
    // Expand input image to optimal size, on the border add zero values
    Mat padded;                             
    int m = getOptimalDFTSize(image.rows);
    int n = getOptimalDFTSize(image.cols);  
    copyMakeBorder(image, padded, 0, m - image.rows, 0, n -image.cols, BORDER_CONSTANT, Scalar::all(0));
    
    // Computing DFT
    Mat DFTimage;
    dft(padded, DFTimage); 
    
    // Forming the Gaussian filter
    Mat kernel = createGausFilterMask(padded.size(), r);
    shift(kernel);
    Mat DFTkernel;
    dft(kernel, DFTkernel);
    
    // Convolution
    mulSpectrums(DFTimage, DFTkernel, DFTimage, DFT_ROWS);
    
    // Display Fourier-domain result
    Mat magI = updateMag(DFTimage);
    imshow("spectrum magnitude", magI);
    
    // IDFT
    Mat work;
    idft(complex, work); // <- NOTE! Don't inverse transform log-transformed magnitude image!
    
    Mat image=imread(文件、CV\u加载\u图像\u灰度);
    //将输入图像展开到最佳大小,在边框上添加零值
    垫垫;
    int m=GetOptimizedTfsize(image.rows);
    int n=GetOptimizedTfsize(image.cols);
    copyMakeBorder(image,padded,0,m-image.rows,0,n-image.cols,BORDER_常量,Scalar::all(0));
    //
    
    Mat image = imread(file, CV_LOAD_IMAGE_GRAYSCALE);
    
    // Expand input image to optimal size, on the border add zero values
    Mat padded;                             
    int m = getOptimalDFTSize(image.rows);
    int n = getOptimalDFTSize(image.cols);  
    copyMakeBorder(image, padded, 0, m - image.rows, 0, n -image.cols, BORDER_CONSTANT, Scalar::all(0));
    
    // Computing DFT
    Mat DFTimage;
    dft(padded, DFTimage); 
    
    // Forming the Gaussian filter
    Mat kernel = createGausFilterMask(padded.size(), r);
    shift(kernel);
    Mat DFTkernel;
    dft(kernel, DFTkernel);
    
    // Convolution
    mulSpectrums(DFTimage, DFTkernel, DFTimage, DFT_ROWS);
    
    // Display Fourier-domain result
    Mat magI = updateMag(DFTimage);
    imshow("spectrum magnitude", magI);
    
    // IDFT
    Mat work;
    idft(complex, work); // <- NOTE! Don't inverse transform log-transformed magnitude image!