感知哈希算法--python实现

来源:互联网 发布:mac怎么打开rar 编辑:程序博客网 时间:2024/05/16 10:36

    最近在看运动目标跟踪方面的资料,偶然间看到zouxy09大神的一篇《基于感知哈希算法的视觉跟踪》,觉得挺有意思的。于是去查了相关的感知哈希的资料,发现在图片搜索领域里面应用非常广泛,这种技术我觉得有点像模板匹配,其核心在于:通过指纹的相似度来搜索最相似的图片。

    基于zouxy09大神的c++程序改写了一个python版本,并且增加了差值哈希的实现。

——————————————————————————————————————————

一、算法原理及过程

1.ahash算法

    在向下采样的过程中,只保留图像的低频信息。

    工作过程:

    ①缩小尺寸,简化色彩:8*8灰度

    ②计算灰度均值:64个灰度值的平均

    ③生成哈希码:将64个灰度值与上一步生成的平均值进行比较。比均值大,则置1;比均值小,则置0。从而得到一个包含64个元素的“指纹”。

    ④计算汉明距离:将两个“指纹”进行比较,计算它们的汉明距离。(==0):非常相似;(<5): 相似; (>10): 不同


2.phash算法

    均值哈希虽然简单,但是受均值影响大。如果对图像进行伽马校正或者进行直方图均值化都会影响均值,从而影响哈希值的计算。所以就有人提出更健壮的方法,通过离散余弦(DCT)进行低频提取。

    离散余弦变换(DCT)是种图像压缩算法,它将图像从像素域变换到频率域。然后一般图像都存在很多冗余和相关性的,所以转换到频率域之后,只有很少的一部分频率分量的系数才不为0,大部分系数都为0(或者说接近于0)。下图的右图是对lena图进行离散余弦变换(DCT)得到的系数矩阵图。从左上角依次到右下角,频率越来越高,由图可以看到,左上角的值比较大,到右下角的值就很小很小了。换句话说,图像的能量几乎都集中在左上角这个地方的低频系数上面了。

    工作过程:

    ①缩小尺寸,简化色彩:32*32灰度

    ②计算DCT:对图像进行离散余弦变换,得到32*32的DCT系数矩阵

    ③截取DCT:因为只有左上角的部分呈现图像的最低频部分,所以我们截取左上角的8*8矩阵

    ④计算均值:64个DCT系数的均值

    ⑤生成哈希码:将64个DCT系数与上一步生成的平均值进行比较。之后过程同均值哈希


3.dhash算法

    这是一种通过渐变实现的哈希算法,相比phash,速度上有较大的优势。

    工作过程:

    ①缩小尺寸、简化色彩:9*8灰度

    ②计算差异:每行9个像素做差得到8个差异值,总共64个差异值

    ③生成哈希码:差异值与0比较。大于0,置1;小于0,置0。之后过程同均值哈希

——————————————————————————————————————————

二、代码实现:

# coding:UTF-8import cv2import numpy as npimport timefrom glob import iglobclass HashTracker:    def __init__(self, path):        # 初始化图像        self.img = cv2.imread(path)        self.gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)    def cal_hash_code(self, cur_gray):        s_img = cv2.resize(cur_gray, dsize=(8, 8))        img_mean = cv2.mean(s_img)        return s_img > img_mean[0]    def cal_phash_code(self, cur_gray):        # 缩小至32*32        m_img = cv2.resize(cur_gray, dsize=(32, 32))        # 浮点型用于计算        m_img = np.float32(m_img)        # 离散余弦变换,得到dct系数矩阵        img_dct = cv2.dct(m_img)        img_mean = cv2.mean(img_dct[0:8, 0:8])        # 返回一个8*8bool矩阵        return img_dct[0:8, 0:8] > img_mean[0]    def cal_dhash_code(self, cur_gray):        # dsize=(width, height)        m_img = cv2.resize(cur_gray, dsize=(9, 8))        m_img = np.int8(m_img)        # 得到8*8差值矩阵        m_img_diff = m_img[:, :-1] - m_img[:, 1:]        return m_img_diff > 0    def cal_hamming_distance(self, model_hash_code, search_hash_code):        # 返回不相同的个数        diff = np.uint8(model_hash_code - search_hash_code)        return cv2.countNonZero(diff)    def hash_track(self, roi, rect, flag=0):        # 获得矩形框信息        width = abs(rect[0] - rect[2])        height = abs(rect[1] - rect[3])        # 获得当前图像的长宽信息        img_w, img_h = self.img.shape[:2]        # 根据flag,选择方法,计算前一帧的hash值        if flag == 0:            model_hash_code = self.cal_hash_code(roi)        elif flag == 1:            model_hash_code = self.cal_phash_code(roi)        elif flag == 2:            model_hash_code = self.cal_dhash_code(roi)        # 初始化汉明距离        min_dis = 64        # 滑动窗口匹配,步长为2        for i in xrange(0, img_h, 2):            for j in xrange(0, img_w, 2):                if flag == 0:                    search_hash_code = self.cal_hash_code(self.gray[j:j + height, i:i + width])                elif flag == 1:                    search_hash_code = self.cal_phash_code(self.gray[j:j + height, i:i + width])                elif flag == 2:                    search_hash_code = self.cal_dhash_code(self.gray[j:j + height, i:i + width])                # 计算汉明距离                distance = self.cal_hamming_distance(model_hash_code, search_hash_code)                # 获得最小汉明距离,同时得到此时的匹配框                if distance < min_dis:                    rect = i, j, i + width, j + height                    min_dis = distance        # 根据匹配框,获得下一帧的匹配模板        roi = self.gray[rect[1]:rect[3], rect[0]:rect[2]]        # 显示当前帧矩形框位置        cv2.rectangle(self.img, (rect[0], rect[1]), (rect[2], rect[3]), (255, 0, 0), 2)        return roi# 鼠标响应函数box = [0]*4def mouse_handler(event, x, y, flag, param):    if event == cv2.EVENT_LBUTTONDOWN:        # 起始点记录        box[0], box[1] = x, y    elif event == cv2.EVENT_MOUSEMOVE and flag == cv2.EVENT_FLAG_LBUTTON:        box[2], box[3] = x, y    elif event == cv2.EVENT_LBUTTONUP:        # 获得右下角点        box[2], box[3] = x, ydef main():    # 读取第一张图片,用来画框    img = cv2.imread('./img/0001.jpg')    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)    cv2.namedWindow('hashTracker', 1)    cv2.setMouseCallback('hashTracker', mouse_handler)    while True:        cv2.imshow('hashTracker', img)        if cv2.waitKey(1) == 27:            break    # 获取初始化模型    model = gray[box[1]:box[3], box[0]:box[2]]    # 获取图片序列    paths = iglob(r'./img/*.jpg')    # 计数用    frame_count = 0    # 循环读入图片    for path in paths:        frame_count += 1        # 实例创建        h = HashTracker(path)        # 感知哈希跟踪        start_time = time.clock()        model = h.hash_track(model, box)        fin_time = time.clock()        print "%d: delta time:%.2f" % (frame_count, fin_time - start_time)        cv2.imshow('hashTracker', h.img)        if cv2.waitKey(20) == 27:            breakif __name__ == '__main__':    main()
——————————————————————————————————————————
三、实验结果及分析

下面是基于均值哈希跟踪的实验结果


                              第25帧                                                             第50帧


                            第100帧                                                            第200帧

结果:均值哈希和差值哈希速度相对比较快,phash效果好,但是速度好慢;其实总体上,如果要用作跟踪算法的话,我觉得速度都比较慢OTZ。但是思路还是很好。

——————————————————————————————————————————

参考文献:

① 基于感知哈希算法的视觉目标跟踪

②三种基于感知哈希算法的相似图像检索技术

③相似图片搜索原理


    


  


0 0
原创粉丝点击