构造Classifier类
初始化net_
并读入网络模型文件.prototxt
net_.reset(new Net<float>(model_file, TEST)); //model_file = models/bvlc_reference_caffenet/deploy.prototxt
net_->CopyTrainedLayersFrom(trained_file); //trained_file = models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel
通过net初始化来了解Net类
blob_names_
—— 读取各层blob名字
blob_names_index
—— map
类存储的网络名称和对应编号blob_need_backward_
—— 是否需要反向传播梯度blobs
—— 用于存储数据和梯度std::vector<boost::shared_ptr<caffe::Blob<float>>>
bottom_id_vecs_
—— 存储每一层bottom
的vector
这里注意要配合
layer_names_
来理解,[0]
是data
层,故其没有bottom
,[2]-relu1
[3]-pool1
由于[2]-relu1
是in-place存储
,所以其bottom
和[3]-pool1
相同
bottom_vecs_
—— 存储对应bottom
的地址layer_names_
—— 各层名称
layer_names_index_
—— 各层顺序存储每一层
top
的vector
读取输入信息并设置输出blob
num_input() num_output()
函数分别返回net_input_blobs_.size() net_output_blobs_.size()
CHECK_EQ(net_->num_inputs(), 1) << "Network should have exactly one input.";
CHECK_EQ(net_->num_outputs(), 1) << "Network should have exactly one output.";
可以看到初始网络输入维度为10×3*227*227
输出维度为10*1000
(注意这里只是读取model_file网络
的输入输出维度,网络至少要有一个输入和输出)
设置一个指向输入层的Blob
(10×3×227*227)
指针
Blob<float>* input_layer = net_->input_blobs()[0]; //input_blobs()函数返回net_input_blobs_
num_channels_ = input_layer->channels(); //显然,这里的num_channels_为输入的channel数 = 3
input_geometry_ = cv::Size(input_layer->width(), input_layer->height()); //input_geometry_ = (227,227)
加载均值文件
SetMean(mean_file);
读入类别及对应的编号
std::ifstream labels(label_file.c_str());
同样的,设置一个指向输出层的Blob(10×1000)
指针用于读取网络运行结果
Blob<float>* output_layer = net_->output_blobs()[0]; //10*1000的blob
Classify函数
std::vector<float> output = Predict(img);
Predict函数
Predict(const cv::Mat& img)
Blob<float>* input_layer = net_->input_blobs()[0]; //与上文中的input_layer相同 input_layer->Reshape(1, num_channels_, input_geometry_.height, input_geometry_.width); //10×3*227*227→1*3*227*227
接下来对
net_
进行reshape
,结果就是所有blob
的第一个维度由10变为1
net_->Reshape(); WrapInputLayer(&input_channels);
WrapInputLayer函数
Blob<float>* input_layer = net_->input_blobs()[0]; //同上问一样,只是第一个维度为1 int width = input_layer->width(); //227 int height = input_layer->height(); //227 float* input_data = input_layer->mutable_cpu_data(); //input_data为指向数据的指针 for (int i = 0; i < input_layer->channels(); ++i) { cv::Mat channel(height, width, CV_32FC1, input_data); //32位浮点型单通道 //在input_data处创建一个Mat用于Preprocess函数中通道分离之后存储数据 input_channels->push_back(channel); //注意是指针,channel就是在CPU中为输入图像预留的空间 input_data += width * height; }
Preprocess(img, &input_channels);
Preprocess函数
else sample = img; //(360×480) cv::Mat sample_resized; if (sample.size() != input_geometry_) //判断输入图像尺寸是否和网络输入尺寸大小相同 cv::resize(sample, sample_resized, input_geometry_); //调整为网络输入尺寸大小(227*227) else sample_resized = sample; cv::Mat sample_float; if (num_channels_ == 3) sample_resized.convertTo(sample_float, CV_32FC3); //转化为CV_32FC3 else sample_resized.convertTo(sample_float, CV_32FC1); cv::Mat sample_normalized; cv::subtract(sample_float, mean_, sample_normalized); //减去均值操作 /* This operation will write the separate BGR planes directly to the * input layer of the network because it is wrapped by the cv::Mat * objects in input_channels. */ cv::split(sample_normalized, *input_channels); //见《OpenCV3》书籍P125、分成B、G、R三个单独通道 //同时也将sample_normalized读入了*input_channels,即读入到input_layer->mutable_cpu_data();中
执行前向传播
net_->Forward(); Blob<float>* output_layer = net_->output_blobs()[0]; const float* begin = output_layer->cpu_data(); const float* end = begin + output_layer->channels(); return std::vector<float>(begin, end);
输出结果
为什么输出5个结果详见Classify构造函数设置了变量
N