首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >图片相似度识别:pHash算法

图片相似度识别:pHash算法

作者头像
三猫
发布2019-10-31 11:33:57
发布2019-10-31 11:33:57
8.2K0
举报
前面已经整理了aHash和dH

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进行算法实现:

代码语言:javascript
复制
# -*- coding: utf-8 -*-
import pandas as pd
import cv2
import time
import numpy as np

def pHash(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])
    return hash

def dHash(img,leng=9,wid=8):
    img=cv2.resize(img,(leng, wid))
    image=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #每行前一个像素大于后一个像素为1,相反为0,生成哈希
    hash=[]
    for i in range(wid):
        for j in range(wid):
            if image[i,j]>image[i,j+1]:
                hash.append(1)
            else:
                hash.append(0)
    return hash

def aHash(img,leng=8,wid=8):
    img=cv2.resize(img,(leng, wid))
    image=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    avreage = np.mean(image)                           
    hash = [] 
    for i in range(image.shape[0]): 
        for j in range(image.shape[1]): 
            if image[i,j] >= avreage: 
                hash.append(1) 
            else: 
                hash.append(0) 
    return hash

def Hamming_distance(hash1,hash2):
    num = 0
    for index in range(len(hash1)):
        if hash1[index] != hash2[index]:
            num += 1
    return num 

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_dist is '+'%d' % a_dist + ', similarity is ' +'%f' % (1 - a_dist * 1.0 / 64) + ', time is ' +'%f' % (end3-start3))
    print('p_dist is '+'%d' % p_dist + ', similarity is ' +'%f' % (1 - p_dist * 1.0 / 64) + ', time is ' +'%f' % (end2-start2))
    print('d_dist is '+'%d' % d_dist + ', similarity is ' +'%f' % (1 - d_dist * 1.0 / 64) + ', time is ' +'%f' % (end1-start1))

结果为:

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

代码语言:javascript
复制
from PIL import Image
import os
import numpy as np
import time

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

def aHash(image,leng=8,wid=8):
    image = np.array(image.resize((leng, wid), Image.ANTIALIAS).convert('L'), 'f')
    #计算均值
    avreage = np.mean(image) 
    hash = [] 
    for i in range(image.shape[0]): 
        for j in range(image.shape[1]): 
            if image[i,j] >= avreage: 
                hash.append(1) 
            else: 
                hash.append(0) 
    return hash

def pHash(image,leng=32,wid=32):
    image = np.array(image.resize((leng,wid), Image.ANTIALIAS).convert('L'), 'f')
    A=[]
    for i in range(0,32):
        for j in range(0,32):
            if i==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)
    for i in range(8): 
        for j in range(8): 
            if b[i,j] >= avreage: 
                hash.append(1) 
            else: 
                hash.append(0)
    return hash


#计算汉明距离
def Hamming_distance(hash1,hash2): 
    num = 0
    for index in range(len(hash1)): 
        if hash1[index] != hash2[index]: 
            num += 1
    return num

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_dist is '+'%d' % a_dist + ', similarity is ' +'%f' % (1 - a_dist * 1.0 / 64) + ', time is ' +'%f' % (end3-start3))
    print('p_dist is '+'%d' % p_dist + ', similarity is ' +'%f' % (1 - p_dist * 1.0 / 64) + ', time is ' +'%f' % (end2-start2))
    print('d_dist is '+'%d' % d_dist + ', similarity is ' +'%f' % (1 - d_dist * 1.0 / 64) + ', time is ' +'%f' % (end1-start1))

结果为:

3

优缺点

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器学习养成记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档