1. 程式人生 > >基於深度學習的影象檢索 image retrieval based on deep learning (code ,程式碼)

基於深度學習的影象檢索 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。然後才可以計算距離。這樣的化,距離排個倒敘就能得到了我們最後的檢索結果。