本次代码分享主要是用的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。然后才可以计算距离。这样的化,距离排个倒叙就能得到了我们最后的检索结果。