感知哈希算法:pHash算法实现图像相似度比较(附完整c++代码)

数字内容安全课实验

感知哈希算法

在浏览器的图片搜索中,用户可以上传一张图片,浏览器显示因特网中与此图片相同或者相似的图,实现这种功能的关键技术叫做"感知哈希算法"(Perceptual Hash Algorithm), 意思是为图片生成一个指纹(字符串格式), 两张图片的指纹越相似, 说明两张图片就越相似。感知哈希算法是一类算法的总称,包括

  • aHash:平均值哈希
  • pHash:感知哈希
  • dHash:差异值哈希

在这里我使用的是pHash算法,即对图像进行DCT变换,获取DCT系数均值,基于其变换域特征来实现图片相似度计算。

实现原理

  • 第一步 缩小图片尺寸
    将图片缩小到32x32的尺寸, 这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构、明暗等基本信息
    (我一开始缩小尺寸到8x8,但检测后发现性能很差,于是改成32x32,性能得到大幅提高)

  • 第二步 转为灰度图片
    将缩小后的图片转为灰度图片

  • 第三步 计算DCT
    计算DCT,但只取左上角8*8的矩阵,这部分呈现了图片中的最低频率

  • 第四步 计算平均值
    计算DCT系数平均值

  • 第五步 计算哈希值
    将每个像素的灰度与DCT系数平均值进行比较, 如果大于或等于平均值记为1, 小于平均值记为0,结果组合在一起就构成了一个64位的二进制整数, 这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了

  • 第六步 对比图片指纹
    得到图片的指纹后, 就可以对比不同的图片的指纹, 计算出64位中有多少位是不一样的. 如果不相同的数据位数不超过5, 就说明两张图片很相似, 如果大于10, 说明它们是两张不同的图片

实验环境

  • vs2017
  • opencv库

代码实现

#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;

}

实验材料

使用以下两张图片,并对其做翻转、旋转、调节亮度饱和度等操作
Zelda
在这里插入图片描述
peppers
在这里插入图片描述

实验结果

  • Zelda vs Zelda
    汉明距离:0
    在这里插入图片描述

  • peppers vs Zelda
    汉明距离:28

  • peppers vs 翻转后的peppers
    汉明距离:29

  • peppers vs 旋转后的peppers
    汉明距离:28

  • peppers vs 调节亮度色彩饱和度的peppers
    汉明距离:4

  • peppers vs 调节大小后的peppers
    汉明距离:1

算法性能分析

由实验结果可以看出,pHash对于图像的旋转、翻转不具有鲁棒性,但由于经过DCT变换后提取了其低频成分(包含了图像主要内容信息),所以pHash算法对于图像比例大小、亮度饱和度等变化的抵抗力较强。我还测试了同一个视频截取的两张连续帧,但算法结果显示其相似度并不高,所以此基本算法的性能和应用还有局限性,例如无法根据图片颜色进行相似度的比对,因此可以根据具体的应用要求来对算法作出改进。

参考资料

openCV中利用感知哈希算法实现图片相似度计算https://jingyan.baidu.com/article/915fc414a461b451394b20f9.html

  • 7
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值