Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等优点。学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的接口。Caffe的数据层可以从数据库(支持leveldb、lmdb、hdf5)、图片、和内存中读入。我们要在程序中使用,当然得从内存中读入。参见http://caffe.berkeleyvision.org/tutorial/layers.html#data-layers和MemoryDataLayer源码,我们首先在模型定义文件中定义数据层:

layers {
name: "mydata"
type: MEMORY_DATA
top: "data"
top: "label"
transform_param {
scale: 0.00390625
}
memory_data_param {
batch_size: 10
channels: 1
height: 24
width: 24
}
}

这里必须设置memory_data_param中的四个参数,对应这些参数可以参见源码中caffe.proto文件。现在,我们可以设计一个Classifier类来封装一下:

#ifndef CAFFE_CLASSIFIER_H
#define CAFFE_CLASSIFIER_H #include <string>
#include <vector>
#include "caffe/net.hpp"
#include "caffe/data_layers.hpp"
#include <opencv2/core.hpp>
using cv::Mat; namespace caffe { template <typename Dtype>
class Classifier {
public:
explicit Classifier(const string& param_file, const string& weights_file);
Dtype test(vector<Mat> &images, vector<int> &labels, int iter_num);
virtual ~Classifier() {}
inline shared_ptr<Net<Dtype> > net() { return net_; }
void predict(vector<Mat> &images, vector<int> *labels);
void predict(vector<Dtype> &data, vector<int> *labels, int num);
void extract_feature(vector<Mat> &images, vector<vector<Dtype>> *out); protected:
shared_ptr<Net<Dtype> > net_;
MemoryDataLayer<Dtype> *m_layer_;
int batch_size_;
int channels_;
int height_;
int width_; DISABLE_COPY_AND_ASSIGN(Classifier);
};
}//namespace
#endif //CAFFE_CLASSIFIER_H

构造函数中我们通过模型定义文件(.prototxt)和训练好的模型(.caffemodel)文件构造一个Net对象,并用m_layer_指向Net中的memory data层,以便待会调用MemoryDataLayer中AddMatVector和Reset函数加入数据。

#include <cstdio>

#include <algorithm>
#include <string>
#include <vector> #include "caffe/net.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/upgrade_proto.hpp"
#include "caffe_classifier.h" namespace caffe { template <typename Dtype>
Classifier<Dtype>::Classifier(const string& param_file, const string& weights_file) : net_()
{
net_.reset(new Net<Dtype>(param_file, TEST));
net_->CopyTrainedLayersFrom(weights_file);
//m_layer_ = (MemoryDataLayer<Dtype>*)net_->layer_by_name("mnist").get();
m_layer_ = (MemoryDataLayer<Dtype>*)net_->layers()[0].get();
batch_size_ = m_layer_->batch_size();
channels_ = m_layer_->channels();
height_ = m_layer_->height();
width_ = m_layer_->width();
} template <typename Dtype>
Dtype Classifier<Dtype>::test(vector<Mat> &images, vector<int> &labels, int iter_num)
{
m_layer_->AddMatVector(images, labels);
//
int iterations = iter_num;
vector<Blob<Dtype>* > bottom_vec; vector<int> test_score_output_id;
vector<Dtype> test_score;
Dtype loss = 0;
for (int i = 0; i < iterations; ++i) {
Dtype iter_loss;
const vector<Blob<Dtype>*>& result =
net_->Forward(bottom_vec, &iter_loss);
loss += iter_loss;
int idx = 0;
for (int j = 0; j < result.size(); ++j) {
const Dtype* result_vec = result[j]->cpu_data();
for (int k = 0; k < result[j]->count(); ++k, ++idx) {
const Dtype score = result_vec[k];
if (i == 0) {
test_score.push_back(score);
test_score_output_id.push_back(j);
} else {
test_score[idx] += score;
}
const std::string& output_name = net_->blob_names()[
net_->output_blob_indices()[j]];
LOG(INFO) << "Batch " << i << ", " << output_name << " = " << score;
}
}
}
loss /= iterations;
LOG(INFO) << "Loss: " << loss;
return loss;
} template <typename Dtype>
void Classifier<Dtype>::predict(vector<Mat> &images, vector<int> *labels)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i < valid_length; i++)
{
images.push_back(images[0].clone());
}
}
vector<int> valid_labels, predicted_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector<Blob<Dtype>* > bottom_vec;
for(int i = 0; i < valid_length / batch_size_; i++)
{
const vector<Blob<Dtype>*>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result[1]->cpu_data();
for(int j = 0; j < result[1]->count(); j++)
{
predicted_labels.push_back(result_vec[j]);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
}
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
} template <typename Dtype>
void Classifier<Dtype>::predict(vector<Dtype> &data, vector<int> *labels, int num)
{
int size = channels_*height_*width_;
CHECK_EQ(data.size(), num*size);
int original_length = num;
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i < valid_length; i++)
{
for(int j = 0; j < size; j++)
data.push_back(0);
}
}
vector<int> predicted_labels;
Dtype * label_ = new Dtype[valid_length];
memset(label_, 0, valid_length);
m_layer_->Reset(data.data(), label_, valid_length);
vector<Blob<Dtype>* > bottom_vec;
for(int i = 0; i < valid_length / batch_size_; i++)
{
const vector<Blob<Dtype>*>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result[1]->cpu_data();
for(int j = 0; j < result[1]->count(); j++)
{
predicted_labels.push_back(result_vec[j]);
}
}
if(original_length != valid_length)
{
data.erase(data.begin()+original_length*size, data.end());
}
delete [] label_;
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template <typename Dtype>
void Classifier<Dtype>::extract_feature(vector<Mat> &images, vector<vector<Dtype>> *out)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i < valid_length; i++)
{
images.push_back(images[0].clone());
}
}
vector<int> valid_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector<Blob<Dtype>* > bottom_vec;
out->clear();
for(int i = 0; i < valid_length / batch_size_; i++)
{
const vector<Blob<Dtype>*>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result[0]->cpu_data();
const int dim = result[0]->count(1);
for(int j = 0; j < result[0]->num(); j++)
{
const Dtype * ptr = result_vec + j * dim;
vector<Dtype> one_;
for(int k = 0; k < dim; ++k)
one_.push_back(ptr[k]);
out->push_back(one_);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
out->erase(out->begin()+original_length, out->end());
}
}
INSTANTIATE_CLASS(Classifier);
} // namespace caffe

由于加入的数据个数必须是batch_size的整数倍,所以我们在加入数据时采用填充的方式。

CHECK_EQ(num % batch_size_, 0) <<
"The added data must be a multiple of the batch size.";  //AddMatVector

在模型文件的最后,我们把训练时的loss层改为argmax层:

layers {
name: "predicted"
type: ARGMAX
bottom: "prob"
top: "predicted"
}

作者:waring  出处:http://www.cnblogs.com/waring  欢迎转载或分享,但请务必声明文章出处。

如何在程序中调用Caffe做图像分类的更多相关文章

  1. 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案

    方案特点: 在网页程序或Java程序中调用接口实现短信猫收发短信的解决方案,简化软件开发流程,减少各应用系统相同模块的重复开发工作,提高系统稳定性和可靠性. 基于HTTP协议的开发接口 使用特点在网页 ...

  2. Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)

    文章目录:                   1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Ap ...

  3. windows下用c++调用caffe做前向

    参考博客: https://blog.csdn.net/muyouhang/article/details/54773265 https://blog.csdn.net/hhh0209/article ...

  4. C++程序中调用WebService的实现

    前言 因为最近的项目中需要运用到在MFC程序中调用WebService里面集成好了的函数,所以特意花了一天的时间来研究WebService的构建以及如何在MFC的程序中添加Web引用,进而来实现在C+ ...

  5. 从C#程序中调用非受管DLLs

    从C#程序中调用非受管DLLs 文章概要: 众所周知,.NET已经渐渐成为一种技术时尚,那么C#很自然也成为一种编程时尚.如何利用浩如烟海的Win32 API以及以前所编写的 Win32 代码已经成为 ...

  6. iOS程序中调用系统自带应用(短信,邮件,浏览器,地图,appstore,拨打电话,iTunes,iBooks )

    在网上找到了下在记录下来以后方便用 在程序中调用系统自带的应用,比如我进入程序的时候,希望直接调用safar来打开一个网页,下面是一个简单的使用:

  7. Java程序中调用Python脚本的方法

    在程序开发中,有时候需要Java程序中调用相关Python脚本,以下内容记录了先关步骤和可能出现问题的解决办法. 1.在Eclipse中新建Maven工程: 2.pom.xml文件中添加如下依赖包之后 ...

  8. 利用 gnuplot_i 在你的 c 程序中调用 GNUPLOT

    这是一篇非常早曾经写的小文章,最初发表于我的搜狐博客(2008-09-23 22:55).由于自从转移到这里后,sohu 博客就不再维护了,所以把这篇文章也一起挪了过来. GNUPLOT 是一款功能强 ...

  9. ASP程序中调用Now()总显示“上午”和“下午”,如何解决?

    ASP程序中调用Now()总显示这样的格式:“2007-4-20 下午 06:06:38”,我要的正确格式为“2007-4-20 18:06:38”,我已经通过控制面板==>区域和语言选项==& ...

随机推荐

  1. 基于drools创建自己的关系操作符

    我们知道drools提供了12种关系操作符 但是有些时候这12种操作符依然不能满足我们的业务需求,我们可以扩展自己的操作符,下面是为某一航空公司做项目时扩展了操作符,在这分享下 首先,我们要实现的逻辑 ...

  2. java基础之数组

    数组的定义 数组的应用 1, 2, 3, 4,

  3. 自定义VS的ItemTemplates 实现任意文件结构

    上一篇说到重写IHttpHandler实现前后端分离,这次说一下如何建立一个如下文件结构. VS建立webform时是根据模板来的.C#的模板目录如下: F:\Program Files (x86)\ ...

  4. update-database时出现Cannot attach the file

    在进行Migrations时,如果直接删除了Db文件,在使用update-database时会出现Cannot attach the file发问题 解决方案:

  5. 视图View

    视图UI层的HTML,JavaScript,Css等元素 asp.net mvc 框架支持惯例优先配置原则 视图路径:view/controller名/Action \view\home\index. ...

  6. 如何在pl/sql developer 7运行到oracle存储过程设置断点的地方

    如何高效调试oracle存储过程,尤其是父子网状调用的存储过程 1,在需要设置断点的oracle存储过程处设置断点         如何设置断点:直接在某行oracle存储过程处单击行首,会在行首显示 ...

  7. 使用Dataset

    string sqlStr="Select * from Tb_news"; SqlDataAdapter myDa=new SqlDataAdapter(SqlStr,myCon ...

  8. mysql配置文件my.cnf解析转载

    basedir = path 使用给定目录作为根目录(安装目录). character-sets-dir = path 给出存放着字符集的目录. datadir = path 从给定目录读取数据库文件 ...

  9. mysql分库分表总结<转>

    单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 单库多表 随着用户数量的增加,user表的数据量会越来越大,当数 ...

  10. Oracle11g R2学习系列 之九 PL/SQL语言

    这是个重头戏,如果精通了PL/SQL,毫不夸张的说明精通了Oracle了.PL/SQL由以下三个部分组成(Definition,Manipulation,Control): DDL:数据定义语言,Cr ...