0 图像灰度化、直方图均衡、灰度线性拉伸
- 原理太简单,略~
1 图像的离散傅里叶变换与逆变换
1.1 简介
- 离散傅里叶变换 (DFT),是傅里叶变换在时域和频域上都呈现离散的形式,将时域信号的采样变换为在离散时间傅里叶变换 (DTFT) 频域的采样。在形式上,变换两端 (时域和频域上) 的序列是有限长的,而实际上这两组序列都应当被认为是离散周期信号的主值序列。即使对有限长的离散信号作 DFT,也应当将其看作经过周期延拓成为周期信号再作变换。在实际应用中通常采用快速傅里叶变换 (FFT) 以高效计算 DFT。FFT的逆变换可通过共轭->FFT->共轭->缩放实现。
1.2 公式
1.3 基2 FFT算法原理
1.4 效果
2 图像的离散余弦变换与逆变换
2.1 简介
- 离散余弦变换 (DCT for Discrete Cosine Transform) 是与傅里叶变换相关的一种变换,它类似于离散傅里叶变换 (DFT for Discrete Fourier Transform) , 但是只使用实数。离散余弦变换相当于一个长度大概是它两倍的离散傅里叶变换,这个离散傅里叶变换是对一个实偶函数进行的 (因为一个实偶函数的傅里叶变换仍然是一个实偶函数) ,在有些变形里面需要将输入或者输出的位置移动半个单位。DCT有8种标准类型,其中4种是常见的。
2.2 公式
2.3 基于FFT的快速离散余弦变换算法原理
- 借助傅里叶变换计算余弦变换的步骤:
- 把 $f(x)$ 延拓成 $f_e(x)$ ,长度为 $2N$
- 求 $f_e(x)$ 的 $2N$ 点的 FFT
- 对 $u$ 各项乘上对应的因子 $\sqrt{2}exp{-j\frac{\pi u}{2N}}$
- 取实部,并乘上因子 $\sqrt{\frac{1}{N}}$
- 取 $F(u)$ 的前 $N$ 项,即为 $f(x)$ 的余弦变换
- 同理,IDCT 可由 $F_e(u)e^{j\frac{\pi u}{2N}}$ 的 $2N$ 点的 IFFT 实现。
2.4 效果
3 基于JPEG压缩算法的图像压缩
3.1 步骤与原理
- DCT:由于 DCT 主要应用在数据和图像的压缩,因此希望原信号的能量在变换后能尽量集中在少数系数上,且这些大能量的系数能处在相对集中的位置,这将有利于进一步的量化和编码。但是如果对整段的数据或整幅图像来做 DCT,那就很难保证大能量的系数能处在相对集中的位置。因此,在实际应用中,一般都是将数据分成一段一段来做,一般分成 8x8。
- 首先将颜色空间转换为YUV,转换公式为:
- 然后将图像的像素值减去 128,将像素值控制在 -128~127 之间进行 DCT 变换。
- 由于是 8x8 矩阵的 DCT 变换,故采用基于 FFT 的算法没有优势,更由于 FFT 引入复数运算,使计算过程变得复杂。因此此处采用基于变换矩阵的算法,即:
- 程序中使用了矩阵运算的第三方 Java 库 jama ,结果表明使用变换矩阵算法的速度远远高于 FFT 和原生 DCT 算法。
- 量化:经过DCT变换后的数据,极大限度的去除了相关性,并且实现了能量的集中,那么会出现数据特别大而多数数据特别小的情况。量化的过程就是对这些数据做新的映射处理,目的是减少非“0”系数的幅度以及增加“0”值系数的数目。
- Zigzag 扫描:DCT 将一个 8x8 的数组变换成另一个 8x8 的数组. 但是内存里所有数据都是线形存放的, 如果我们一行行的存放这 64 个数字, 每行的结尾的点和下行开始的点就没有什么关系, 所以 JPEG 规定按如下顺序依次保存和读取 64 个 DCT 的系数值:从 8*8 矩阵的左上角开始,按照英文字母 Z 的形状进行扫描的,一般将其称之为 Zigzag 扫描排序。如下图所示:
- Huffman 编码:对出现概率大的字符分配字符长度较短的二进制编码,对出现概率小的字符分配字符长度较长的二进制编码,从而使得字符的平均编码长度最短。Huffman 编码时 DC 系数与 AC 系数分别采用不同的 Huffman 编码表,对于亮度和色度也采用不同的 Huffman 编码表。因此,需要 4 张 Huffman 编码表才能完成熵编码的工作。具体的 Huffman 编码采用查表的方式来高效地完成。然而,在 JPEG 标准中没有定义缺省的 Huffman 表,用户可以根据实际应用自由选择,也可以使用 JPEG 标准推荐的 Huffman 表。或者预先定义一个通用的 Huffman 表,也可以针对一副特定的图像,在压缩编码前通过搜集其统计特征来计算 Huffman 表的值。
3.2 效果
- 标题栏注明了量化参数和压缩后文件大小,单位 KB
4 基于DCT的图像数字水印嵌入与提取
4.1 步骤与原理
- 首先参照图像压缩时的步骤,将原图像按照8x8的大小分块并进行DCT变换。然后对照原图像的分块将水印图像进行适当扩展并分成同样数量的块。对于嵌入位置的选择,由于嵌入低频区域对原图像影响较大,而嵌入高频区域很容易被过滤,因此选择将水印图像嵌入8x8块的中频部分。
- 显然,嵌入位置的选择限制了水印图像的大小,使用过大的图片会导致嵌入失败。
- 提取水印时只需将携带水印的图像分块进行DCT变换,然后从每一块的中频区域提取水印图像的信息。
4.2 效果
- 由于DCT变换涉及浮点数与整数的转换,因此图像会有一定损失,但是我不太清楚是不是因为代码写错了,使得图像有如此规律的黑色竖纹。诡异的是,对于下面这幅图像就没有黑色竖纹。时间原因,没能解决这一问题。
5 边缘检测
5.1 原理
- 边缘 (edge) 是指图像局部强度变化最显著的部分。主要存在于目标与目标、目标与背景、区域与区域(包括不同色彩)之间,是图像分割、纹理特征和形状特征等图像分析的重要基础。
- 图像强度的显著变化可分为:
- 阶跃变化函数,即图像强度在不连续处的两边的像素灰度值有着显著的差异;
- 线条 (屋顶) 变化函数,即图像强度突然从一个值变化到另一个值,保持一较小行程后又回到原来的值。
- 图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈.边缘上的这种变化可以用微分算子检测出来,通常用一阶或二阶导数来检测边缘。
5.2 常用算子
5.2.1 Sobel算子
- 主要用于边缘检测,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值,Sobel 算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。Sobel 算子对于像素位置的影响做了加权,与 Prewitt 算子、Roberts 算子相比效果更好。
- Sobel 算子包含两组3x3的矩阵,分别为横向及纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。实际使用中,常用如下两个模板来检测图像边缘:
- 检测水平边沿横向模板:
- 检测垂直平边沿纵向模板:
- 图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小:
- 缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。
5.2.2 Prewitt算子
- Prewitt 算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。
- 经典 Prewitt 算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值 $T$ ,若 $P(i,j)\ge T$ ,则 $(i,j)$ 为边缘点,$P(i,j)$ 为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。
- Prewitt 算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以 Prewitt 算子对边缘的定位不如 Roberts 算子。
- 因为平均能减少或消除噪声,Prewitt 梯度算子法就是先求平均,再求差分来求梯度。水平和垂直梯度模板分别为:
- 该算子与 Sobel 算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知 Sobel 要比 Prewitt 更能准确检测图像边缘。
5.2.3 Laplace算子
- Laplace 算子是一种各向同性算子,二阶微分算子,在只关心边缘的位置而不考虑其周围的象素灰度差值时比较合适。Laplace 算子对孤立象素的响应要比对边缘或线的响应要更强烈,因此只适用于无噪声图象。存在噪声情况下,使用 Laplacian 算子检测边缘之前需要先进行低通滤波。所以,通常的分割算法都是把 Laplacian 算子和平滑算子结合起来生成一个新的模板。
- Laplace 算子也是最简单的各向同性微分算子,具有旋转不变性。一个二维图像函数的拉普拉斯变换是各向同性的二阶导数,定义:
- Laplace 算子一般不以其原始形式用于边缘检测,因为其作为一个二阶导数,Laplace 算子对噪声具有无法接受的敏感性,同时其幅值产生算边缘,这是复杂的分割不希望有的结果,最后 Laplace 算子不能检测边缘的方向,所以 Laplace 在分割中所起的作用包括:
- 利用它的零交叉性质进行边缘定位
- 确定一个像素是在一条边缘暗的一面还是亮的一面
- 一般使用的是高斯型拉普拉斯算子 (Laplace of a Gaussian,LoG) ,由于二阶导数是线性运算,利用 LoG 卷积一幅图像与首先使用高斯型平滑函数卷积改图像,然后计算所得结果的拉普拉斯是一样的。所以在 LoG 公式中使用高斯函数的目的就是对图像进行平滑处理,使用 Laplace 算子的目的是提供一幅用零交叉确定边缘位置的图像;图像的平滑处理减少了噪声的影响并且它的主要作用还是抵消由 Laplace 算子的二阶导数引起的逐渐增加的噪声影响。
5.3 效果
6 源码
MainWindow.java (应用主窗口)
import java.awt.*; |
Complex.java (复数运算)
public class Complex { |
OpenListener.java (打开图片)
import javax.imageio.ImageIO; |
GrayListener.java (图像灰度化)
import java.awt.event.ActionEvent; |
HistogramListener.java (图像直方图均衡)
import java.awt.event.ActionEvent; |
GrayStretchListener.java (图像灰度线性拉伸)
import javax.swing.*; |
FFT.java (快速傅里叶变换与逆变换算法)
import javax.imageio.ImageIO; |
fourierListener.java (快速傅里叶变换应用)
import javax.swing.*; |
cosineListener.java (基于快速傅里叶变换的快速离散余弦变换)
import javax.swing.*; |
RevokeListener.java (撤销操作)
import java.awt.event.ActionEvent; |
blockDCT.java (基于矩阵运算的离散余弦变换)
import Jama.Matrix; |
Compress.java (基于离散余弦变换的图像压缩算法)
import Jama.Matrix; |
compressListener.java (基于离散余弦变换的图像压缩应用)
import javax.imageio.ImageIO; |
waterMark.java (水印嵌入与提取算法)
import Jama.Matrix; |
watermarkListener.java (水印嵌入应用)
import javax.imageio.ImageIO; |
waterrecoverListener.java (水印提取应用)
import javax.swing.*; |
edgeListener.java (边缘检测)
import javax.swing.*; |