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


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

Hamming distance可以作為數據差異度的基準,詳細可以到維基百科了解一下。
對於圖像差異度的比較,Python也有一套 Library叫 PhotoHash可以幫忙比較圖像的差異。
先計算整張圖像的平均 Hash值後,再計算 Hamming distance。
  1. import os
  2. import glob
  3. import photohash
  4.  
  5. def filter(member):
  6. img_list = glob.glob('./{0}/*.png'.format(member))
  7.  
  8. # def order_by_num(num):
  9. # return int(num.split('/')[-1].split('.')[0])
  10. # img_list.sort(key=order_by_num)
  11.  
  12. img_list.sort(key=lambda num: int(num.split('/')[-1].split('.')[0]))
  13. for img in img_list:
  14. hash_one_path = img
  15. hash_one = photohash.average_hash(hash_one_path)
  16. if 'hash_two' in locals():
  17. distance = photohash.hash_distance(hash_one, hash_two)
  18. if distance <= 2:
  19. if not os.path.isdir("./{0}/similar".format(member)):
  20. os.system("mkdir ./{0}/similar".format(member))
  21. print("make dir ./{0}/similar".format(member))
  22. os.system("mv {0} ./{1}/similar".format(hash_one_path, member))
  23. print("move {0} to {1}".format(hash_one_path, member))
  24. continue
  25. hash_two = hash_one
  26.  
  27. names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
  28.  
  29. for member in names:
  30. 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的邏輯,有點像一群人握手次數的問題。
  1. import os
  2. import glob
  3. import photohash
  4.  
  5. def filter(member):
  6. img_list = glob.glob('./{0}/*.png'.format(member))
  7. while True:
  8. if img_list==[]:
  9. break
  10. img_target = img_list.pop()
  11. img_hash_target = photohash.average_hash(img_target)
  12. for img in img_list:
  13. hash_one = photohash.average_hash(img)
  14. distance = photohash.hash_distance(img_hash_target, hash_one)
  15. if distance <= 2:
  16. if not os.path.isdir("./{0}/similar".format(member)):
  17. os.system("mkdir ./{0}/similar".format(member))
  18. print("make dir ./{0}/similar".format(member))
  19. os.system("mv {0} ./{1}/similar".format(img, member))
  20. print("move {0} to ./{1}/similar".format(img, member))
  21. img_list.remove(img)
  22.  
  23. names = ["Nayeon", "Jeongyeon", "Momo", "Sana", "Jihyo", "Mina", "Dahyun", "Chaeyoung", "Tzuyu"]
  24.  
  25. for member in names:
  26. filter(member)

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

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

留言