感知哈希

感知哈希(Perceptual Image Hash) 常常被用来提取图像的指纹, 用于判定形似图片. 算法速度快被用来作为图像搜索.

算法简单, 直接贴源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int ph_dct_imagehash(const char* file,ulong64 &hash){

if (!file){
return -1;
}
CImg<uint8_t> src;
try {
src.load(file);
} catch (CImgIOException ex){
return -1;
}
// 1. 图像均值滤波, 去噪预处理
CImg<float> meanfilter(7,7,1,1,1);
CImg<float> img;
if (src.spectrum() == 3){
img = src.RGBtoYCbCr().channel(0).get_convolve(meanfilter);
} else if (src.spectrum() == 4){
int width = img.width();
int height = img.height();
int depth = img.depth();
img = src.crop(0,0,0,0,width-1,height-1,depth-1,2).RGBtoYCbCr().channel(0).get_convolve(meanfilter);
} else {
img = src.channel(0).get_convolve(meanfilter);
}

// 1. 缩放到 32x32 标准大小, 计算余弦变换
img.resize(32,32);
CImg<float> *C = ph_dct_matrix(32);
CImg<float> Ctransp = C->get_transpose();

CImg<float> dctImage = (*C)*img*Ctransp;

// 2. 选取 左上角 8x8 的余弦变换结果, 这部分是图像的低频信号, 表征图像的结构信息.
CImg<float> subsec = dctImage.crop(1,1,8,8).unroll('x');;

// 3. 统计均值和哈希: > 均值的取1, <= 均值的取 0 得到 64 位的二进制哈希, 代表图像的指纹
float median = subsec.median();
ulong64 one = 0x0000000000000001;
hash = 0x0000000000000000;
for (int i=0;i< 64;i++){
float current = subsec(i);
if (current > median)
hash |= one;
one = one << 1;
}

delete C;

return 0;
}

感知哈希通过调整第三步统计窗口的大小, 和量化的分辨率能够得到不同精度指纹.

参考