在上一篇博客里介绍了图片相似计算中的最简单的一个ahash, 其是基于内容检索最简单的一种,这篇介绍它的增强版感知哈希算法(phash—perceptual hash)。主要也是用缩略图搜索原图并能达到较好点的效果。
其实现一共包含以下几个步骤:
- 图像缩放–将图像缩放到32*32大小,这一步的作用是取出各种图片尺寸和图片比例的差异,只保留结构、明暗等信息。
- 灰度化–将32*32大小的图像进行灰度化
- 离散余弦变换(DCT)–对32*32大小图像进行DCT
- 计算均值–用3232大小图片前面88的像素值中大于均值的则用1表示,小于的用0表示,这样就得到一个二进制码作为该图像的phash值(保留左上角的8*8,这些代表的图片的最低频率)
- 计算两幅图像的ahash值的汉明距离,距离越小,表明两幅图像越相似;距离越大,表明两幅图像越不相似。
代码实现
#include <opencv2\opencv.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\objdetect\objdetect.hpp>
#include <opencv2\imgproc\types_c.h>
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
using namespace cv;
string pHashValue(Mat &srcImg);//pHash方法计算图片哈希值
int hanmingDist(string &str1, string &str2);//计算哈希值字符串的汉明距离
int main()
{
Mat orgImg = imread("图片1路径");
Mat img = imread("图片2路径");
imshow("OrgImg", orgImg);
imshow("Img", img);
string str1 = pHashValue(orgImg);
string str2 = pHashValue(img);
int distance = hanmingDist(str1, str2);
cout << "两张图片的汉明距离:" << distance << endl;
if (distance < 5) //若汉明距离小于5,则两张图片相似
{
cout << "两张图片相似" << endl;
}
else
{
cout << "两张图片不相似" << endl;
}
waitKey(0);
return 0;
}
string pHashValue(Mat &srcImg)//pHash方法计算图片哈希值
{
Mat img, dstImg;
string rst(64, '\0');
double dIndex[64];
double mean = 0.0;
int k = 0;
if (srcImg.channels() == 3)//若为彩色图像则转换为灰度图像
{
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
img = Mat_<double>(srcImg);
}
else
{
img = Mat_<double>(srcImg);
}
//缩放尺寸
resize(img, img, Size(32, 32));
//离散余弦变换 DCT
dct(img, dstImg);
//获取dct系数均值
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
dIndex[k] = dstImg.at<double>(i, j);
//计算每个像素的均值
mean += dstImg.at<double>(i, j) / 64;
++k;
}
}
//计算hash
for (int i = 0; i < 64; ++i)
{
if (dIndex[i] >= mean)
{
rst[i] = '1';
}
else {
rst[i] = '0';
}
}
return rst;
}
int hanmingDist(string &str1, string &str2)//计算哈希值字符串的汉明距离
{
if ((str1.size() != 64) || (str2.size() != 64))
{
return -1;
}
int distValue = 0;
for (int i = 0; i < 64; i++)
{
if (str1[i] != str2[i])
{
distValue++;
}
}
return distValue;
}