PHash從0到1

背景

在重複圖識別領域,對於識別肉眼相同圖片,PHash是頗有用的,並且算法複雜度很低。它抓住了 」 人眼對於細節信息不是很敏感 「 的特性,利用DCT變換把高頻信息去掉,再加上合適&簡單的二值化方式,使得算法效果比較魯棒。html

PHash算法

  • 附上python代碼:
def phash(image, hash_size=8, highfreq_factor=4):
     import scipy.fftpack
     img_size = hash_size * highfreq_factor
     image = image.convert("L").resize((img_size, img_size), Image.ANTIALIAS)// 一、【預處理】轉灰度圖,resize
     pixels = numpy.asarray(image)
     dct = scipy.fftpack.dct(scipy.fftpack.dct(pixels, axis=0), axis=1) //DCT變換
     dctlowfreq = dct[:hash_size, :hash_size] //二、只留下直流&&低頻變量
     med = numpy.median(dctlowfreq) //取中值
     diff = dctlowfreq > med //三、【二值化】大於中值爲1,小於等於中值爲0
     return diff

PHash算法其實很簡單,主要就3步:python

  • 圖片預處理
  • DCT變換
  • 二值化

其中圖片預處理很簡單,這裏就不詳細講解了。下面主要給你們直觀介紹下DCT變換到底是什麼,還有這裏是怎麼二值化的。算法

DCT變換

DCT變換,全稱是Discrete Cosine Transform,也就是離散餘弦變換,具體的公式跟原理這裏就不詳述了,具體能夠看DCT變換公式&原理函數

  • DCT變換能把圖像轉成頻譜圖,DCT逆變換能把頻譜圖轉回原圖,以下圖所示。

其中頻譜圖中,左上角屬於低頻變量,右下角屬於高頻變量,而後比較特殊的一點是左上角a[0][0]這點屬於直流變量。因爲人眼對於細節信息不是很敏感,因此咱們在識別肉眼相同級別重複圖的時候,只用頻譜圖中的低頻信息就足夠了,因此這就是phash中只取DCT低頻信息的緣由。ui

怎麼理解圖片中的低頻跟高頻

在頻譜圖中,咱們知道左上角那些是低頻信息,右下角是高頻信息。那麼在一張圖片中,哪些信息是低頻,哪些信息是高頻呢?code

因爲DCT是可逆變換,那麼咱們能夠只用頻譜圖中某一塊進行DCT逆變換,那麼就能夠直觀看到頻譜圖中這一塊表明什麼信息?orm

接下來,咱們利用DCT逆變換生成兩列圖片(以下所示):htm

  • 【左下角】第一列直接用頻譜圖左上角N*N的矩陣,進行DCT逆變換生成的圖片。
  • 【除左下角】第二列把頻譜圖中左上角N*N矩陣置0,進行DCT逆變換生成的圖片。

從上能夠得出結論:blog

  • 圖片中低頻信息是那些像素點色塊連續的部分
  • 圖片中高頻信息是那些色塊邊界點
  • 左上角那一點,屬於直流變量,直接置0,影響不大
  • 當N(600)很大的時候,DCT變換能夠用坐降噪、壓縮

附上代碼,方便你們理解圖片

import cv2
import copy
import numpy as np
import matplotlib.pyplot as plt

#展現圖片
def show_img(img):
    plt.imshow(img, cmap='Greys_r')
    plt.show()
    
#左上角低頻矩陣,進行DCT逆變換
def low_frequency_idct(dct,dct_size):
	#非左上角N*N區域置0
    dct[dct_size+1:,:] = 0
    dct[:,dct_size+1:] = 0
    #逆DCT變換
    img = cv2.idct(dct)
    #展現圖片
    show_img(img)
    
#把左上角信息清除後,進行DCT逆變換
def hight_frequency_idct(dct,dct_size):
	#左上角N*N區域置0
    dct[0:dct_size,0:dct_size]=0
    #逆DCT變換
    img = cv2.idct(dct)
    #展現圖片
    show_img(img)

#主函數
def work(image_name, img_size, dct_size):
    #圖片預處理
    img = cv2.imread(image_name,0)
    show_img(img)
    img = cv2.resize(img,(img_size,img_size),interpolation=cv2.INTER_CUBIC)
    show_img(img)
    img = np.float32(img)
    #DCT變換
    dct = cv2.dct(img)
    #用左上角,進行逆dct變換
    low_frequency_idct(copy.deepcopy(dct),dct_size)
    #左上角置0,進行逆dct變換
    hight_frequency_idct(copy.deepcopy(dct),dct_size)

image_name = '11.png'
img_size = 1000
dct_size = 30
work(image_name,img_size,dct_size)

二值化

目前咱們獲取到了肉眼最敏感的信息,這裏應該怎麼二值化呢?
首先咱們須要選取一個基準值,而後大於基準值的置1,小於等於基準值的置0。
那麼問題來了,怎麼選擇這個基準值呢?這裏有兩種方式:

一、均值

因爲頻譜圖左上角那一點(直流變量),就是用原圖全部像素點加起來獲得的,因此這個點會很大,徹底偏離整體的值。
而後這裏基準值若是用均值的話,會致使phash值中1的個數會偏少,並且左上角那邊大機率是1,右下角那邊大概爲0。這就會致使phash中0,1的分佈不均勻。那麼其實對於phash值的特徵空間就有必定的縮小不少了。(如上圖所示,1個數不多)

PS: 改進策略:去除頻譜圖中第一行&&第一列的元素。

這樣能把一些很離譜的偏離點刪除,可是未必偏離點就在第一行&第一列,只是大機率在這裏。其實這樣還不如直接用中值更加直接。
改進以後效果好不少,可是並無中值魯棒。

二、中值

利用中值來當基準值,效果會好不少。phash值中,0,1分佈機率同樣,而且特徵空間比均值大不少。

相關文章
相關標籤/搜索