基於深度學習的影象檢索 image retrieval based on deep learning (code ,程式碼)
本次程式碼分享主要是用的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。然後才可以計算距離。這樣的化,距離排個倒敘就能得到了我們最後的檢索結果。