這個(gè)周末解決了一個(gè)實(shí)際問題。
硬盤里存有大量圖片厨埋。(大約2萬)
當(dāng)需要找某一圖片時(shí)邪媳,如何找出與之相似的呢。
在查資料的過程中荡陷。我知道可以使用
PIL(Python Image Library)或者是openCV雨效。
對(duì)于學(xué)習(xí)來說,使用后者更好废赞,因?yàn)閛pencv具有跨平臺(tái)的特性徽龟,且支持多種語言的接口。而且在python上也不乏很多資料可查唉地。
開發(fā)環(huán)境我選擇了 opencv3.1 + pycharm
在python中圖像使用numpy.ndarray表示据悔,所以要提前裝好numpy庫(kù)传透。
下一步就是匹配算法了。
一開始我想用的是Template Matching
后來發(fā)現(xiàn)這種模式匹配的方式還需要涉及縮放問題极颓。
于是朱盐,簡(jiǎn)單點(diǎn)的話還是使用直方圖匹配的方式進(jìn)行。
參考 pil的這個(gè)博客菠隆。不過我使用的是opencv兵琳。
opencv的直方圖使用資料可以從readdoc網(wǎng)查到
好的。講了這么多骇径。還是直接貼代碼吧~
import cv2
import numpy as np
import os
import os.path
from matplotlib import pyplot as plt
此處安裝后躯肌,需要配置cv2的so庫(kù)地址。報(bào)錯(cuò)不要緊破衔,不影響調(diào)用清女。見我的安裝筆記即可。
獲取直方圖算法:簡(jiǎn)單說一下运敢,就是分別獲取rgb三個(gè)通道的直方圖校仑,然后加在一起,成為一個(gè)768*1的數(shù)組传惠。
返回的是一個(gè)元組迄沫。分別是直方圖向量和圖像的像素點(diǎn)總和。我利用這個(gè)比例縮放來進(jìn)行圖片的大小匹配卦方。
def get_histGBR(path):
img = cv2.imread(path)
pixal = img.shape[0] * img.shape[1]
# print(pixal)
# scale = pixal/100000.0
# print(scale)
total = np.array([0])
for i in range(3):
histSingle = cv2.calcHist([img], [i], None, [256], [0, 256])
total = np.vstack((total, histSingle))
# plt.plot(total)
# plt.xlim([0, 768])
# plt.show()
return (total, pixal)
相似度計(jì)算:
def hist_similar(lhist, rhist, lpixal,rpixal):
rscale = rpixal/lpixal
rhist = rhist/rscale
assert len(lhist) == len(rhist)
likely = sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lhist, rhist)) / len(lhist)
if likely == 1.0:
return [1.0]
return likely
該算法反悔一個(gè)0到1的[float]相似度羊瘩。來代表兩個(gè)向量之間的幾何距離。輸入的4個(gè)參數(shù)分別為圖像的直方圖和圖像的尺寸盼砍。
最后加上一些文件訪問的代碼和排序代碼尘吗。即可給出
對(duì)于指定圖片,在目標(biāo)文件夾中的所有圖片中相似度排名最高的N個(gè)圖的路徑和相似度浇坐。
import cv2
import numpy as np
import os
import os.path
from matplotlib import pyplot as plt
# 文件讀取并顯示
# img = cv2.imread("kitchen.jpeg")
# print("hello opencv "+ str(type(img)))
# # print(img)
# cv2.namedWindow("Image")
# cv2.imshow("Image",img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# img = cv2.imread('kitchen.jpeg', 1)
# temp = cv2.imread('Light.png', 1)
# w,h = temp.shape[::-1]
#
# print(w,h,sep=" ")
# # print(img)
# cv2.namedWindow("Image")
# cv2.imshow("Image", img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# img = cv2.imread('kitchen.jpeg', 0)
# img2 = img.copy()
# template = cv2.imread('Light.png', 0)
# w, h = template.shape[::-1]
# print(template.shape)
#
# # All the 6 methods for comparison in a list
# methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
# 'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
#
# for meth in methods:
# img = img2.copy()
# method = eval(meth)
#
# # Apply template Matching
# res = cv2.matchTemplate(img, template, method)
# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
#
# # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
# if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
# top_left = min_loc
# else:
# top_left = max_loc
# bottom_right = (top_left[0] + w, top_left[1] + h)
#
# cv2.rectangle(img, top_left, bottom_right, 255, 2)
#
# plt.subplot(121), plt.imshow(res, cmap='gray')
# plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
# plt.subplot(122), plt.imshow(img, cmap='gray')
# plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
# plt.suptitle(meth)
#
# plt.show()
# image = cv2.imread("kitchen.jpeg", 0)
# hist = cv2.calcHist([image],
# [0],
# None,
# [256],
# [0, 256])
# plt.plot(hist),plt.xlim([0,256])
# plt.show()
# # hist = cv2.calcHist([image],[0,1,2],None,[256,256,256],[[0,255],[0,255],[0,255]])
# # cv2.imshow("img",image)
# cv2.imshow("hist", hist)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# image = cv2.imread("kitchen.jpeg", 0)
# hist = plt.hist(image.ravel(), 256, [0, 256])
# plt.show(hist)
def hist_similar(lhist, rhist, lpixal,rpixal):
rscale = rpixal/lpixal
rhist = rhist/rscale
assert len(lhist) == len(rhist)
likely = sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lhist, rhist)) / len(lhist)
if likely == 1.0:
return [1.0]
return likely
def get_histGBR(path):
img = cv2.imread(path)
pixal = img.shape[0] * img.shape[1]
# print(pixal)
# scale = pixal/100000.0
# print(scale)
total = np.array([0])
for i in range(3):
histSingle = cv2.calcHist([img], [i], None, [256], [0, 256])
total = np.vstack((total, histSingle))
# plt.plot(total)
# plt.xlim([0, 768])
# plt.show()
return (total, pixal)
if __name__ == '__main__':
targetHist, targetPixal = get_histGBR('test.jpg')
rootdir = "/Users/YM/Desktop/DCIM"
# aHist = get_histGBR('a.png')
# bHist = get_histGBR('Light.png')
#
# print(hist_similar(aHist, bHist))
resultDict = {}
for parent, dirnames, filenames in os.walk(rootdir):
# for dirname in dirnames:
# print("parent is: " + parent)
# print("dirname is: " + dirname)
for filename in filenames:
if (filename[-3:] == 'jpg'):
jpgPath = os.path.join(parent, filename)
testHist, testPixal = get_histGBR(jpgPath)
# print(hist_similar(targetHist,testHist)[0])
resultDict[jpgPath]=hist_similar(targetHist,testHist,targetPixal,testPixal)[0]
# print(resultDict)
# for each in resultDict:
# print(each, resultDict[each],sep="----")
sortedDict = sorted(resultDict.items(), key=lambda asd: asd[1], reverse=True)
for i in range(5):
print(sortedDict[i])