图像相似度匹配

这个周末解决了一个实际问题。
硬盘里存有大量图片。(大约2万)
当需要找某一图片时,如何找出与之相似的呢。

在查资料的过程中。我知道可以使用
PIL(Python Image Library)或者是openCV。
对于学习来说,使用后者更好,因为opencv具有跨平台的特性,且支持多种语言的接口。而且在python上也不乏很多资料可查。

开发环境我选择了 opencv3.1 + pycharm
在python中图像使用numpy.ndarray表示,所以要提前装好numpy库。

下一步就是匹配算法了。
一开始我想用的是Template Matching
后来发现这种模式匹配的方式还需要涉及缩放问题。
于是,简单点的话还是使用直方图匹配的方式进行。
参考 pil的这个博客。不过我使用的是opencv。
opencv的直方图使用资料可以从readdoc网查到

好的。讲了这么多。还是直接贴代码吧~

import cv2
import numpy as np
import os
import os.path
from matplotlib import pyplot as plt

此处安装后,需要配置cv2的so库地址。报错不要紧,不影响调用。见我的安装笔记即可。

获取直方图算法:简单说一下,就是分别获取rgb三个通道的直方图,然后加在一起,成为一个768*1的数组。
返回的是一个元组。分别是直方图向量和图像的像素点总和。我利用这个比例缩放来进行图片的大小匹配。

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)

相似度计算:

计算公式
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

该算法反悔一个0到1的[float]相似度。来代表两个向量之间的几何距离。输入的4个参数分别为图像的直方图和图像的尺寸。

最后加上一些文件访问的代码和排序代码。即可给出
对于指定图片,在目标文件夹中的所有图片中相似度排名最高的N个图的路径和相似度。

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])
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 这些年计算机视觉识别和搜索这个领域非常热闹,后期出现了很多的创业公司,大公司也在这方面也花了很多力气在做。做视觉搜...
    方弟阅读 6,416评论 6 24
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,421评论 6 428
  • GitHub 上有一个 Awesome - XXX 系列的资源整理,资源非常丰富,涉及面非常广。awesome-p...
    若与阅读 18,591评论 4 418
  • 环境管理管理Python版本和环境的工具。p–非常简单的交互式python版本管理工具。pyenv–简单的Pyth...
    MrHamster阅读 3,775评论 1 61
  • 买菜时经过海鲜区,见有个摊位上腾出一块地方来,反放着货箱盖,上面摆放着一块一块白莹莹透明的“果冻”。是海蜇!我的眼...
    春山雨阅读 697评论 2 1