在OpenCV中使用Caffe中训练好的model
以 LeNet 为例子
第一步: 改写deploy文件
按下面的步骤修改文件 lenet_train_test.prototxt
(1) 去掉数据输入层,即将top 为 “data” 的layers 去掉
即将下面的内容删掉
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TRAIN
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_train_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "mnist"
type: "Data"
top: "data"
top: "label"
include {
phase: TEST
}
transform_param {
scale: 0.00390625
}
data_param {
source: "examples/mnist/mnist_test_lmdb"
batch_size: 100
backend: LMDB
}
}
(2) 重新建立输入
即添加下面的内容
input: "data"
input_shape {
dim: 1 # batchsize
dim: 1 # number of channels
dim: 28 # width
dim: 28 # height
}
(3) 去掉输出层,即将bottom 为 “label” 的layers 去掉
即将下面的内容删掉
layer {
name: "accuracy"
type: "Accuracy"
bottom: "ip2"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "ip2"
bottom: "label"
top: "loss"
}
(4) 重新建立输出
即添加下面的内容
layer {
name: "prob"
type: "Softmax"
bottom: "ip2"
top: "prob"
}
修改完后保存到文件中(mnist_deploy.prototxt),内容如下
name: "LeNet"
input: "data"
input_shape {
dim: 1 # batchsize
dim: 1 # number of channels
dim: 28 # width
dim: 28 # height
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 20
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "pool1"
top: "conv2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
convolution_param {
num_output: 50
kernel_size: 5
stride: 1
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 2
stride: 2
}
}
layer {
name: "ip1"
type: "InnerProduct"
bottom: "pool2"
top: "ip1"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 500
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "ip1"
top: "ip1"
}
layer {
name: "ip2"
type: "InnerProduct"
bottom: "ip1"
top: "ip2"
param {
lr_mult: 1
}
param {
lr_mult: 2
}
inner_product_param {
num_output: 10
weight_filler {
type: "xavier"
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "prob"
type: "Softmax"
bottom: "ip2"
top: "prob"
}
代码如下:
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::dnn;
/* Find best class for the blob (i. e. class with maximal probability) */
static void getMaxClass(const Mat &probBlob, int *classId, double *classProb)
{
Mat probMat = probBlob.reshape(1, 1);
Point classNumber;
minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
*classId = classNumber.x;
}
int main(int argc, char *argv[])
{
string modelTxt = "../mnist_deploy.prototxt";
string modelBin = "../lenet_iter_10000.caffemodel";
string imgFileName = argc > 1 ? argv[1] : "../4.jpg";
//read image
Mat imgSrc = imread(imgFileName);
if(imgSrc.empty()){
cout << "Failed to read image " << imgFileName << endl;
exit(-1);
}
Mat img;
cvtColor(imgSrc, img, COLOR_BGR2GRAY);
//LeNet accepts 28*28 gray image
resize(img, img , Size(28, 28));
//in the file "lenet_train_test.prototxt", in the Data input layer, we can see
// transform_param {
// scale: 0.00390625
// }
//which means the input data need to be normalized before feed the data into the net
// 0.00390625 = 1/255 after this operation, the value of the image pixel will be transferred to be one value between 0 and 1
img /=255;
//transfer image(1*28*28) to blob data with 4 dimensions(1*1*28*28)
Mat inputBlob = dnn::blobFromImage(img);
dnn::Net net;
try{
net = dnn::readNetFromCaffe(modelTxt, modelBin);
}catch(cv::Exception &ee){
cerr << "Exception: " << ee.what() << endl;
if(net.empty()){
cout << "Can't load the network by using the flowing files:" << endl;
cout << "modelTxt: " << modelTxt << endl;
cout << "modelBin: " << modelBin << endl; exit(-1);
}
}
Mat pred;
net.setInput(inputBlob, "data");//set the network input, "data" is the name of the input layer
pred = net.forward("prob");//compute output, "prob" is the name of the output layer
cout << pred << endl; int classId; double classProb; getMaxClass(pred, &classId, &classProb);
cout << "Best Class: " << classId << endl;
cout << "Probability: " << classProb*100 << "%" << endl;
}
编译代码并运行,测试结果如下:
下面的代码是测试多张图片
#include
#include
#include
using namespace std;
using namespace cv;
using namespace cv::dnn;
/* Find best class for the blob (i. e. class with maximal probability) */
static void getMaxClass(const Mat &probBlob, int *classId, double *classProb)
{
Mat probMat = probBlob.reshape(1, 1);
Point classNumber;
minMaxLoc(probMat, NULL, classProb, NULL, &classNumber);
*classId = classNumber.x;
}
int main(int argc, char *argv[])
{
string modelTxt = "../mnist_deploy.prototxt";
string modelBin = "../lenet_iter_10000.caffemodel";
if(argc < 2){
cout << "Please input image file name!!!" << endl;
return -1;
}
Mat inputBlob;
vector imgVec;
for(int i = 1; i < argc; i++){
string imgFileName = argv[i];
Mat imgSrc = imread(imgFileName);
if(imgSrc.empty()){
cout << "Failed to read image " << imgFileName << endl;
exit(-1);
}
Mat img;
cvtColor(imgSrc, img, COLOR_BGR2GRAY);
resize(img, img , Size(28, 28));
img /= 255;
imgVec.push_back(img);
}
inputBlob = dnn::blobFromImages(imgVec);
dnn::Net net;
try{
net = dnn::readNetFromCaffe(modelTxt, modelBin);
}catch(cv::Exception &ee){
cerr << "" << ee.what() << endl;
if(net.empty()){
cout << "Can't load the network by using the flowing files:" << endl;
cout << "modelTxt: " << modelTxt << endl;
cout << "modelBin: " << modelBin << endl;
exit(-1);
}
}
Mat pred;
net.setInput(inputBlob, "data");
pred = net.forward("prob");
cout << endl;
cout << pred << endl;
cout << endl;
for(int i = 0; i < imgVec.size(); i++){
int classId;
double classProb;
getMaxClass(pred.row(i), &classId, &classProb);
cout << "The class id of image " << argv[i+1] << " is: " << classId << endl;
cout << "The probability is: " << classProb*100 << "%" << endl;
}
cout << endl;
}
测试用到的几张图片:
注:本文中的代码所调用的API是基于OpenCV 3.3.0版本的