zoukankan      html  css  js  c++  java
  • 小试“以图搜图”

    毕业设计做的User-based Recommend System。当中涉及到了“余弦相似度”这个概念。

    阮一峰大牛的博客介绍了以图搜图的原理(这里就不赘言),并给除了Python实现的代码 ,这个程序对于我来说,有的地方感觉稍显复杂。搜图的效果也不那么棒,于是我重写了下。


    改进的地方有两点:

    1.图片哈希:

    原来的代码是这种:

    reduce(lambda x, (y, z): x | (z << y),
                      enumerate(map(lambda i: 0 if i < avg else 1, im.getdata())),
                      0)

    我直接用python的map+lambda给简化成求64个像素点的像素值,然后又平均像素值比較,转化成0、1组成的向量:

    avg = reduce(lambda x, y: x + y, im.getdata()) / 64
    return map(lambda x: 0 if x>avg else 1, im.getdata())


    打印看下效果:

    [1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

    2.比較相似度的方法:


    原来的代码用hamming距离求:

    def hamming(h1, h2):
        h, d = 0, h1 ^ h2
        while d:
            h += 1
            d &= d - 1
        return h

    我用余弦距离求:

    def cos_dist(a, b):
        if len(a) != len(b):
            return None
        part_up = 0.0
        a_sq = 0.0
        b_sq = 0.0
        for x, y in zip(a,b):
            part_up += x*y
            a_sq += x**2
            b_sq += y**2
        part_down = math.sqrt(a_sq*b_sq)
        if part_down == 0.0:
            return None
        else:
            return part_up / part_down
    

    最后,写出的代码例如以下:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    
    import math
    import glob
    from PIL import Image
    
    
    EXTS = ['png']
    
    
    master = Image.open('image.png')
    master = master.resize((8, 8), Image.ANTIALIAS).convert('L')
    
    
    avg = reduce(lambda x, y: x + y, master.getdata()) / 64
    master_data = map(lambda x: 0 if x>avg else 1, master.getdata())
    #print master_data
    
    
    def cos_dist(a, b):
        if len(a) != len(b):
            return None
        part_up = 0.0
        a_sq = 0.0
        b_sq = 0.0
        for x, y in zip(a,b):
            part_up += x*y
            a_sq += x**2
            b_sq += y**2
        part_down = math.sqrt(a_sq*b_sq)
        if part_down == 0.0:
            return None
        else:
            return part_up / part_down
    
    
    images = []
    for ext in EXTS:
        #fill in images list
        images.extend(glob.glob('*.%s' % ext))
        
        #print repr(images)
        dists = []
        for f in images:
            if f == "image.png":
                continue
            im = Image.open(f)
            im = im.resize((8, 8), Image.ANTIALIAS).convert('L')
            avg = reduce(lambda x, y: x + y, im.getdata()) / 64
    
    
            #这里还有问题。得出的像素是反的,所以我把">"号变成了"<"号
            im_data = map(lambda x: 0 if x<avg else 1, im.getdata())
            
            dist = cos_dist(master_data, im_data)
            
            print "image: %s	 avg: %f	 dist:%s	" % (f, avg, dist)
            print im_data
            
            dists.append((f, dist))
            
    for f, dist in sorted(dists, key=lambda i: i[1]):
        print "%f	%s" % (dist, f)        
                  
            
    

    我有这样几张图片:


    1. 基准图片 image.png:




    以下这几张图片就是要測试的图片,与基准图片的相似度 image1 > image2 > image3 > image4 > image5


    2.image1.png


    3.image2.png



    4.image3.png


    5.image4.png


    6.image5.png:



    执行结果例如以下:

    image: image1.png	 avg: 215.000000	 dist:0.970142500145	
    [1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    image: image2.png	 avg: 210.000000	 dist:0.857250857251	
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    image: image3.png	 avg: 168.000000	 dist:0.735980072194	
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    image: image4.png	 avg: 108.000000	 dist:0.714434508312	
    [1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1]
    image: image5.png	 avg: 76.000000	 dist:0.25	
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    0.250000	image5.png
    0.714435	image4.png
    0.735980	image3.png
    0.857251	image2.png
    0.970143	image1.png

    可见。计算出的余弦相似度 image1 > image2 > image3 > image4 > image5

    这与我们看到的是一样的。最后,我们搜索出的最相似的图片就是image1喽~

    --------------

    代码放在了github上。


  • 相关阅读:
    Python3基础 list [] 创建空列表
    Python3基础 list [] 创建整数列表
    Python3基础 iter+next 进行迭代时超出了范围 产生StopIteration异常
    Python3基础 frozenset 使用list创建frozenset
    Python3基础 filter+lambda 筛选出1-20之间的奇数
    Python3基础 filter 第一个参数为NONE时 结果只返回为True的对象
    Python3基础 dict 推导式 生成10以内+奇数的值为True 偶数为False的字典
    Python3基础 dict 创建字典 空字典
    Python3基础 dict setdefault 根据键查找值,找不到键会添加
    Python3基础 dict pop 弹出指定键的项
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/6767811.html
Copyright © 2011-2022 走看看