Caffe2 - C++ API
A deep learning, cross platform ML framework
image_input_op.h
1 
2 #ifndef CAFFE2_IMAGE_IMAGE_INPUT_OP_H_
3 #define CAFFE2_IMAGE_IMAGE_INPUT_OP_H_
4 
5 #include <opencv2/opencv.hpp>
6 
7 #include <iostream>
8 #include <algorithm>
9 
10 #include "caffe/proto/caffe.pb.h"
11 #include "caffe2/core/db.h"
12 #include "caffe2/utils/cast.h"
13 #include "caffe2/utils/math.h"
14 #include "caffe2/utils/thread_pool.h"
15 #include "caffe2/operators/prefetch_op.h"
16 #include "caffe2/image/transform_gpu.h"
17 
18 namespace caffe2 {
19 
20 class CUDAContext;
21 
22 template <class Context>
23 class ImageInputOp final
24  : public PrefetchOperator<Context> {
25  // SINGLE_LABEL: single integer label for multi-class classification
26  // MULTI_LABEL_SPARSE: sparse active label indices for multi-label classification
27  // MULTI_LABEL_DENSE: dense label embedding vector for label embedding regression
28  // MULTI_LABEL_WEIGHTED_SPARSE: sparse active label indices with per-label weights
29  // for multi-label classification
30  // SINGLE_LABEL_WEIGHTED: single integer label for multi-class classification with weighted sampling
31  enum LABEL_TYPE {
32  SINGLE_LABEL = 0,
33  MULTI_LABEL_SPARSE = 1,
34  MULTI_LABEL_DENSE = 2,
35  MULTI_LABEL_WEIGHTED_SPARSE = 3,
36  SINGLE_LABEL_WEIGHTED = 4
37  };
38 
39  // INCEPTION_STYLE: Random crop with size 8% - 100% image area and aspect
40  // ratio in [3/4, 4/3]. Reference: GoogleNet paper
41  enum SCALE_JITTER_TYPE {
42  NO_SCALE_JITTER = 0,
43  INCEPTION_STYLE = 1
44  // TODO(zyan3): ResNet-style random scale jitter
45  };
46 
47  public:
48  using OperatorBase::OutputSize;
51  explicit ImageInputOp(const OperatorDef& operator_def,
52  Workspace* ws);
53  ~ImageInputOp() {
55  }
56 
57  bool Prefetch() override;
58  bool CopyPrefetched() override;
59 
60  private:
61  using BoundingBox = struct {
62  bool valid;
63  int ymin;
64  int xmin;
65  int height;
66  int width;
67  };
68 
69  // Structure to store per-image information
70  // This can be modified by the DecodeAnd* so needs
71  // to be privatized per launch.
72  using PerImageArg = struct {
73  BoundingBox bounding_params;
74  };
75 
76  bool GetImageAndLabelAndInfoFromDBValue(
77  const string& value, cv::Mat* img, PerImageArg& info, int item_id,
78  std::mt19937* randgen);
79  void DecodeAndTransform(
80  const std::string& value, float *image_data, int item_id,
81  const int channels, std::size_t thread_index);
82  void DecodeAndTransposeOnly(
83  const std::string& value, uint8_t *image_data, int item_id,
84  const int channels, std::size_t thread_index);
85 
86  unique_ptr<db::DBReader> owned_reader_;
87  const db::DBReader* reader_;
88  CPUContext cpu_context_;
89  TensorCPU prefetched_image_;
90  TensorCPU prefetched_label_;
91  vector<TensorCPU> prefetched_additional_outputs_;
92  Tensor<Context> prefetched_image_on_device_;
93  Tensor<Context> prefetched_label_on_device_;
94  vector<Tensor<Context>> prefetched_additional_outputs_on_device_;
95  // Default parameters for images
96  PerImageArg default_arg_;
97  int batch_size_;
98  LABEL_TYPE label_type_;
99  int num_labels_;
100 
101  bool color_;
102  bool color_jitter_;
103  float img_saturation_;
104  float img_brightness_;
105  float img_contrast_;
106  bool color_lighting_;
107  float color_lighting_std_;
108  std::vector<std::vector<float>> color_lighting_eigvecs_;
109  std::vector<float> color_lighting_eigvals_;
110  SCALE_JITTER_TYPE scale_jitter_type_;
111  int scale_;
112  // Minsize is similar to scale except that it will only
113  // force the image to scale up if it is too small. In other words,
114  // it ensures that both dimensions of the image are at least minsize_
115  int minsize_;
116  bool warp_;
117  int crop_;
118  std::vector<float> mean_;
119  std::vector<float> std_;
120  Tensor<Context> mean_gpu_;
121  Tensor<Context> std_gpu_;
122  bool mirror_;
123  bool is_test_;
124  bool use_caffe_datum_;
125  bool gpu_transform_;
126  bool mean_std_copied_ = false;
127 
128  // thread pool for parse + decode
129  int num_decode_threads_;
130  int additional_inputs_offset_;
131  int additional_inputs_count_;
132  std::shared_ptr<TaskThreadPool> thread_pool_;
133 
134  // Output type for GPU transform path
135  TensorProto_DataType output_type_;
136 
137  // random minsize
138  vector<int> random_scale_;
139  bool random_scaling_;
140 
141 
142  // Working variables
143  std::vector<std::mt19937> randgen_per_thread_;
144 };
145 
146 template <class Context>
148  const OperatorDef& operator_def,
149  Workspace* ws)
150  : PrefetchOperator<Context>(operator_def, ws),
151  reader_(nullptr),
152  prefetched_additional_outputs_(OutputSize() - 2),
153  prefetched_additional_outputs_on_device_(OutputSize() - 2),
154  batch_size_(
155  OperatorBase::template GetSingleArgument<int>("batch_size", 0)),
156  label_type_(static_cast<LABEL_TYPE>(
157  OperatorBase::template GetSingleArgument<int>("label_type", 0))),
158  num_labels_(
159  OperatorBase::template GetSingleArgument<int>("num_labels", 0)),
160  color_(OperatorBase::template GetSingleArgument<int>("color", 1)),
161  color_jitter_(
162  OperatorBase::template GetSingleArgument<int>("color_jitter", 0)),
163  img_saturation_(OperatorBase::template GetSingleArgument<float>(
164  "img_saturation",
165  0.4)),
166  img_brightness_(OperatorBase::template GetSingleArgument<float>(
167  "img_brightness",
168  0.4)),
169  img_contrast_(
170  OperatorBase::template GetSingleArgument<float>("img_contrast", 0.4)),
171  color_lighting_(
172  OperatorBase::template GetSingleArgument<int>("color_lighting", 0)),
173  color_lighting_std_(OperatorBase::template GetSingleArgument<float>(
174  "color_lighting_std",
175  0.1)),
176  scale_jitter_type_(static_cast<SCALE_JITTER_TYPE>(
177  OperatorBase::template GetSingleArgument<int>(
178  "scale_jitter_type",
179  0))),
180  scale_(OperatorBase::template GetSingleArgument<int>("scale", -1)),
181  minsize_(OperatorBase::template GetSingleArgument<int>("minsize", -1)),
182  warp_(OperatorBase::template GetSingleArgument<int>("warp", 0)),
183  crop_(OperatorBase::template GetSingleArgument<int>("crop", -1)),
184  mirror_(OperatorBase::template GetSingleArgument<int>("mirror", 0)),
185  is_test_(OperatorBase::template GetSingleArgument<int>(
186  OpSchema::Arg_IsTest,
187  0)),
188  use_caffe_datum_(
189  OperatorBase::template GetSingleArgument<int>("use_caffe_datum", 0)),
190  gpu_transform_(OperatorBase::template GetSingleArgument<int>(
191  "use_gpu_transform",
192  0)),
193  num_decode_threads_(
194  OperatorBase::template GetSingleArgument<int>("decode_threads", 4)),
195  thread_pool_(std::make_shared<TaskThreadPool>(num_decode_threads_)),
196  // output type only supported with CUDA and use_gpu_transform for now
197  output_type_(
198  cast::GetCastDataType(ArgumentHelper(operator_def), "output_type")),
199  random_scale_(
200  OperatorBase::template GetRepeatedArgument<int>("random_scale", {-1,-1})) {
201  if ((random_scale_[0] == -1) || (random_scale_[1] == -1)) {
202  random_scaling_ = false;
203  } else {
204  random_scaling_ = true;
205  minsize_ = random_scale_[0];
206  }
207 
208  mean_ = OperatorBase::template GetRepeatedArgument<float>(
209  "mean_per_channel",
210  {OperatorBase::template GetSingleArgument<float>("mean", 0.)});
211 
212  std_ = OperatorBase::template GetRepeatedArgument<float>(
213  "std_per_channel",
214  {OperatorBase::template GetSingleArgument<float>("std", 1.)});
215 
216  vector<int> additional_output_sizes =
217  OperatorBase::template GetRepeatedArgument<int>(
218  "output_sizes", vector<int>(OutputSize() - 2, 1));
219  additional_inputs_count_ = OutputSize() - 2;
220 
221  default_arg_.bounding_params = {
222  false,
223  OperatorBase::template GetSingleArgument<int>("bounding_ymin", -1),
224  OperatorBase::template GetSingleArgument<int>("bounding_xmin", -1),
225  OperatorBase::template GetSingleArgument<int>("bounding_height", -1),
226  OperatorBase::template GetSingleArgument<int>("bounding_width", -1),
227  };
228 
229  if (operator_def.input_size() == 0) {
230  LOG(ERROR) << "You are using an old ImageInputOp format that creates "
231  "a local db reader. Consider moving to the new style "
232  "that takes in a DBReader blob instead.";
233  string db_name =
234  OperatorBase::template GetSingleArgument<string>("db", "");
235  CAFFE_ENFORCE_GT(db_name.size(), 0, "Must specify a db name.");
236  owned_reader_.reset(new db::DBReader(
237  OperatorBase::template GetSingleArgument<string>(
238  "db_type", "leveldb"),
239  db_name));
240  reader_ = owned_reader_.get();
241  }
242 
243  // hard-coded PCA eigenvectors and eigenvalues, based on RBG channel order
244  color_lighting_eigvecs_.push_back(
245  std::vector<float>{-144.7125, 183.396, 102.2295});
246  color_lighting_eigvecs_.push_back(
247  std::vector<float>{-148.104, -1.1475, -207.57});
248  color_lighting_eigvecs_.push_back(
249  std::vector<float>{-148.818, -177.174, 107.1765});
250 
251  color_lighting_eigvals_ = std::vector<float>{0.2175, 0.0188, 0.0045};
252 
253  CAFFE_ENFORCE_GT(batch_size_, 0, "Batch size should be nonnegative.");
254  if (use_caffe_datum_) {
255  CAFFE_ENFORCE(label_type_ == SINGLE_LABEL || label_type_ == SINGLE_LABEL_WEIGHTED,
256  "Caffe datum only supports single integer label");
257  }
258  if (label_type_ != SINGLE_LABEL && label_type_ != SINGLE_LABEL_WEIGHTED) {
259  CAFFE_ENFORCE_GT(num_labels_, 0,
260  "Number of labels must be set for using either sparse label indices or dense label embedding.");
261  }
262  if (label_type_ == MULTI_LABEL_WEIGHTED_SPARSE ||
263  label_type_ == SINGLE_LABEL_WEIGHTED) {
264  additional_inputs_offset_ = 3;
265  } else {
266  additional_inputs_offset_ = 2;
267  }
268  CAFFE_ENFORCE((scale_ > 0) != (minsize_ > 0),
269  "Must provide one and only one of scaling or minsize");
270  CAFFE_ENFORCE_GT(crop_, 0, "Must provide the cropping value.");
271  CAFFE_ENFORCE_GE(
272  scale_ > 0 ? scale_ : minsize_,
273  crop_, "The scale/minsize value must be no smaller than the crop value.");
274 
275  CAFFE_ENFORCE_EQ(
276  mean_.size(),
277  std_.size(),
278  "The mean and std. dev vectors must be of the same size.");
279  CAFFE_ENFORCE(mean_.size() == 1 || mean_.size() == 3,
280  "The mean and std. dev vectors must be of size 1 or 3");
281  CAFFE_ENFORCE(
282  !use_caffe_datum_ || OutputSize() == 2,
283  "There can only be 2 outputs if the Caffe datum format is used");
284  CAFFE_ENFORCE(
285  additional_output_sizes.size() == OutputSize() - 2,
286  "If the output sizes are specified, they must be specified for all "
287  "additional outputs");
288 
289  CAFFE_ENFORCE(random_scale_.size() == 2,
290  "Must provide [scale_min, scale_max]");
291  CAFFE_ENFORCE_GE(random_scale_[1], random_scale_[0],
292  "random scale must provide a range [min, max]");
293 
294  if (default_arg_.bounding_params.ymin < 0
295  || default_arg_.bounding_params.xmin < 0
296  || default_arg_.bounding_params.height < 0
297  || default_arg_.bounding_params.width < 0) {
298  default_arg_.bounding_params.valid = false;
299  } else {
300  default_arg_.bounding_params.valid = true;
301  }
302 
303  if (mean_.size() == 1) {
304  // We are going to extend to 3 using the first value
305  mean_.resize(3, mean_[0]);
306  std_.resize(3, std_[0]);
307  }
308 
309  LOG(INFO) << "Creating an image input op with the following setting: ";
310  LOG(INFO) << " Using " << num_decode_threads_ << " CPU threads;";
311  if (gpu_transform_) {
312  LOG(INFO) << " Performing transformation on GPU";
313  }
314  LOG(INFO) << " Outputting in batches of " << batch_size_ << " images;";
315  LOG(INFO) << " Treating input image as "
316  << (color_ ? "color " : "grayscale ") << "image;";
317  if (default_arg_.bounding_params.valid) {
318  LOG(INFO) << " Applying a default bounding box of Y ["
319  << default_arg_.bounding_params.ymin << "; "
320  << default_arg_.bounding_params.ymin +
321  default_arg_.bounding_params.height
322  << ") x X ["
323  << default_arg_.bounding_params.xmin << "; "
324  << default_arg_.bounding_params.xmin +
325  default_arg_.bounding_params.width
326  << ")";
327  }
328  if (scale_ > 0 && !random_scaling_) {
329  LOG(INFO) << " Scaling image to " << scale_
330  << (warp_ ? " with " : " without ") << "warping;";
331  } else {
332  if (random_scaling_) {
333  // randomly set min_size_ for each image
334  LOG(INFO) << " Randomly scaling shortest side between "
335  << random_scale_[0] << " and "
336  << random_scale_[1];
337  } else {
338  // Here, minsize_ > 0
339  LOG(INFO) << " Ensuring minimum image size of " << minsize_
340  << (warp_ ? " with " : " without ") << "warping;";
341  }
342  }
343  LOG(INFO) << " " << (is_test_ ? "Central" : "Random")
344  << " cropping image to " << crop_
345  << (mirror_ ? " with " : " without ") << "random mirroring;";
346  LOG(INFO) << "Label Type: " << label_type_;
347  LOG(INFO) << "Num Labels: " << num_labels_;
348 
349  auto mit = mean_.begin();
350  auto sit = std_.begin();
351 
352  for (int i = 0;
353  mit != mean_.end() && sit != std_.end();
354  ++mit, ++sit, ++i) {
355  LOG(INFO) << " Default [Channel " << i << "] Subtract mean " << *mit
356  << " and divide by std " << *sit << ".";
357  // We actually will use the inverse of std, so inverse it here
358  *sit = 1.f / *sit;
359  }
360  LOG(INFO) << " Outputting images as "
361  << OperatorBase::template GetSingleArgument<string>("output_type", "unknown") << ".";
362 
363  std::mt19937 meta_randgen(time(nullptr));
364  for (int i = 0; i < num_decode_threads_; ++i) {
365  randgen_per_thread_.emplace_back(meta_randgen());
366  }
367  prefetched_image_.Resize(
368  TIndex(batch_size_),
369  TIndex(crop_),
370  TIndex(crop_),
371  TIndex(color_ ? 3 : 1));
372  if (label_type_ != SINGLE_LABEL && label_type_ != SINGLE_LABEL_WEIGHTED) {
373  prefetched_label_.Resize(TIndex(batch_size_), TIndex(num_labels_));
374  } else {
375  prefetched_label_.Resize(vector<TIndex>(1, batch_size_));
376  }
377 
378  for (int i = 0; i < additional_output_sizes.size(); ++i) {
379  prefetched_additional_outputs_[i].Resize(
380  TIndex(batch_size_), TIndex(additional_output_sizes[i]));
381  }
382 }
383 
384 // Inception-stype scale jittering
385 template <class Context>
386 bool RandomSizedCropping(
387  cv::Mat* img,
388  const int crop,
389  std::mt19937* randgen
390 ) {
391  cv::Mat scaled_img;
392  bool inception_scale_jitter = false;
393  int im_height = img->rows, im_width = img->cols;
394  int area = im_height * im_width;
395  std::uniform_real_distribution<> area_dis(0.08, 1.0);
396  std::uniform_real_distribution<> aspect_ratio_dis(3.0 / 4.0, 4.0 / 3.0);
397 
398  cv::Mat cropping;
399  for (int i = 0; i < 10; ++i) {
400  int target_area = int(ceil(area_dis(*randgen) * area));
401  float aspect_ratio = aspect_ratio_dis(*randgen);
402  int nh = floor(std::sqrt(((float)target_area / aspect_ratio)));
403  int nw = floor(std::sqrt(((float)target_area * aspect_ratio)));
404  if (nh >= 1 && nh <= im_height && nw >=1 && nw <= im_width) {
405  int height_offset = std::uniform_int_distribution<>(
406  0, im_height - nh)(*randgen);
407  int width_offset = std::uniform_int_distribution<>(
408  0,im_width - nw)(*randgen);
409  cv::Rect ROI(width_offset, height_offset, nw, nh);
410  cropping = (*img)(ROI);
411  cv::resize(
412  cropping,
413  scaled_img,
414  cv::Size(crop, crop),
415  0,
416  0,
417  cv::INTER_AREA);
418  *img = scaled_img;
419  inception_scale_jitter = true;
420  break;
421  }
422  }
423  return inception_scale_jitter;
424 }
425 
426 template <class Context>
428  const string& value,
429  cv::Mat* img,
430  PerImageArg& info,
431  int item_id,
432  std::mt19937* randgen) {
433  //
434  // recommend using --caffe2_use_fatal_for_enforce=1 when using ImageInputOp
435  // as this function runs on a worker thread and the exceptions from
436  // CAFFE_ENFORCE are silently dropped by the thread worker functions
437  //
438  cv::Mat src;
439 
440  // Use the default information for images
441  info = default_arg_;
442  if (use_caffe_datum_) {
443  // The input is a caffe datum format.
444  caffe::Datum datum;
445  CAFFE_ENFORCE(datum.ParseFromString(value));
446 
447  prefetched_label_.mutable_data<int>()[item_id] = datum.label();
448  if (datum.encoded()) {
449  // encoded image in datum.
450  src = cv::imdecode(
451  cv::Mat(
452  1,
453  datum.data().size(),
454  CV_8UC1,
455  const_cast<char*>(datum.data().data())),
456  color_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE);
457  } else {
458  // Raw image in datum.
459  CAFFE_ENFORCE(datum.channels() == 3 || datum.channels() == 1);
460 
461  int src_c = datum.channels();
462  src.create(
463  datum.height(), datum.width(), (src_c == 3) ? CV_8UC3 : CV_8UC1);
464 
465  if (src_c == 1) {
466  memcpy(src.ptr<uchar>(0), datum.data().data(), datum.data().size());
467  } else {
468  // Datum stores things in CHW order, let's do HWC for images to make
469  // things more consistent with conventional image storage.
470  for (int c = 0; c < 3; ++c) {
471  const char* datum_buffer =
472  datum.data().data() + datum.height() * datum.width() * c;
473  uchar* ptr = src.ptr<uchar>(0) + c;
474  for (int h = 0; h < datum.height(); ++h) {
475  for (int w = 0; w < datum.width(); ++w) {
476  *ptr = *(datum_buffer++);
477  ptr += 3;
478  }
479  }
480  }
481  }
482  }
483  } else {
484  // The input is a caffe2 format.
485  TensorProtos protos;
486  CAFFE_ENFORCE(protos.ParseFromString(value));
487  const TensorProto& image_proto = protos.protos(0);
488  const TensorProto& label_proto = protos.protos(1);
489  vector<TensorProto> additional_output_protos;
490  int start = additional_inputs_offset_;
491  int end = start + additional_inputs_count_;
492  for (int i = start; i < end; ++i) {
493  additional_output_protos.push_back(protos.protos(i));
494  }
495 
496  if (protos.protos_size() == end + 1) {
497  // We have bounding box information
498  const TensorProto& bounding_proto = protos.protos(end);
499  DCHECK_EQ(bounding_proto.data_type(), TensorProto::INT32);
500  DCHECK_EQ(bounding_proto.int32_data_size(), 4);
501  info.bounding_params.valid = true;
502  info.bounding_params.ymin = bounding_proto.int32_data(0);
503  info.bounding_params.xmin = bounding_proto.int32_data(1);
504  info.bounding_params.height = bounding_proto.int32_data(2);
505  info.bounding_params.width = bounding_proto.int32_data(3);
506  }
507 
508  if (image_proto.data_type() == TensorProto::STRING) {
509  // encoded image string.
510  DCHECK_EQ(image_proto.string_data_size(), 1);
511  const string& encoded_image_str = image_proto.string_data(0);
512  int encoded_size = encoded_image_str.size();
513  // We use a cv::Mat to wrap the encoded str so we do not need a copy.
514  src = cv::imdecode(
515  cv::Mat(
516  1,
517  &encoded_size,
518  CV_8UC1,
519  const_cast<char*>(encoded_image_str.data())),
520  color_ ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE);
521  } else if (image_proto.data_type() == TensorProto::BYTE) {
522  // raw image content.
523  int src_c = (image_proto.dims_size() == 3) ? image_proto.dims(2) : 1;
524  CAFFE_ENFORCE(src_c == 3 || src_c == 1);
525 
526  src.create(
527  image_proto.dims(0),
528  image_proto.dims(1),
529  (src_c == 3) ? CV_8UC3 : CV_8UC1);
530  memcpy(
531  src.ptr<uchar>(0),
532  image_proto.byte_data().data(),
533  image_proto.byte_data().size());
534  } else {
535  LOG(FATAL) << "Unknown image data type.";
536  }
537 
538  if (label_proto.data_type() == TensorProto::FLOAT) {
539  if (label_type_ == SINGLE_LABEL || label_type_ == SINGLE_LABEL_WEIGHTED) {
540  DCHECK_EQ(label_proto.float_data_size(), 1);
541  prefetched_label_.mutable_data<float>()[item_id] =
542  label_proto.float_data(0);
543  } else if (label_type_ == MULTI_LABEL_SPARSE) {
544  float* label_data = prefetched_label_.mutable_data<float>() +
545  item_id * num_labels_;
546  memset(label_data, 0, sizeof(float) * num_labels_);
547  for (int i = 0; i < label_proto.float_data_size(); ++i) {
548  label_data[(int)label_proto.float_data(i)] = 1.0;
549  }
550  } else if (label_type_ == MULTI_LABEL_WEIGHTED_SPARSE) {
551  const TensorProto& weight_proto = protos.protos(2);
552  float* label_data =
553  prefetched_label_.mutable_data<float>() + item_id * num_labels_;
554  memset(label_data, 0, sizeof(float) * num_labels_);
555  for (int i = 0; i < label_proto.float_data_size(); ++i) {
556  label_data[(int)label_proto.float_data(i)] =
557  weight_proto.float_data(i);
558  }
559  } else if (label_type_ == MULTI_LABEL_DENSE) {
560  CAFFE_ENFORCE(label_proto.float_data_size() == num_labels_);
561  float* label_data = prefetched_label_.mutable_data<float>() +
562  item_id * num_labels_;
563  for (int i = 0; i < label_proto.float_data_size(); ++i) {
564  label_data[i] = label_proto.float_data(i);
565  }
566  } else {
567  LOG(ERROR) << "Unknown label type:" << label_type_;
568  }
569  } else if (label_proto.data_type() == TensorProto::INT32) {
570  if (label_type_ == SINGLE_LABEL || label_type_ == SINGLE_LABEL_WEIGHTED) {
571  DCHECK_EQ(label_proto.int32_data_size(), 1);
572  prefetched_label_.mutable_data<int>()[item_id] =
573  label_proto.int32_data(0);
574  } else if (label_type_ == MULTI_LABEL_SPARSE) {
575  int* label_data = prefetched_label_.mutable_data<int>() +
576  item_id * num_labels_;
577  memset(label_data, 0, sizeof(int) * num_labels_);
578  for (int i = 0; i < label_proto.int32_data_size(); ++i) {
579  label_data[label_proto.int32_data(i)] = 1;
580  }
581  } else if (label_type_ == MULTI_LABEL_WEIGHTED_SPARSE) {
582  const TensorProto& weight_proto = protos.protos(2);
583  float* label_data =
584  prefetched_label_.mutable_data<float>() + item_id * num_labels_;
585  memset(label_data, 0, sizeof(float) * num_labels_);
586  for (int i = 0; i < label_proto.int32_data_size(); ++i) {
587  label_data[label_proto.int32_data(i)] = weight_proto.float_data(i);
588  }
589  } else if (label_type_ == MULTI_LABEL_DENSE) {
590  CAFFE_ENFORCE(label_proto.int32_data_size() == num_labels_);
591  int* label_data = prefetched_label_.mutable_data<int>() +
592  item_id * num_labels_;
593  for (int i = 0; i < label_proto.int32_data_size(); ++i) {
594  label_data[i] = label_proto.int32_data(i);
595  }
596  } else {
597  LOG(ERROR) << "Unknown label type:" << label_type_;
598  }
599  } else {
600  LOG(FATAL) << "Unsupported label data type.";
601  }
602 
603  for (int i = 0; i < additional_output_protos.size(); ++i) {
604  auto additional_output_proto = additional_output_protos[i];
605 
606  if (additional_output_proto.data_type() == TensorProto::FLOAT) {
607  float* additional_output =
608  prefetched_additional_outputs_[i].template mutable_data<float>() +
609  item_id * additional_output_proto.float_data_size();
610 
611  for (int j = 0; j < additional_output_proto.float_data_size(); ++j) {
612  additional_output[j] = additional_output_proto.float_data(j);
613  }
614  } else if (additional_output_proto.data_type() == TensorProto::INT32) {
615  int* additional_output =
616  prefetched_additional_outputs_[i].template mutable_data<int>() +
617  item_id * additional_output_proto.int32_data_size();
618 
619  for (int j = 0; j < additional_output_proto.int32_data_size(); ++j) {
620  additional_output[j] = additional_output_proto.int32_data(j);
621  }
622  } else if (additional_output_proto.data_type() == TensorProto::INT64) {
623  int64_t* additional_output =
624  prefetched_additional_outputs_[i].template mutable_data<int64_t>() +
625  item_id * additional_output_proto.int64_data_size();
626 
627  for (int j = 0; j < additional_output_proto.int64_data_size(); ++j) {
628  additional_output[j] = additional_output_proto.int64_data(j);
629  }
630  }
631  else {
632  LOG(FATAL) << "Unsupported output type.";
633  }
634  }
635  }
636 
637  //
638  // convert source to the color format requested from Op
639  //
640  int out_c = color_ ? 3 : 1;
641  if (out_c == src.channels()) {
642  *img = src;
643  } else {
644  cv::cvtColor(src, *img, (out_c == 1) ? CV_BGR2GRAY : CV_GRAY2BGR);
645  }
646 
647  // Note(Yangqing): I believe that the mat should be created continuous.
648  CAFFE_ENFORCE(img->isContinuous());
649 
650  // Sanity check now that we decoded everything
651 
652  // Ensure that the bounding box is legit
653  if (info.bounding_params.valid
654  && (src.rows < info.bounding_params.ymin + info.bounding_params.height
655  || src.cols < info.bounding_params.xmin + info.bounding_params.width
656  )) {
657  info.bounding_params.valid = false;
658  }
659 
660  // Apply the bounding box if requested
661  if (info.bounding_params.valid) {
662  // If we reach here, we know the parameters are sane
663  cv::Rect bounding_box(info.bounding_params.xmin, info.bounding_params.ymin,
664  info.bounding_params.width, info.bounding_params.height);
665  *img = (*img)(bounding_box);
666 
667  /*
668  LOG(INFO) << "Did bounding with ymin:"
669  << info.bounding_params.ymin << " xmin:" << info.bounding_params.xmin
670  << " height:" << info.bounding_params.height
671  << " width:" << info.bounding_params.width << "\n";
672  LOG(INFO) << "Bounded matrix: " << img;
673  */
674  } else {
675  // LOG(INFO) << "No bounding\n";
676  }
677 
678  cv::Mat scaled_img;
679  bool inception_scale_jitter = false;
680  if (scale_jitter_type_ == INCEPTION_STYLE) {
681  if (!is_test_) {
682  // Inception-stype scale jittering is only used for training
683  inception_scale_jitter = RandomSizedCropping<Context>(img, crop_, randgen);
684  // if a random crop is still not found, do simple random cropping later
685  }
686  }
687 
688  if ((scale_jitter_type_ == NO_SCALE_JITTER) ||
689  (scale_jitter_type_ == INCEPTION_STYLE && !inception_scale_jitter)) {
690  int scaled_width, scaled_height;
691  int scale_to_use = scale_ > 0 ? scale_ : minsize_;
692 
693  // set the random minsize
694  if (random_scaling_) {
695  scale_to_use = std::uniform_int_distribution<>(random_scale_[0],
696  random_scale_[1])(*randgen);
697  }
698 
699  if (warp_) {
700  scaled_width = scale_to_use;
701  scaled_height = scale_to_use;
702  } else if (img->rows > img->cols) {
703  scaled_width = scale_to_use;
704  scaled_height =
705  static_cast<float>(img->rows) * scale_to_use / img->cols;
706  } else {
707  scaled_height = scale_to_use;
708  scaled_width =
709  static_cast<float>(img->cols) * scale_to_use / img->rows;
710  }
711  if ((scale_ > 0 &&
712  (scaled_height != img->rows || scaled_width != img->cols))
713  || (scaled_height > img->rows || scaled_width > img->cols)) {
714  // We rescale in all cases if we are using scale_
715  // but only to make the image bigger if using minsize_
716  /*
717  LOG(INFO) << "Scaling to " << scaled_width << " x " << scaled_height
718  << " From " << img->cols << " x " << img->rows;
719  */
720  cv::resize(
721  *img,
722  scaled_img,
723  cv::Size(scaled_width, scaled_height),
724  0,
725  0,
726  cv::INTER_AREA);
727  *img = scaled_img;
728  }
729  }
730  // TODO(Yangqing): return false if any error happens.
731  return true;
732 }
733 
734 // assume HWC order and color channels BGR
735 template <class Context>
736 void Saturation(
737  float* img,
738  const int img_size,
739  const float alpha_rand,
740  std::mt19937* randgen
741 ) {
742  float alpha = 1.0f +
743  std::uniform_real_distribution<float>(-alpha_rand, alpha_rand)(*randgen);
744  // BGR to Gray scale image: R -> 0.299, G -> 0.587, B -> 0.114
745  int p = 0;
746  for (int h = 0; h < img_size; ++h) {
747  for (int w = 0; w < img_size; ++w) {
748  float gray_color = img[3 * p] * 0.114f + img[3 * p + 1] * 0.587f +
749  img[3 * p + 2] * 0.299f;
750  for (int c = 0; c < 3; ++c) {
751  img[3 * p + c] = img[3 * p + c] * alpha + gray_color * (1.0f - alpha);
752  }
753  p++;
754  }
755  }
756 }
757 
758 // assume HWC order and color channels BGR
759 template <class Context>
760 void Brightness(
761  float* img,
762  const int img_size,
763  const float alpha_rand,
764  std::mt19937* randgen
765 ) {
766  float alpha = 1.0f +
767  std::uniform_real_distribution<float>(-alpha_rand, alpha_rand)(*randgen);
768  int p = 0;
769  for (int h = 0; h < img_size; ++h) {
770  for (int w = 0; w < img_size; ++w) {
771  for (int c = 0; c < 3; ++c) {
772  img[p++] *= alpha;
773  }
774  }
775  }
776 }
777 
778 // assume HWC order and color channels BGR
779 template <class Context>
780 void Contrast(
781  float* img,
782  const int img_size,
783  const float alpha_rand,
784  std::mt19937* randgen
785 ){
786  float gray_mean = 0;
787  int p = 0;
788  for (int h = 0; h < img_size; ++h) {
789  for (int w = 0; w < img_size; ++w) {
790  // BGR to Gray scale image: R -> 0.299, G -> 0.587, B -> 0.114
791  gray_mean += img[3 * p] * 0.114f + img[3 * p + 1] * 0.587f +
792  img[3 * p + 2] * 0.299f;
793  p++;
794  }
795  }
796  gray_mean /= (img_size * img_size);
797 
798  float alpha = 1.0f +
799  std::uniform_real_distribution<float>(-alpha_rand, alpha_rand)(*randgen);
800  p = 0;
801  for (int h = 0; h < img_size; ++h) {
802  for (int w = 0; w < img_size; ++w) {
803  for (int c = 0; c < 3; ++c) {
804  img[p] = img[p] * alpha + gray_mean * (1.0f - alpha);
805  p++;
806  }
807  }
808  }
809 }
810 
811 // assume HWC order and color channels BGR
812 template <class Context>
813 void ColorJitter(
814  float* img,
815  const int img_size,
816  const float saturation,
817  const float brightness,
818  const float contrast,
819  std::mt19937* randgen
820 ) {
821  std::srand (unsigned(std::time(0)));
822  std::vector<int> jitter_order{0, 1, 2};
823  // obtain a time-based seed:
824  unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
825  std::shuffle(jitter_order.begin(), jitter_order.end(),
826  std::default_random_engine(seed));
827 
828  for (int i = 0; i < 3; ++i) {
829  if (jitter_order[i] == 0) {
830  Saturation<Context>(img, img_size, saturation, randgen);
831  } else if (jitter_order[i] == 1) {
832  Brightness<Context>(img, img_size, brightness, randgen);
833  } else {
834  Contrast<Context>(img, img_size, contrast, randgen);
835  }
836  }
837 }
838 
839 // assume HWC order and color channels BGR
840 template <class Context>
841 void ColorLighting(
842  float* img,
843  const int img_size,
844  const float alpha_std,
845  const std::vector<std::vector<float>>& eigvecs,
846  const std::vector<float>& eigvals,
847  std::mt19937* randgen
848 ) {
849  std::normal_distribution<float> d(0, alpha_std);
850  std::vector<float> alphas(3);
851  for (int i = 0; i < 3; ++i) {
852  alphas[i] = d(*randgen);
853  }
854 
855  std::vector<float> delta_rgb(3, 0.0);
856  for (int i = 0; i < 3; ++i) {
857  for (int j = 0; j < 3; ++j) {
858  delta_rgb[i] += eigvecs[i][j] * eigvals[j] * alphas[j];
859  }
860  }
861 
862  int p = 0;
863  for (int h = 0; h < img_size; ++h) {
864  for (int w = 0; w < img_size; ++w) {
865  for (int c = 0; c < 3; ++c) {
866  img[p++] += delta_rgb[2 - c];
867  }
868  }
869  }
870 
871 }
872 
873 // assume HWC order and color channels BGR
874 // mean subtraction and scaling.
875 template <class Context>
876 void ColorNormalization(
877  float* img,
878  const int img_size,
879  const int channels,
880  const std::vector<float>& mean,
881  const std::vector<float>& std
882 ) {
883  int p = 0;
884  for (int h = 0; h < img_size; ++h) {
885  for (int w = 0; w < img_size; ++w) {
886  for (int c = 0; c < channels; ++c) {
887  img[p] = (img[p] - mean[c]) * std[c];
888  p++;
889  }
890  }
891  }
892 }
893 
894 // Factored out image transformation
895 template <class Context>
896 void TransformImage(
897  const cv::Mat& scaled_img,
898  const int channels,
899  float* image_data,
900  const bool color_jitter,
901  const float saturation,
902  const float brightness,
903  const float contrast,
904  const bool color_lighting,
905  const float color_lighting_std,
906  const std::vector<std::vector<float>>& color_lighting_eigvecs,
907  const std::vector<float>& color_lighting_eigvals,
908  const int crop,
909  const bool mirror,
910  const std::vector<float>& mean,
911  const std::vector<float>& std,
912  std::mt19937* randgen,
913  std::bernoulli_distribution* mirror_this_image,
914  bool is_test = false) {
915  CAFFE_ENFORCE_GE(
916  scaled_img.rows, crop, "Image height must be bigger than crop.");
917  CAFFE_ENFORCE_GE(
918  scaled_img.cols, crop, "Image width must be bigger than crop.");
919 
920  // find the cropped region, and copy it to the destination matrix
921  int width_offset, height_offset;
922  if (is_test) {
923  width_offset = (scaled_img.cols - crop) / 2;
924  height_offset = (scaled_img.rows - crop) / 2;
925  } else {
926  width_offset =
927  std::uniform_int_distribution<>(0, scaled_img.cols - crop)(*randgen);
928  height_offset =
929  std::uniform_int_distribution<>(0, scaled_img.rows - crop)(*randgen);
930  }
931 
932  float* image_data_ptr = image_data;
933  if (!is_test && mirror && (*mirror_this_image)(*randgen)) {
934  // Copy mirrored image.
935  for (int h = height_offset; h < height_offset + crop; ++h) {
936  for (int w = width_offset + crop - 1; w >= width_offset; --w) {
937  const uint8_t* cv_data = scaled_img.ptr(h) + w * channels;
938  for (int c = 0; c < channels; ++c) {
939  *(image_data_ptr++) = static_cast<float>(cv_data[c]);
940  }
941  }
942  }
943  } else {
944  // Copy normally.
945  for (int h = height_offset; h < height_offset + crop; ++h) {
946  for (int w = width_offset; w < width_offset + crop; ++w) {
947  const uint8_t* cv_data = scaled_img.ptr(h) + w * channels;
948  for (int c = 0; c < channels; ++c) {
949  *(image_data_ptr++) = static_cast<float>(cv_data[c]);
950  }
951  }
952  }
953  }
954 
955  if (color_jitter && channels == 3 && !is_test) {
956  ColorJitter<Context>(image_data, crop, saturation, brightness, contrast,
957  randgen);
958  }
959  if (color_lighting && channels == 3 && !is_test) {
960  ColorLighting<Context>(image_data, crop, color_lighting_std,
961  color_lighting_eigvecs, color_lighting_eigvals, randgen);
962  }
963 
964  // Color normalization
965  // Mean subtraction and scaling.
966  ColorNormalization<Context>(image_data, crop, channels, mean, std);
967 }
968 
969 // Only crop / transose the image
970 // leave in uint8_t dataType
971 template <class Context>
972 void CropTransposeImage(const cv::Mat& scaled_img, const int channels,
973  uint8_t *cropped_data, const int crop,
974  const bool mirror, std::mt19937 *randgen,
975  std::bernoulli_distribution *mirror_this_image,
976  bool is_test = false) {
977  CAFFE_ENFORCE_GE(
978  scaled_img.rows, crop, "Image height must be bigger than crop.");
979  CAFFE_ENFORCE_GE(
980  scaled_img.cols, crop, "Image width must be bigger than crop.");
981 
982  // find the cropped region, and copy it to the destination matrix
983  int width_offset, height_offset;
984  if (is_test) {
985  width_offset = (scaled_img.cols - crop) / 2;
986  height_offset = (scaled_img.rows - crop) / 2;
987  } else {
988  width_offset =
989  std::uniform_int_distribution<>(0, scaled_img.cols - crop)(*randgen);
990  height_offset =
991  std::uniform_int_distribution<>(0, scaled_img.rows - crop)(*randgen);
992  }
993 
994  if (mirror && (*mirror_this_image)(*randgen)) {
995  // Copy mirrored image.
996  for (int h = height_offset; h < height_offset + crop; ++h) {
997  for (int w = width_offset + crop - 1; w >= width_offset; --w) {
998  const uint8_t* cv_data = scaled_img.ptr(h) + w*channels;
999  for (int c = 0; c < channels; ++c) {
1000  *(cropped_data++) = cv_data[c];
1001  }
1002  }
1003  }
1004  } else {
1005  // Copy normally.
1006  for (int h = height_offset; h < height_offset + crop; ++h) {
1007  for (int w = width_offset; w < width_offset + crop; ++w) {
1008  const uint8_t* cv_data = scaled_img.ptr(h) + w*channels;
1009  for (int c = 0; c < channels; ++c) {
1010  *(cropped_data++) = cv_data[c];
1011  }
1012  }
1013  }
1014  }
1015 }
1016 
1017 // Parse datum, decode image, perform transform
1018 // Intended as entry point for binding to thread pool
1019 template <class Context>
1021  const std::string& value, float *image_data, int item_id,
1022  const int channels, std::size_t thread_index) {
1023 
1024  CAFFE_ENFORCE((int)thread_index < num_decode_threads_);
1025 
1026  std::bernoulli_distribution mirror_this_image(0.5f);
1027  std::mt19937* randgen = &(randgen_per_thread_[thread_index]);
1028 
1029  cv::Mat img;
1030  // Decode the image
1031  PerImageArg info;
1032  CHECK(GetImageAndLabelAndInfoFromDBValue(value, &img, info, item_id,
1033  randgen));
1034 
1035  // Factor out the image transformation
1036  TransformImage<Context>(img, channels, image_data,
1037  color_jitter_, img_saturation_, img_brightness_, img_contrast_,
1038  color_lighting_, color_lighting_std_, color_lighting_eigvecs_,
1039  color_lighting_eigvals_, crop_, mirror_, mean_, std_,
1040  randgen, &mirror_this_image, is_test_);
1041 }
1042 
1043 template <class Context>
1045  const std::string& value, uint8_t *image_data, int item_id,
1046  const int channels, std::size_t thread_index) {
1047 
1048  CAFFE_ENFORCE((int)thread_index < num_decode_threads_);
1049 
1050  std::bernoulli_distribution mirror_this_image(0.5f);
1051  std::mt19937* randgen = &(randgen_per_thread_[thread_index]);
1052 
1053  cv::Mat img;
1054  // Decode the image
1055  PerImageArg info;
1056  CHECK(GetImageAndLabelAndInfoFromDBValue(value, &img, info, item_id,
1057  randgen));
1058 
1059  // Factor out the image transformation
1060  CropTransposeImage<Context>(img, channels, image_data, crop_, mirror_,
1061  randgen, &mirror_this_image, is_test_);
1062 }
1063 
1064 
1065 template <class Context>
1067  if (!owned_reader_.get()) {
1068  // if we are not owning the reader, we will get the reader pointer from
1069  // input. Otherwise the constructor should have already set the reader
1070  // pointer.
1071  reader_ = &OperatorBase::Input<db::DBReader>(0);
1072  }
1073  const int channels = color_ ? 3 : 1;
1074  // Call mutable_data() once to allocate the underlying memory.
1075  if (gpu_transform_) {
1076  // we'll transfer up in int8, then convert later
1077  prefetched_image_.mutable_data<uint8_t>();
1078  } else {
1079  prefetched_image_.mutable_data<float>();
1080  }
1081 
1082  prefetched_label_.mutable_data<int>();
1083  // Prefetching handled with a thread pool of "decode_threads" threads.
1084 
1085  for (int item_id = 0; item_id < batch_size_; ++item_id) {
1086  std::string key, value;
1087  cv::Mat img;
1088 
1089  // read data
1090  reader_->Read(&key, &value);
1091 
1092  // determine label type based on first item
1093  if( item_id == 0 ) {
1094  if( use_caffe_datum_ ) {
1095  prefetched_label_.mutable_data<int>();
1096  } else {
1097  TensorProtos protos;
1098  CAFFE_ENFORCE(protos.ParseFromString(value));
1099  TensorProto_DataType labeldt = protos.protos(1).data_type();
1100  if( labeldt == TensorProto::INT32 ) {
1101  prefetched_label_.mutable_data<int>();
1102  } else if ( labeldt == TensorProto::FLOAT) {
1103  prefetched_label_.mutable_data<float>();
1104  } else {
1105  LOG(FATAL) << "Unsupported label type.";
1106  }
1107 
1108  for (int i = 0; i < additional_inputs_count_; ++i) {
1109  int index = additional_inputs_offset_ + i;
1110  TensorProto additional_output_proto = protos.protos(index);
1111 
1112  if (additional_output_proto.data_type() == TensorProto::FLOAT) {
1113  prefetched_additional_outputs_[i].template mutable_data<float>();
1114  } else if (
1115  additional_output_proto.data_type() == TensorProto::INT32) {
1116  prefetched_additional_outputs_[i].template mutable_data<int>();
1117  } else if (
1118  additional_output_proto.data_type() == TensorProto::INT64) {
1119  prefetched_additional_outputs_[i].template mutable_data<int64_t>();
1120  } else {
1121  LOG(FATAL) << "Unsupported output type.";
1122  }
1123  }
1124  }
1125  }
1126 
1127  // launch into thread pool for processing
1128  // TODO: support color jitter and color lighting in gpu_transform
1129  if (gpu_transform_) {
1130  // output of decode will still be int8
1131  uint8_t* image_data = prefetched_image_.mutable_data<uint8_t>() +
1132  crop_ * crop_ * channels * item_id;
1133  thread_pool_->runTaskWithID(std::bind(
1135  this,
1136  std::string(value),
1137  image_data,
1138  item_id,
1139  channels,
1140  std::placeholders::_1));
1141  } else {
1142  float* image_data = prefetched_image_.mutable_data<float>() +
1143  crop_ * crop_ * channels * item_id;
1144  thread_pool_->runTaskWithID(std::bind(
1146  this,
1147  std::string(value),
1148  image_data,
1149  item_id,
1150  channels,
1151  std::placeholders::_1));
1152  }
1153  }
1154  thread_pool_->waitWorkComplete();
1155 
1156  // If the context is not CPUContext, we will need to do a copy in the
1157  // prefetch function as well.
1158  if (!std::is_same<Context, CPUContext>::value) {
1159  prefetched_image_on_device_.CopyFrom(prefetched_image_, &context_);
1160  prefetched_label_on_device_.CopyFrom(prefetched_label_, &context_);
1161 
1162  for (int i = 0; i < prefetched_additional_outputs_on_device_.size(); ++i) {
1163  prefetched_additional_outputs_on_device_[i].CopyFrom(
1164  prefetched_additional_outputs_[i], &context_);
1165  }
1166  }
1167  return true;
1168 }
1169 
1170 template <class Context>
1172  auto* image_output = OperatorBase::Output<Tensor<Context> >(0);
1173  auto* label_output = OperatorBase::Output<Tensor<Context> >(1);
1174  vector<Tensor<Context>*> additional_outputs_output;
1175 
1176  for (int i = 2; i < OutputSize(); ++i) {
1177  additional_outputs_output.push_back(
1178  OperatorBase::Output<Tensor<Context>>(i));
1179  }
1180 
1181  // Note(jiayq): The if statement below should be optimized away by the
1182  // compiler since std::is_same is a constexpr.
1183  if (std::is_same<Context, CPUContext>::value) {
1184  image_output->CopyFrom(prefetched_image_, &context_);
1185  label_output->CopyFrom(prefetched_label_, &context_);
1186 
1187  for (int i = 0; i < additional_outputs_output.size(); ++i) {
1188  additional_outputs_output[i]->CopyFrom(
1189  prefetched_additional_outputs_[i], &context_);
1190  }
1191  } else {
1192  // TODO: support color jitter and color lighting in gpu_transform
1193  if (gpu_transform_) {
1194  if (!mean_std_copied_) {
1195  mean_gpu_.Resize(mean_.size());
1196  std_gpu_.Resize(std_.size());
1197 
1198  context_.template Copy<float, CPUContext, Context>(
1199  mean_.size(), mean_.data(), mean_gpu_.template mutable_data<float>());
1200  context_.template Copy<float, CPUContext, Context>(
1201  std_.size(), std_.data(), std_gpu_.template mutable_data<float>());
1202  mean_std_copied_ = true;
1203  }
1204  // GPU transform kernel allows explicitly setting output type
1205  if (output_type_ == TensorProto_DataType_FLOAT) {
1206  TransformOnGPU<uint8_t,float,Context>(prefetched_image_on_device_,
1207  image_output, mean_gpu_,
1208  std_gpu_, &context_);
1209  } else if (output_type_ == TensorProto_DataType_FLOAT16) {
1210  TransformOnGPU<uint8_t,float16,Context>(prefetched_image_on_device_,
1211  image_output, mean_gpu_,
1212  std_gpu_, &context_);
1213  } else {
1214  return false;
1215  }
1216  } else {
1217  image_output->CopyFrom(prefetched_image_on_device_, &context_);
1218  }
1219  label_output->CopyFrom(prefetched_label_on_device_, &context_);
1220 
1221  for (int i = 0; i < additional_outputs_output.size(); ++i) {
1222  additional_outputs_output[i]->CopyFrom(
1223  prefetched_additional_outputs_on_device_[i], &context_);
1224  }
1225  }
1226  return true;
1227 }
1228 } // namespace caffe2
1229 
1230 #endif // CAFFE2_IMAGE_IMAGE_INPUT_OP_H_
void Read(string *key, string *value) const
Read a set of key and value from the db and move to next.
Definition: db.h:222
A reader wrapper for DB that also allows us to serialize it.
Definition: db.h:144
Definition: types.h:72
The CPU Context, representing the bare minimum of what a Context class in Caffe2 should implement...
Definition: context.h:66
A helper class to index into arguments.
Definition: proto_utils.h:198
T * mutable_data()
Returns a typed pointer of the underlying storage.
Definition: tensor.h:578
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:47
void Resize(Ts...dim_source)
Resizes a tensor.
Definition: tensor.h:288
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...