DFT / IDFT计算的负值

时间:2018-02-08 10:32:31

标签: c++ image opencv image-processing imagefilter

我在频域创建一个低通滤波器,截止频率为0.015,相应的高通滤波器如下所示:

auto getGaussianFilter(int32_t size) {
    const auto sigmaf = 0.015f*size; // cutoff freq.

    cv::Mat kernel = cv::Mat::zeros(size, size, CV_32FC1);

    for (auto fy = -size/2; fy < size/2; ++fy)
        for (auto fx = -size/2; fx < size/2; ++fx) 
            kernel.at<float>(fy + size/2, fx + size/2) = std::exp(-(fy*fy+fx*fx)/(2*sigmaf*sigmaf));
    return kernel;
} 

...

lowpassFilter  = getGaussianFilter(256);
highpassFilter = 1 - lowpassFilter; 

,这些应用于以下代码段的图片。

std::vector<cv::Mat> planes { src, cv::Mat::zeros(src.size(), src.type()) }; 
std::vector<cv::Mat> fplanes { filter, filter };
cv::Mat complex, complexFilter;
cv::merge(planes, complex);
cv::merge(fplanes, complexFilter);

cv::dft(complex, complex, cv::DFT_COMPLEX_OUTPUT);
shift(complex); // swapping quadrants
cv::mulSpectrums(complex, complexFilter, complex, cv::DFT_ROWS);
shift(complex); 
cv::idft(complex, complex, cv::DFT_SCALE | cv::DFT_REAL_OUTPUT); 

输入图像为256x256,采用对数刻度,低通滤波器应用于高通滤波输入图像的平方。

然后我想从最后的idft结果的平方根, 但它包含负值;因此,出现了几个NaN值。

applyFilter(src, dst, highpassFilter);
cv::pow(dst, 2, dst);
applyFilter(dst, dst, lowpassFilter);
cv::sqrt(dst, dst);  // NaN !

为什么会有负值,我怎样才能处理它们以取平方根?

编辑:添加了shift

的代码
void shift(cv::Mat& src) {
    src = src(cv::Rect(0, 0, src.cols & -2, src.rows & -2)); 
    const auto cy = src.rows/2, cx = src.cols/2;
    cv::Mat q0(src, cv::Rect(0, 0, cx, cy));
    cv::Mat q1(src, cv::Rect(cx, 0, cx, cy));
    cv::Mat q2(src, cv::Rect(0, cy, cx, cy));
    cv::Mat q3(src, cv::Rect(cx, cy, cx, cy));
    cv::Mat tmp;
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);

    q1.copyTo(tmp);
    q2.copyTo(q1);
    tmp.copyTo(q2);
 } 

1 个答案:

答案 0 :(得分:0)

直到第三次或第四次阅读你的代码,我才发现这个......

std::vector<cv::Mat> fplanes { filter, filter };
cv::merge(fplanes, complexFilter);

这里创建一个复值滤波器,它不是共轭对称的。应用此滤波器会创建一个非共轭对称的频谱,因此您将获得非实数逆变换。

你想要做的是拥有一个纯粹真实的过滤器:

std::vector<cv::Mat> fplanes { filter, cv::Mat::zeros(filter(), filter()) };
cv::merge(fplanes, complexFilter);

根据幅度和相位(而不是实部和虚部)来考虑频域滤波器。幅度会改变您更改图像中每个频率成分幅度的方式,相移会使每个频率成分发生偏移。一般情况下(除非在特定情况下),您不希望移动图像中的频率成分,因为它会弄乱您的图像。因此,将频域滤波器的相位保持为0.零相位意味着每个值都是非负的和真实的。

那就是说,你移动图像的频谱,应用滤波器,然后向后移动的序列:

cv::dft(complex, complex, cv::DFT_COMPLEX_OUTPUT);
shift(complex); // swapping quadrants
cv::mulSpectrums(complex, complexFilter, complex, cv::DFT_ROWS);
shift(complex); 
cv::idft(complex, complex, cv::DFT_SCALE | cv::DFT_REAL_OUTPUT); 

与简单地移动过滤器相同:

cv::dft(complex, complex, cv::DFT_COMPLEX_OUTPUT);
shift(complexFilter); // swapping quadrants
cv::mulSpectrums(complex, complexFilter, complex, 0);
cv::idft(complex, complex, cv::DFT_SCALE | cv::DFT_REAL_OUTPUT); 

另请注意,我将cv::DFT_ROWS替换为0.根据the documentation,该标志表示每个图像行都是独立的1D变换。我的猜测是,这只对CCS包装的矩阵很重要,但不管怎么说最好不要这样做。