前言
百度谷歌等搜索引擎有一个用图片来搜索图片的功能,如下图所示:
网上看到一篇介绍实现它的一种方法原理的文章,依据该原理方法自己动手写了一个类似功能的软件(软件含源码)。
开发环境
Windows10+VS2013+OpenCV300+Qt560
理论与代码实现
上面那篇文章实现意图搜图功能大概有以下几个步骤(注:以下理论引用自上面那篇文章,算法自己实现,只列出主要代码):
第一步:缩小尺寸:
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异,代码如下:
Mat PerceptualHashAlgorithm::reductionTo64Pixel(Mat image){
cv::Mat image64Pix;
cv::Size newsize = cv::Size(8, 8);
if (image.channels() == 1) image64Pix = cv::Mat(newsize, CV_8UC1);
else if (image.channels() == 3) image64Pix = cv::Mat(newsize, CV_8UC3);
else if (image.channels() == 4) image64Pix = cv::Mat(newsize, CV_8UC4);
else return image;
cv::resize(image, image64Pix, newsize);
return image64Pix; // 返回缩小到8*8的图像
}
第二步,简化色彩:
将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色,代码如下:
Mat PerceptualHashAlgorithm::cvtTo64LevelGray(Mat image){
Mat image64PixGray;
cvtColor(image, image64PixGray, CV_BGR2GRAY);
for (int i = 0; i < image64PixGray.rows; i++){
unsigned char *p = image64PixGray.ptr<unsigned char>(i);
for (int j = 0; j < image64PixGray.cols; j++){
p[j] = p[j] / 256. * 64.;
}
}
return image64PixGray; // 返回64级灰度
}
第三步,计算平均值
:
计算所有64个像素的灰度平均值,代码如下:
int PerceptualHashAlgorithm::calc64LevelGrayAverage(Mat image){
int sum = 0;
for (int i = 0; i < image.rows; i++){
unsigned char *p = image.ptr<unsigned char>(i);
for (int j = 0; j < image.cols; j++){
sum += p[j];
}
}
return (sum / 64); // 返回平均值
}
第四步,比较像素的灰度
:
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为255;小于平均值,记为0,代码如下:
Mat PerceptualHashAlgorithm::calcHashMask(Mat image, int ave){
hashValue = 0;
for (int i = 0; i < image.rows; i++){
unsigned char *p = image.ptr<unsigned char>(i);
for (int j = 0; j < image.cols; j++){
if (p[j] >= ave) p[j] = 255; // 大等于均值,标记255
else p[j] = 0; // 小于均值,标记0
hashValue |= ((p[j] == 0) ? (0) : (1)); // 计算哈希值
hashValue = hashValue << 1;
}
}
return image;
}
第五步,计算哈希值:
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。(注:上一步代码的hashValue就是哈希值)。
图片示意
以下用一张图片演示上面步骤的全过程:
上面全过程代码封装如下:
Mat PerceptualHashAlgorithm::calcFingerprint(Mat image){
Mat image64Pix = reductionTo64Pixel(image);
Mat image64PixGray = cvtTo64LevelGray(image64Pix);
int ave = calc64LevelGrayAverage(image64PixGray);
Mat mask = calcHashMask(image64PixGray, ave);
return mask;
}
软件展示
软件这边下载(传送门),含源码。
初始界面,可以选择训练图片或者搜索图片(当然前提是数据库中已经训练了一些图片供搜索),如下图:
训练图片的界面,选择图片(伟大的国旗)后,会进行训练生成对应的哈希值和哈希指纹图片,点击加入数据库,就会把图像和哈希指纹(哈希指纹图像加入数据库要以bmp格式加入,如果以jpg会导致压缩过程失真导致数据发生轻微变形,进行汉明距离计算的时候发生错误)加入数据库,如下图:
搜索图片界面,选择要搜索的图片后,就会进行将该图片与数据库进行比对,得到每一张图片的汉明距离,所有符合汉明距离要求的图像就是相似图像,会列出所有的相似图像的缩略图,点击对应的图像的缩略图可以看到对应的哈希指纹和汉明距离的值,如下图:
OK,大概功能就是这样子,(不过有时候识别有误,不过还算可以了,哈哈,做毕业设计的可以拿去参考参考,可以更换不同的特征特点生成类似这样的哈希指纹,原理都差不多,只不过实现的过程算法不一样)。
附录
软件和源码下载地址:http://download.youkuaiyun.com/download/kaychangeek/10244711