感知哈希算法
感知哈希算法(Perceptual Hash Algorithm)是Neal Krawetz博士提出一种图片相似度检索算法,它的作用是为每张图片生成一个“指纹”(Fingerprint)字符串,然后比较不同图片的指纹,结果越接近,就说明图片越相似。
1实现原理
(1)缩小图片尺寸。将图片缩小到8x8的尺寸,总共64个像素,作用是去除各种图片尺寸和图片比例的差异,只保留结构、明暗等基本信息。
(2)转为灰度图片。将缩小后的图片,转为64级灰度图片。
(3)计算灰度平均值。计算图片中所有像素的灰度平均值。
(4)比较像素的灰度。将每个像素的灰度与平均值进行比较,如果大于或等于平均值记为1,小于平均值记为0。
(5)计算哈希值。将上一步的比较结果,组合在一起,就构成了一个64位的二进制整数即图片指纹。
(6)对比图片指纹,计算汉明距离。得到图片的指纹后,就可以对比不同的图片的指纹,计算出64位中有多少位是不一样的即汉明距离。如果不相同的数据位数不超过5,就说明两张图片很相似,如果大于10,说明它们是两张不同的图片。
2计算哈希指纹
均值Hash算法
string hashValue(Mat &src)
{
string rst(64, '\0');
Mat img;
if(src.channels() == 3) {
cvtColor(src, img, CV_BGR2GRAY);
} else {
img = src.clone();
}
// 第一步,缩小尺寸。将图片缩小到8x8的尺寸,总共64个像素,去除图片的细节。
resize(img, img, Size(8, 8));
// 第二步,简化色彩(Color Reduce)。将缩小后的图片,转为64级灰度。
uchar *pData;
for(int i = 0; i < img.rows; i++)
{
pData = img.ptr<uchar>(i);
for(int j = 0; j < img.cols; j++)
{
pData[j] = pData[j] / 4;
}
}
// 第三步,计算平均值。计算所有64个像素的灰度平均值。
int average = mean(img).val[0];
// 第四步,比较像素的灰度。将每个像素的灰度,与平均值进行比较。大于或等于平均值记为1,小于平均值记为0。
Mat mask = (img >= (uchar)average);
// 第五步,计算哈希值。
int index = 0;
for(int i=0;i<mask.rows;i++)
{
pData = mask.ptr<uchar>(i);
for(int j=0;j<mask.cols;j++)
{
if(pData[j]==0) {
rst[index++] = '0';
} else {
rst[index++] = '1';
}
}
}
return rst;
}
pHash算法,相对Hash算法鲁棒性稍强一些。
string pHashValue(Mat &src)
{
Mat img ,dst;
string rst(64,'\0');
double dIdex[64];
double mean = 0.0;
int k = 0;
if(src.channels()==3) {
cvtColor(src,src,CV_BGR2GRAY);
img = Mat_<double>(src);
} else {
img = Mat_<double>(src);
}
// 第一步,缩放尺寸。
resize(img, img, Size(8,8));
// 第二步,离散余弦变换,DCT系数求取。
dct(img, dst);
// 第三步,求取DCT系数均值(左上角8*8区块的DCT系数)。
for (int i = 0; i < 8; ++i)
{
for (int j = 0; j < 8; ++j)
{
dIdex[k] = dst.at<double>(i, j);
mean += dst.at<double>(i, j) / 64;
++k;
}
}
// 第四步,计算哈希值。
for (int i =0;i<64;++i)
{
if (dIdex[i]>=mean) {
rst[i] = '1';
} else {
rst[i] = '0';
}
}
return rst;
}
3计算汉明距离
int hanmingDistance(string &str1,string &str2)
{
if((str1.size() != 64) || (str2.size() != 64)) return -1;
int difference = 0;
for(int i = 0; i < 64; i++)
{
if(str1[i] != str2[i]) difference++;
}
return difference;
}
4MySQL下的指纹存储与检索
(1)字段类型
`hash` bit(64) NOT NULL COMMENT '图片指纹'
(2)添加数据
insert into table_name (`hash`) values (b'1010010001010100101101011000010010110101101000010001010110100100')
(3)汉明距离查询
select hash, LPAD(BIN(hash), 64, '0') as phash, BIT_COUNT(`hash` ^ b'1001010000110100100101001011010010010110010100100001101010000001') as distance from table_name where BIT_COUNT(`hash` ^ b'1001010000110100100101001011010010010110010100100001101010000001') <= 10 order by distance asc