在此之前你需要把caffe(最好为GPU版)安装好,ubuntu安装参考,windows安装参考。
顺便把scikit-learn安装一下:
pip install scikit-learn
读图像可以用opencv:
pip install opencv-python
首先贴上通用代码,具体介绍请看注释
import cv2
import caffe
import numpy as np
from caffe.proto import caffe_pb2
class CaffeNet:
# caffemodel .caffemodel文件
# deploy_file .protxt文件
# transformer transformer对象,在下面介绍
# caffemodel 显卡编号,从0开始,如果你只有一个显卡那就填0吧
def __init__(self, caffemodel, deploy_file, transformer, device_id=0):
caffe.set_device(device_id)
caffe.set_mode_gpu()
self.net = caffe.Net(deploy_file, caffemodel, caffe.TEST)
self.transformer = transformer
# 前向传播,images表示输入图片序列,这里的每张图片应该与你网络的输入层尺寸一样。
# batch_size,同时前向传播的图片数量,看显存设置,设置高了显存可能会溢出。
# layer,这是个字符串,是你网络中某层网络的名称,当传入改参数时,会返回该层的输出。
#
def forward_pass(self, images, batch_size=1, layer=None):
caffe_images = []
for image in images:
if image.ndim == 2:
caffe_images.append(image[:, :, np.newaxis])
else:
caffe_images.append(image)
caffe_images = np.array(caffe_images)
dims = self.transformer.inputs['data'][1:]
scores = None
fea = None
for chunk in [caffe_images[x:x+batch_size] for x in range(0, len(caffe_images), batch_size)]:
new_shape = (len(chunk), ) + tuple(dims)
if self.net.blobs['data'].data.shape != new_shape:
self.net.blobs['data'].reshape(*new_shape)
for idx, img in enumerate(chunk):
image_data = self.transformer.preprocess('data', img)
self.net.blobs['data'].data[idx] = image_data
# 网络的输出层在这,当然,你的deploy.prototxt中可能去掉了输出层,但这里始终表示网络的最后一层的输出。
output = self.net.forward()[self.net.outputs[-1]]
if layer is not None:
if fea is None:
fea = np.copy(self.net.blobs[layer].data)
else:
fea = np.vstack((fea, self.net.blobs[layer].data))
if scores is None:
scores = np.copy(output)
else:
scores = np.vstack((scores, output))
# 这里返回得分、指定layer层的输出
return scores, fea
# 前向传播,这里加了一个锁,并发时如果出现报错的情况可以使用此方法,但是会降低效率。
def forward_pass_sync(self, images, batch_size, layer, lock):
lock.acquire()
try:
return self.forward_pass(images, batch_size, layer)
except Exception as e:
print(e)
finally:
lock.release()
# 分类方法,其实也就是在之前的forward_pass方法上对得分进行取最大值的角标,即取所属分类
def classify(self, image_list, layer_name=None):
if self.net.blobs['data'].data.shape[1] == 1:
image_list = [cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) for img in image_list]
scores, fea = self.forward_pass(image_list, batch_size=1, layer=layer_name)
return scores, np.argmax(scores, 1), fea
有了上述代码之后就可以进一步初始化一个Caffe网络了,这里以VGG FACE网络为例。
import caffe
import cv2
import numpy as np
import sklearn.metrics.pairwise
from facetk.engine.basis.caffenet import CaffeNet
from present import present
caffe_model = 'VGG_FACE.caffemodel'
deploy_file = 'VGG_FACE_deploy.prototxt'
# 构建transformer对象
# 输入层
transformer = caffe.io.Transformer(inputs={'data': [1, 3, 224, 224]})
# opencv读入图像时是height * width * channel,即长×宽×通道数,需要将通道数提前到第一个,这是Caffe要求的,照做就OK
transformer.set_transpose('data', (2, 0, 1))
# 当你的图片是OPENCV读入时,是BGR,这里2, 1, 0指将BR调换,即将BGR转RGB
transformer.set_channel_swap('data', (2, 1, 0))
# 设置均值,减均值大家都知道的吧。当然有些网络不需要减均值,这里就可以不写。
transformer.set_mean('data', np.array([129.1863,104.7624,93.5940]))
# 归一化了解一下
transformer.set_input_scale('data', 0.00390625)
#由于我最近在搞人脸识别,需要将某全连接层提出作为特征向量,所以有这么个东西,前向传播的时候会返回对应层的输出。
feature_layer = "fc7"
net = CaffeNet(self, caffe_model, deploy_file, transformer)
#有了网络就可以进行前向传播了,这里输入的两张图片是我裁好的人脸图像,大小均为224×224,也就是VGG的输入层尺寸,当然也可以不是这个尺寸,但会降低人脸识别的准确率。
images = [cv2.imread('a.jpg'),cv2.imread('b.jpg')]
# 这里会返回两张图像的得分、以及对应的特征向量
scores, fea = net.forward_pass(images ,2 , feature_layer)
# 计算两张图片的特征向量的cos距离,得出两张人脸的相似度
sim = sklearn.metrics.pairwise.cosine_similarity([fea[0]], [fea[1]])[0][0]
print(sim)
那么至此也就结束了,代码中如果有什么错误的地方欢迎指正。