欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-09 23:15:22
...

数字内容安全课实验

感知哈希算法

在浏览器的图片搜索中,用户可以上传一张图片,浏览器显示因特网中与此图片相同或者相似的图,实现这种功能的关键技术叫做"感知哈希算法"(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
感知哈希算法:pHash算法实现图像相似度比较(附完整c++代码)
peppers
感知哈希算法:pHash算法实现图像相似度比较(附完整c++代码)

实验结果

  • Zelda vs Zelda
    汉明距离:0
    感知哈希算法:pHash算法实现图像相似度比较(附完整c++代码)

  • 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

相关标签: 实验报告