来自 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::idft
和 DFT_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 计算的负值的主要内容,如果未能解决你的问题,请参考以下文章