上一教程: 更改图像的对比度和亮度!
下一教程: 使用 XML 和 YAML 文件进行文件输入和输出
原作者 | Bernát Gábor |
兼容性 | OpenCV >= 3.0 |
我们将寻找以下问题的答案
你可以 从这里下载,或者在 OpenCV 源代码库的 samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
中找到它。
你可以 从这里下载,或者在 OpenCV 源代码库的 samples/java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java
中找到它。
你可以 从这里下载,或者在 OpenCV 源代码库的 samples/python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py
中找到它。
以下是 dft() 的示例用法
傅里叶变换将分解图像为正弦和余弦分量。换句话说,它将图像从空间域转换为频率域。其理念是,任何函数都可以用无限个正弦和余弦函数的总和精确逼近。傅里叶变换是一种如何做到这一点的方法。在数学上,二维图像的傅里叶变换为
\[F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})}\]
\[e^{ix} = \cos{x} + i\sin {x}\]
这里,f 是图像在空间域中的值,F 是在频率域中的值。变换的结果是复数。可以通过实图像和复图像,或通过幅值图像和相位图像来显示。但是,在整个图像处理算法中,只有幅值图像是有趣的,因为它包含了关于图像几何结构的所有信息。但是,如果你想以这些形式对图像进行一些修改,然后你需要重新转换它,那么你需要保留这两者。
在本示例中,我将展示如何计算和显示傅里叶变换的幅值图像。在数字图像的情况下是离散的。这意味着它们可以从给定的域值中取值。例如,在基本的灰度图像中,值通常在 0 到 255 之间。因此,傅里叶变换也需要是离散类型,从而产生离散傅里叶变换 (DFT)。当你需要从几何角度确定图像的结构时,你将需要使用它。以下是要遵循的步骤(在灰度输入图像I 的情况下):
DFT 的性能取决于图像大小。它在图像大小为 2、3 和 5 的倍数时往往最快。因此,为了获得最大性能,通常最好在图像中填充边界值,以获得具有这种特征的大小。getOptimalDFTSize() 返回此最佳大小,我们可以使用copyMakeBorder() 函数扩展图像的边框(附加的像素初始化为零)。
傅里叶变换的结果是复数。这意味着,对于每个图像值,结果是两个图像值(每个分量一个)。此外,频率域的范围远大于其空间对应域。因此,我们通常至少将它们存储在float 格式中。因此,我们将把我们的输入图像转换为这种类型,并用另一个通道扩展它以保存复数值。
可以使用就地计算(输入和输出相同)。
复数具有实部 (Re) 和复部(虚部 - Im)。DFT 的结果是复数。DFT 的幅值为
\[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\]
翻译成 OpenCV 代码
事实证明,傅里叶系数的动态范围太大,无法在屏幕上显示。我们有一些小的变化值和一些大的变化值,我们无法用这种方式观察到。因此,较大的值将全部显示为白色点,而较小的值将显示为黑色。为了将灰度值用于可视化,我们可以将线性刻度转换为对数刻度。
\[M_1 = \log{(1 + M)}\]
翻译成 OpenCV 代码
请记住,我们是在第一步扩展了图像?现在是时候丢弃新引入的值了。为了可视化,我们还可以重新排列结果的象限,以便原点(零,零)对应于图像中心。
这再次是为了可视化。现在我们有了幅值,但它们仍然超出了我们的图像显示范围 0 到 1。我们使用cv::normalize() 函数将我们的值规范化到此范围。
一个应用理念是确定图像中存在的几何方向。例如,让我们找出文本是水平的还是不是水平的?观察一些文本,你会注意到文本行在某种程度上也形成了水平线,字母在某种程度上也形成了垂直线。文本片段的这两个主要组成部分也可能在傅里叶变换中看到。让我们使用这个水平 和 这个旋转 的关于文本的图像。
在水平文本的情况下
在旋转文本的情况下
您可以看到,频域中最有影响力的成分(幅度图像中最亮的点)遵循图像中物体的几何旋转。由此,我们可以计算偏移量并执行图像旋转以校正最终的错位。