caffe源码修改:抽取任意一张图片的特征
目前caffe不是很完善,输入的图片数据需要在prototxt指定路径。但是我们往往有这么一个需求:训练后得到一个模型文件,我们想拿这个模型文件来对一张图片抽取特征或者预测分类等。如果非得在prototxt指定路径,就很不方便。因此,这样的工具才是我们需要的:给一个可执行文件通过命令行来传递图片路径,然后caffe读入图片数据,进行一次正向传播。
因此我做了这么一个工具,用来抽取任意一张图片的特征。
这工具的使用方法如下:
extract_one_feature.bin ./model/caffe_reference_imagenet_model ./examples/_temp/imagenet_val.prototxt fc7 ./examples/_temp/features /media/G/imageset/clothing/针织衫/针织衫_426.jpg CPU
参数1:./model/caffe_reference_imagenet_model是训练后的模型文件
参数2:./examples/_temp/imagenet_val.prototxt 网络配置文件
参数3:fc7是blob的名字
参数4:./examples/_temp/features 将该图片的特征保存在该文件
参数5:图片路径
参数6:GPU或者CPU模式
(其实我还想到更好的工具,如果该可执行文件是监听模式的,就是通过一定的方式,给该进程传递 图片路径,进程接到任务就执行。
这样子的话,就不需要每次抽一张图片都要申请内存空间。(*^__^*) 嘻嘻……)
下面给出初步修改方法,大家可以根据自己需求再修改。
extract_one_feature.cpp(该文件参考过源码中extract_features.cpp修改)
- #include <stdio.h> // for snprintf
- #include <string>
- #include <vector>
- #include <iostream>
- #include <fstream>
-
- #include "boost/algorithm/string.hpp"
- #include "google/protobuf/text_format.h"
- #include "leveldb/db.h"
- #include "leveldb/write_batch.h"
-
- #include "caffe/blob.hpp"
- #include "caffe/common.hpp"
- #include "caffe/net.hpp"
- #include "caffe/proto/caffe.pb.h"
- #include "caffe/util/io.hpp"
- #include "caffe/vision_layers.hpp"
-
- using namespace caffe;
-
- template<typename Dtype>
- int feature_extraction_pipeline(int argc, char** argv);
-
- int main(int argc, char** argv) {
- return feature_extraction_pipeline<float>(argc, argv);
-
- }
-
- template<typename Dtype>
- class writeDb
- {
- public:
- void open(string dbName)
- {
- db.open(dbName.c_str());
- }
- void write(const Dtype &data)
- {
- db<<data;
- }
- void write(const string &str)
- {
- db<<str;
- }
- virtual ~writeDb()
- {
- db.close();
- }
- private:
- std::ofstream db;
- };
-
- template<typename Dtype>
- int feature_extraction_pipeline(int argc, char** argv) {
- ::google::InitGoogleLogging(argv[0]);
- const int num_required_args = 6;
- if (argc < num_required_args) {
- LOG(ERROR)<<
- "This program takes in a trained network and an input data layer, and then"
- " extract features of the input data produced by the net.\n"
- "Usage: extract_features pretrained_net_param"
- " feature_extraction_proto_file extract_feature_blob_name1[,name2,...]"
- " save_feature_leveldb_name1[,name2,...] img_path [CPU/GPU]"
- " [DEVICE_ID=0]\n"
- "Note: you can extract multiple features in one pass by specifying"
- " multiple feature blob names and leveldb names seperated by ','."
- " The names cannot contain white space characters and the number of blobs"
- " and leveldbs must be equal.";
- return 1;
- }
- int arg_pos = num_required_args;
-
- arg_pos = num_required_args;
- if (argc > arg_pos && strcmp(argv[arg_pos], "GPU") == 0) {
- LOG(ERROR)<< "Using GPU";
- uint device_id = 0;
- if (argc > arg_pos + 1) {
- device_id = atoi(argv[arg_pos + 1]);
- CHECK_GE(device_id, 0);
- }
- LOG(ERROR) << "Using Device_id=" << device_id;
- Caffe::SetDevice(device_id);
- Caffe::set_mode(Caffe::GPU);
- } else {
- LOG(ERROR) << "Using CPU";
- Caffe::set_mode(Caffe::CPU);
- }
- Caffe::set_phase(Caffe::TEST);
-
- arg_pos = 0;
- string pretrained_binary_proto(argv[++arg_pos]);
-
- string feature_extraction_proto(argv[++arg_pos]);
-
- shared_ptr<Net<Dtype> > feature_extraction_net(
- new Net<Dtype>(feature_extraction_proto));
-
- feature_extraction_net->CopyTrainedLayersFrom(pretrained_binary_proto);
-
-
- string extract_feature_blob_names(argv[++arg_pos]);
- vector<string> blob_names;
- boost::split(blob_names, extract_feature_blob_names, boost::is_any_of(","));
-
- string save_feature_leveldb_names(argv[++arg_pos]);
- vector<string> leveldb_names;
- boost::split(leveldb_names, save_feature_leveldb_names,
- boost::is_any_of(","));
- CHECK_EQ(blob_names.size(), leveldb_names.size()) <<
- " the number of blob names and leveldb names must be equal";
- size_t num_features = blob_names.size();
-
- for (size_t i = 0; i < num_features; i++) {
- CHECK(feature_extraction_net->has_blob(blob_names[i]))
- << "Unknown feature blob name " << blob_names[i]
- << " in the network " << feature_extraction_proto;
- }
-
-
- vector<shared_ptr<writeDb<Dtype> > > feature_dbs;
- for (size_t i = 0; i < num_features; ++i)
- {
- LOG(INFO)<< "Opening db " << leveldb_names[i];
- writeDb<Dtype>* db = new writeDb<Dtype>();
- db->open(leveldb_names[i]);
- feature_dbs.push_back(shared_ptr<writeDb<Dtype> >(db));
- }
-
-
-
- LOG(ERROR)<< "Extacting Features";
-
- const shared_ptr<Layer<Dtype> > layer = feature_extraction_net->layer_by_name("data");
- MyImageDataLayer<Dtype>* my_layer = (MyImageDataLayer<Dtype>*)layer.get();
- my_layer->setImgPath(argv[++arg_pos],1);
-
-
- vector<Blob<float>*> input_vec;
- vector<int> image_indices(num_features, 0);
- int num_mini_batches = 1;
- for (int batch_index = 0; batch_index < num_mini_batches; ++batch_index)
- {
- feature_extraction_net->Forward(input_vec);
- for (int i = 0; i < num_features; ++i)
- {
- const shared_ptr<Blob<Dtype> > feature_blob = feature_extraction_net
- ->blob_by_name(blob_names[i]);
- int batch_size = feature_blob->num();
- int dim_features = feature_blob->count() / batch_size;
-
- Dtype* feature_blob_data;
-
- for (int n = 0; n < batch_size; ++n)
- {
- feature_blob_data = feature_blob->mutable_cpu_data() +
- feature_blob->offset(n);
- feature_dbs[i]->write("3 ");
- for (int d = 0; d < dim_features; ++d)
- {
- feature_dbs[i]->write((Dtype)(d+1));
- feature_dbs[i]->write(":");
- feature_dbs[i]->write(feature_blob_data[d]);
- feature_dbs[i]->write(" ");
- }
- feature_dbs[i]->write("\n");
-
- }
- }
- }
-
-
- LOG(ERROR)<< "Successfully extracted the features!";
- return 0;
- }
my_data_layer.cpp(参考image_data_layer修改)
- #include <fstream> // NOLINT(readability/streams)
- #include <iostream> // NOLINT(readability/streams)
- #include <string>
- #include <utility>
- #include <vector>
-
- #include "caffe/layer.hpp"
- #include "caffe/util/io.hpp"
- #include "caffe/util/math_functions.hpp"
- #include "caffe/util/rng.hpp"
- #include "caffe/vision_layers.hpp"
-
- namespace caffe {
-
-
- template <typename Dtype>
- MyImageDataLayer<Dtype>::~MyImageDataLayer<Dtype>() {
- }
-
-
- template <typename Dtype>
- void MyImageDataLayer<Dtype>::setImgPath(string path,int label)
- {
- lines_.clear();
- lines_.push_back(std::make_pair(path, label));
- }
-
-
- template <typename Dtype>
- void MyImageDataLayer<Dtype>::SetUp(const vector<Blob<Dtype>*>& bottom,
- vector<Blob<Dtype>*>* top) {
- Layer<Dtype>::SetUp(bottom, top);
- const int new_height = this->layer_param_.image_data_param().new_height();
- const int new_width = this->layer_param_.image_data_param().new_width();
- CHECK((new_height == 0 && new_width == 0) ||
- (new_height > 0 && new_width > 0)) << "Current implementation requires "
- "new_height and new_width to be set at the same time.";
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- lines_.push_back(std::make_pair("/home/linger/init.jpg",1));
-
-
-
- lines_id_ = 0;
-
- Datum datum;
- CHECK(ReadImageToDatum(lines_[lines_id_].first, lines_[lines_id_].second,
- new_height, new_width, &datum));
-
- const int crop_size = this->layer_param_.image_data_param().crop_size();
- const int batch_size = 1;
- const string& mean_file = this->layer_param_.image_data_param().mean_file();
- if (crop_size > 0) {
- (*top)[0]->Reshape(batch_size, datum.channels(), crop_size, crop_size);
- prefetch_data_.Reshape(batch_size, datum.channels(), crop_size, crop_size);
- } else {
- (*top)[0]->Reshape(batch_size, datum.channels(), datum.height(),
- datum.width());
- prefetch_data_.Reshape(batch_size, datum.channels(), datum.height(),
- datum.width());
- }
- LOG(INFO) << "output data size: " << (*top)[0]->num() << ","
- << (*top)[0]->channels() << "," << (*top)[0]->height() << ","
- << (*top)[0]->width();
-
- (*top)[1]->Reshape(batch_size, 1, 1, 1);
- prefetch_label_.Reshape(batch_size, 1, 1, 1);
-
- datum_channels_ = datum.channels();
- datum_height_ = datum.height();
- datum_width_ = datum.width();
- datum_size_ = datum.channels() * datum.height() * datum.width();
- CHECK_GT(datum_height_, crop_size);
- CHECK_GT(datum_width_, crop_size);
-
- if (this->layer_param_.image_data_param().has_mean_file()) {
- BlobProto blob_proto;
- LOG(INFO) << "Loading mean file from" << mean_file;
- ReadProtoFromBinaryFile(mean_file.c_str(), &blob_proto);
- data_mean_.FromProto(blob_proto);
- CHECK_EQ(data_mean_.num(), 1);
- CHECK_EQ(data_mean_.channels(), datum_channels_);
- CHECK_EQ(data_mean_.height(), datum_height_);
- CHECK_EQ(data_mean_.width(), datum_width_);
- } else {
-
- data_mean_.Reshape(1, datum_channels_, datum_height_, datum_width_);
- }
-
-
-
-
- prefetch_data_.mutable_cpu_data();
- prefetch_label_.mutable_cpu_data();
- data_mean_.cpu_data();
-
-
- }
-
-
- template <typename Dtype>
- void MyImageDataLayer<Dtype>::fetchData() {
- Datum datum;
- CHECK(prefetch_data_.count());
- Dtype* top_data = prefetch_data_.mutable_cpu_data();
- Dtype* top_label = prefetch_label_.mutable_cpu_data();
- ImageDataParameter image_data_param = this->layer_param_.image_data_param();
- const Dtype scale = image_data_param.scale();
- const int batch_size = 1;
-
- const int crop_size = image_data_param.crop_size();
- const bool mirror = image_data_param.mirror();
- const int new_height = image_data_param.new_height();
- const int new_width = image_data_param.new_width();
-
- if (mirror && crop_size == 0) {
- LOG(FATAL) << "Current implementation requires mirror and crop_size to be "
- << "set at the same time.";
- }
-
- const int channels = datum_channels_;
- const int height = datum_height_;
- const int width = datum_width_;
- const int size = datum_size_;
- const int lines_size = lines_.size();
- const Dtype* mean = data_mean_.cpu_data();
-
- for (int item_id = 0; item_id < batch_size; ++item_id) {
-
- CHECK_GT(lines_size, lines_id_);
- if (!ReadImageToDatum(lines_[lines_id_].first,
- lines_[lines_id_].second,
- new_height, new_width, &datum)) {
- continue;
- }
- const string& data = datum.data();
- if (crop_size) {
- CHECK(data.size()) << "Image cropping only support uint8 data";
- int h_off, w_off;
-
- h_off = (height - crop_size) / 2;
- w_off = (width - crop_size) / 2;
-
-
- for (int c = 0; c < channels; ++c) {
- for (int h = 0; h < crop_size; ++h) {
- for (int w = 0; w < crop_size; ++w) {
- int top_index = ((item_id * channels + c) * crop_size + h)
- * crop_size + w;
- int data_index = (c * height + h + h_off) * width + w + w_off;
- Dtype datum_element =
- static_cast<Dtype>(static_cast<uint8_t>(data[data_index]));
- top_data[top_index] = (datum_element - mean[data_index]) * scale;
- }
- }
- }
-
- } else {
-
- if (data.size()) {
- for (int j = 0; j < size; ++j) {
- Dtype datum_element =
- static_cast<Dtype>(static_cast<uint8_t>(data[j]));
- top_data[item_id * size + j] = (datum_element - mean[j]) * scale;
- }
- } else {
- for (int j = 0; j < size; ++j) {
- top_data[item_id * size + j] =
- (datum.float_data(j) - mean[j]) * scale;
- }
- }
- }
- top_label[item_id] = datum.label();
-
- }
- }
-
- template <typename Dtype>
- Dtype MyImageDataLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
- vector<Blob<Dtype>*>* top) {
-
-
- fetchData();
-
-
- caffe_copy(prefetch_data_.count(), prefetch_data_.cpu_data(),
- (*top)[0]->mutable_cpu_data());
- caffe_copy(prefetch_label_.count(), prefetch_label_.cpu_data(),
- (*top)[1]->mutable_cpu_data());
-
- return Dtype(0.);
- }
-
- #ifdef CPU_ONLY
- STUB_GPU_FORWARD(ImageDataLayer, Forward);
- #endif
-
- INSTANTIATE_CLASS(MyImageDataLayer);
-
- }
在data_layers.hpp添加一下代码,参考ImageDataLayer写的。
- template <typename Dtype>
- class MyImageDataLayer : public Layer<Dtype> {
- public:
- explicit MyImageDataLayer(const LayerParameter& param)
- : Layer<Dtype>(param) {}
- virtual ~MyImageDataLayer();
- virtual void SetUp(const vector<Blob<Dtype>*>& bottom,
- vector<Blob<Dtype>*>* top);
-
- virtual inline LayerParameter_LayerType type() const {
- return LayerParameter_LayerType_MY_IMAGE_DATA;
- }
- virtual inline int ExactNumBottomBlobs() const { return 0; }
- virtual inline int ExactNumTopBlobs() const { return 2; }
- void fetchData();
- void setImgPath(string path,int label);
- protected:
- virtual Dtype Forward_cpu(const vector<Blob<Dtype>*>& bottom,
- vector<Blob<Dtype>*>* top);
-
- virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
- const vector<bool>& propagate_down, vector<Blob<Dtype>*>* bottom) {}
-
-
- vector<std::pair<std::string, int> > lines_;
- int lines_id_;
- int datum_channels_;
- int datum_height_;
- int datum_width_;
- int datum_size_;
- Blob<Dtype> prefetch_data_;
- Blob<Dtype> prefetch_label_;
- Blob<Dtype> data_mean_;
- Caffe::Phase phase_;
- };
修改caffe.proto,在适当的位置添加下面信息,也是参考image_data写的。
MY_IMAGE_DATA = 36;
optional MyImageDataParameter my_image_data_param = 36;
// Message that stores parameters used by MyImageDataLayer
message MyImageDataParameter {
// Specify the data source.
optional string source = 1;
// For data pre-processing, we can do simple scaling and subtracting the
// data mean, if provided. Note that the mean subtraction is always carried
// out before scaling.
optional float scale = 2 [default = 1];
optional string mean_file = 3;
// Specify the batch size.
optional uint32 batch_size = 4;
// Specify if we would like to randomly crop an image.
optional uint32 crop_size = 5 [default = 0];
// Specify if we want to randomly mirror data.
optional bool mirror = 6 [default = false];
// The rand_skip variable is for the data layer to skip a few data points
// to avoid all asynchronous sgd clients to start at the same point. The skip
// point would be set as rand_skip * rand(0,1). Note that rand_skip should not
// be larger than the number of keys in the leveldb.
optional uint32 rand_skip = 7 [default = 0];
// Whether or not ImageLayer should shuffle the list of files at every epoch.
optional bool shuffle = 8 [default = false];
// It will also resize images if new_height or new_width are not zero.
optional uint32 new_height = 9 [default = 0];
optional uint32 new_width = 10 [default = 0];
}
以上每行位置不在一起,可以参考读一个image_data对应的位置。
本文作者:linger
本文链接:http://blog.csdn.net/lingerlanlan/article/details/39400375