本次代码分享主要是用的caffe框架,至于caffe框架的安装过程不再说明。代码修改自“cross
weights”的一篇2016年的文章,但是名字忘记了,谁记得,提醒我下。

一、环境要求

        1、python


        2、gcc


        3、opencv


        4、一些图像数集合,比如holiday、oxford、paris不过这些都是标准数据集,你也可以用到自己的系统上。


        5、完整代码会之后放在github上。


二、使用说明

        1、首先将整个图库进行特征提取


            流程:图像->VGG 最后一个卷积层得到
H*W*512维度的tensor利用*.npy格式进行保存。对于每张图像都需要这么处理,存储每个图像的“特征”。

        

# Copyright 2015, Yahoo Inc.

# Licensed under the terms of the Apache License, Version 2.0. See the LICENSE
file associated with the project for terms.

import _init_paths

import os

import caffe

import numpy as np

from PIL import Image

import scipy







###################################

# Feature Extraction

###################################










##################################

#这个函数用于打开图像并且转换为RGB,其中img1=·~~~~~~~这一行可以用于改变图像尺寸。

##################################







def load_img(path):

    try:

        img = Image.open(path)

        rgb_img = Image.new("RGB",img.size)

        rgb_img.paste(img)

        return rgb_img

    except:

        return None




#################################

#这个函数用于转换输入图像的数据量,转换为32位浮点数,同时减去3个通道上各自的均值,将数据进行通道转换。

#################################




def format_img_for_vgg(img):

    d = np.array(img, dtype=np.float32)

    d = d[:,:,::-1]

    # Subtract mean pixel values of VGG training set

    d -= np.array((104.00698793,116.66876762,122.67891434))

    return d.transpose((2,0,1))




#################################

#这个函数用于特征提取,net.blob中的net代表了我们初始化完成后的网络,blob代表数据块

#################################







def extract_raw_features(net, layer, d):

    """

    Extract raw features for a single image.

    """

    # Shape for input (data blob is N x C x H x W)

    net.blobs['data'].reshape(1, *d.shape)                   
#这个reshape表示将网络的入口修改为适合我们图像尺寸大小的shape

    net.blobs['data'].data[...] = d                           #d就是我们数据,将数据传入网络中

    # run net and take argmax for prediction

    net.forward()                                             #这个是让网络开始计算

    return net.blobs[layer].data[0]                          
#将网络的计算结果输出(定义layer就能得到对应的层的输出)













################################

#下面这两个函数先不用管

################################




def reshape(image):

    mu=np.array((104.00698793,116.66876762,122.67891434))

    transformer=caffe.io.Transformer({'data':net.blobs['data'].data.shape})

    transformer.set_transpose('data',(2,0,1))

    transformer.set_mean('data',mu)

    transformer.set_raw_scale('data',255)

    #transformer.set_channel_swap('data',(2,1,0))




    net.blobs['data'].reshape(10,

                               3,

                                224,224)




    transformed_image=transformer.preprocess('data',image)

    print transformed_image.shape

    return transformed_image




def extract_raw_features_fc6(net, layer, d):

   

    #net.blobs['data'].reshape(1, *d.shape)

    # Shape for input (data blob is N x C x H x W)

   

    net.blobs['data'].data[...]=d

    net.forward()

    return net.blobs[layer].data[0]










#################################


#这是python的主函数入口,parser.add_argument代表我们手动在控制台需要输入的参数,或者提前定义好的一些参数,我们的网络的prototxt和caffemodel的路径也在参数中定义

################################







if __name__ == '__main__':

    from argparse import ArgumentParser

    parser = ArgumentParser()

    parser.add_argument('--images', dest='images', type=str, nargs='+',
required=True, help='glob pattern to image data')

    parser.add_argument('--layer', dest='layer', type=str, default='fc6',
help='model layer to extract')

    parser.add_argument('--prototxt', dest='prototxt', type=str,
default='vgg/VGG_ILSVRC_16_fc6.prototxt', help='path to prototxt')

    parser.add_argument('--caffemodel', dest='caffemodel', type=str,
default='vgg/VGG_ILSVRC_16_layers.caffemodel', help='path to model params')

    parser.add_argument('--out', dest='out', type=str, default='', help='path
to save output')

    args = parser.parse_args()

##################################

#这个net=~~~~~~表示将我们的网络进行提前初始化,方便后面调用

####################################

    net = caffe.Net(args.prototxt, args.caffemodel, caffe.TEST)




    if not os.path.exists(args.out):

        os.makedirs(args.out)                         #这里是检测我们的数据来源的路径是否正确




    for path in args.images:

        #img = load_img(path)

        img=image=caffe.io.load_image(path)           #加载图像




        # Skip if the image failed to load

        if img is None:

            print path

            continue




        d = format_img_for_vgg(img)                   #对图像加载并且网络继续提取特征

        #d=reshape(img)                              

        X = extract_raw_features_fc6(net, args.layer, d)     
#这后面的两句,是我采用的另外的方式进行  ,修改层名,就会从不同的层抽取特征




        filename = os.path.splitext(os.path.basename(path))[0]

        np.save(os.path.join(args.out, filename), X)           
#将得到的结果采用*.npy的数据格式保存下来W*H*C,每一个图片对应一个*.npy

        2、特征进一步计算,这里有很多的文章可以参考,但是我们介绍一种最简单的,就是把H*W*512
变成1*1*512,也就是对于每个H*W进行了sum-pooling也就是求和,这里也可以有别的一些加权方法,很多论文,主要就在这一步做文章。


        这里就一句话:


        def sum-pooling(X): #这里的X就是刚才512*W*H


                return X.sum(axis=1,2)


        3、最后,就是采用欧式距离,和我们求平面中两个点的距离一样。


        distance=(X1-X2)**2


        这里的X需要在计算距离之前进行normlzation。然后才可以计算距离。这样的化,距离排个倒叙就能得到了我们最后的检索结果。