离散傅里叶变换
对一张图像使用傅立叶变换就是将它分解成正弦和余弦两部分,也就是将图像从空间域(spatial domain)转换到频域(frequency domain)。 这一转换的理论基础来自于以下事实:任一函数都可以表示成无数个正弦和余弦函数的和的形式。
灰度图像是一个二维数组,且为离散状态。所以图像傅里叶变换,实际上就是二维数组的傅里叶变换。图像的频率是指图像灰度变换的强烈程度,将二维图像由空间域变为频域后,图像上的每个点的值都变成了复数,也就是所谓的复频域,通过复数的实部和虚部,可以计算出幅值和相位,计算幅值即对复数取模值,将取模值后的矩阵显示出来,即为其频谱图。但是问题来了,复数取模后,数字有可能变的很大,远大于255,如果数据超过255,则在显示图像的时候会都当做255来处理,图像就成了全白色。因此,一般会对模值再取对数,在在0~255的范围内进行归一化,这样才能够准确的反映到图像上,发现数据之间的差别,区分高频和低频分量,这也是进行傅里叶变换的意义。
二维图像的傅立叶变换可以用以下数学公式表达:
傅里叶变换的动画过程如下:
API
dft最优尺寸大小函数
public static int getOptimalDFTSize(int vecsize)
边缘填充函数
public static void copyMakeBorder(Mat src, Mat dst, int top, int bottom, int left, int right, int borderType, Scalar value)
- 参数一:src,待填充的原图;
- 参数二:dst,填充结果存放图;
- 参数三:top,顶部填充像素数量;
- 参数四:bottom,底部填充像素数量;
- 参数五:left,左侧填充像素数量;
- 参数六:right,右侧填充像素数量;
- 参数七:borderType,填充类型;
- 参数八:value,填充像素值,borderType==BORDER_CONSTANT。
离散傅里叶变换
public static void dft(Mat src, Mat dst, int flags)
- 参数一:src,待变换原图;
- 参数二:dst,转换后的图像;
- 参数三:flags,转换标志。
public static final int
DFT_INVERSE = 1,
DFT_SCALE = 2,
DFT_ROWS = 4,
DFT_COMPLEX_OUTPUT = 16,
DFT_REAL_OUTPUT = 32,
DFT_COMPLEX_INPUT = 64,
DCT_INVERSE = DFT_INVERSE,
DCT_ROWS = DFT_ROWS;
计算幅度
dst
(
I
)
=
x
(
I
)
2
+
y
(
I
)
2
\texttt{dst} (I) = \sqrt{\texttt{x}(I)^2 + \texttt{y}(I)^2}
dst(I)=x(I)2+y(I)2
?
public static void magnitude(Mat x, Mat y, Mat magnitude)
- 参数一:x,向量 x 坐标的浮点数组;
- 参数二:y,向量 y 坐标的浮点数组,大小与 x 相同;
- 参数三:magnitude,输出结果。
场景
空间域转频域
val padded = Mat()
val m = Core.getOptimalDFTSize(gray.rows())
val n = Core.getOptimalDFTSize(gray.cols())
Core.copyMakeBorder(
gray,
padded,
0,
m - gray.rows(),
0,
n - gray.cols(),
BORDER_CONSTANT,
Scalar.all(0.0)
)
val planes = mutableListOf<Mat>()
planes.add(padded)
planes.add(Mat.zeros(padded.size(), CvType.CV_32FC1))
val complexImg = Mat()
Core.merge(planes, complexImg)
Core.dft(complexImg, complexImg);
var mag = Mat()Core.magnitude(planes[0], planes[1], mag)
- 对数尺度(logarithmic scale)缩放
Core.add(mag, Scalar(1.0), mag)Core.log(mag, mag)
mag = Mat(mag, Rect(0, 0, mag.cols() and -2, mag.rows() and -2))val cx = mag.cols() / 2val cy = mag.rows() / 2val tmp = Mat()val q0 = Mat(mag, Rect(0, 0, cx, cy))val q1 = Mat(mag, Rect(cx, 0, cx, cy))val q2 = Mat(mag, Rect(0, cy, cx, cy))val q3 = Mat(mag, Rect(cx, cy, cx, cy))q0.copyTo(tmp)q3.copyTo(q0)tmp.copyTo(q3)q1.copyTo(tmp)q2.copyTo(q1)tmp.copyTo(q2)
Core.normalize(mag, mag, 0.0, 255.0, Core.NORM_MINMAX, CvType.CV_8UC1, Mat())mag.convertTo(mag, CvType.CV_8U)
盲水印
图像盲水印将水印信息以不可见的形式添加到原图信息中,可对疑似被盗取的资源进行盲水印提取,验证图片归属。利用傅里叶变换和逆变换,盲水印可以简单实践。
添加水印步骤:
- 图像由空间域转为频域
- 频域图像添加水印
- 图像由频率转为空间域
解析水印步骤:
针对 BGR 三通道图像,利用 BGR 图像内任意一个通道进行空间域和频域的转换并添加水印即可。
实际效果如下:
|