精华内容
下载资源
问答
  • phash
    2021-06-08 05:24:50

    6bf81bd4a385e0d5feef5cbae2b34579.gif

    本发明涉及一种基于改进PHash算法的图像相似判定方法,属于图像处理技术领域。

    背景技术:

    图像相似判定是图像处理领域的一项基本研究,指对两张或多张图像的内容相似程度进行分析、比较,广泛应用于图像检索、目标检测、照片过滤等领域。

    目前,有多种图像相似判定的方法,就感知哈希算法(PHash)而言,其准确率及效率并不尽人意,一个主要原因就是感知哈希算法(PHash)没有考虑到图像旋转的情况,若对图像进行上下或左右反转后再比对,其结果较不理想。

    技术实现要素:

    本发明要解决的技术问题是针对现有技术的局限和不足,提供一种基于改进PHash算法的图像相似判定方法,以解决现有技术在对图像相似判定时存在的未考虑图像旋转、灵活性不足、准确度低等现象,致力于提高利用PHash算法对图像相似判定的灵活性和准确度。

    本发明的技术方案是:一种基于改进PHash算法的图像相似判定方法,首先通过基准图及考察图对图像进行全方位旋转操作,生成上、下、左、右、左上、左下、右上、右下等8张剪裁旋转图,其后通过PHash算法对图像进行Hash化,两两计算剪裁旋转图之间基于PHash的汉明距离,并取其最小值作为图像之间基于PHash的汉明距离;若基于PHash的汉明距离小于阈值,则判定为图像相似;若基于PHash的汉明距离大于阈值,则判定为图像不相似。

    具体步骤为:

    Step1:获取图像A和图像B;

    Step2:将图像A、B作为基准图,并在图像A、B中相同位置截取相同大小的图像作为考察图

    Step3:对图像A、B分别进行一定规则的方向旋转变化,得到新的图像A″m,m∈[1,8]和B″n,n∈[1,8];具体如Step3.1~Step3.4所示;

    Step3.1:记考察图的大小为M×N,其中心位置坐标为(x,y);

    Step3.2:以中心位置坐标(x,y)作为基准图像A、B的中心,重新在基准图上截取大小为M′×N′的截取后的图像A′、B′,其中M′≥M,N′≥N;

    Step3.3:对截取后的图像A′、B′进行45°t,t∈[1,8]方向旋转变化,得到旋转后的图像A′m,m∈[1,8]和B′n,n∈[1,8];

    Step3.4:对图像A′m,m∈[1,8]和B′n,n∈[1,8]以中心位置坐标(x,y)为中心,截取大小为M×N的图像,得到最终的旋转图像A″m,m∈[1,8]和B″n,n∈[1,8];

    Step4:通过PHash算法对图像A″m,m∈[1,8]和图像B″n,n∈[1,8]进行Hash化,得到图像A″m、B″n之间基于PHash的汉明距离Dis(A″m,B″n),m∈[1,8],n∈[1,8];具体如Step4.1~Step4.6所示;

    Step4.1:将图像A″m、B″n的大小压缩至32×32;

    Step4.2:将压缩后的图像A″m、B″n转化为256阶灰度图像,并表示为矩阵形式其中为矩阵元素值;

    Step4.3:计算矩阵的DCT矩阵其中矩阵元素值表示为

    Step4.4:保留矩阵的左上角8×8部分,删除其余部分;

    Step4.5:计算矩阵的平均值将矩阵按照从上至下(i=1→8)、从左至右(j=1→8)的规则,根据公式(1)进行值优化,并对其连接生成图像A″m、B″n的PHash值其中T(i,j)代表适用于u代表适用于

    Step4.6:通过图像A″m、B″n的PHash值计算图像A″m、B″n之间基于PHash的汉明距离Dis(A″m,B″n),m∈[1,8],n∈[1,8];

    Step5:根据图像A″m、B″n两两组合得到Max(m)×Max(n)=8×8=64个汉明距离,根据公式(2)选出其中最小值作为图像A、B之间基于PHash的汉明距离Dis(A,B);

    Dis(A,B)=Min(Dis(A″m,B″n)),m∈[1,8],n∈[1,8] (2)

    Step6:定义汉明距离阈值若图像A、B之间基于PHash的汉明距离Dis(A,B)满足公式(3)要求,则判定图像A和图像B相似;若满足公式(4)要求,则判定图像A和图像B不相似。

    进一步地,所述步骤Step1中,图像A和图像B可以为任意大小的图像,且图像格式任意。

    进一步地,所述步骤Step3.2中,截取的图像的大小要大于考察图像的大小,即满足M′≥M,N′≥N的要求,作用是为了保证旋转后得到的图像在区域内没有空白区域。

    进一步地,所述步骤Step4.4中,不一定非要保留矩阵的左上角8×8部分,也可以保存其他局部矩阵,相应的汉明距离阈值要根据矩阵的元素个数进行更改。

    进一步地,所述步骤Step4.5中,汉明距离指两串Hash值进行异或运算后,结果为“1”的个数。

    进一步地,所述步骤Step5中为了降低处理速度,去除重复后可简化计算至1+2+…+Max(m,n)=36个汉明距离。

    进一步地,所述步骤Step6中,汉明距离阈值通常取可自行修改。

    本发明的有益效果是:解决了现有技术在对图像相似判定时存在的未考虑图像旋转、灵活性不足、准确度低等现象,提高了利用PHash算法对图像相似判定的灵活性和准确度。

    附图说明

    图1是本发明流程示意图;

    图2是本发明对图像进行旋转过程示意图。

    具体实施方式

    下面结合附图和具体实施方式,对本发明作进一步说明。

    实施例1:如图1-2所示,一种基于改进PHash算法的图像相似判定方法,首先通过基准图及考察图对图像进行全方位旋转操作,生成上、下、左、右、左上、左下、右上、右下等8张剪裁旋转图,其后通过PHash算法对图像进行Hash化,两两计算剪裁旋转图之间基于PHash的汉明距离,并取其最小值作为图像之间基于PHash的汉明距离;若基于PHash的汉明距离小于阈值,则判定为图像相似;若基于PHash的汉明距离大于阈值,则判定为图像不相似。

    具体步骤为:

    Step1:获取图像A和图像B;

    Step2:将图像A、B作为基准图,并在图像A、B中相同位置截取相同大小的图像作为考察图

    Step3:对图像A、B分别进行一定规则的方向旋转变化,得到新的图像A″m,m∈[1,8]和B″n,n∈[1,8];具体如Step3.1~Step3.4所示;

    Step3.1:记考察图的大小为M×N,其中心位置坐标为(x,y);

    Step3.2:以中心位置坐标(x,y)作为基准图像A、B的中心,重新在基准图上截取大小为M′×N′的截取后的图像A′、B′,其中M′≥M,N′≥N;

    Step3.3:对截取后的图像A′、B′进行45°t,t∈[1,8]方向旋转变化,得到旋转后的图像A′m,m∈[1,8]和B′n,n∈[1,8];

    Step3.4:对图像A′m,m∈[1,8]和B′n,n∈[1,8]以中心位置坐标(x,y)为中心,截取大小为M×N的图像,得到最终的旋转图像A″m,m∈[1,8]和B″n,n∈[1,8];

    Step4:通过PHash算法对图像A″m,m∈[1,8]和图像B″n,n∈[1,8]进行Hash化,得到图像A″m、B″n之间基于PHash的汉明距离Dis(A″m,B″n),m∈[1,8],n∈[1,8];具体如Step4.1~Step4.6所示;

    Step4.1:将图像A″m、B″n的大小压缩至32×32;

    Step4.2:将压缩后的图像A″m、B″n转化为256阶灰度图像,并表示为矩阵形式其中为矩阵元素值;

    Step4.3:计算矩阵的DCT矩阵其中矩阵元素值表示为

    Step4.4:保留矩阵的左上角8×8部分,删除其余部分;

    Step4.5:计算矩阵的平均值将矩阵按照从上至下(i=1→8)、从左至右(j=1→8)的规则,根据公式(1)进行值优化,并对其连接生成图像A″m、B″n的PHash值其中T(i,j)代表适用于u代表适用于

    Step4.6:通过图像A″m、B″n的PHash值计算图像A″m、B″n之间基于PHash的汉明距离Dis(A″m,B″n),m∈[1,8],n∈[1,8];

    Step5:根据图像A″m、B″n两两组合得到Max(m)×Max(n)=8×8=64个汉明距离,根据公式(2)选出其中最小值作为图像A、B之间基于PHash的汉明距离Dis(A,B);

    Dis(A,B)=Min(Dis(A″m,B″n)),m∈[1,8],n∈[1,8] (2)

    Step6:定义汉明距离阈值若图像A、B之间基于PHash的汉明距离Dis(A,B)满足公式(3)要求,则判定图像A和图像B相似;若满足公式(4)要求,则判定图像A和图像B不相似。

    进一步地,所述步骤Step1中,图像A和图像B可以为任意大小的图像,且图像格式任意。

    进一步地,所述步骤Step3.2中,截取的图像的大小要大于考察图像的大小,即满足M′≥M,N′≥N的要求,作用是为了保证旋转后得到的图像在区域内没有空白区域。

    进一步地,所述步骤Step4.4中,不一定非要保留矩阵的左上角8×8部分,也可以保存其他局部矩阵,相应的汉明距离阈值要根据矩阵的元素个数进行更改。

    进一步地,所述步骤Step4.5中,汉明距离指两串Hash值进行异或运算后,结果为“1”的个数。

    进一步地,所述步骤Step5中为了降低处理速度,去除重复后可简化计算至1+2+…+Max(m,n)=36个汉明距离。

    进一步地,所述步骤Step6中,汉明距离阈值通常取可自行修改。

    实施例2:在实施例1的基础上,若步骤Step4.6中图像A″m、B″n之间基于PHash的汉明距离Dis(A″m,B″n),m∈[1,8],n∈[1,8]分别为12/14/5/7/4/21/18/26/42/7/…/6/32/21,则步骤Step5中由公式(2)选出其中最小值作为图像A、B之间基于PHash的汉明距离Dis(A,B),其值为Dis(A,B)=Min(Dis(A″m,B″n)),m∈[1,8],n∈[1,8]=4。

    实施例3:在实施例1的基础上,取汉明距离阈值若步骤Step5中图像A、B之间基于PHash的汉明距离Dis(A,B)=4,则判定图像A和图像B相似;若步骤Step5中图像A、B之间基于PHash的汉明距离Dis(A,B)=12,则判定图像A和图像B不相似。

    以上结合附图对本发明的具体实施方式作了详细说明,但是本发明并不限于上述实施方式,在本领域普通技术人员所具备的知识范围内,还可以在不脱离本发明宗旨的前提下作出各种变化。

    更多相关内容
  • pHash lib 是该工具的核心,可以轻松检测裁剪和修饰的图像,或不同分辨率和格式的相同内容。 工具遍历指定的路径,计算那里所有图像的哈希值,在运行之间将它们腌制到一个 db 文件中(以节省大量重新计算所有图像的...
  • 图像匹配-pHash 我与pHash的第二年最终项目
  • pHash忽略图像大小和文件大小,而是根据图像像素创建哈希。 这使您可以查找已旋转,已更改元数据并进行了轻微编辑的重复图片。 该脚本对添加的图像进行哈希处理,将哈希存储到数据库(MongoDB)中。 为了找到重复的...
  • PHash算法即感知哈希算法/Perceptual Hash algorithm,计算基于低频的均值哈希.对每张图像生成一个指纹字符串,通过对该字符串比较可以判断图像间的相似度. PHash算法原理 将图像转为灰度图,然后将图片大小调整为...
  • 密码 一个简单的 Go 库,用于计算 JPEG 图像的 phash 字符串。
  • phash-0.9.6

    2018-03-24 16:02:44
    自己移植的pHash 0.96版本的vs2013版本,分享给大家。开源的图像相似度感知hash算法,支持jpg等主流图像的相似度匹配。
  • pHash_opencv

    2018-01-21 22:02:36
    网上找的opencv实现的pHash代码,分享给大家。图像相似度感知hash算法,支持jpg等主流图像的相似度匹配。
  • 存储库的一些说明 因为正式的pHash项目( )没有公共代码RCS,所以创建了该存储库。 目的是将此回购用于公共合作,并将其反馈给主项目。 显然,任何来自官方来源的进一步发布也将被引入此处。 该存储库以可下载的...
  • pHash-0.9.4-vs2015

    2018-01-21 22:00:36
    网上找的pHash 0.94版本的vs2015版本,分享给大家。开源的图像相似度感知hash算法,支持jpg等主流图像的相似度匹配。
  • 基于图像phash特征通过KMeans聚类算法实现图像聚类,通过熵来进行对结果的评估,顺便用PCA降维特征来可视化聚类结果
  • Phashion是围绕pHash库的一个Ruby封装
  • 参考luoweifu《看起来像它——图像搜索其实也不难》介绍的方法,实现的php类,和直接调用生成图片phash.java实现请参考:http://blog.csdn.net/luoweifu/article/details/8220992
  • 哈希算法之pHash算法

    2021-07-07 17:41:09
    1.pHash算法 pHash中文叫感知哈希算法,通过离散余弦变换(DCT)降低图片频率,相比aHash有更好鲁棒性。 基本原理: (1)缩小尺寸。将图片缩小为32*32大小。 (2)灰度化处理。 (3)计算DCT,并选取左上角8*8的矩阵...

    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)

    完整算法

    这里同步给出3种hash的完整代码,便于进行效果比较。首先使用opencv进行算法实现:
    
    # -*- 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变换为自己写的,如有错误欢迎指出):

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

    展开全文
  • 验证码 扩展以允许通过比较文档。...Phash比较设置可以如下修改: [ Test ] public Task LocalSettings () { return Verifier . VerifyFile ( " sample.png " ) . PhashCompareSettings ( thresh
  • var pngToPhash = require('png-to-phash') pngToPhash('test.png', function(error,phash) { console.log(phash) })
  • Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法毕业论文提交之后,老师交给自己一项任务:图像配准,也就是给你两幅图像,通过系统来判定两幅图像是否为同一副图像。自己作为这一方面的小白,先去网上搜索...

    Java进阶(五十七)-基于感知哈希算法的pHash图像配准算法

    毕业论文提交之后,老师交给自己一项任务:图像配准,也就是给你两幅图像,通过系统来判定两幅图像是否为同一副图像。自己作为这一方面的小白,先去网上搜索一下相应的检测方法,当然有现成的API调用最好,花钱也无所谓。

    我们这里采用的基础关键技术叫做 “感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个”指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。

    感知哈希算法

    下面是一个最简单的实现:

    第一步,缩小尺寸。

    将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

    第二步,简化色彩。

    将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。

    第三步,计算平均值。

    计算所有64个像素的灰度平均值。

    第四步,比较像素的灰度。

    将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

    第五步,计算哈希值。

    将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。

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

    具体的代码实现,可以参见Wote用Python语言写的imgHash.py。代码很短,只有53行。使用的时候,第一个参数是基准图片,第二个参数是用来比较的其他图片所在的目录,返回结果是两张图片之间不相同的数据位数量(汉明距离)。

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

    实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。这些算法虽然更复杂,但是原理与上面的简便算法是一样的,就是先将图片转化成Hash字符串,然后再进行比较。

    均值哈希虽然简单,但受均值的影响非常大。例如对图像进行伽马校正或直方图均衡就会影响均值,从而影响最终的hash值。存在一个更健壮的算法叫pHash。它将均值的方法发挥到极致。使用离散余弦变换(DCT)来获取图片的低频成分。

    离散余弦变换(DCT)是种图像压缩算法,它将图像从像素域变换到频率域。然后一般图像都存在很多冗余和相关性的,所以转换到频率域之后,只有很少的一部分频率分量的系数才不为0,大部分系数都为0(或者说接近于0)。

    pHash

    pHash的工作过程如下:

    (1)缩小尺寸:pHash以小图片开始,但图片大于8*8,32*32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。

    (2)简化色彩:将图片转化成灰度图像,进一步简化计算量。

    (3)计算DCT:计算图片的DCT变换,得到32*32的DCT系数矩阵。

    (4)缩小DCT:虽然DCT的结果是32*32大小的矩阵,但我们只要保留左上角的8*8的矩阵,这部分呈现了图片中的最低频率。

    (5)计算平均值:如同均值哈希一样,计算DCT的均值。

    (6)计算hash值:这是最主要的一步,根据8*8的DCT矩阵,设置0或1的64位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。

    结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。

    与均值哈希一样,pHash同样可以用汉明距离来进行比较。(只需要比较每一位对应的位置并算计不同的位的个数)

    下面我们来看下上述理论用Java来做一个DEMO版的具体实现:

    import java.awt.Graphics2D;

    import java.awt.color.ColorSpace;

    import java.awt.image.BufferedImage;

    import java.awt.image.ColorConvertOp;

    import java.io.File;

    import java.io.FileInputStream;

    import java.io.FileNotFoundException;

    import java.io.InputStream;

    import javax.imageio.ImageIO;

    /*

    * function: 用汉明距离进行图片相似度检测的Java实现

    * pHash-like image hash.

    * Author: Sun Huaqiang

    * Based On: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html

    */

    public class ImagePHash {

    private int size = 32;

    private int smallerSize = 8;

    public ImagePHash() {

    initCoefficients();

    }

    private ImagePHash(int size, int smallerSize) {

    this.size = size;

    this.smallerSize = smallerSize;

    initCoefficients();

    }

    private int distance(String s1, String s2) {

    int counter = 0;

    for (int k = 0; k < s1.length();k++) {

    if(s1.charAt(k) != s2.charAt(k)) {

    counter++;

    }

    }

    return counter;

    }

    // Returns a 'binary string' (like. 001010111011100010) which is easy to do a hamming distance on.

    private String getHash(InputStream is) throws Exception {

    BufferedImage img = ImageIO.read(is);

    /* 1. Reduce size(缩小尺寸).

    Like Average Hash, pHash starts with a small image.

    However, the image is larger than 8x8; 32x32 is a good size.This is really done to simplify the DCT computation and not because it is needed to reduce the high frequencies.

    */

    img = resize(img, size, size);

    /* 2. Reduce color(简化色彩).

    The image is reduced to a grayscale just to further simplify the number of computations.

    */

    img = grayscale(img);

    double[][] vals = new double[size][size];

    for (int x = 0; x < img.getWidth(); x++) {

    for (int y = 0; y < img.getHeight(); y++) {

    vals[x][y] = getBlue(img, x, y);

    }

    }

    /* 3. Compute the DCT(计算DCT).

    The DCT(Discrete Cosine Transform,离散余弦转换) separates the image into a collection of frequencies and scalars. While JPEG uses an 8x8 DCT, this algorithm uses a 32x32 DCT.

    */

    long start = System.currentTimeMillis();

    double[][] dctVals = applyDCT(vals);

    // System.out.println("DCT_COST_TIME: " + (System.currentTimeMillis() - start));

    /* 4. Reduce the DCT.

    This is the magic step. While the DCT is 32x32, just keep the top-left 8x8. Those represent the lowest frequencies in the picture.

    */

    /* 5. Compute the average value.

    Like the Average Hash, compute the mean DCT value (using only the 8x8 DCT low-frequency values and excluding the first term since the DC coefficient can be significantly different from the other values and will throw off the average).

    */

    double total = 0;

    for (int x = 0; x < smallerSize; x++) {

    for (int y = 0; y < smallerSize; y++) {

    total += dctVals[x][y];

    }

    }

    total -= dctVals[0][0];

    double avg = total / (double) ((smallerSize * smallerSize) - 1);

    /* 6. Further reduce the DCT.

    This is the magic step. Set the 64 hash bits to 0 or 1

    depending on whether each of the 64 DCT values is above or below the average value. The result doesn't tell us the

    actual low frequencies; it just tells us the very-rough

    relative scale of the frequencies to the mean. The result

    will not vary as long as the overall structure of the image remains the same; this can survive gamma and color histogram adjustments without a problem.

    */

    String hash = "";

    for (int x = 0; x < smallerSize; x++) {

    for (int y = 0; y < smallerSize; y++) {

    if (x != 0 && y != 0) {

    hash += (dctVals[x][y] > avg?"1":"0");

    }

    }

    }

    return hash;

    }

    private BufferedImage resize(BufferedImage image, int width, int height) {

    BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

    Graphics2D g = resizedImage.createGraphics();

    g.drawImage(image, 0, 0, width, height, null);

    g.dispose();

    return resizedImage;

    }

    private ColorConvertOp colorConvert = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);

    private BufferedImage grayscale(BufferedImage img) {

    colorConvert.filter(img, img);

    return img;

    }

    private static int getBlue(BufferedImage img, int x, int y) {

    return (img.getRGB(x, y)) & 0xff;

    }

    // DCT function stolen from http://stackoverflow.com/questions/4240490/problems-with-dct-and-idct-algorithm-in-java

    private double[] c;

    private void initCoefficients() {

    c = new double[size];

    for (int i=1;i

    c[i]=1;

    }

    c[0]=1/Math.sqrt(2.0);

    }

    private double[][] applyDCT(double[][] f) {

    int N = size;

    double[][] F = new double[N][N];

    for (int u=0;u

    for (int v=0;v

    double sum = 0.0;

    for (int i=0;i

    for (int j=0;j

    sum+=Math.cos(((2*i+1)/(2.0*N))*u*Math.PI)*Math.cos(((2*j+1)/(2.0*N))*v*Math.PI)*(f[i][j]);

    }

    }

    sum*=((c[u]*c[v])/4.0);

    F[u][v] = sum;

    }

    }

    return F;

    }

    /**

    *

    *@param img1

    *@param img2

    *@param tv

    *@return boolean

    */

    public boolean imgChk(String img1, String img2, int tv){

    ImagePHash p = new ImagePHash();

    String image1;

    String image2;

    try {

    image1 = p.getHash(new FileInputStream(new File(img1)));

    image2 = p.getHash(new FileInputStream(new File(img2)));

    int dt = p.distance(image1, image2);

    System.out.println("["+img1 + "] : [" + img2 + "] Score is " + dt);

    if (dt <= tv)

    return true;

    } catch (FileNotFoundException e) {

    e.printStackTrace();

    } catch (Exception e) {

    e.printStackTrace();

    }

    return false;

    }

    public static void main(String[] args) {

    ImagePHash p = new ImagePHash();

    String imagePath = "C:/Users/SHQ/Desktop/image/";

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"2.jpg", 10));

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"3.jpg", 10));

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"4.jpg", 10));

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"5.jpg", 10));

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"6.png", 10));

    System.out.println(p.imgChk(imagePath+"1.jpg", imagePath+"7.jpg", 10));

    System.out.println(p.imgChk(imagePath+"2.jpg", imagePath+"3.jpg", 10));

    }

    }

    测试结果

    fb4c68eb925e7abdac76a454a31c3766.png

    结果说明:汉明距离越大表明图片差异越大,如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。从结果可以看到1、5、6、7是相似图片,1、2、3差异太大,是两张不同的图片。

    附 测试图片

    图1 1.jpg

    46cd386d5c99cb3ead7c86dbc05b9db0.png

    图2 2.jpg

    a59d4915b968383264c2e4184cda8511.png

    图3 3.jpg

    d648f206482fbf0b8530a25c2d5ca6d5.png

    图4 4.jpg

    3fc0d9a2951f235ccccf706103b89624.png

    图5 5.png

    9e9027982648a7f16f21e051ce581ae5.png

    图6 6.jpg(图1的缩略图)

    c7c47a54da63cadc19fc0acddf0a47da.png

    图7 7.jpg(图1的缩略图)

    0a725d6221ac2ca1ee7b97b4e5ae897e.png

    f12d014838597e5c4975850cfa323106.gif

    展开全文
  • 在Unity中利用PHash识别图像是否相同

    千次阅读 2021-12-16 14:21:46
    按照自己的思路总结了一下如何使用PHash来对两张图片进行差异比较可以扩展为检查Unity项目内是否有相同的图片来优化图集。

    1. 傅里叶变换公式

    具体推到流程到处都有我就拿来直接用了

    2. 欧拉公式

    在这里插入图片描述
    简化
    在这里插入图片描述

    3. 简单推导

    将傅里叶变换公式拆开可以获得
    在这里插入图片描述
    DFT变换的结果:
    实数部分为
    在这里插入图片描述
    虚数部分为
    在这里插入图片描述

    在这里插入图片描述
    则可以转换为
    实数部分
    在这里插入图片描述
    虚数系数部分
    在这里插入图片描述
    由于cos是一个偶函数,sin是一个奇函数
    奇函数 + 奇函数=奇函数(相减一样)
    偶函数 + 偶函数=偶函数(相减一样)
    奇函数 * 奇函数=偶函数(相除一样)
    偶函数 * 偶函数=偶函数(相除一样)
    奇函数 * 偶函数=奇函数(相除一样)
    在这里插入图片描述
    因此当X[n]是一个实数函数时,其频域的实部是偶函数,虚部是一个奇函数,那么假设原信号X[n]是一个全身实数的偶函数信号会怎么样?
    X[n]sin(kt)是一个奇函数所以
    在这里插入图片描述
    所以当原时域信号是一个实偶信号时,我们就可以把DFT写成
    在这里插入图片描述
    DCT变换就是DFT变换的一种特殊形式,其特殊点在于其原始变换信号是一个实偶函数,但是实际应用中并没有那么多实偶函数信号给我们,因此为了适用面更广,自然界没有那么多实偶信号,我们可以用用实信号造一个。
    设一长度为N的实数离散信号{x[0],x[1],x[2]…x[N-1]}首先,先将这个信号长度扩大成原来的两倍,并变成2N,定义新信号x’[m]为
    x’[m] = x[m] (0<=m<=N-1)
    x’[m] = x[-m-1] (-N<=m<=-1)

    所以DFT公式由于范围变成了[-N,N-1]
    在这里插入图片描述
    但是,这样的插值之后也随之带来了一个问题,这个信号并不关于m=0偶对称,它关于m = -1/2对称,因此为了让信号仍然关于原点对称,把整个信号向右平移1/2个单位得到
    在这里插入图片描述
    之后根据欧拉公式对其展开,由于已经证明虚部为0所以只需要实数部分
    在这里插入图片描述
    这个序列是一个偶对称序列继续变形
    在这里插入图片描述
    设n = m - 1/2 带入上式
    在这里插入图片描述
    将C(u)带入得到DCT公式。

    4. PHash

    流程:

    • 灰度化

    • 缩小尺寸 => 8x8 px

    • 二值化: 基于不同的hash算法

    • DCT变换

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

    • 比较距离(相似性) = 比较fingerprint

    5. 代码

    • 编译器部分
    namespace PicDifficulty
    {
        public class PicDifficulty : EditorWindow
        {
            [MenuItem("MyTool/PicDifficulty")]
            public static void showPicDifficultyWindow()
            {
                var window = (PicDifficulty)GetWindow(typeof(PicDifficulty));
                window.Show();
            }
            private Texture2D E_Texture;
            private Texture2D E_Texture1;
            public void OnGUI()
            {
                EditorGUILayout.BeginVertical();
                E_Texture = EditorGUILayout.ObjectField("贴图", E_Texture, typeof(Texture), true) as Texture2D;
                E_Texture1 = EditorGUILayout.ObjectField("贴图1", E_Texture1, typeof(Texture), true) as Texture2D;
    
                if (GUILayout.Button("转换"))
                {
                	Debug.LogError(pHash.PicSimilarity(E_Texture, E_Texture1));                
                }
                EditorGUILayout.EndVertical();
            }
    
    • PHash部分
    using PicDifficulty;
    using UnityEngine;
    
    public static class pHash
    {
        /// <summary>
        /// 压缩图片为8x8px
        /// </summary>
        /// <param name="tex"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public static Texture2D ReduceSize(Texture2D tex, int size)
        {
            if (tex == null || size <= 0)
            {
                Debug.LogError("图片或缩放比例错误");
                return null;
            }
            Texture2D newTex = new Texture2D(size, size, TextureFormat.RGBA32, false);
            //比率
            float ratioW = tex.width / size;
            float ratioH = tex.height / size;
    
            Color color;
    
            for (int h = 0; h < newTex.height; h++)
            {
                for (int w = 0; w < newTex.width; w++)
                {
                    color = tex.GetPixel(Mathf.RoundToInt(w * ratioW), Mathf.RoundToInt(h * ratioH));
                    newTex.SetPixel(w, h, color);
                }
            }
            newTex.Apply();
            return newTex;
        }
        /// <summary>
        /// 将8x8px的图片转换为灰度图
        /// </summary>
        /// <param name="tex"></param>
        /// <returns></returns>
        public static Texture2D Tex2Gray(Texture2D tex)
        {
            Texture2D newTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
            Color color;
            for (int h = 0; h < tex.height; h++)
            {
                for (int w = 0; w < tex.width; w++)
                {
                    color = tex.GetPixel(w, h);
                    float gray = (color.r * 29.9f + color.g * 58.7f + color.b * 11.4f) / 100;
                    newTex.SetPixel(w, h, new Color(gray, gray, gray));
                }
            }
            newTex.Apply();
            return newTex;
        }
        public static bool PicSimilarity(Texture2D E_Texture, Texture2D E_Texture1)
        {
            if (E_Texture == null || E_Texture1 == null)
            {
                Debug.AssertFormat(!(E_Texture == null || E_Texture1 == null), "请检查是否有图片为空");
                return false;
            }
            var tex64 = ReduceSize(E_Texture, 8);
            var texGray = Tex2Gray(tex64);
            var texFloat = DCT.image2F(texGray);
            float[,] DCTMatrix = Matrix.Multiply(Matrix.Multiply(DCT.DCTMatrix, texFloat), DCT.DCTMatrixT);
            float aver = DCT.averageDCT(DCTMatrix);
            string hash = DCT.getHash(DCTMatrix, aver);
    
            var tex641 = ReduceSize(E_Texture1, 8);
            var texGray1 = Tex2Gray(tex641);
            var texFloat1 = DCT.image2F(texGray1);
            float[,] DCTMatrix1 = Matrix.Multiply(Matrix.Multiply(DCT.DCTMatrix, texFloat1), DCT.DCTMatrixT);
            float aver1 = DCT.averageDCT(DCTMatrix1);
            string hash1 = DCT.getHash(DCTMatrix1, aver1);
            float dis = DCT.computeDistance(hash, hash1);
            
            Debug.LogError(dis);
            return dis >= 0.6 ? true : false;
        }
    }
    
    • DCT部分
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    namespace PicDifficulty
    {
        public class DCT
        {
            public static float[,] DCTMatrix => creatDCTMatrix(8);
            public static float[,] DCTMatrixT => Matrix.Transpose(DCTMatrix);
    
            //图片转矩阵
            public static float[,] image2F(Texture2D tex)
            {
                int size = tex.width;
                float[,] f = new float[size, size];
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        f[i, j] = tex.GetPixel(i, j).r;
                    }
                }
                return f;
            }
            //计算DCT矩阵
            public static float[,] creatDCTMatrix(int size)
            {
                float[,] ret = new float[size, size];
                for (int x = 0; x < size; x++)
                {
                    for (int y = 0; y < size; y++)
                    {
                        float angle = ((y + 0.5f) * Mathf.PI * x / size);
                        ret[x, y] = cfunc(x, size) * (float)Mathf.Cos(angle);
                    }
                }
                return ret;
            }
            static float cfunc(int n, int size)
            {
                if (n == 0)
                    return Mathf.Sqrt(1f / size);
                else
                    return Mathf.Sqrt(2f / size);
            }
            //DCT均值
            public static float averageDCT(float[,] dct)
            {
                int size = dct.GetLength(0);
                float aver = 0;
                for (int i = 0; i < size; i++)
                {
                    for (int j = 0; j < size; j++)
                    {
                        aver += dct[i, j];
                    }
                }
                return aver / (size * size);
            }
            //获取当前图片pHash值
            public static string getHash(float[,] dct, float aver)
            {
                string hash = string.Empty;
                for (int i = 0; i < 8; i++)
                {
                    for (int j = 0; j < 8; j++)
                    {
                        hash += (dct[i, j] >= aver ? "1" : "0");
                    }
                }
                return hash;
            }
            //计算两图片哈希值的汉明距离
            public static float computeDistance(string hash1, string hash2)
            {
                float dis = 0;
                for (int i = 0; i < hash1.Length; i++)
                {
                    if (hash1[i] == hash2[i])
                    {
                        dis++;
                    }
                }
                return dis / hash1.Length;
            }
        }
    }
    
    • 矩阵转置和矩阵乘法
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    namespace PicDifficulty
    {
        public static class Matrix
        {
            //矩阵转置
            public static float[,] Transpose(float[,] C)
            {
                int size = C.GetLength(0);
                float[,] ret = new float[size, size];
                for (var x = 0; x < size; x++)
                {
                    for (var y = 0; y < size; y++)
                    {
                        ret[y, x] = C[x, y];
                    }
                }
                return ret;
            }
            //矩阵相乘
            public static float[,] Multiply(float[,] C1, float[,] C2)
            {
                int size = C1.GetLength(0);
                float[,] ret = new float[size, size];
                for (var y = 0; y < size; y++)
                {
                    for (var x = 0; x < size; x++)
                    {
                        float sum1 = 0;
                        for (int k = 0; k < size; k++)
                        {
                            sum1 += C1[x, k] * C2[k, y];
                        }
                        ret[x, y] = sum1;
                    }
                }
                return ret;
            }
        }
    }
    

    6. 参考

    https://zhuanlan.zhihu.com/p/85299446 --详解离散余弦变换(DCT)
    https://blog.csdn.net/Getyoufly/article/details/108254558 --Unity C#使用pHash算法实现图片相似度计算

    QQ群:152798166

    展开全文
  • 提取pHash特征,利用得到的pHashFeature.txt中的数据实现后续图像匹配
  • 对于图像纯色或接近纯色的检索,小白菜以为应该做得轻巧简洁,因为本身PHash做拷贝检索就是一个很轻量的服务,如果图像纯色或接近纯色的检索做的过重,比如用DL对图像纯色与非纯色进行分类,就失去了用PHash做拷贝...
  • # -*- coding: utf-8 -*-import pandas as pdimport cv2import timeimport numpy as npdef pHash(img,leng=32,wid=32):img = cv2.resize(img, (leng, wid))gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)dct = cv2.d...
  • phash

    2013-01-11 14:20:24
    found that Average Hash is significantly faster than pHash. Average Hash is a great algorithm if you are looking for something specific. For example, if I have a small thumbnail of an image and I ...
  • 了解一下,发现最简单易实现的就是pHash算法了,所以决定写一下,作为图像相似度比较的入门学习。 算法思路 基本思路就是灰度化->压缩至同一尺寸(32*32)->离散变换->计算相似hash(指纹)->计算汉明距,具体逻辑...
  • 这是拍照找同款、哼歌找曲的核心算法,懂的自然懂。原理不外乎是对媒体数据计算摘要,拍照或哼歌时也计算摘要,通过匹配摘要找到想要的资料。虽然有示例,但并不能直接使用,需要根据您的应用场景做适配。
  • 密码 一个用于计算图像 phash 的 Clojure 库用法整我执照版权所有 :copyright: 2014 FIXME 根据 Eclipse 公共许可证分发 1.0 版或(由您选择)任何更高版本。
  • 相似图像搜索的哈希算法有三种:(1)均值哈希算法(2)差值哈希算法(3)感知哈希算法我们这里介绍Phash,附录有另外两种的实现。感知哈希算法步骤:1、设置图片大小:一般是32 * 32,一个较好的大小,方便DCT计算;2、...
  • int distance = hammingDistance(phash, phash2); // 计算汉明距离 cout 【" 】 "; } cout ; for(int i = 0; i ; i++) delete []iMatrix[i]; delete []iMatrix; system("pause"); return 0; } ...
  • pHash的Java实现

    2021-02-27 13:32:15
    此算法中的DCT变换是从http://blog.csdn.net/luoweifu/article/details/8214959抄过来的,因为这种需要大量的复杂的数学运算,我看不来,完全不懂...都已经还给老师了...package .../*** pHash*...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,973
精华内容 789
关键字:

phash