以图搜图的关键技术叫做“感知哈希算法”(Perceptual hash algorithm),用一个特征向量(“指纹”(fingerprint)字符串)来描叙一张图片,然后比较不同图像的指纹,差异越小,说明图像越相似。
1、均值hash算法
实现步骤如下:
(1)缩小尺寸,将图像缩小到8*8大小
(2)将图像灰度级减小到64级
(3)计算8*8图像大小的灰度均值
(4)比较像素的灰度,用8*8图像大小的每一个像素灰度值与均值进行比较,大于均值的为1,小于的为0
(5)将得到的结果排列成一个64位的向量,该向量就是该图的“指纹”,即hash值
如果要判断两张图是否相似,只需要计算这两张图的hash值,然后计算两个hash值的汉明距离(异或运算,不同的位为1),一般来说,汉明距离小于等于5,表示两张图比较相似,如果大于10,就基本不相似。
代码如下:
string Hash(Mat src)
{
Mat img,img1;
if (src.channels()==3)
{
cvtColor(src,img1,CV_BGR2BGRA);
}
else
{
img1 = src.clone();
}
//第一步,缩小尺寸
resize(img1, img, Size(8, 8));
//第二步,灰度级减小到64
int sum=0;
for (int i = 0; i < img.rows;i++)
{
for (int j = 0; j < img.cols;j++)
{
img.at<uchar>(i, j) = (int)img.at<uchar>(i, j)/ 4;
sum += img.at<uchar>(i, j);
}
}
//第三步,计算均值
//int average= mean(img).val[0];
int average = sum / 64;
//第四步,比较灰度大小
//第五步,计算hash值
//Mat mask = img >= (uchar)average;
string rst1(64, '\0');
int index1 = 0;
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
if (img.at<uchar>(i, j) >= (uchar)average)
{
rst1[index1] = '1';
}
else
{
rst1[index1] = '0';
}
index1++;
}
}
////第五步,计算hash值
//string rst(64, '\0');
//int index = 0;
//for (int i = 0; i < mask.rows; i++)
//{
// for (int j = 0; j < mask.cols; j++)
// {
// if (mask.at<uchar>(i,j)==0)
// {
// rst[index] = '0';
// }
// else
// rst[index] = '1';
// index++;
// }
//}
//返回hash值字符串
return(rst1);
}
2、pHash算法
hash算法计算简单,但是容易受均值的影响,另有一个增强版的算法叫pHash算法,主要区别是在减小原始图像大小的时候,采用离散余弦变换(dct)来获取图像的低频部分。
图像是一个二维信号,其低频信息代表了图像的整体轮廓,高频信息代表了图像的边缘(细节)。
实现步骤如下:
(1)缩小尺寸,将图像缩小到8*8大小
(2)彩色图转灰度图
(3)计算dct变换,求取dct系数均值(左上角8*8区块的dct系数)
(4)比较8*8区块的dct值,大于dct均值的为1,小于的为0
(5)将得到的结果排列成一个64位的向量,该向量就是该图的“指纹”,即hash值
实现代码就不贴了,跟上面的基本一样,opencv自带dct函数,dct(img, dst);
hash算法是不抗旋转和尺度变化的,不过在一定程度上,pHash算法的鲁棒性要高于均值Hash算法,而真正像google和百度的商用的以图搜图功能,是需要对其进行改进并配上其他的算法。
当然,hash算法是可以用来进行目标跟踪的,后面研究了之后再写。
总的来说,就是用一个向量来表示一张图,然后计算向量之间的差异来进行判断。
主要参考:
http://blog.csdn.net/lsjseu/article/details/17692229
http://blog.csdn.net/eternity1118_/article/details/51088019
感谢博主。