使用FFT进行卷积会产生不好的结果

如何解决使用FFT进行卷积会产生不好的结果

我正在尝试使用FFT对图像进行卷积。我使用openCV,因此图像位于Mat容器中。我将彩色图像转换为灰色图像,然后为第二个全为零的虚数添加第二个通道。然后,我将这个2通道Mat并与Prewitt的内核进行卷积。我得到的结果与使用普通卷积算法时得到的结果截然不同。左图是我通过FFT获得的输出,右图是正常卷积的输出。

enter image description here

enter descripsion hier

下面是我如何操作的伪算法;

Convert image Mat and kernel Mat to complex Mats by adding second channel (Result Mat type is CV_32FC2)
Assign all Mat elements to complex vectors
Zero pad the vectors to the same next power of 2
FFT the vectors
Signal multiply both vectors elementwise and assign result to result vector
Inverse FFT the result vector
Convert result vector to Mat

我认为FFT算法不是问题,因为当我拍摄图像时,先对它进行FFT,然后对其进行逆FFT,就可以得到原始图像。但是我可能是错的。因此here是FFT算法。请注意其中有两个。我用第二个。我还尝试了其他FFT算法,它们都输出相同的结果。 FFT和IFFT相同的图像仅跳过上面的信号乘法步骤。所以我认为这就是问题所在。这是操作代码;

std::vector<cf> signalMultiplication(std::vector<cf> lh,std::vector<cf> rh) {
    std::vector<cf> imVec = lh,kerVec = rh,resultVec;

    resultVec.resize(imVec.size());

    std::transform(imVec.begin(),imVec.end(),kerVec.begin(),resultVec.begin(),std::multiplies<cf>());

    return resultVec;
}

我尝试使用for循环将它们相乘,但结果相同。我不知道问题所在,因为太长,所以我无法在此处键入整个代码,所以请告诉我您认为问题出在哪里,然后我将给出该部分的代码。

@Paul是代码的主体;

cv::Mat convolution2D(cv::Mat image,cv::Mat kernel) {
cv::Mat imMat,kerMat; 

imMat = convertToComplexMat(image);
kerMat = convertToComplexMat(kernel);

std::vector<cf> imVec,kerVec,resultVec;

imVec = matElementsToVector<cf>(imMat);
kerVec = matElementsToVector<cf>(kerMat);

float power = log2f(imVec.size());
if (abs(power - (int)power) == 0)
    power++;
else
    power = ceil(power);

zeroPadding(imVec,power);
zeroPadding(kerVec,power);

//FFT code I linked takes valarray as argument so I convert vectors to valarray and back
std::valarray<cf> imCArr(imVec.data(),imVec.size());
std::valarray<cf> kerCArr(kerVec.data(),kerVec.size());

fftRosetta(imCArr);
fftRosetta(kerCArr);

imVec.assign(std::begin(imCArr),std::end(imCArr));
kerVec.assign(std::begin(kerCArr),std::end(kerCArr));

resultVec = signalMultiplication(imVec,kerVec);    
std::valarray<cf> resCArr(resultVec.data(),resultVec.size());

ifftRosetta(resCArr);
resultVec.assign(std::begin(resCArr),std::end(resCArr));

cv::Mat resultMat;
resultMat = vectorToMatElementsRowMajor(resultVec,imMat.rows,imMat.cols,imMat.type());

std::vector<cv::Mat> matVec;
cv::split(resultMat,matVec);

return matVec[0]; }

这些是自定义函数;

convertToComplexMat,matElementsToVector,zeroPadding,fftRosetta,ifftRosetta,signalMultiplication,vectorToMatElementsRowMajor

signalMultiplication已发布,fftRosetta和ifftRosetta已链接在一起,因此此处是其余功能;

using cf = std::complex<float>;

cv::Mat convertToComplexMat(cv::Mat imageMat) {
    cv::Mat matOper;
    if (imageMat.channels() == 3)
        cv::cvtColor(imageMat,matOper,cv::COLOR_BGR2GRAY);
    else
        matOper = imageMat.clone();
    matOper.convertTo(matOper,CV_32FC1);
    cv::Mat compChannel = cv::Mat::zeros(matOper.rows,matOper.cols,CV_32FC1);
    std::vector<cv::Mat> channels;
    channels.push_back(matOper);
    channels.push_back(compChannel);
    cv::merge(channels,matOper);

    return matOper;
}

template <typename T>
std::vector<T> matElementsToVector(cv::Mat operand) {
    std::vector<T> vecOper;
    int cn = operand.channels();
    int lele = operand.total();
    for (int i = 0; i < operand.total(); i++) {
        if (cn == 1)
            vecOper.push_back(operand.at<cv::Vec<T,1>>(i)[0]);
        else if (cn == 2) {
            if (typeid(T) == typeid(cf)) {
                T xd = operand.at<T>(i);
                vecOper.push_back(xd);
            }
            else
                for (int k = 0; k < cn; k++)
                    vecOper.push_back(operand.at<cv::Vec<T,2>>(i)[k]);
        }
        else if (cn == 3)
            for (int k = 0; k < cn; k++)
                vecOper.push_back(operand.at<cv::Vec<T,3>>(i)[k]);
    }
    return vecOper;
}

void zeroPadding(std::vector<cf>& a,int power) {
    int p,ioper;

    if (power == -1)
        p = ceil(log2f(a.size()));
    else
        p = power;

    ioper = pow(2,p);
    int size = a.size();
    for (int i = 0; i < ioper - size; i++) {
        a.push_back(0);
    }
}

template <typename T>
cv::Mat vectorToMatElementsRowMajor(std::vector<T> operand,int mrows,int mcols,int mtype) {
    cv::Mat matoper(mrows,mcols,mtype);
    for (int j = 0; j < matoper.total(); j++) {
        matoper.at<T>(j) = operand[j];
    }
    return matoper;
}

@Cris我按照here的说明,使用openCV DFT再次尝试了。我将DFT应用于图像和内核,然后逐个元素相乘,然后应用IDFT。但是结果现在已经大不相同了。我可以在其中看到原始图像的相似之处,但是有不同角度的多个阴影。我认为问题出在我如何进行信号乘法,但是我找不到如何乘法2D信号的任何答案。这是代码,输出图像在其下方;

cv::Mat convolution2DopenCV(cv::Mat image,cv::Mat kernel) {
    cv::Mat paddedImage,paddedKernel,imgOper,kerOper;
    if (image.channels() == 3)
        cv::cvtColor(image,cv::COLOR_BGR2GRAY);
    else
        imgOper = image.clone();

    kerOper = kernel;

    int m = cv::getOptimalDFTSize(imgOper.rows);
    int n = cv::getOptimalDFTSize(imgOper.cols);

    cv::copyMakeBorder(imgOper,paddedImage,m - imgOper.rows,n - imgOper.cols,cv::BORDER_CONSTANT,cv::Scalar::all(0));
    cv::copyMakeBorder(kerOper,m - kerOper.rows,n - kerOper.cols,cv::Scalar::all(0));

    cv::Mat planesImage[] = { cv::Mat_<float>(paddedImage),cv::Mat::zeros(paddedImage.size(),CV_32F) };
    cv::Mat cmpImgMat;
    cv::merge(planesImage,2,cmpImgMat);
    cv::dft(cmpImgMat,cmpImgMat);

    cv::Mat planesKernel[] = { cv::Mat_<float>(paddedKernel),cv::Mat::zeros(paddedKernel.size(),CV_32F) };
    cv::Mat cmpKerMat;
    cv::merge(planesKernel,cmpKerMat);
    cv::dft(cmpKerMat,cmpKerMat);

    cv::Mat resultMat = cmpImgMat.mul(cmpKerMat);

    cv::Mat planes[2];
    cv::idft(resultMat,resultMat);
    cv::split(resultMat,planes);
    cv::normalize(planes[0],planes[0],255,cv::NORM_MINMAX);
    return planes[0];
}

enter image description here

这就是一切,如果我缺少什么,请告诉我。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-