国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗

這篇具有很好參考價值的文章主要介紹了OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1. 介紹

感知哈希算法(Perceptual Hash Algorithm,簡稱pHash) 是哈希算法的一種,主要可以用來做以圖搜索/相似圖片搜索工作。

?

2. 原理

感知哈希算法(pHash)首先將原圖像縮小成一個固定大小的像素圖像,然后將圖像轉(zhuǎn)換為灰度圖像,通過使用離散余弦變換(DCT)來獲取頻域信息。然后,根據(jù)DCT系數(shù)的均值生成一組哈希值。最后,利用兩組圖像的哈希值的漢明距離來評估圖像的相似度。

魔法: 概括地講,感知哈希算法一共可細分八步:

  1. 縮小圖像: 將目標圖像縮小為一個固定的大小,通常為32x32像素。作用是去除各種圖像尺寸和圖像比例的差異,只保留結(jié)構(gòu)、明暗等基本信息,目的是確保圖像的一致性,降低計算的復(fù)雜度。
  2. 圖像灰度化: 將縮小的圖像轉(zhuǎn)換為灰度圖像。
  3. 離散余弦變換(DCT): 感知哈希算法的核心是應(yīng)用離散余弦變換。DCT將圖像從空間域(像素級別)轉(zhuǎn)換為頻域,得到32×32的DCT變換系數(shù)矩陣,以捕獲圖像的低頻信息。
  4. 縮小DCT: 經(jīng)過DCT變換后,圖像的頻率特征集中在圖像的左上角,保留系數(shù)矩陣左上角的8×8系數(shù)子矩陣(因為雖然DCT的結(jié)果是32×32大小的矩陣,但左上角8×8的矩陣呈現(xiàn)了圖片中的最低頻率)。
  5. 計算灰度均值: 計算DCT變換后圖像塊的均值,以便后面確定每個塊的明暗情況。
  6. 生成二進制哈希值: 如果塊的DCT系數(shù)高于均值,表示為1,否則表示為0(由于我們只提取了DCT矩陣左上角的8×8系數(shù)子矩陣,所以,最后會得到一個64位的二進制值(8x8像素的灰度圖像))。
  7. 生成哈希值: 由于64位二進制值太長,所以按每4個字符為1組,由2進制轉(zhuǎn)成16進制。這樣就轉(zhuǎn)為一個長度為16的字符串。這個字符串也就是這個圖像可識別的哈希值,也叫圖像指紋,即這個圖像所包含的特征。
  8. 哈希值比較: 通過比較兩個圖像的哈希值的漢明距離(Hamming Distance),就可以評估圖像的相似度,距離越小表示圖像越相似。

?

3. 實驗

第一步:縮小圖像

將目標圖像縮小為一個固定的大小,通常為32x32像素。作用是去除各種圖像尺寸和圖像比例的差異,只保留結(jié)構(gòu)、明暗等基本信息,目的是確保圖像的一致性,降低計算的復(fù)雜度。

1)讀取原圖

# 測試圖片路徑
img_path = 'img_test/apple-01.jpg'
 
# 通過OpenCV加載圖像
img = cv2.imread(img_path)

# 通道重排,從BGR轉(zhuǎn)換為RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

2)縮放原圖

使用 OpenCV 的 resize 函數(shù)將圖像縮放為32x32像素。

# 縮小圖像:使用OpenCV的resize函數(shù)將圖像縮放為32x32像素,采用Cubic插值方法進行圖像重采樣
img_32 = cv2.resize(img, (32, 32), cv2.INTER_CUBIC)

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索
OpenCV 的 cv2.resize() 函數(shù)提供了4種插值方法,以根據(jù)圖像的尺寸變化來進行圖像重采樣。

  1. cv2.INTER_NEAREST: 最近鄰插值,也稱為最近鄰算法。它簡單地使用最接近目標像素的原始像素的值。雖然計算速度快,但可能導(dǎo)致圖像質(zhì)量下降。
  2. cv2.INTER_LINEAR: 雙線性插值,通過對最近的4個像素進行線性加權(quán)來估計目標像素的值。比最近鄰插值更精確,但計算成本略高。
  3. cv2.INTER_CUBIC: 雙三次插值,使用16個最近像素的加權(quán)平均值來估計目標像素的值。通常情況下,這是一個不錯的插值方法,適用于圖像縮小。
  4. cv2.INTER_LANCZOS4: Lanczos插值,一種高質(zhì)量的插值方法,使用Lanczos窗口函數(shù)。通常用于縮小圖像,以保留圖像中的細節(jié)和紋理。

第二步:圖像灰度化

將縮小的圖像轉(zhuǎn)換為灰度圖像。

# 圖像灰度化:將彩色圖像轉(zhuǎn)換為灰度圖像。
img_gray = cv2.cvtColor(img_32, cv2.COLOR_BGR2GRAY)
print(f"縮放32x32的圖像中每個像素的顏色=\n{img_gray}")

輸出打印:

縮放32x32的圖像中每個像素的顏色=
[[253 253 253 ... 253 253 253]
 [253 253 253 ... 253 253 253]
 [253 253 253 ... 253 253 253]
 ...
 [253 253 253 ... 253 253 253]
 [253 253 253 ... 253 253 253]
 [253 253 253 ... 253 253 253]]

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

第三步:離散余弦變換(DCT)

感知哈希算法的核心是應(yīng)用離散余弦變換。DCT將圖像從空間域(像素級別)轉(zhuǎn)換為頻域,得到32×32的DCT變換系數(shù)矩陣,以捕獲圖像的低頻信息。這里我們使用 OpenCV 的 cv2.dct 函數(shù)來執(zhí)行DCT。

# 離散余弦變換(DCT):計算圖像的DCT變換,得到32×32的DCT變換系數(shù)矩陣
img_dct = cv2.dct(np.float32(img_gray))
print(f"灰度圖像離散余弦變換(DCT)={img_dct}")

這行代碼執(zhí)行了離散余弦變換(DCT),它將圖像數(shù)據(jù)從空間域(像素級別)轉(zhuǎn)換為頻域,以便在頻域上分析圖像。

  • cv2.dct: 這是 OpenCV 庫中的函數(shù),用于執(zhí)行離散余弦變換。DCT是一種數(shù)學變換,類似于傅里葉變換,它將圖像分解為不同頻率的分量。
  • np.float32(img_gray): 這是將灰度圖像 img_gray 轉(zhuǎn)換為32位浮點數(shù)的操作。DCT通常需要浮點數(shù)作為輸入。
  • img_dct: 這是存儲DCT變換后結(jié)果的變量。在執(zhí)行DCT后,img_dct 將包含圖像的頻域表示。

基于DCT的圖像感知哈希算法是一種能夠有效感知圖像全局特征的算法,將圖片認為是一個二維信號,包含了表現(xiàn)大范圍內(nèi)的亮度變化小的低頻部分與局部范圍亮度變化劇烈的高頻部分,而高頻部分一般存在大量的冗余和相關(guān)性。通過DCT變換,可以將高能量信息集中到圖像的左上角區(qū)域??梢岳斫鉃閳D像的特征頻率區(qū)域。

# 離散余弦變換(DCT):計算圖像的DCT變換,得到32×32的DCT變換系數(shù)矩陣
img_dct = cv2.dct(np.float32(img_gray))
print(f"灰度圖像離散余弦變換(DCT)={img_dct}")

# 縮放DCT系數(shù)
dct_scaled = cv2.normalize(img_dct, None, 0, 255, cv2.NORM_MINMAX)
img_dct_scaled = dct_scaled.astype(np.uint8)

# 顯示DCT系數(shù)的圖像
plt.imshow(img_dct_scaled, cmap='gray')
plt.show()

如下圖,將圖像進行DCT后得到其變換結(jié)果,圖像左上角變化明顯,而右下角幾乎沒有變化。
OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

第四步:縮小DCT

經(jīng)過DCT變換后,圖像的頻率特征集中在圖像的左上角,保留系數(shù)矩陣左上角的8×8系數(shù)子矩陣(因為雖然DCT的結(jié)果是32×32大小的矩陣,但左上角8×8的矩陣呈現(xiàn)了圖片中的最低頻率)。

備注: 這里為什么要縮放DCT?以及其它縮放方式有哪些?不同縮放方式結(jié)果有何不同?不進行縮放DCT會怎么樣?等等問題,我們在文末對比解答。

# 離散余弦變換(DCT):計算圖像的DCT變換,得到32×32的DCT變換系數(shù)矩陣
img_dct = cv2.dct(np.float32(img_gray))
# print(f"灰度圖像離散余弦變換(DCT)={img_dct}")

# 縮放DCT:將DCT系數(shù)的大小顯式地調(diào)整為8x8,然后它計算調(diào)整后的DCT系數(shù)的均值,并生成哈希值。
img_dct.resize(8, 8)

# 縮放DCT系數(shù)
dct_scaled = cv2.normalize(dct_roi, None, 0, 255, cv2.NORM_MINMAX)
img_dct_scaled = dct_scaled.astype(np.uint8)

# 顯示DCT系數(shù)的圖像
plt.imshow(img_dct_scaled, cmap='gray')
plt.show()

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

第五步:計算灰度均值

計算DCT變換后圖像塊的均值,以便后面確定每個塊的明暗情況。

# 計算灰度均值:計算DCT變換后圖像塊的均值
img_avg = np.mean(img_dct)
print(f"DCT變換后圖像塊的均值={img_avg}")

輸出打?。?/p>

DCT變換后圖像塊的均值=7.814879417419434

第六步:生成二進制哈希值

如果塊的DCT系數(shù)高于均值,表示為1,否則表示為0。

由于我們只提取了DCT矩陣左上角的8×8系數(shù)子矩陣(圖片特征頻率區(qū)域),所以,最后會得到一個64位的二進制值(8x8像素的灰度圖像)。

# 生成二進制哈希值
img_hash_str = ''
for i in range(8):
    for j in range(8):
        if img_dct[i, j] > img_avg:
            img_hash_str += '1'
        else:
            img_hash_str += '0'
print(f"圖像的二進制哈希值={img_hash_str}")

或者,使用等價的 lambda 表達式。效果一樣。

img_hash_str = ""
for i in range(8):
    img_hash_str += ''.join(map(lambda i: '0' if i < img_avg else '1', img_dct[i]))
print(f"圖像的二進制哈希值={img_hash_str}")

輸出打印:

圖像的二進制哈希值=1011000010001000100000100010000000001000000000001000000000000000

第七步:圖像可識別的哈希值

由于64位二進制值太長,所以按每4個字符為1組,由2進制轉(zhuǎn)成16進制。這樣就轉(zhuǎn)為一個長度為16的字符串。這個字符串也就是這個圖像可識別的哈希值,也叫圖像指紋,即這個圖像所包含的特征。

# 生成圖像可識別哈希值
img_hash = ''
for i in range(0, 64, 4):
    img_hash += ''.join('%x' % int(img_hash_str[i: i + 4], 2))
print(f"圖像可識別的哈希值={img_hash}")

輸出打?。?/p>

圖像可識別的哈希值=b088822008008000

第八步:哈希值比較

通過比較兩個圖像的哈希值的漢明距離(Hamming Distance),就可以評估圖像的相似度,距離越小表示圖像越相似。

def hamming_distance(s1, s2):
    # 檢查這兩個字符串的長度是否相同。如果長度不同,它會引發(fā) ValueError 異常,因為漢明距離只適用于等長的字符串
    if len(s1) != len(s2):
        raise ValueError("Input strings must have the same length")
    
    distance = 0
    for i in range(len(s1)):
        # 遍歷兩個字符串的每個字符,比較它們在相同位置上的值。如果發(fā)現(xiàn)不同的字符,將 distance 的值增加 1
        if s1[i] != s2[i]:
            distance += 1
    return distance

漢明距離:兩個長度相同的字符串在相同位置上的字符不同的個數(shù)。即一組二進制數(shù)據(jù)變成另一組數(shù)據(jù)所需要的步驟數(shù)。漢明距離越小,則相似度越高。漢明距離為0,即兩張圖片完全一樣。
OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

?

4. 測試

實驗場景

我們來簡單測試一下基于感知哈希算法的以圖搜圖 – 基于一張原圖找最相似圖片,看看效果如何。
這里,我準備了10張圖片,其中9張是蘋果,但形態(tài)不一,1張是梨子。

實驗素材

OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索

實驗代碼

"""
以圖搜圖:感知哈希算法(Perceptual Hash Algorithm,簡稱pHash)的原理與實現(xiàn)
測試環(huán)境:win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1
實驗時間:2023-10-31
"""

import os
import cv2
import time
import numpy as np
import matplotlib.pyplot as plt

def get_pHash(img_path):
    # 讀取圖像:通過OpenCV的imread加載RGB圖像
    img_rgb = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
    # 縮小圖像:使用OpenCV的resize函數(shù)將圖像縮放為32x32像素,采用Cubic插值方法進行圖像重采樣
    img_resize = cv2.resize(img_rgb, (32, 32), cv2.INTER_CUBIC)
    # 圖像灰度化:將彩色圖像轉(zhuǎn)換為灰度圖像
    img_gray = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    # print(f"縮放32x32的圖像中每個像素的顏色=\n{img_gray}")

    # 離散余弦變換(DCT):計算圖像的DCT變換,得到32×32的DCT變換系數(shù)矩陣
    img_dct = cv2.dct(np.float32(img_gray))
    # print(f"灰度圖像離散余弦變換(DCT)={img_dct}")

    # 縮放DCT:將DCT系數(shù)的大小顯式地調(diào)整為8x8。然后它計算調(diào)整后的DCT系數(shù)的均值,并生成哈希值。
    img_dct.resize(8, 8)

    # 計算灰度均值:計算DCT變換后圖像塊的均值
    img_avg = np.mean(img_dct)
    # print(f"DCT變換后圖像塊的均值={img_avg}")

    img_hash_str = ""
    for i in range(8):
        img_hash_str += ''.join(map(lambda i: '0' if i < img_avg else '1', img_dct[i]))
    # print(f"圖像的二進制哈希值={img_hash_str}")

    # 生成圖像可識別哈希值
    img_hash = ''.join(map(lambda x:'%x' % int(img_hash_str[x : x + 4], 2), range(0, 64, 4)))
    # print(f"圖像可識別的哈希值={img_hash}")

    """
    # # 版本二
    # # 生成二進制哈希值
    # img_hash_str = ''
    # for i in range(8):
    #     for j in range(8):
    #         if img_dct[i, j] > img_avg:
    #             img_hash_str += '1'
    #         else:
    #             img_hash_str += '0'
    # print(f"圖像的二進制哈希值={img_hash_str}")

    # # 生成圖像可識別哈希值
    # img_hash = ''
    # for i in range(0, 64, 4):
    #     img_hash += ''.join('%x' % int(img_hash_str[i: i + 4], 2))
    # print(f"圖像可識別的哈希值={img_hash}")
    """

    return img_hash


# 漢明距離:計算兩個等長字符串(通常是二進制字符串或位字符串)之間的漢明距離。用于確定兩個等長字符串在相同位置上不同字符的數(shù)量。
def hamming_distance(s1, s2):
    # 檢查這兩個字符串的長度是否相同。如果長度不同,它會引發(fā) ValueError 異常,因為漢明距離只適用于等長的字符串
    if len(s1) != len(s2):
        raise ValueError("Input strings must have the same length")
    
    distance = 0
    for i in range(len(s1)):
        # 遍歷兩個字符串的每個字符,比較它們在相同位置上的值。如果發(fā)現(xiàn)不同的字符,將 distance 的值增加 1
        if s1[i] != s2[i]:
            distance += 1
    return distance


# ------------------------------------------------- 測試 -------------------------------------------------
time_start = time.time()

# 指定測試圖像庫目錄
img_dir = 'img_test'
# 指定測試圖像文件擴展名
img_suffix = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']

# 獲取當前執(zhí)行腳本所在目錄
script_dir = os.path.dirname(__file__)
# 獲取目標測試圖像的全路徑
img_org_path = os.path.join(script_dir, img_dir, 'apple-01.jpg')
# 獲取目標圖像可識別哈希值(圖像指紋)
org_img_hash = get_pHash(img_org_path)
print(f"目標圖像:{os.path.relpath(img_org_path)},圖片HASH:{org_img_hash}")

# 獲取測試圖像庫中所有文件
all_files = os.listdir(os.path.join(script_dir, img_dir))
# 篩選出指定后綴的圖像文件
img_files = [file for file in all_files if any(file.endswith(suffix) for suffix in img_suffix)]

img_hash_all = []
# 遍歷測試圖像庫中的每張圖像
for img_file in img_files:
    # 獲取相似圖像文件路徑
    img_path = os.path.join(script_dir, img_dir, img_file)
    # 獲取相似圖像可識別哈希值(圖像指紋)
    img_hash = get_pHash(img_path)
    # 獲取相似圖像與目標圖像的漢明距離
    distance = hamming_distance(org_img_hash, img_hash)
    # 存儲相似圖像的相對路徑、哈希值、漢明距離
    img_hash_all.append((os.path.relpath(img_path), img_hash, distance))

for img in img_hash_all:
    print(f"圖像:{img[0]},圖像HASH:{img[1]},與圖像目標的相似值(漢明距離):{img[2]}")

time_end = time.time()
print(f"耗時:{time_end - time_start}")

輸出打?。?/p>

目標圖像:..\..\P1_Hash\02_pHash\img_test\apple-01.jpg,圖片HASH:b080000088000000
圖像:..\..\P1_Hash\02_pHash\img_test\apple-01.jpg,圖像HASH:b080000088000000,與目標圖像的相似值(漢明距離):0
圖像:..\..\P1_Hash\02_pHash\img_test\apple-02.jpg,圖像HASH:a080000018000000,與目標圖像的相似值(漢明距離):2
圖像:..\..\P1_Hash\02_pHash\img_test\apple-03.jpg,圖像HASH:b020000080000000,與目標圖像的相似值(漢明距離):2
圖像:..\..\P1_Hash\02_pHash\img_test\apple-04.jpg,圖像HASH:a480000020000000,與目標圖像的相似值(漢明距離):4
圖像:..\..\P1_Hash\02_pHash\img_test\apple-05.jpg,圖像HASH:a400000044000000,與目標圖像的相似值(漢明距離):5
圖像:..\..\P1_Hash\02_pHash\img_test\apple-06.jpg,圖像HASH:f881000084000000,與目標圖像的相似值(漢明距離):4
圖像:..\..\P1_Hash\02_pHash\img_test\apple-07.jpg,圖像HASH:e408000090000000,與目標圖像的相似值(漢明距離):6
圖像:..\..\P1_Hash\02_pHash\img_test\apple-08.jpg,圖像HASH:cad9522236480010,與目標圖像的相似值(漢明距離):13
圖像:..\..\P1_Hash\02_pHash\img_test\apple-09.jpg,圖像HASH:b000000098000000,與目標圖像的相似值(漢明距離):2
圖像:..\..\P1_Hash\02_pHash\img_test\pear-001.jpg,圖像HASH:e0000000c8000000,與目標圖像的相似值(漢明距離):3
耗時:0.09674215316772461

簡單的測試分析:

原圖 相似圖片 相似值(漢明距離) 相似圖片特點 相似圖片與原圖Hash對比結(jié)果
圖片01 圖片01 0 自己 自己與自己相似度100%
圖片01 圖片02、03、09 2 主體形狀、位置、背景基本相似 最相似。相同背景、相同物體、同相位置下最相似。
圖片01 圖片pear-001 3 黃色的梨子 意外相似。相似搜索并不能識別物體/內(nèi)容。
圖片01 圖片04、圖片06 4 原圖像的180度旋轉(zhuǎn)圖;多主體 比較相似。對于多主體、原圖旋轉(zhuǎn)變換相似搜索友好,因為經(jīng)過DCT變換后,圖像的能量特征集中在圖像的左上角。
圖片01 圖片05 5 青蘋果(2D) 比較相似。對于2D的扁平相似圖片搜索也相對友好。
圖片01 圖片07 6 背景差異、多色調(diào) 勉強相似。對于背景差異、多色調(diào)的圖片開始查找吃力。
圖片01 圖片08 10以上 背景差異、多色調(diào) 較難分辨。復(fù)雜背景差異、多色調(diào)的圖片較難與原圖相似。

10張測試圖片中,漢明距離在5以內(nèi)的有8張圖片;漢明距離在10以外只有1張圖片。
從抽樣簡單測試結(jié)果看,感知哈希算法表現(xiàn)更友好、更準確。

備注:如果漢明距離0,則表示這兩張圖片非常相似;如果漢明距離小于5,則表示有些不同,但比較相近;如果漢明距離大于10,則表明是完全不同的圖片。

?

5. 總結(jié)

經(jīng)過實驗和測試,感知哈希算法的擼棒性更好??傮w與均值哈希算法(aHash)差不多,區(qū)別在于二值化方式不一樣。

特點: 傳統(tǒng), 屬于一種外觀相似哈希算法。
優(yōu)點: 簡單、相對準確、計算效率高;感知哈??紤]了圖像的全局特征,對圖像的尺寸和旋轉(zhuǎn)變化具有一定的魯棒性;適用于快速圖像相似性搜索。
缺點: 對一些局部變化不夠敏感;對于復(fù)雜、多色調(diào)的圖像較難辨別,屬于一種外觀相似的相似度計算。

?

6. 實驗問題

為什么要縮放DCT?DCT縮放方式有哪些?不同DCT縮放方式有何不同?不進行DCT縮放效果會怎么樣?

對于這些問題,我們來通過下面三組對比分析,一探究竟。

6.1 過程對比

方式一: DCT變換后,無DCT特征頻率區(qū)域縮放
方式二: DCT變換后,將DCT系數(shù)顯式調(diào)整為8x8
方式三: DCT變換后,只提取DCT系數(shù)左上角8x8像素
OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索
從上圖的DCT變換過程來看,從原圖讀取,到縮小到指定大小的像素圖像,再到像素圖像灰度化,對于圖像的加工結(jié)果都是一樣的。區(qū)別僅在于對灰度圖像使用離散余弦變換(DCT)之后,對DCT系數(shù)的使用方式不一樣。

6.2 結(jié)果對比

1)縱向?qū)Ρ?/h4>

同一張圖片使用不同DCT變換方式獲得的哈希值結(jié)果:

方式一:離散余弦變換DCT變換后,無DCT特征頻率區(qū)域縮放,獲得圖像的二進制哈希值=b3c3c682c9306640
方式二:離散余弦變換DCT變換后,將DCT系數(shù)顯式調(diào)整為8x8,獲得圖像的二進制哈希值=b080000088000000
方式三:離散余弦變換DCT變換后,只提取DCT系數(shù)左上角8x8像素,獲得圖像的二進制哈希值=b088822008008000

從上述的DCT變換結(jié)果來看,同一張圖片獲得圖像的二進制哈希值各不一樣。

  • 方式一與方式二、方式三的結(jié)果相差較大。
  • 方式二與方式三的結(jié)果也不盡一致。

2)橫向?qū)Ρ?/h4>

不同圖片使用相同DCT變換方式獲得的哈希值結(jié)果:
OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗,Python,算法,OpenCV,opencv,哈希算法,感知哈希算法,python,以圖搜圖,相似圖片搜索
從上圖的DCT變換結(jié)果來看,不同圖片使用不同方式的DCT變換,最終查找的相似圖片結(jié)果都不盡相同。

  • 從DCT變換方式維度看,方式二,將DCT系數(shù)顯示調(diào)整為8x8的查找效果最好。方式三其次。方式一最次。
  • 從DCT變換方式的計算效率來看,方式二與方式三耗時相當,計算效率較高;而方式一,由于無DCT特征頻率區(qū)域縮放,所以計算量最大,效率最次。

6.3 代碼對比

"""
以圖搜圖:感知哈希算法(Perceptual Hash Algorithm,簡稱pHash)的原理與實現(xiàn)
測試環(huán)境:win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1
實驗時間:2023-10-22
"""

# ---------------------------------------------------------------------------------------------------------------------
# 測試:為什么要縮放DCT?DCT縮放方式有哪些?不同DCT縮放方式有何不同?不進行DCT縮放效果會怎么樣?
# ---------------------------------------------------------------------------------------------------------------------

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

# DCT變換后:無特征頻率區(qū)域縮放,使用整個32x32圖像塊的頻率分布,計算整個DCT系數(shù)的均值,并根據(jù)這個均值生成哈希值。
def get_pHash1(img_path):
    img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img = cv2.resize(img, (32, 32), cv2.INTER_CUBIC)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # plt.imshow(img_gray, cmap='gray')
    # plt.show()

    img_dct = cv2.dct(np.float32(img_gray))

    # 顯示DCT系數(shù)的圖像
    # dct_scaled = cv2.normalize(img_dct, None, 0, 255, cv2.NORM_MINMAX)
    # img_dct_scaled = dct_scaled.astype(np.uint8)
    # plt.imshow(img_dct_scaled, cmap='gray')
    # plt.show()
    
    img_avg = np.mean(img_dct)
    # print(f"DCT變換后圖像塊的均值={img_avg}")

    img_hash_str = get_img_hash_binary(img_dct, img_avg)
    # print(f"圖像的二進制哈希值={img_hash_str}")

    img_hash = get_img_hash(img_hash_str)
    return img_hash


# DCT變換后:將DCT系數(shù)的大小顯式地調(diào)整為8x8,使用8x8的DCT系數(shù)塊的頻率分布,計算調(diào)整后的DCT系數(shù)的均值,并生成哈希值。
def get_pHash2(img_path):
    img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img = cv2.resize(img, (32, 32), cv2.INTER_CUBIC)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # plt.imshow(img_gray, cmap='gray')
    # plt.show()

    img_dct = cv2.dct(np.float32(img_gray))
    img_dct.resize(8, 8)

    # 顯示DCT系數(shù)的圖像
    # dct_scaled = cv2.normalize(img_dct, None, 0, 255, cv2.NORM_MINMAX)
    # img_dct_scaled = dct_scaled.astype(np.uint8)
    # plt.imshow(img_dct_scaled, cmap='gray')
    # plt.show()

    img_avg = np.mean(img_dct)
    # print(f"DCT變換后圖像塊的均值={img_avg}")

    img_hash_str = get_img_hash_binary(img_dct, img_avg)
    # print(f"圖像的二進制哈希值={img_hash_str}")

    img_hash = get_img_hash(img_hash_str)
    return img_hash


# DCT變換后:只提取DCT系數(shù)的左上角8x8塊的信息,然后計算這個塊的均值。此法只考慮圖像一小部分的頻率分布,并生成哈希值。
def get_pHash3(img_path):
    img = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img = cv2.resize(img, (32, 32), cv2.INTER_CUBIC)
    # plt.imshow(img, cmap='gray')
    # plt.show()

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # plt.imshow(img_gray, cmap='gray')
    # plt.show()

    img_dct = cv2.dct(np.float32(img_gray))
    dct_roi = img_dct[0:8, 0:8]

    # 顯示DCT系數(shù)的圖像
    # dct_scaled = cv2.normalize(dct_roi, None, 0, 255, cv2.NORM_MINMAX)
    # img_dct_scaled = dct_scaled.astype(np.uint8)
    # plt.imshow(img_dct_scaled, cmap='gray')
    # plt.show()

    img_avg = np.mean(dct_roi)
    # print(f"DCT變換后圖像塊的均值={img_avg}")

    img_hash_str = get_img_hash_binary(dct_roi, img_avg)
    # print(f"圖像的二進制哈希值={img_hash_str}")

    img_hash = get_img_hash(img_hash_str)
    return img_hash

def get_img_hash_binary(img_dct, img_avg):
    img_hash_str = ''
    for i in range(8):
        img_hash_str += ''.join(map(lambda i: '0' if i < img_avg else '1', img_dct[i]))
    # print(f"圖像的二進制哈希值={img_hash_str}")
    return img_hash_str

def get_img_hash(img_hash_str):
    img_hash = ''.join(map(lambda x:'%x' % int(img_hash_str[x : x + 4], 2), range(0, 64, 4)))
    # print(f"圖像可識別的哈希值={img_hash}")
    return img_hash

# 漢明距離:計算兩個等長字符串(通常是二進制字符串或位字符串)之間的漢明距離。用于確定兩個等長字符串在相同位置上不同字符的數(shù)量。
def hamming_distance(s1, s2):
    # 檢查這兩個字符串的長度是否相同。如果長度不同,它會引發(fā) ValueError 異常,因為漢明距離只適用于等長的字符串
    if len(s1) != len(s2):
        raise ValueError("Input strings must have the same length")
    
    distance = 0
    for i in range(len(s1)):
        # 遍歷兩個字符串的每個字符,比較它們在相同位置上的值。如果發(fā)現(xiàn)不同的字符,將 distance 的值增加 1
        if s1[i] != s2[i]:
            distance += 1
    return distance



# ======================================== 測試場景一 ========================================

# img = 'img_test/apple-01.jpg'

# img_hash1 = get_phash1(img)
# img_hash2 = get_phash2(img)
# img_hash3 = get_phash3(img)

# print(f"方式一:DCT變換后,無DCT特征頻率區(qū)域縮放,獲得圖像的二進制哈希值={img_hash1}")
# print(f"方式二:DCT變換后,將DCT系數(shù)顯式調(diào)整為8x8,獲得圖像的二進制哈希值={img_hash2}")
# print(f"方式三:DCT變換后,只提取DCT系數(shù)左上角8x8像素,獲得圖像的二進制哈希值={img_hash3}")



# ======================================== 測試場景二 ========================================

time_start = time.time()

img_1 = 'img_test/apple-01.jpg'
img_2 = 'img_test/apple-02.jpg'
img_3 = 'img_test/apple-03.jpg'
img_4 = 'img_test/apple-04.jpg'
img_5 = 'img_test/apple-05.jpg'
img_6 = 'img_test/apple-06.jpg'
img_7 = 'img_test/apple-07.jpg'
img_8 = 'img_test/apple-08.jpg'
img_9 = 'img_test/apple-09.jpg'
img_10 = 'img_test/pear-001.jpg'

# ------------------------------------- 測試場景二:方式一 --------------------------------------

# img_hash1 = get_pHash1(img_1)
# img_hash2 = get_pHash1(img_2)
# img_hash3 = get_pHash1(img_3)
# img_hash4 = get_pHash1(img_4)
# img_hash5 = get_pHash1(img_5)
# img_hash6 = get_pHash1(img_6)
# img_hash7 = get_pHash1(img_7)
# img_hash8 = get_pHash1(img_8)
# img_hash9 = get_pHash1(img_9)
# img_hash10 = get_pHash1(img_10)

# ------------------------------------- 測試場景二:方式二 --------------------------------------

img_hash1 = get_pHash2(img_1)
img_hash2 = get_pHash2(img_2)
img_hash3 = get_pHash2(img_3)
img_hash4 = get_pHash2(img_4)
img_hash5 = get_pHash2(img_5)
img_hash6 = get_pHash2(img_6)
img_hash7 = get_pHash2(img_7)
img_hash8 = get_pHash2(img_8)
img_hash9 = get_pHash2(img_9)
img_hash10 = get_pHash2(img_10)

# ------------------------------------- 測試場景二:方式三 --------------------------------------

# img_hash1 = get_pHash3(img_1)
# img_hash2 = get_pHash3(img_2)
# img_hash3 = get_pHash3(img_3)
# img_hash4 = get_pHash3(img_4)
# img_hash5 = get_pHash3(img_5)
# img_hash6 = get_pHash3(img_6)
# img_hash7 = get_pHash3(img_7)
# img_hash8 = get_pHash3(img_8)
# img_hash9 = get_pHash3(img_9)
# img_hash10 = get_pHash3(img_10)

distance1 = hamming_distance(img_hash1, img_hash1)
distance2 = hamming_distance(img_hash1, img_hash2)
distance3 = hamming_distance(img_hash1, img_hash3)
distance4 = hamming_distance(img_hash1, img_hash4)
distance5 = hamming_distance(img_hash1, img_hash5)
distance6 = hamming_distance(img_hash1, img_hash6)
distance7 = hamming_distance(img_hash1, img_hash7)
distance8 = hamming_distance(img_hash1, img_hash8)
distance9 = hamming_distance(img_hash1, img_hash9)
distance10 = hamming_distance(img_hash1, img_hash10)

time_end = time.time()

print(f"圖片名稱:{img_1},圖片HASH:{img_hash1},與圖片1的近似值(漢明距離):{distance1}")
print(f"圖片名稱:{img_2},圖片HASH:{img_hash2},與圖片1的近似值(漢明距離):{distance2}")
print(f"圖片名稱:{img_3},圖片HASH:{img_hash3},與圖片1的近似值(漢明距離):{distance3}")
print(f"圖片名稱:{img_4},圖片HASH:{img_hash4},與圖片1的近似值(漢明距離):{distance4}")
print(f"圖片名稱:{img_5},圖片HASH:{img_hash5},與圖片1的近似值(漢明距離):{distance5}")
print(f"圖片名稱:{img_6},圖片HASH:{img_hash6},與圖片1的近似值(漢明距離):{distance6}")
print(f"圖片名稱:{img_7},圖片HASH:{img_hash7},與圖片1的近似值(漢明距離):{distance7}")
print(f"圖片名稱:{img_8},圖片HASH:{img_hash8},與圖片1的近似值(漢明距離):{distance8}")
print(f"圖片名稱:{img_9},圖片HASH:{img_hash9},與圖片1的近似值(漢明距離):{distance9}")
print(f"圖片名稱:{img_10},圖片HASH:{img_hash10},與圖片1的近似值(漢明距離):{distance10}")

print(f"耗時:{time_end - time_start}")

如上代碼,這三種方法獲取到的圖像二進制哈希值之所以不同,是因為它們在DCT變換后的處理方式不同:

  • get_pHash1 方法: 這種方法首先將圖像進行灰度化,然后執(zhí)行DCT變換。接著,它計算整個DCT系數(shù)的均值,并根據(jù)這個均值生成哈希值。這意味著它考慮了整個32x32圖像塊的頻率分布。
  • get_pHash2 方法: 這種方法在執(zhí)行DCT后,將DCT系數(shù)的大小顯式地調(diào)整為8x8。然后它計算調(diào)整后的DCT系數(shù)的均值,并生成哈希值。這個方法只考慮了8x8的DCT系數(shù)塊的頻率分布。
  • get_pHash3 方法: 這種方法與 get_pHash2 類似,但它只提取了DCT系數(shù)的左上角8x8塊的信息(即ROI,感興趣區(qū)域),然后計算這個塊的均值。這個方法只考慮了圖像的一個小部分頻率分布。

由于這些方法考慮的DCT系數(shù)區(qū)域不同,它們生成的哈希值會有差異。一般來說,get_pHash1 方法考慮了整個圖像塊的頻率分布,因此哈希值可能更穩(wěn)定,但它也可能受到圖像整體性的影響。而 get_pHash2 和 get_pHash3 方法只考慮了一個小塊的頻率信息,所以哈希值可能更容易受到圖像的局部特征影響。

選擇哪種方法取決于你的應(yīng)用需求。如果你希望更穩(wěn)定的哈希值,get_pHash1 可能是一個不錯的選擇。如果你希望更靈敏地檢測局部特征,get_pHash2 或 get_pHash3 可能更適合。

?

7. 系列書簽

OpenCV書簽 #均值哈希算法的原理與相似圖片搜索實驗
OpenCV書簽 #感知哈希算法的原理與相似圖片搜索實驗
OpenCV書簽 #差值哈希算法的原理與相似圖片搜索實驗
OpenCV書簽 #直方圖算法的原理與相似圖片搜索實驗文章來源地址http://www.zghlxwxcb.cn/news/detail-744967.html

到了這里,關(guān)于OpenCV #以圖搜圖:感知哈希算法(Perceptual hash algorithm)的原理與實驗的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 關(guān)于Secure Hash Algorithm加密算法

    關(guān)于Secure Hash Algorithm加密算法

    一、概述 SHA(Secure Hash Algorithm)加密算法是一種廣泛應(yīng)用的密碼散列函數(shù),由美國國家安全局(NSA)設(shè)計,用于保障數(shù)據(jù)的安全性和完整性。SHA算法經(jīng)歷了多個版本的更新,目前主要應(yīng)用于各種網(wǎng)絡(luò)安全和數(shù)據(jù)加密領(lǐng)域。 SHA在線加密 | 一個覆蓋廣泛主題工具的高效在線平臺

    2024年02月04日
    瀏覽(16)
  • 某某88以圖搜圖

    某某88以圖搜圖

    聲明:本文僅限學習交流使用,禁止用于非法用途、商業(yè)活動等。否則后果自負。如有侵權(quán),請告知刪除,謝謝!本教程也沒有專門針對某個網(wǎng)站而編寫,單純的技術(shù)研究 cnVub29iaHR0cHM6Ly9zLjE2ODguY29tL3lvdXl1YW4vaW5kZXguaHRtPw== 1.對應(yīng)接口: 2.難點: 1.sign從直觀來看是32位,所以我們可

    2024年02月15日
    瀏覽(24)
  • 使用火山云搜索ESCloud服務(wù)構(gòu)建圖文檢索應(yīng)用(以文搜圖/以圖搜圖)

    使用火山云搜索ESCloud服務(wù)構(gòu)建圖文檢索應(yīng)用(以文搜圖/以圖搜圖)

    圖文檢索在生活中具有廣泛的應(yīng)用,常見的圖片檢索包括基于文本內(nèi)容搜索和基于圖片內(nèi)容搜索。用戶通過輸入文字描述或上傳圖片就可以在海量的圖片庫中快速找到同款或者相似圖片,這種搜索方式被廣泛應(yīng)用于電商、廣告、設(shè)計以及搜索引擎等熱門領(lǐng)域。 本文 基于 火山

    2024年02月14日
    瀏覽(26)
  • Java+ElasticSearch+Pytorch實現(xiàn)以圖搜圖

    Java+ElasticSearch+Pytorch實現(xiàn)以圖搜圖

    以圖搜圖,涉及兩大功能:1、提取圖像特征向量。2、相似向量檢索。 第一個功能我通過編寫pytorch模型并在java端借助djl調(diào)用實現(xiàn),第二個功能通過elasticsearch7.6.2的dense_vector、cosineSimilarity實現(xiàn)。 創(chuàng)建demo.py,輸入代碼,借助resnet提取圖像特征 保存好的model.pt文件放入java項目的

    2024年02月02日
    瀏覽(31)
  • java elasticsearch 實現(xiàn)以圖搜圖效果

    java elasticsearch 實現(xiàn)以圖搜圖效果

    前言: 現(xiàn)在需要用java+elasticsearch的方式實現(xiàn)以圖搜圖的效果,效果如下: 相關(guān)文章:https://blog.csdn.net/m0_52640724/article/details/129357847 實現(xiàn)效果如下: java:jdk11 elasticsearch:7.17.3 windows:win10 linux:centos7.9 此算法是使用pytorch中resnet50模型計算圖片的張量,數(shù)據(jù)存入elasticsearch中,

    2024年02月10日
    瀏覽(21)
  • 圖片搜索引擎網(wǎng)站大全,以圖搜圖網(wǎng)站

    圖片搜索引擎網(wǎng)站大全,以圖搜圖網(wǎng)站

    當我們需要搜索一些圖片的時候使用圖片搜索引擎網(wǎng)站可以幫我們更快地找到自己需要的圖片,那么有哪些圖片搜索引擎網(wǎng)站可以搜索圖片呢?下面小編就來和大家分享幾個以圖搜圖的網(wǎng)站。 1.百度圖片搜索引擎網(wǎng)站 百度是最大的中文搜索引擎,百度的圖片搜索以中文網(wǎng)站的

    2024年02月07日
    瀏覽(28)
  • ES 如何實現(xiàn)向量搜索【以圖搜圖/語義搜索】

    在 ES 的使用過程中,通過設(shè)置分詞器可以靈活地按照文本字面實現(xiàn)搜索和查詢。但是在某些場景下,向量搜索非常有必要,比如 CV 方面的以圖搜圖和 NLP 領(lǐng)域的語義搜索。較新的 ES 版本支持稠密向量搜索,詳情如下。相關(guān)片段設(shè)置重在強調(diào)特定的關(guān)鍵點,需要根據(jù)自己具體

    2024年02月11日
    瀏覽(22)
  • 損失函數(shù)——感知損失(Perceptual Loss)

    損失函數(shù)——感知損失(Perceptual Loss)

    感知損失(Perceptual Loss) 是一種基于深度學習的圖像風格遷移方法中常用的損失函數(shù)。與傳統(tǒng)的均方誤差損失函數(shù)(Mean Square Error,MSE)相比,感知損失更注重圖像的感知質(zhì)量,更符合人眼對圖像質(zhì)量的感受。 感知損失是通過預(yù)訓(xùn)練的神經(jīng)網(wǎng)絡(luò)來計算兩張圖片之間的差異。通

    2024年02月04日
    瀏覽(21)
  • OpenCV每日函數(shù) 了解不同的圖像哈希函數(shù)、以及OpenCV的img_hash哈希模塊

    ????????圖像哈希是 使用算法為圖像分配唯一哈希值的過程 。圖像的副本都具有完全相同的哈希值。因此,它有時被稱為“數(shù)字指紋”。 ????????在深度學習普及之前,一些搜索引擎使用散列技術(shù)來索引圖像。這就需要一個哈希函數(shù),對于文件的微小更改,該函數(shù)會

    2024年02月11日
    瀏覽(29)
  • 【milvus】向量數(shù)據(jù)庫,用來做以圖搜圖+人臉識別的特征向量

    【milvus】向量數(shù)據(jù)庫,用來做以圖搜圖+人臉識別的特征向量

    ref:https://milvus.io/docs 第一次裝東西,要把遇到的問題和成功經(jīng)驗都記錄下來。 1.Download the YAML file 看一下下載下來的是什么東西 Start Milvus In the same directory as the docker-compose.yml file, start up Milvus by running: 報錯則需要安裝docker-compose了 下載最新版的docker-compose 文件 添加可執(zhí)行權(quán)限

    2024年02月16日
    瀏覽(36)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包