我正在研究图像频率过滤的GPU实现。 我的代码在CPU上工作得很好(我使用了像this这样的东西),但我花了一整天的时间尝试在GPU上做同样的工作 - 没有成功。我想在频域中应用滤波器,因此我需要正向变换的完整(复杂)结果。 我已经读过,我需要传递两个复数矩阵(src和dst)来转发dft来获得全谱(32FC2)。但是,我无法在逆变换后获得相同的图像(返回的图像非常失真)。
我的代码(结果最接近):
gpu.img1 = gpu::GpuMat(vrH, imgWidth, CV_32FC2);
gpu.img2 = gpu::GpuMat(vrH, imgWidth, CV_32FC2);
gpu.img4 = gpu::GpuMat(vrH, imgWidth, CV_32FC1);
gpu.img5 = gpu::GpuMat(vrH, imgWidth, CV_8UC1);
Mat planes[] = {imageIn, Mat::zeros(imageIn.size(), CV_32FC1)};
merge(planes, 2, imageIn);
gpu::Stream stream;
gpu.img1.upload(imageIn);
gpu::dft(gpu.img1, gpu.img2, gpu.img1.size(), 0, stream);
gpu::dft(gpu.img2, gpu.img4, gpu.img1.size(), DFT_INVERSE | DFT_REAL_OUTPUT | DFT_SCALE, stream);
stream.enqueueConvert(gpu.img4, gpu.img5, CV_8U);
stream.waitForCompletion();
gpu.img5.download(imageOut);
namedWindow("processed",1); imshow("processed", imageOut); waitKey(1000);
非常感谢您的帮助和建议。
答案 0 :(得分:2)
我花了好几个小时,但我最终解决了这个问题。有两个选项
1)实数到复数(CV_32FC1 - > CV_32FC2)正向和复数到实数(CV_32FC2 - > CV_32FC1)反向
作为正向变换的结果,获得了更窄的频谱矩阵(newWidth = oldWidth / 2 + 1,如documentation中所述)。它不是CSS紧凑矩阵,就像非gpu dft一样。它是一个复杂的矩阵,它使用频谱对称的事实。因此,这里也可以应用任何滤波器,其速度比第二种情况下的乘法减少近一半。
在这种情况下,应设置以下标志:
这对我很有用。记得早先正确声明用于其类型的GpuMat(CV_32FC1或CV_32FC2)
2)复杂到复杂(CV_32FC2 - > CV_32FC2)正向和复杂到复杂(CV_32FC2 - > CV_32FC2)反向 在前向DFT中产生全尺寸光谱(CV_32FC2)。在这种情况下,标志是
逆变换的结果是复杂矩阵(CV_32FC2),因此您需要将其拆分并从零通道中提取所需的结果。之后数据需要明确缩放:
Mat lenaAfter;
Mat lena = imread("C:/Users/Fundespa/Desktop/lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
lena.convertTo(lena, CV_32F, 1);
std::vector<Mat> planes;
planes.push_back(lena);
planes.push_back(Mat::zeros(lena.size(), CV_32FC1));
merge(planes, lena);
gpu::GpuMat lenaGPU = gpu::GpuMat(512, 512, CV_32FC2);
gpu::GpuMat lenaSpectrum = gpu::GpuMat(512, 512, CV_32FC2);
gpu::GpuMat lenaOut = gpu::GpuMat(512, 512, CV_32FC2);
lenaGPU.upload(lena);
gpu::dft(lenaGPU, lenaSpectrum, lenaGPU.size(), 0, stream);
int c = lenaSpectrum.channels();
Size s = lenaSpectrum.size();
gpu::dft(lenaSpectrum, lenaOut, lenaGPU.size(), DFT_INVERSE, stream);
gpu::split(lenaOut, splitter, stream);
stream.waitForCompletion();
splitter[0].download(lenaAfter);
// lenaOut.download(lenaAfter);
c = lenaAfter.channels();
double n,x;
minMaxIdx(lenaAfter, &n, &x);
lenaAfter.convertTo(lenaAfter, CV_8U, 255.0/x);
namedWindow("lena after",1); imshow("lena after", lenaAfter); waitKey(1000);
就这么简单!我不知道为什么我之前没有遇到这个问题。我决定以任何方式发布它,因为有人可能有相同的问题或需要一些指导。