失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 图片相相似度计算(Hash SSIM compareHist)

图片相相似度计算(Hash SSIM compareHist)

时间:2019-10-08 23:04:30

相关推荐

图片相相似度计算(Hash SSIM compareHist)

哈希相似度算法(Hash algorithm)

用一个快速算法,就达到基本的效果。哈希算法(Hash algorithm),它的作用是对每张图片生成一个固定位数的Hash 值(指纹 fingerprint)字符串,然后比较不同图片的指纹,结果越接近,就说明图片越相似。一般有如下三种生成Hash 值方法:

差值DHash

缩小尺寸:将图片缩小到8x9的尺寸,总共72个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。简化色彩:将缩小后的图片,转为64级灰度(或者256级也行)。计算平均值:计算所有64个像素的灰度平均值。比较:同行相邻间对比,像素值大于后一个像素值记作1,相反记作0。每行9个像素,8个差值,有8行共64位计算哈希值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

均值AHash

缩小尺寸:将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。简化色彩:将缩小后的图片,转为64级灰度。计算平均值:计算所有64个像素的灰度平均值。比较:像素值大于平均值记作1,相反记作0,总共64位。计算哈希值:将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

感知 PHash

感知哈希算法可以获得更精确的结果,它采用的是DCT(离散余弦变换)来降低频率。

缩小尺寸

为了简化了DCT的计算,pHash以小图片开始(建议图片大于8x8,32x32)。简化色彩

与aHash相同,需要将图片转化成灰度图像,进一步简化计算量(具体算法见aHash算法步骤)。计算DCT

DCT是把图片分解频率聚集和梯状形,将空域的信号转换到频域上,具有良好的去相关性的性能。变换后DCT系数能量主要集中在左上角,其余大部分系数接近于零,DCT具有适用于图像压缩的特性。缩小DCT

DCT的结果为32x32大小的矩阵,但只需保留左上角的8x8的矩阵,这部分呈现了图片中的最低频率。计算平均值

同均值哈希一样,计算8x8的DCT矩阵的均值计算Phash值

根据8x8的DCT矩阵进行比较,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。组合成64个bit位生成hash值,顺序随意但前后保持一致性即可。

计算相似度(距离)

得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算汉明距离(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。

具体的代码实现python语言。

import cv2import numpy as np# 均值哈希算法def aHash(img):# 缩放为8*8img = cv2.resize(img, (8, 8))# 转换为灰度图gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# s为像素和初值为0,hash_str为hash值初值为''s = 0hash_str = ''# 遍历累加求像素和for i in range(8):for j in range(8):s = s + gray[i, j]# 求平均灰度avg = s / 64# 灰度大于平均值为1相反为0生成图片的hash值for i in range(8):for j in range(8):if gray[i, j] > avg:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_str# 差值感知算法def dHash(img):img = cv2.resize(img, (9, 8))gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)hash_str = ''# 每行前一个像素大于后一个像素为1,相反为0,生成哈希for i in range(8):for j in range(8):if gray[i, j] > gray[i, j + 1]:hash_str = hash_str + '1'else:hash_str = hash_str + '0'return hash_str# 感知哈希算法(pHash)def pHash(img):img = cv2.resize(img, (32, 32)) # , interpolation=cv2.INTER_CUBICgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 将灰度图转为浮点型,再进行dct变换dct = cv2.dct(np.float32(gray))# opencv实现的掩码操作dct_roi = dct[0:8, 0:8]hash = []avreage = np.mean(dct_roi)for i in range(dct_roi.shape[0]):for j in range(dct_roi.shape[1]):if dct_roi[i, j] > avreage:hash.append(1)else:hash.append(0)return hash# Hash值对比def cmpHash(hash1, hash2):n = 0# hash长度不同则返回-1代表传参出错if len(hash1)!=len(hash2):return -1# 遍历判断for i in range(len(hash1)):# 不相等则n计数+1,n最终为相似度if hash1[i] != hash2[i]:n = n + 1return nif __name__ == '__main__':img1 = cv2.imread('./1.png') img2 = cv2.imread('./2.png')hash1 = aHash(img1)hash2 = aHash(img2)n = cmpHash(hash1, hash2)print('均值哈希算法相似度:', n)hash1 = dHash(img1)hash2 = dHash(img2)n = cmpHash(hash1, hash2)print('差值哈希算法相似度:', n)hash1 = pHash(img1)hash2 = pHash(img2)n = cmpHash(hash1, hash2)print('感知哈希算法相似度:', n)

使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。

这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。如果在图片上加几个文字,它就认不出来了。

比较三种方法

均值Hash比感知显著地要快。如果你找一些明确的东西,均值Hash能算法快速地找到它,所以,它的最佳用途是根据缩略图,找出原图。如果图片有些修改,如过都添加了一些内容或头部叠加在一起,均值Hash就无法处理,虽然感知Hash比较慢,但它能很好地容忍一些小的变型(变型度小于25%的图片)。相比感知Hash,差值Hash的速度更快,相比均值Hash,差值Hash在效率几乎相同的情况下的效果要更好

结构性相似度SSIM

在两幅图的距离时,更偏重于两图的结构相似性,而不是逐像素计算两图的差异。提出了基于 structural similarity 的度量,声称其比 MSE 更能反映人类视觉系统对两幅图相似性的判断。把两幅图 x, y 的相似性按三个维度进行比较:

亮度(对应均值)(luminance)l(x,y),

对比度(对应方差)(contrast)c(x,y),

结构(对应协方差)(structure)s(x,y),

令C3=C2/2,c(x,y)的分子和s(x,y)的分母可以约分,最终 x 和 y 的相似度为这三者的函数,默认每个项的重要性最后是相等的:

上述中μx,μy,σx,σy,σxy\mu{x},\mu{y},\sigma{x},\sigma{y},\sigma{xy}μx,μy,σx,σy,σxy分别为x图像均值,y图像均值,x图像方差,y图像方差,两图像协方差,C1,C2C1,C2C1,C2为非零的小的常数避免分母为零。

通常, SSIM 不能用于一整幅图, 因为在整幅图的跨度上,均值和方差往往变化剧烈;同时,图像上不同区块的失真程度也有可能不同,不能一概而论;此外类比人眼睛每次只能聚焦于一处的特点。作者采用 sliding window 以步长为 1 计算两幅图各个对应 sliding window 下的 patch 的 SSIM,然后取平均值作为两幅图整体的 SSIM,称为 Mean SSIM。简写为 MSSIM(不同于multi-scale SSIM:MS-SSIM )。

如下为计算MSSIM的一种c++代码,使用了11*11的对称高斯加权函数作为加权窗口,标准差为1.5,C1 = 6.5025, C2 = 58.5225。

#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>using namespace std; using namespace cv;Scalar CalcMSSIM(Mat inputimage1, Mat inputimage2){Mat i1 = inputimage1;Mat i2 = inputimage2;const double C1 = 6.5025, C2 = 58.5225;int d = CV_32F;Mat I1, I2;i1.convertTo(I1, d);i2.convertTo(I2, d);Mat I2_2 = I2.mul(I2);Mat I1_2 = I1.mul(I1);Mat I1_I2 = I1.mul(I2);Mat mu1, mu2;GaussianBlur(I1, mu1, Size(11, 11), 1.5);GaussianBlur(I2, mu2, Size(11, 11), 1.5);Mat mu1_2 = mu1.mul(mu1);Mat mu2_2 = mu2.mul(mu2);Mat mu1_mu2 = mu1.mul(mu2);Mat sigma1_2, sigma2_2, sigma12;GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);sigma1_2 -= mu1_2;GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);sigma2_2 -= mu2_2;GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);sigma12 -= mu1_mu2;Mat t1, t2, t3;t1 = 2 * mu1_mu2 + C1;t2 = 2 * sigma12 + C2;t3 = t1.mul(t2);t1 = mu1_2 + mu2_2 + C1;t2 = sigma1_2 + sigma2_2 + C2;t1 = t1.mul(t2);Mat ssim_map;divide(t3, t1, ssim_map);Scalar mssim = mean(ssim_map);return mssim;}int main( int argc, char** argv ){src_1 = imread("./1.jpg");src_2 = imread("./2.jog");Scalar result = CalcMSSIM(src_1,src_2);cout<<"the r g b channles similarity is :"<<result<<endl;}

直方图相似度函数compareHist

图像处理之相似图片识别函数compareHist包含了如下四种方法;

1;Correlation 相关性比较 [1,0] 越大越相似

2;Chi-Square 卡方比较 [0,+200] 越小越相似

3;Intersection 十字交叉性 [+50,0] 越大越相似

4;Bhattacharyya distance 巴氏距离 [0, 1] 越小越相似

算法概述:

首先对源图像与要筛选的图像进行直方图数据采集,对采集的各自图像直方图进行归一化;然后使用compareHist函数对直方图数据进行计算,最终得出图像相似度值。

第一步:直方图计算

直方图分为灰度直方图与RGB直方图,对于灰度图像直方图计算十分简单,只要初始化一

个大小为256的直方图数组H,然后根据像素值完成频率分布统计,假设像素值为124,则

H[124] += 1, 而对于彩色RGB像素来说直方图表达有两种方式,一种是单一直方图,另外一

种是三维直方图,三维直方图比较简单明了,分别对应RGB三种颜色,定义三个直方图HR,

HG, HB, 假设某一个像素点P的RGB值为(4, 231,129), 则对于的直方图计算为HR[4] += 1,

HG[231] += 1, HB[129] += 1, 如此对每个像素点完成统计以后,RGB彩色直方图数据就生成了。

而RGB像素的单一直方图SH表示稍微复杂点,每个颜色的值范围为0 ~ 255之间的,假设

可以分为一定范围等份,当8等份时,每个等份的值范围为32, 16等份时,每个等份值范

围为16,当4等份时候,每个等份值的范围为64,假设RGB值为(14, 68, 221), 16等份之

后,它对应直方图索引值(index)分别为: (0, 4, 13), 根据计算索引值公式:index = R + G16 + B16*16

对应的直方图index = 0 + 4*16 + 13 * 16 * 16, SH[3392] += 1

如此遍历所有RGB像素值,完成直方图数据计算。

第二步: 直接使用compareHist函数,选择合适参数计算即可

c++实现

#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include <iostream>#include <stdio.h>using namespace std;using namespace cv;int main( int argc, char** argv ){src_1 = imread("./1.jpg");src_2 = imread("./2.jog");// 转换到 HSV , 图片是RGB格式用CV_RGB2HSVcvtColor( src_1 , src_1 , CV_BGR2HSV );cvtColor( src_12, src_12, CV_BGR2HSV );// 对hue通道使用30个bin,对saturatoin通道使用32个binint h_bins = 50; int s_bins = 60;int histSize[] = {h_bins, s_bins };// hue的取值范围从0到256, saturation取值范围从0到180float h_ranges[] = {0, 256 };float s_ranges[] = {0, 180 };const float* ranges[] = {h_ranges, s_ranges };// 使用第0和第1通道int channels[] = {0, 1 };// 直方图MatND src_1_hist,src_2_hist;// 计算HSV图像的直方图calcHist( &src_1 , 1, channels, Mat(), src_1_hist, 2, histSize, ranges, true, false );normalize( src_1_hist, src_1_hist, 0, 1, NORM_MINMAX, -1, Mat() );calcHist( &src_2 , 1, channels, Mat(), src_2_hist, 2, histSize, ranges, true, false );normalize( src_2_hist, src_2_hist, 0, 1, NORM_MINMAX, -1, Mat() );//4种对比方法for( int i = 0; i < 4; i++ ){int compare_method = i;double result = compareHist( src_1_hist, src_2_hist, compare_method );printf( " Method [%d] Perfect,result= %f \n", i, result );}return 0;}

python实现

from PIL import Imagedef make_regalur_image(img, size = (256, 256)):return img.resize(size).convert('RGB')def hist_similar(lh, rh):assert len(lh) == len(rh)return sum(1 - (0 if l == r else float(abs(l - r))/max(l, r)) for l, r in zip(lh, rh))/len(lh)def calc_similar(li, ri):return hist_similar(li.histogram(), ri.histogram())if __name__ == '__main__':img1 = Image.open('./1.jpg')img2 = Image.open('./2.jpg')img1 = make_regalur_image(img1)img2 = make_regalur_image(img2)print(calc_similar(img1, img2))

总结

对比三种方法来说稳定性较好的是MSSIM方法,直方图过于简单,只能捕捉颜色信息的相似性,捕捉不到更多的信息。只要颜色分布相似,就会判定二者相似度较高,显然不合理。

基于互信息(Mutual Information)

如果两张图片尺寸相同,还是能在一定程度上表征两张图片的相似性的。但是,大部分情况下图片的尺寸不相同,如果把两张图片尺寸调成相同的话,又会让原来很多的信息丢失,应用中此种方法的确很难把握。

哈希算法

可以实现快速搜索简略图

如果觉得《图片相相似度计算(Hash SSIM compareHist)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。