您的位置:首页 > 其它

图片相似度识别:pHash算法

2021-03-23 20:43 39 查看

1

pHash算法

  pHash中文叫感知哈希算法,通过离散余弦变换(DCT)降低图片频率,相比aHash有更好鲁棒性。

基本原理:

  1. 缩小尺寸。将图片缩小为32*32大小。

  2. 灰度化处理

  3. 计算DCT,并选取左上角8*8的矩阵。DCT是一种特殊的傅立叶变换,将图片从像素域变换为频率域,并且DCT矩阵从左上角到右下角代表越来越高频率的系数,但是除左上角外,其他地方的系数为0或接近0,因此只保留左上角的低频区域。

  4. 计算DCT均值

  5. 哈希值计算。将每个DCT值,与平均值进行比较。大于或等于平均值,记为1,小于平均值,记为0,由此生成二进制数组。(与aHash类似)

  6. 图片配对,计算汉明距离

2

DCT

一维DCT变换公式

f(i)为原始的信号,F(u)是DCT变换后的系数,N为原始信号的点数,c(u)是补偿系数。

二维DCT变换公式

二维变换是在一维变换的基础上得来的,并且上述公式可以转化为

此形式更方便计算。DCT变换是对称的,因此可以对经过DCT变换的图片进行还原操作


3

Python实现

本例中依然计算以下两张图片的相似度:

(image1)

(image2)


  • 完整算法

    这里同步给出三种hash的完整代码,便于进行效果比较。首先使用opencv进行算法实现:







































































#-*-coding:utf-8-*-
import pandas as pd
importcv2
importtime
importnumpyasnp

defpHash(img,leng=32,wid=32):
  img=cv2.resize(img,(leng,wid)) 
  gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  dct=cv2.dct(np.float32(gray))
    dct_roi = dct[0:8, 0:8]            
    avreage = np.mean(dct_roi)
  phash_01=(dct_roi>avreage)+0
  phash_list=phash_01.reshape(1,-1)[0].tolist()
    hash = ''.join([str(x) for x in phash_list])
  returnhash

defdHash(img,leng=9,wid=8):
  img=cv2.resize(img,(leng,wid))
  image=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  #每行前一个像素大于后一个像素为1,相反为0,生成哈希
  hash=[]
  foriinrange(wid):
    forjinrange(wid):
      ifimage[i,j]>image[i,j+1]:
        hash.append(1)
      else:
        hash.append(0)
  returnhash

defaHash(img,leng=8,wid=8):
  img=cv2.resize(img,(leng,wid))
  image=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
  avreage=np.mean(image)             
  hash=[]
  foriinrange(image.shape[0]):
    forjinrange(image.shape[1]):
      ifimage[i,j]>=avreage:
        hash.append(1)
      else:
        hash.append(0)
  returnhash

defHamming_distance(hash1,hash2):
  num=0
  forindexinrange(len(hash1)):
    ifhash1[index]!=hash2[index]:
      num+=1
  returnnum

if__name__=='__main__':
  image1=cv2.imread('image1')
  image2=cv2.imread('image2')
  start1=time.time()
  d_dist=Hamming_distance(dHash(image1),dHash(image2))
  end1=time.time()
  start2=time.time()
  p_dist=Hamming_distance(pHash(image1),pHash(image2))
  end2=time.time()
  start3=time.time()
  a_dist=Hamming_distance(aHash(image1),aHash(image2))
  end3=time.time()
    
  print('a_distis'+'%d'%a_dist+',similarityis'+'%f'%(1-a_dist*1.0/64)+',timeis'+'%f'%(end3-start3))
  print('p_distis'+'%d'%p_dist+',similarityis'+'%f'%(1-p_dist*1.0/64)+',timeis'+'%f'%(end2-start2))
  print('d_distis'+'%d'%d_dist+',similarityis'+'%f'%(1-d_dist*1.0/64)+',timeis'+'%f'%(end1-start1))

结果为:

下面通过PIL进行算法实现(此部分DCT变换为自己写的,如有错误欢迎指出):


















































































fromPILimportImage
importos
importnumpyasnp
importtime

#差异哈希算法
defdHash(image,leng=9,wid=8):
  image=np.array(image.resize((leng,wid),Image.ANTIALIAS).convert('L'),'f')
  hash=[]
  #每行前一个像素大于后一个像素为1,相反为0,生成哈希
  foriinrange(wid):
    forjinrange(wid):
      ifimage[i,j]>image[i,j+1]:
        hash.append(1)
      else:
        hash.append(0)
  returnhash

defaHash(image,leng=8,wid=8):
  image=np.array(image.resize((leng,wid),Image.ANTIALIAS).convert('L'),'f')
  #计算均值
  avreage=np.mean(image)
  hash=[]
  foriinrange(image.shape[0]):
    forjinrange(image.shape[1]):
      ifimage[i,j]>=avreage:
        hash.append(1)
      else:
        hash.append(0)
  returnhash

defpHash(image,leng=32,wid=32):
  image=np.array(image.resize((leng,wid),Image.ANTIALIAS).convert('L'),'f')
  A=[]
  foriinrange(0,32):
    forjinrange(0,32):
      ifi==0:
        a=np.sqrt(1/32)
      else:
        a=np.sqrt(2/32)
      A.append(a*np.cos(np.pi*(2*j+1)*i/(2*32)))
  dct=np.dot(np.dot(image,np.reshape(A,(32,32))),np.transpose(image))
  b=dct[0:8][0:8]
  hash=[]
  avreage=np.mean(b)
  foriinrange(8):
    forjinrange(8):
      ifb[i,j]>=avreage:
        hash.append(1)
      else:
        hash.append(0)
  returnhash


#计算汉明距离
defHamming_distance(hash1,hash2):
  num=0
  forindexinrange(len(hash1)):
    ifhash1[index]!=hash2[index]:
      num+=1
  returnnum

if__name__=="__main__":
  image1=Image.open('image1.png')
  image2=Image.open('image2.png')

  start1=time.time()
  d_dist=Hamming_distance(dHash(image1),dHash(image2))
  end1=time.time()
  start2=time.time()
  p_dist=Hamming_distance(pHash(image1),pHash(image2))
  end2=time.time()
  start3=time.time()
  a_dist=Hamming_distance(aHash(image1),aHash(image2))
  end3=time.time()

  print('a_distis'+'%d'%a_dist+',similarityis'+'%f'%(1-a_dist*1.0/64)+',timeis'+'%f'%(end3-start3))
  print('p_distis'+'%d'%p_dist+',similarityis'+'%f'%(1-p_dist*1.0/64)+',timeis'+'%f'%(end2-start2))
  print('d_distis'+'%d'%d_dist+',similarityis'+'%f'%(1-d_dist*1.0/64)+',timeis'+'%f'%(end1-start1))

结果为:

3

优缺点

pHash相对aHash鲁棒性更好,但速度会略慢。从上述例子也可以看出,用不同的方法最后的相似度数值不同,因此在实际应用中还需结合实际效果不断调整确定阈值。




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: