0

I've been trying to compile my program which should push a string and a float pair back on a vector:

typedef std::pair<string, float> Prediction;

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

However, every time I try to compile this, I get this error:

error: no matching member function for call to 'push_back' predictions.push_back(std::make_pair(labels_[idx], output[idx]));

I also get a few other warnings saying things like

candidate function not viable: no known conversion from 'pair<[...], typename __make_pair_return > &>::type>' to 'const pair<[...], float>' for 1st argument _LIBCPP_INLINE_VISIBILITY void push_back(const_reference __x);

and

candidate function not viable: no known conversion from 'pair<[...], typename __make_pair_return > &>::type>' to 'pair<[...], float>' for 1st argument _LIBCPP_INLINE_VISIBILITY void push_back(value_type&& __x);

I've been trying to rewrite things and modify my functions but I can't work out why this error remains, does anyone know what I can do to fix this?

Here is the code in context if that helps, the header file:

/**
 * Classification System
 */

#ifndef __CLASSIFIER_H__
#define __CLASSIFIER_H__

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


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& label_file);

  std::vector< Prediction > Classify(const std::vector<cv::Mat>& img);

 private:

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

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

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

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

#endif /* __CLASSIFIER_H__ */

Class File:

#define CPU_ONLY
#include "Classifier.h"

using namespace caffe;  // NOLINT(build/namespaces)
using std::string;

Classifier::Classifier(const string& model_file,
                       const string& trained_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 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;
}

std::vector<Prediction> Classifier::Classify(const std::vector<cv::Mat>& img) {
  std::vector< std::vector<float> > output = Predict(img, img.size());

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

std::vector< std::vector<float> > Classifier::Predict(const std::vector<cv::Mat>& img, int nImages) {
  Blob<float>* input_layer = net_->input_blobs()[0];
  input_layer->Reshape(nImages, 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, nImages);

  Preprocess(img, &input_channels, nImages);

  net_->ForwardPrefilled();

  /* Copy the output layer to a std::vector */

  Blob<float>* output_layer = net_->output_blobs()[0];
  std::vector <std::vector<float> > ret;
  for (int i = 0; i < nImages; i++) {
    const float* begin = output_layer->cpu_data() + i*output_layer->channels();
    const float* end = begin + output_layer->channels();
    ret.push_back( std::vector<float>(begin, end) );
  }
  return ret;
}

/* 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, int nImages) {
  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()* nImages; ++i) {
    cv::Mat channel(height, width, CV_32FC1, input_data);
    input_channels->push_back(channel);
    input_data += width * height;
  }
}

void Classifier::Preprocess(const std::vector<cv::Mat>& img,
                            std::vector<cv::Mat>* input_channels, int nImages) {
  for (int i = 0; i < nImages; i++) {
      vector<cv::Mat> channels;
      cv::split(img[i], channels);
      for (int j = 0; j < channels.size(); j++){
           channels[j].copyTo((*input_channels)[i*num_channels_[0]+j]);
      }
  }
}

Thanks so much!

Jack Simpson
  • 1,681
  • 3
  • 30
  • 54
  • 2
    I'm not sure we can answer this without more info (like an [MCVE](http://stackoverflow.com/help/mcve)). You haven't shown the types given to `make_pair` when it's complaining that the pair you make has the wrong type. The error doesn't contain the type of the pair that it's complaining about. – chris Sep 30 '15 at 01:11
  • Hi Chris, thanks for the advice, I hadn't initially put in the rest of the code because I didn't want to swamp things but you're right it probably made sense to have it there for context. – Jack Simpson Sep 30 '15 at 01:17
  • 1
    output[idx] returns a vector of floats. Don't you only want a single float? – ArchHaskeller Sep 30 '15 at 01:31
  • Ah I think you're right! If I change `output[idx]` to `output[i][idx]` do you think that should solve the issue? – Jack Simpson Sep 30 '15 at 01:35
  • No idea what is your code supposed to do. Try tinkering with it. – ArchHaskeller Sep 30 '15 at 01:36
  • It did, that solved it! Thanks so much! – Jack Simpson Sep 30 '15 at 01:36
  • 1
    @JackSimpson, I have to note that "the rest of the code" doesn't qualify as an [MCVE](http://stackoverflow.com/help/mcve). It very much fails the "minimal" part. MCVEs are a nice gesture for the people helping, as you've already done the work of narrowing down the problem and making sure it's reproducible. But they also help you. You often figure out the problem while making the MCVE. Even if you don't immediately, you now much a much smaller piece of code to deal with and can begin to challenge your assumptions about what it does. – chris Sep 30 '15 at 01:40
  • 1
    As an example, when first using Python seriously for something small in a project, I challenged my assumption that `++foo` modified `foo`, and found out [it actually doesn't](http://stackoverflow.com/questions/1485841/behaviour-of-increment-and-decrement-operators-in-python). After clearly seeing the behaviour differ from what I expected, it was easy to find a Stack Overflow question about it. – chris Sep 30 '15 at 01:43
  • Thanks a lot Chris, I've only just started trying to use Deep Learning libraries like Caffe and find their structure really confusing and was struggling to work out how to reduce the amount of code while still conveying the data I was trying to get back from the library and process it. – Jack Simpson Sep 30 '15 at 01:45
  • 2
    @JackSimpson Commenting out lines and figuring with lines cause the problem could help too. – ArchHaskeller Sep 30 '15 at 02:01
  • Not really a Caffe question but still a useful C++ question. I recommend stripping the code to the smallest snippet with relevant info. – ypx Sep 30 '15 at 13:51

1 Answers1

2
typedef std::pair<string, float> Prediction;
std::vector<Prediction> predictions;
std::vector< std::vector<float> > output = Predict(img, img.size());

make_pair expects a string and a float. output[idx] gives a vector of floats. So you need output[i][idx] for only a float.

ArchHaskeller
  • 1,270
  • 1
  • 12
  • 28