均值哈希算法是各像素灰度值与灰度均值的差异获得的哈希值。
算法步骤是:(1)图像转为灰度值;(2)计算灰度平均值;(3)逐个像素计算差异获得哈希值。
///
/// 均值计算
///
///
///
public static double Mean(double[,] A)
{
int M = A.GetLength(0);
int N = A.GetLength(1);
double mean = 0.0;
for (int y = 0; y < M; y++)
{
for (int x = 0; x < N; x++)
{
mean += A[y, x];
}
}
mean /= (M * N);
return mean;
}
均值哈希常常用于缩略图的比较。
///
/// 图像均值哈希算法(Average Hash)
///
///
///
public static byte[,] aHash(double[,] A)
{
int M = A.GetLength(0);
int N = A.GetLength(1);
double mean = Mean(A);
byte[,] r = new byte[M, N];
for (int y = 0; y < M; y++)
{
for (int x = 0; x < N; x++)
{
r[y, x] = (byte)((A[y, x] > mean) ? 1 : 0);
}
}
return r;
}
感知哈希是图像经过计算离散余弦变换(Discrete Cosine Transform,DCT)之后的值计算得到的哈希值。
DCT变换
DCT变换又称离散余弦变换(DCT for Discrete Cosine Transform)是与傅里叶变换相关的一种变换,
它类似于离散傅里叶变换(DFT for Discrete Fourier Transform),但是只使用实数。
离散余弦变换相当于一个长度大概是它两倍的离散傅里叶变换,
这个离散傅里叶变换是对一个实偶函数进行的(因为一个实偶函数的傅里叶变换仍然是一个实偶函数),
在有些变形里面需要将输入或者输出的位置移动半个单位(DCT有8种标准类型,其中4种是常见的)。
DCT变换原因:DCT变换的时候,滤掉了高频的部分,一般图形高频部分的系数是比较小的,
在量化的时候可以忽略掉,因而还原的时候就是一个反傅里叶变化,就可以得到一个图形。
对于那些颜色比较绚丽的图形,在压缩的时候,高频的系数已经不能忽略了,
如果量化的时候压缩比过高,就可能导致明显的失真。
///
/// DCT变换
///
///
public static double[,] DCT(double[,] A)
{
int M = A.GetLength(0);
int N = A.GetLength(1);
double[,] B = new double[M, N];
for (int y = 0; y < M; y++)
{
for (int x = 0; x < N; x++)
{
double ay = (y == 0) ? Math.Sqrt(1.0 / M) : Math.Sqrt(2.0 / M);
double ax = (x == 0) ? Math.Sqrt(1.0 / N) : Math.Sqrt(2.0 / N);
double tmp = 0.0;
for (int yy = 0; yy < M; yy++)
{
for (int xx = 0; xx < N; xx++)
{
tmp += A[yy, xx] * Math.Cos((2.0 * yy + 1) * y * Math.PI / (2.0 * M)) * Math.Cos((2.0 * xx + 1) * x * Math.PI / (2.0 * N));
}
}
B[y, x] = ay * ax * tmp;
}
}
return B;
}
///
/// DCT逆变换
///
///
///
public static double[,] Inverse_DCT(double[,] B)
{
int M = B.GetLength(0);
int N = B.GetLength(1);
double[,] C = new double[M, N];
for (int y = 0; y < M; y++)
{
for (int x = 0; x < N; x++)
{
double tmp = 0.0;
for (int yy = 0; yy < M; yy++)
{
for (int xx = 0; xx < N; xx++)
{
double ay = (yy == 0) ? (1.0 / Math.Sqrt(M)) : Math.Sqrt(2.0 / M);
double ax = (xx == 0) ? (1.0 / Math.Sqrt(N)) : Math.Sqrt(2.0 / N);
tmp += ay * ax * B[yy, xx] * Math.Cos(Math.PI * (2.0 * y + 1.0) * yy / (2.0 * M)) * Math.Cos(Math.PI * (2.0 * xx + 1.0) * x / (2.0 * N));
}
}
C[y, x] = (tmp);
}
}
return C;
}
无论你改变图片的高宽、亮度甚至颜色,都不会改变感知哈希值。
///
/// 图像感知哈希算法(Perceptual Hash)
///
///
///
public static byte[,] pHash(double[,] A)
{
int M = A.GetLength(0);
int N = A.GetLength(1);
double[,] B = DCT(A);
double mean = Mean(B);
byte[,] r = new byte[M, N];
for (int y = 0; y < M; y++)
{
for (int x = 0; x < N; x++)
{
r[y, x] = (byte)((B[y, x] > mean) ? 1 : 0);
}
}
return r;
}
计算差异值,获得最后哈希值(与aHash主要区别处)。比较每行左右两个像素,如果左边的像素比右边的更亮(左边像素值大于右边像素值),则记录为1,否则为0。因为每行有9个像素,左右两个依次比较可得出8个值,所以8行像素共可以得出64个值,因此此时哈希值为长度是64的0-1序列。
///
/// 图像差异哈希算法(Difference Hash)
///
///
///
public static byte[,] dHash(double[,] A)
{
int M = A.GetLength(0);
int N = A.GetLength(1);
byte[,] r = new byte[M, N];
for (int y = 0; y < M; y++)
{
for (int x = 0; x < (N - 1); x++)
{
r[y, x] = (byte)((A[y, x] > A[y, x + 1]) ? 1 : 0);
}
r[y, N - 1] = 0;
}
return r;
}
POWER BY 联高软件。