图片对比 感知哈希法(pHash)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图片对比 感知哈希法(pHash)相关的知识,希望对你有一定的参考价值。

■环境 
    Python 3.6.0    
    Pycharm 2017.1.3    

■库、库的版本 
    Numpy 1.14.2 (cp36)     
    OpenCV 3.4.1 (cp36)     

    下载      https://www.lfd.uci.edu/~gohlke/pythonlibs/     
            opencv_python?3.4.1?cp36?cp36m?win32.whl        
            numpy?1.14.2+mkl?cp36?cp36m?win32.whl   
    安装      pip install …/opencv_python?3.4.1?cp36?cp36m?win32.whl      

■参考 
    https://segmentfault.com/a/1190000004467183     

■逻辑 
    1. 打开图片。    
        cv2.imread()不能读取路径中包含中文的图片,用“cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), cv2.IMREAD_UNCHANGED)”代替。
    2. 修改图片大小,32*32。
    3. 转成灰度图。
    4. 转换灰度图成浮点型。
    5. 获取灰度图的DCT集合。 
    6. 获取灰度图DCT集合的左上角8*8。
    7. 获取灰度图DCT集合的左上角8*8对应的平均值。
    8. 获取图片指纹:遍历灰度图左上8*8的所有像素,比平均值大则记录为1,否则记录为0。
    9. 对比两张图片的指纹,得到汉明距离。
    10. 汉明距离等于0说明两张图片完全一样。
    11. 当两张图片的字节数大小一样时才对比。

■缺陷     
    1.cv2.imread()和cv2.imdecode()都不能读取修改过后缀名的图片,比如从.gif改成.jpg。
import cv2
import numpy as np
import os
import datetime

def get_img_fingerprints(gray_dct_ul64_list, gray_dct_ul64_avg):
    """
    获取图片指纹:遍历灰度图左上8*8的所有像素,比平均值大则记录为1,否则记录为0。
    :param gray_dct_ul64_list: 灰度图左上8*8的所有像素
    :param gray_dct_ul64_avg: 灰度图左上8*8的所有像素平均值
    :return: 图片指纹
    """
    img_fingerprints = ‘‘
    avg = gray_dct_ul64_avg[0]
    for i in range(8):
        for j in range(8):
            if gray_dct_ul64_list[i][j] > avg:
                img_fingerprints += ‘1‘
            else:
                img_fingerprints += ‘0‘
    return img_fingerprints

def get_img_gray_bit(img, resize=(32, 32)):
    """
    获取图片指纹
    :param img: 图片
    :param resize: Resize的图片大小
    :return: 图片指纹
    """
    # 修改图片大小
    image_resize = cv2.resize(img, resize, interpolation=cv2.INTER_CUBIC)
    # 修改图片成灰度图
    image_gray = cv2.cvtColor(image_resize, cv2.COLOR_BGR2GRAY)
    # 转换灰度图成浮点型
    image_gray_f = np.float32(image_gray)
    # 获取灰度图的DCT集合
    image_gray_dct = cv2.dct(image_gray_f)
    # 获取灰度图DCT集合的左上角8*8
    # gray_dct_ul64_list = get_gray_dct_ul64_list(image_gray_dct)
    gray_dct_ul64_list = image_gray_dct[0:8, 0:8]
    # 获取灰度图DCT集合的左上角8*8对应的平均值
    # gray_dct_ul64_avg = get_gray_dct_ul64_avg(gray_dct_ul64_list)
    gray_dct_ul64_avg = cv2.mean(gray_dct_ul64_list)
    # 获取图片指纹
    img_fingerprints = get_img_fingerprints(gray_dct_ul64_list, gray_dct_ul64_avg)
    return img_fingerprints

def get_mh(img_fingerprints1, img_fingerprints2):
    """
    获取汉明距离
    :param img_fingerprints1: 比较对象1的指纹
    :param img_fingerprints2: 比较对象2的指纹
    :return: 汉明距离
    """
    hm = 0
    for i in range(0, len(img_fingerprints1)):
        if img_fingerprints1[i] != img_fingerprints2[i]:
            hm += 1
    return hm

def is_image_file(file_name):
    """
    判断文件是否是图片
    :param file_name: 文件名称(包含后缀信息)
    :return: 1:图片,0:非图片
    """
    ext = (os.path.splitext(file_name)[1]).lower()
    if ext == ".jpg" or ext == ".jpeg" or ext == ".bmp" or ext == ".png":
        return 1
    return 0

def get_all_img_list(root_path):
    """
    获取目标文件夹下所有图片路径集合
    :param root_path: 目标文件夹
    :return: 图片集合
    """
    img_list = []
    # 获取目标文件夹下所有元组
    root = os.walk(root_path)
    # 循环元组,获取目标文件夹下所有图片路径集合
    for objects in root:
        for obj in objects:
            if "/" in str(obj):
                # 记录文件夹路径
                path = str(obj)
            elif len(obj) > 0:
                # 如果是文件,判断是否是图片。如果是图片则保存进
                for file in obj:
                    if "." in str(file) and is_image_file(file) == 1:
                        full_path = path + "/" + str(file)
                        img_list.append(full_path.replace("\\", "/"))
    return img_list

def compare_img(root_path):
    """
    比较图片 (Main)
    :param root_path: 目标文件夹
    """
    compared_list = []
    # 获取目标文件夹下所有图片路径集合
    img_list = get_all_img_list(root_path)
    # 遍历目标文件夹下所有图片进行两两比较
    for file1 in img_list:
        # 已经发现是相同的图片不再比较
        if file1 in compared_list:
            continue
        # im1 = cv2.imread(files1)
        im1 = cv2.imdecode(np.fromfile(file1, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
        if im1 is None:
            continue
        im1_size = os.path.getsize(file1)
        img_fingerprints1 = get_img_gray_bit(im1)
        for file2 in img_list:
            if file1 != file2:
                # im2 = cv2.imread(files2)
                im2 = cv2.imdecode(np.fromfile(file2, dtype=np.uint8), cv2.IMREAD_UNCHANGED)
                if im2 is None:
                    continue
                im2_size = os.path.getsize(file2)
                # 如果两张图片字节数大小一样再判断汉明距离
                if im1_size == im2_size:
                    img_fingerprints2 = get_img_gray_bit(im2)
                    compare_result = get_mh(img_fingerprints1, img_fingerprints2)
                    # 汉明距离等于0,说明两张图片完全一样
                    if compare_result == 0:
                        print("图片相同:" + file1 + "::::::" + file2)
                        compared_list.append(file1)
                        compared_list.append(file2)

start_time = datetime.datetime.now()
start_time = start_time.strftime(‘%Y-%m-%d %H:%M:%S‘)
print("start time: " + start_time)

compare_img("C:/Users/x230/Desktop/test")

end_time = datetime.datetime.now()
end_time = end_time.strftime(‘%Y-%m-%d %H:%M:%S‘)
print("end time: " + end_time)

d1 = datetime.datetime.strptime(start_time, ‘%Y-%m-%d %H:%M:%S‘)
d2 = datetime.datetime.strptime(end_time, ‘%Y-%m-%d %H:%M:%S‘)
print("耗时: " + str((d2 - d1).seconds))

技术分享图片

以上是关于图片对比 感知哈希法(pHash)的主要内容,如果未能解决你的问题,请参考以下文章

代码手记笔录——哈希法

第十五周 项目2--用哈希法组织关键字

负载均衡算法--源地址哈希法(Hash)

图像视频相似度算法

基于Dubbo实现一致哈希法与最少活跃优先法结合

C# Dictionary源码剖析---哈希处理冲突的方法有:开放定址法再哈希法链地址法建立一个公共溢出区等