1. 程式人生 > >使用深度學習Caffe框架的C++介面進行物體分類

使用深度學習Caffe框架的C++介面進行物體分類

實現目標:

1、載入一張圖片,應用深度學習框架Caffe訓練好的模型分類圖片,顯示圖片的類別,輸出到控制檯;
2、載入一個資料夾,分類所有資料夾內的圖片,非圖片檔案選擇無視,生成同名txt儲存所屬類別。

3、工程project實現

一、/home/name/caffe-master/examples/cpp_classification/classification.cpp這個檔案是呼叫Caffe框架介面的事例,它對應的程式是這個:/home/name/caffe-master/build/examples/cpp_classification/classification.bin

執行這個程式並填上網路、模型、均值檔案、標籤引數和圖片即可對單張圖片進行分類。C++分類的呼叫介面也參照這個CPP。

二、現在仿照這個CPP對本地某個檔案內的所有jpg圖片分類。假設需要對某些輸入的照片進行分類,修改圖片來源介面即可。

txt按行為程式提供引數:資料夾名、模型網路、模型、均值檔案、標籤
修改如下:

//#define USE_OPENCV
#include <caffe/caffe.hpp>
#ifdef USE_OPENCV
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif  // USE_OPENCV
#include <algorithm>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <sys/types.h>
#include<dirent.h>
#include<stdio.h>
#include<sys/stat.h>
#include<iostream>
#include<fstream>

//下面這行不可用!
//using namespace std;
#ifdef USE_OPENCV
using namespace caffe;  // NOLINT(build/namespaces)
using std::string;

/* Pair (label, confidence) representing a prediction. */
typedef std::pair<string, float> Prediction;

class Classifier {
 public:
  Classifier(const string& model_file,
             const string& trained_file,
             const string& mean_file,
             const string& label_file);

  std::vector<Prediction> Classify(const cv::Mat& img, int N = 5);

 private:
  void SetMean(const string& mean_file);

  std::vector<float> Predict(const cv::Mat& img);

  void WrapInputLayer(std::vector<cv::Mat>* input_channels);

  void Preprocess(const cv::Mat& img,
                  std::vector<cv::Mat>* input_channels);

 private:
  shared_ptr<Net<float> > net_;
  cv::Size input_geometry_;
  int num_channels_;
  cv::Mat mean_;
  std::vector<string> labels_;
};

Classifier::Classifier(const string& model_file,
                       const string& trained_file,
                       const string& mean_file,
                       const string& label_file) {
#ifdef CPU_ONLY
  Caffe::set_mode(Caffe::CPU);
#else
  Caffe::set_mode(Caffe::GPU);
#endif

  /* Load the network. */
  net_.reset(new Net<float>(model_file, TEST));
  net_->CopyTrainedLayersFrom(trained_file);

  CHECK_EQ(net_->num_inputs(), 1) << "Network should have exactly one input.";
  CHECK_EQ(net_->num_outputs(), 1) << "Network should have exactly one output.";

  Blob<float>* input_layer = net_->input_blobs()[0];
  num_channels_ = input_layer->channels();
  CHECK(num_channels_ == 3 || num_channels_ == 1)
    << "Input layer should have 1 or 3 channels.";
  input_geometry_ = cv::Size(input_layer->width(), input_layer->height());

  /* Load the binaryproto mean file. */
  SetMean(mean_file);

  /* Load labels. */
  std::ifstream labels(label_file.c_str());
  CHECK(labels) << "Unable to open labels file " << label_file;
  string line;
  while (std::getline(labels, line))
    labels_.push_back(string(line));

  Blob<float>* output_layer = net_->output_blobs()[0];
  CHECK_EQ(labels_.size(), output_layer->channels())
    << "Number of labels is different from the output layer dimension.";
}

static bool PairCompare(const std::pair<float, int>& lhs,
                        const std::pair<float, int>& rhs) {
  return lhs.first > rhs.first;
}

/* Return the indices of the top N values of vector v. */
static std::vector<int> Argmax(const std::vector<float>& v, int N) {
  std::vector<std::pair<float, int> > pairs;
  for (size_t i = 0; i < v.size(); ++i)
    pairs.push_back(std::make_pair(v[i], i));
  std::partial_sort(pairs.begin(), pairs.begin() + N, pairs.end(), PairCompare);

  std::vector<int> result;
  for (int i = 0; i < N; ++i)
    result.push_back(pairs[i].second);
  return result;
}

/* Return the top N predictions. */
std::vector<Prediction> Classifier::Classify(const cv::Mat& img, int N) {
  std::vector<float> output = Predict(img);

  N = std::min<int>(labels_.size(), N);
  std::vector<int> maxN = Argmax(output, N);
  std::vector<Prediction> predictions;
  for (int i = 0; i < N; ++i) {
    int idx = maxN[i];
    predictions.push_back(std::make_pair(labels_[idx], output[idx]));
  }

  return predictions;
}

/* Load the mean file in binaryproto format. */
void Classifier::SetMean(const string& mean_file) {
  BlobProto blob_proto;
  ReadProtoFromBinaryFileOrDie(mean_file.c_str(), &blob_proto);

  /* Convert from BlobProto to Blob<float> */
  Blob<float> mean_blob;
  mean_blob.FromProto(blob_proto);
  CHECK_EQ(mean_blob.channels(), num_channels_)
    << "Number of channels of mean file doesn't match input layer.";

  /* The format of the mean file is planar 32-bit float BGR or grayscale. */
  std::vector<cv::Mat> channels;
  float* data = mean_blob.mutable_cpu_data();
  for (int i = 0; i < num_channels_; ++i) {
    /* Extract an individual channel. */
    cv::Mat channel(mean_blob.height(), mean_blob.width(), CV_32FC1, data);
    channels.push_back(channel);
    data += mean_blob.height() * mean_blob.width();
  }

  /* Merge the separate channels into a single image. */
  cv::Mat mean;
  cv::merge(channels, mean);

  /* Compute the global mean pixel value and create a mean image
   * filled with this value. */
  cv::Scalar channel_mean = cv::mean(mean);
  mean_ = cv::Mat(input_geometry_, mean.type(), channel_mean);
}

std::vector<float> Classifier::Predict(const cv::Mat& img) {
  Blob<float>* input_layer = net_->input_blobs()[0];
  input_layer->Reshape(1, num_channels_,
                       input_geometry_.height, input_geometry_.width);
  /* Forward dimension change to all layers. */
  net_->Reshape();

  std::vector<cv::Mat> input_channels;
  WrapInputLayer(&input_channels);

  Preprocess(img, &input_channels);

  net_->Forward();

  /* Copy the output layer to a std::vector */
  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);
}

/* Wrap the input layer of the network in separate cv::Mat objects
 * (one per channel). This way we save one memcpy operation and we
 * don't need to rely on cudaMemcpy2D. The last preprocessing
 * operation will write the separate channels directly to the input
 * layer. */
void Classifier::WrapInputLayer(std::vector<cv::Mat>* input_channels) {
  Blob<float>* input_layer = net_->input_blobs()[0];

  int width = input_layer->width();
  int height = input_layer->height();
  float* input_data = input_layer->mutable_cpu_data();
  for (int i = 0; i < input_layer->channels(); ++i) {
    cv::Mat channel(height, width, CV_32FC1, input_data);
    input_channels->push_back(channel);
    input_data += width * height;
  }
}

void Classifier::Preprocess(const cv::Mat& img,
                            std::vector<cv::Mat>* input_channels) {
  /* Convert the input image to the input image format of the network. */
  cv::Mat sample;
  if (img.channels() == 3 && num_channels_ == 1)
    cv::cvtColor(img, sample, cv::COLOR_BGR2GRAY);
  else if (img.channels() == 4 && num_channels_ == 1)
    cv::cvtColor(img, sample, cv::COLOR_BGRA2GRAY);
  else if (img.channels() == 4 && num_channels_ == 3)
    cv::cvtColor(img, sample, cv::COLOR_BGRA2BGR);
  else if (img.channels() == 1 && num_channels_ == 3)
    cv::cvtColor(img, sample, cv::COLOR_GRAY2BGR);
  else
    sample = img;

  cv::Mat sample_resized;
  if (sample.size() != input_geometry_)
    cv::resize(sample, sample_resized, input_geometry_);
  else
    sample_resized = sample;

  cv::Mat sample_float;
  if (num_channels_ == 3)
    sample_resized.convertTo(sample_float, 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);

  CHECK(reinterpret_cast<float*>(input_channels->at(0).data)
        == net_->input_blobs()[0]->cpu_data())
    << "Input channels are not wrapping the input layer of the network.";
}

//遍歷資料夾類檔名函式
char filename[256][256];
int len = 0;
int trave_dir(char* path, int depth)
{
    DIR *d; //宣告一個控制代碼
    struct dirent *file; //readdir函式的返回值就存放在這個結構體中
    struct stat sb;

    if(!(d = opendir(path)))
    {
        printf("error opendir %s!!!\n",path);
        return -1;
    }
    while((file = readdir(d)) != NULL)
    {
        //把當前目錄.,上一級目錄..及隱藏檔案都去掉,避免死迴圈遍歷目錄
        if(strncmp(file->d_name, ".", 1) == 0)
            continue;
        strcpy(filename[len++], file->d_name); //儲存遍歷到的檔名
        //判斷該檔案是否是目錄,及是否已搜尋了三層,這裡我定義只搜尋了三層目錄,太深就不搜了,省得搜出太多檔案
        //stat
        if(stat(file->d_name, &sb) >= 0 && S_ISDIR(sb.st_mode) && depth <= 3)
        {
            trave_dir(file->d_name, depth + 1);
        }
    }
    closedir(d);
    return 0;
}

int main(int argc, char** argv) {
//  if (argc != 6) {
//    std::cerr << "Usage: " << argv[0]
//              << " deploy.prototxt network.caffemodel"
//              << " mean.binaryproto labels.txt img.jpg" << std::endl;
//    return 1;
//  }

  ::google::InitGoogleLogging(argv[0]);

    string ar[10];
    if(argc!=2){
        std::cerr<<"程式需要一個包含5行文字的引數:"<<std::endl<<"第1行資料夾名(含路徑),然後各行依次分別是模型網路、模型、均值檔案、標籤"<<std::endl;
        return 1;
    }
    fstream arg_file;
    arg_file.open(argv[1]);
    if(!arg_file){
        std::cerr<<"打不開引數檔案"<<std::endl;
        return 1;
    }
    else{
        for(int i=0;!arg_file.eof();i++){
            getline(arg_file,ar[i],'\n');
        }
    }
//  string model_file   = argv[1];
//  string trained_file = argv[2];
//  string mean_file    = argv[3];
//  string label_file   = argv[4];
    string dir=ar[0];
    char* p=(char*)dir.data();
    string model_file   = ar[1];
    string trained_file = ar[2];
    string mean_file    = ar[3];
    string label_file   = ar[4];
  Classifier classifier(model_file, trained_file, mean_file, label_file);

//  string file = argv[5];
  int depth=1;
  //遍歷獲取檔名、總數了
  trave_dir(p,depth);
  for(int j=0;j<len;j++){

      //char轉化為string
      string filename_str=filename[j];
      //判斷是不是jpg圖片
      int len1=filename_str.length();
      string argv2;
      for(int i=3;i>=0;i--){
         argv2.push_back(filename_str[len1-i-1]);
      }
      if(argv2!=".jpg"){
          std::cout<<filename[j]<<"不是jpg圖片~~~~~"<<std::endl;
          continue;
      }

      string file=dir+"//"+filename[j];

      std::cout << "---------- Prediction for "
                << filename[j] << " ----------" << std::endl;

      cv::Mat img = cv::imread(file, -1);
      CHECK(!img.empty()) << "Unable to decode image " << file;
      std::vector<Prediction> predictions = classifier.Classify(img);

      /* Print the top N predictions. */
      for (size_t i = 0; i < predictions.size(); ++i) {
        Prediction p = predictions[i];
        std::cout << std::fixed << std::setprecision(4) << p.second << " - \""
                  << p.first << "\"" << std::endl;
      }
  }
}
#else
int main(int argc, char** argv) {
  LOG(FATAL) << "This example requires OpenCV; compile with USE_OPENCV.";
}
#endif  // USE_OPENCV

相關推薦

使用深度學習Caffe框架C++介面進行物體分類

實現目標: 1、載入一張圖片,應用深度學習框架Caffe訓練好的模型分類圖片,顯示圖片的類別,輸出到控制檯; 2、載入一個資料夾,分類所有資料夾內的圖片,非圖片檔案選擇無視,生成同名txt儲存所屬類別。 3、工程project實現 一、/home/name/ca

出入深度學習caffe框架上手教材推薦

博主小白,這段時間在搞深度學習,也沒有人帶我,就靠自己看看書和網上琢磨琢磨。 一開始的時候,總是想快速入門深度學習。所以這裡推薦一本《深度學習21天實戰caffe》這本書,但是這本書有個不好的一點是,它前面的初章和中章都是寫的windows+cpu平臺下的開發,可是裡面給

【機器學習PAI實踐十】深度學習Caffe框架實現影象分類的模型訓練

背景 我們在之前的文章中介紹過如何通過PAI內建的TensorFlow框架實驗基於Cifar10的影象分類,文章連結:https://yq.aliyun.com/articles/72841。使用Tensorflow做深度學習做深度學習的網路搭建和訓練需要通過

11.用深度學習方法為影象中的物體進行分類

這幾個庫現在更新了,用書上的會出錯,未解決,建議直接學新的 # -*- coding: utf-8 -*- """ Created on Sun Oct 14 09:09:58 2018 @author: asus """ #11 用深度學習方法為影象中的物體

深度學習Caffe實戰筆記(10)Windows Caffe使用MATLAB介面提取和視覺化特徵

上一篇部落格中介紹瞭如何使用MATLAB訓練和測試資料,這篇部落格介紹如何從訓練好的模型中提取影象特徵,並介紹把卷積層特徵視覺化方法。 之前提取特徵都是用python,儘管用python提取特徵很方便,但是感覺MATLAB提取特徵更方便,因為博主對MATLAB

深度學習Caffe實戰(9)Windows 平臺caffe用MATLAB介面實現訓練網路和測試

上一篇介紹了網路協議中各個引數的作用,知道了各個引數的作用,想必應該可以嘗試修改網路結構了。前幾篇部落格介紹的都是用命令列訓練和測試網路,這篇部落格介紹如何用MATLAB介面實現訓練和測試網路,window平臺下caffe使用者本來就少,這方面的資料更少了,下一

CaffeC++介面學習(一)

Segnet-Caffe C++ 學習筆記 從之前的Segnet-Slam程式碼中設定斷點進行除錯: // OpenCV #include <opencv2/core/core.hpp> #include <opencv2

Win7(64位)+VS2013+cuda7.5環境下搭建深度學習Caffe計算框架

     0、前言             經過一個星期,在查閱了很多網路資源(例如歐新宇、薛開宇大神等)後,終於在不斷摸索中,找到適合了自己電腦配置【Win7(64)+VS2013+cuda7.5】的Caffe教程。感謝happynear大神[]、小鹹魚_前輩[],這兩

openSUSE 多個GPU設置 深度學習 Caffe PyTorch 等

div 深度學習 學習 正常 gpo 原來 caf 顯示 nvi 原來只有一張卡Titian x,最近又添加一個1080TI,插好以後 nvidia-smi 顯示只有一張卡。 看了下\dev 下nvidia1普通用戶沒有權限,切換到root下正常。但是在root下還是不太好

深度學習Keras框架筆記之TimeDistributedDense類

nts ini OS sample con stream 輸出 without 實例 深度學習Keras框架筆記之TimeDistributedDense類使用方法筆記 例: keras.layers.core.TimeDistribut

深度學習Keras框架筆記之AutoEncoder類

seq boolean red any ati RR blog data val 深度學習Keras框架筆記之AutoEncoder類使用筆記 keras.layers.core.AutoEncoder(encoder, decoder,output_recon

深度學習Keras框架筆記之Activation類使用

UNC 一個 HA brush theano predict bool sha red 使用 keras.layers.core.Activation(activation) Apply an activation function tothe in

深度學習計算框架實現

並行 方框 向下取整 靈活 非線性 soft 卷積 了解 通道 參考與評述 參考書目《Deep Learning》Lan Goodfellow. 經典的深度學習框架是以計算圖&梯度下降方法實現對前饋網絡的有監督學習。 這裏復現了前饋計算圖的梯度計算實現。 一、前饋

深度學習caffe搭建

經過一個月的時間,從開始接觸深度學習到環境搭建終於成功了! 發表第一篇部落格 慶祝一下   按照21天實戰caffe 進行架構搭建出現的錯誤大致的解決辦法, Ubuntu用的是14.04 在進行依賴包glog下載 由於google的限制需要翻牆很麻煩 我直接在CSDN裡下載的依

利用Clion呼叫caffec++介面

之前簡單學習了一下CMake的知識,現在就應用到caffe裡. CLION呼叫caffe關鍵是CMakeLists.txt檔案的編寫,涉及到一些CMake的知識. 首先用CLion建立一個caffeReadImgClassify工程,這時候發現工程目錄下有main.cpp和CmakeLi

深度學習caffe(4)——caffe配置(GPU)

電腦:win7  64位,NVIDIA GeForce GTX1080 Ti,visual studio 2013. 深度學習caffe(1)——windows配置caffe(vs2013+python+matlab)(cpu): 系統:window,系統版本是7

深度學習caffe製作lmdb資料來源

前提:ubuntu系統,安裝了caffe,python。 Caffe深度學習訓練網路模型需要的資料的格式分三種,資料直接來源於圖片,使用lmdb資料來源,使用hdf5資料來源。 本文件把圖片製作成lmdb資料來源 首先任何位置新建資料夾:比如我新建了xytest

使用caffe的python介面進行特徵提取和人臉驗證,修改程式碼dis=1-dis 使用caffe的python介面進行特徵提取和人臉驗證

原 使用caffe的python介面進行特徵提取和人臉驗證 置頂 2017年03月09日 10:39:07 csuwujiyang 閱讀數:4

深度學習Caffe實戰筆記(2)用LeNet跑車牌識別資料

caffe實戰之“車牌識別” 上一篇部落格寫了如何在cpu的情況下配置環境,配置好環境後編譯成功,就可以用caffe框架訓練卷積神經網路了。今天介紹如何在caffe環境下,跑車牌識別的資料,利用的網路是LeNet,這裡只介紹具體caffe實戰步驟,網路結構不做

初入sqlite3 學習彙總(c介面

介紹  SQLite,是一款輕型的資料庫,是遵守ACID的關係型資料庫管理系統,它包含在一個相對小的C庫中。它是D.RichardHipp建立的公有領域專案。它的設計目標是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它佔用資源非常的低,在嵌入式裝置中,可能只需要幾百K的