来自 DFT/IDFT 计算的负值

Posted

技术标签:

【中文标题】来自 DFT/IDFT 计算的负值【英文标题】:Negative values from DFT/IDFT calculation 【发布时间】:2018-02-08 10:32:30 【问题描述】:

我正在创建一个截止频率为 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; 

,这些应用于像下面的sn-p这样的图像。

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 !

为什么会有负值,我该如何处理它们才能取平方根?


EDIT:添加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);
  

【问题讨论】:

如果输入没有负值,应用高斯滤波器不会引入负值。所以我们要么是在谈论你的代码中的一个错误(检查中间值!),要么是由于数值不准确而低于 0 的值(你是否在寻找那些负值?它们有多大?)。在后一种情况下,只需在 0 处剪辑(将负值设置为 0)。 还要检查您的过滤是否正确。您发送到idft 的图像complex 实际上是对称的吗? shift 函数是否就地工作? 负值不​​小,所以我认为数值不准确不是原因。此外,正方形不能包含负值,移动象限的方式与官方示例相同,并且我检查了过滤器不包含负值,并且每个过滤通过归一化和 cv::imshow 看起来都是正确的。 . 当我运行没有过滤的简单相互转换时,只有非常小的负值,如-1e-5,这可能是由于您所说的数值不准确。所以过滤可能不正确;但是,过滤器的值范围从 0 到 1,我所要做的只是逐元素乘法,所以到目前为止我不知道.. 啊,没有DFT_REAL_OUTPUT 的最终complex 的大小看起来是一个理想的输出。然而,我很困惑。 cv::idftDFT_REAL_OUTPUT 的结果不是经过适当变换的实矩阵吗? 【参考方案1】:

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

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,该标志表示每个图像行都是独立的一维变换。我的猜测是,这只对 CCS 压缩矩阵很重要,但最好还是不要使用它。

【讨论】:

我明白你的意思(至少在理论上),lowpassFilter 现在看起来工作正常;但是,您是否验证了相应的highpassFIlter (1-lowpassFilter)?我可以看到仍然有不小的负值......虽然我确认它只包含非负值和实值。 经过高通滤波的图像通常具有负值。但它应该是真正有价值的。使用高通滤波器,您可以将 0 频率设置为 0。这是直流分量。将其设置为零意味着空间域中的图像将具有 0 均值。因此,您必然会有很多负值。为防止这种情况发生,请将highlassFilter 中间的一个像素设置为 1。 换句话说:将0频率设置为0相当于从每个像素中减去平均像素强度。 正如您所说,当输入的宽度和高度为奇数并且中心周围的四个像素为 1 时甚至 !!但是仍然存在一些(不小)负值...... 你有什么线索吗?

以上是关于来自 DFT/IDFT 计算的负值的主要内容,如果未能解决你的问题,请参考以下文章

多项式与点值式

选择计算 SQL 的正/负值

用负值计算“timeIntervalSinceNow”(CLLocation 示例)?

java 练习递归..计算负值

ECharts 中箱线图计算后数据出现负值的问题

如何计算两个日期之间的月数和天数,而不在 SQL Server 中返回负值