[ PhotoHash ] 利用 PhotoHash篩選相似度過高的圖像


子瑜在這部影片中,前 14秒幾乎沒有任何的動作。
若用 "利用 Dlib進行臉部捕捉" 該文中提供的程式碼,這 14秒的影片大約會擷取300多張的圖像。
用這筆數據訓練後,可能會導致權重失衡。
為了避免權重失衡影響辨識結果,有必要對圖像進行一些篩選。
用爬蟲爬取圖片的時候也容易爬到不同出處但卻是同一張照片,這麼做也很方便呦。

Hamming distance可以作為數據差異度的基準,詳細可以到維基百科了解一下。
對於圖像差異度的比較,Python也有一套 Library叫 PhotoHash可以幫忙比較圖像的差異。
先計算整張圖像的平均 Hash值後,再計算 Hamming distance。
import os
import glob
import photohash

def filter(member):
    img_list = glob.glob('./{0}/*.png'.format(member))

    # def order_by_num(num):
    #     return int(num.split('/')[-1].split('.')[0])
    # img_list.sort(key=order_by_num)

    img_list.sort(key=lambda num: int(num.split('/')[-1].split('.')[0]))
    for img in img_list:
        hash_one_path = img
        hash_one = photohash.average_hash(hash_one_path)
        if 'hash_two' in locals():
            distance = photohash.hash_distance(hash_one, hash_two)
            if distance <= 2:
                if not os.path.isdir("./{0}/similar".format(member)):
                    os.system("mkdir ./{0}/similar".format(member))
                    print("make dir ./{0}/similar".format(member))
                os.system("mv {0} ./{1}/similar".format(hash_one_path, member))
                print("move {0} to {1}".format(hash_one_path, member))
                continue
        hash_two = hash_one

names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]

for member in names:
    filter(member)
最近發現可以用 glob來取代之前文章用的 os.walk,使用起來更加簡單。
之前的文章中,將捕捉的圖像利用 1.png, 2.png, 3.png...這樣的順序儲存。
但無論用 glob或 os.walk都會面臨一樣的問題,就是讀取時,數字並不會連續,顯示如下
['./Tzuyu/1.png', './Tzuyu/10.png', './Tzuyu/100.png', './Tzuyu/1000.png', './Tzuyu/1001.png', './Tzuyu/1002.png', ...]
但影片是連續性的,有必要按照順序讀取來比較,因此需要使用 sort()來重新排列。
擔心有些人看不懂,所以另外寫了幾行被註解的代碼。功能和有 lambda那行是一樣的。
接著就是計算平均 Hash值,和下一個張圖做比較,distance小於等於 2就將該圖片移至 similar資料夾中。
至於 Hamming distance到底要以多少作為基準就要各位自己拿捏了。
這個程序將會將 1.png作為比較的基準,直到有 distance大於 2的圖,便會將該圖作為比較的基準繼續往下比較。

以上十張圖像都是被篩選出來的,不是同一張喔。

以上十張圖像都是被保留的,雖然相似度很高,但大概可以看出差異。
distance設定保留大於 2的圖,其實相似度還是挺高的,需要視情況調整 distance。

如果是想要比較全部的照片,就不需要在意順序,但需要注意 list的邏輯,有點像一群人握手次數的問題。
import os
import glob
import photohash

def filter(member):
    img_list = glob.glob('./{0}/*.png'.format(member))
    while True:
        if img_list==[]:
            break
        img_target = img_list.pop()
        img_hash_target = photohash.average_hash(img_target)
        for img in img_list:
            hash_one = photohash.average_hash(img)
            distance = photohash.hash_distance(img_hash_target, hash_one)
            if distance <= 2:
                if not os.path.isdir("./{0}/similar".format(member)):
                    os.system("mkdir ./{0}/similar".format(member))
                    print("make dir ./{0}/similar".format(member))
                os.system("mv {0} ./{1}/similar".format(img, member))
                print("move {0} to ./{1}/similar".format(img, member))
                img_list.remove(img)

names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]

for member in names:
    filter(member)

之前用 PyTorch寫了一個 CNN的影像辨識,原本就有預期會權重失衡,但實在有點懶就直接拿來訓練了,結果權重好像真的失衡了。
當然原因不只這個,不過本來就有必要剔除相似度過高的圖像,但相似度高不高又不是我說的算,所以才尋找一個比較科學的方式來進行篩選。

最近發現有好像比較好的方式對照片相似進行篩選,有機會再介紹吧。

留言