如何解决使用FFT进行卷积会产生不好的结果
我正在尝试使用FFT对图像进行卷积。我使用openCV,因此图像位于Mat容器中。我将彩色图像转换为灰色图像,然后为第二个全为零的虚数添加第二个通道。然后,我将这个2通道Mat并与Prewitt的内核进行卷积。我得到的结果与使用普通卷积算法时得到的结果截然不同。左图是我通过FFT获得的输出,右图是正常卷积的输出。
下面是我如何操作的伪算法;
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];
}
这就是一切,如果我缺少什么,请告诉我。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。