17
#include <opencv2/opencv.hpp>
21
#include "caffe2/core/common.h"
22
#include "caffe2/core/db.h"
23
#include "caffe2/core/init.h"
24
#include "caffe2/core/logging.h"
25
#include "caffe2/core/timer.h"
26
#include "caffe2/proto/caffe2_pb.h"
27
#include "caffe2/utils/proto_utils.h"
28
#include "caffe2/utils/string_utils.h"
34
"Specify the batch size of the input. The number of items in the "
35
"input needs to be multiples of the batch size. If the batch size "
36
"is less than 0, all inputs are in one batch.")
37
C10_DEFINE_bool(color, true, "If set, load images in color.");
41
"The center cropped hight and width. If the value is less than zero, "
42
"it is not cropped.");
43
C10_DEFINE_string(input_images, "", "Comma separated images");
44
C10_DEFINE_string(input_image_file, "", "The file containing imput images");
45
C10_DEFINE_string(input_text_file, "", "the text file to be written to blobs");
49
"The output tensor file in NCHW for input images");
53
"The output tensor file for the text input specified in input_text_file");
57
"Options to specify the preprocess routines. The available options are "
58
"subtract128, normalize, mean, std, bgrtorgb. If multiple steps are provided, they "
59
"are separated by comma (,) in sequence.");
63
"Report the conversion stage time to screen. "
64
"The format of the string is <type>|<identifier>. "
65
"The valid type is 'json'. "
66
"The valid identifier is nothing or an identifier that prefix every line");
70
"Scale the images to be within the min,max box. The shorter edge is "
71
"min pixels. But if the other edge is more than the max pixels, the "
72
"other edge and scaled to max pixels (and the shorter edge can be less "
73
"than the min pixels");
74
C10_DEFINE_bool(text_output, false, "Write the output in text format.");
75
C10_DEFINE_bool(warp, false, "If warp is set, warp the images to square.");
84
if (FLAGS_report_time == "") {
87
vector<string> s = caffe2::split('|', FLAGS_report_time);
88
assert(s[0] == "json");
89
std::string identifier = "";
93
std::cout << identifier << "{\"type\": \"" << type << "\", \"value\": " << ts
94
<< ", \"metric\": \"" << metric << "\", \"unit\": \"" << unit
95
<< "\"}" << std::endl;
98
void splitSizes(const std::string& arg, int* ptr0, int* ptr1) {
99
vector<string> sizes = caffe2::split(',', arg);
100
if (sizes.size() == 2) {
101
*ptr0 = std::stoi(sizes[0]);
102
*ptr1 = std::stoi(sizes[1]);
103
} else if (sizes.size() == 1) {
104
*ptr0 = std::stoi(sizes[0]);
105
*ptr1 = std::stoi(sizes[0]);
112
cv::Mat resizeImage(cv::Mat& img) {
113
int min_size, max_size;
114
splitSizes(FLAGS_scale, &min_size, &max_size);
115
if ((min_size <= 0) && (max_size <= 0)) {
121
assert(min_size <= max_size);
123
int im_min_size = img.rows > img.cols ? img.cols : img.rows;
124
int im_max_size = img.rows > img.cols ? img.rows : img.cols;
126
double im_scale = 1.0 * min_size / im_min_size;
127
if (im_scale * im_max_size > max_size) {
128
im_scale = 1.0 * max_size / im_max_size;
130
int scaled_width = int(round(img.cols * im_scale));
131
int scaled_height = int(round(img.rows * im_scale));
132
assert((scaled_width <= max_size) && (scaled_height <= max_size));
133
if ((scaled_width < min_size) || (scaled_height < min_size)) {
134
assert((scaled_width == max_size) || (scaled_height == max_size));
136
assert((scaled_width == min_size) || (scaled_height == min_size));
149
cv::Mat cropToRec(cv::Mat& img, int* height_ptr, int* width_ptr) {
150
int height = *height_ptr;
151
int width = *width_ptr;
152
if ((height > 0) && (width > 0) &&
153
((img.rows != height) || (img.cols != width))) {
154
cv::Mat cropped_img, cimg;
156
roi.x = int((img.cols - width) / 2);
157
roi.y = int((img.rows - height) / 2);
158
roi.x = roi.x < 0 ? 0 : roi.x;
159
roi.y = roi.y < 0 ? 0 : roi.y;
160
width = width > img.cols ? img.cols : width;
161
height = height > img.rows ? img.rows : height;
165
0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= img.cols &&
166
0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= img.rows);
167
cropped_img = img(roi);
169
cimg = cropped_img.clone();
170
*height_ptr = height;
178
std::vector<float> convertToVector(cv::Mat& img) {
179
std::vector<float> normalize(3, 1);
180
std::vector<float> mean(3, 0);
181
std::vector<float> std(3, 1);
182
bool bgrtorgb = false;
183
int size = img.cols * img.rows;
184
vector<string> steps = caffe2::split(',', FLAGS_preprocess);
185
for (int i = 0; i < steps.size(); i++) {
186
auto step = steps[i];
187
if (step == "subtract128") {
188
mean = {128, 128, 128};
190
normalize = {1, 1, 1};
191
} else if (step == "normalize") {
192
normalize = {255, 255, 255};
193
} else if (step == "mean") {
194
mean = {0.406f, 0.456f, 0.485f};
195
} else if (step == "std") {
196
std = {0.225f, 0.224f, 0.229f};
197
} else if (step == "bgrtorgb") {
202
"Unsupported preprocess step. The supported steps are: subtract128, "
203
"normalize,mean, std, swaprb.");
207
int C = FLAGS_color ? 3 : 1;
208
int total_size = C * size;
209
std::vector<float> values(total_size);
211
cv::MatIterator_<float> it, end;
213
for (it = img.begin<float>(), end = img.end<float>(); it != end; ++it) {
214
values[idx++] = (*it / normalize[0] - mean[0]) / std[0];
218
cv::MatIterator_<cv::Vec3f> it, end;
219
int b = bgrtorgb ? 2 : 0;
221
int r = bgrtorgb ? 0 : 2;
222
for (it = img.begin<cv::Vec3f>(), end = img.end<cv::Vec3f>(); it != end;
224
values[i] = (((*it)[b] / normalize[0] - mean[0]) / std[0]);
225
int offset = size + i;
226
values[offset] = (((*it)[g] / normalize[1] - mean[1]) / std[1]);
227
offset = size + offset;
228
values[offset] = (((*it)[r] / normalize[2] - mean[2]) / std[2]);
234
std::vector<float> convertOneImage(
235
std::string& filename,
238
assert(filename[0] != '~');
240
std::cout << "Converting " << filename << std::endl;
243
cv::Mat img_uint8 = cv::imread(
244
#if CV_MAJOR_VERSION <= 3
245
filename, FLAGS_color ? CV_LOAD_IMAGE_COLOR : CV_LOAD_IMAGE_GRAYSCALE);
247
filename, FLAGS_color ? cv::IMREAD_COLOR : cv::IMREAD_GRAYSCALE);
253
img_uint8.convertTo(img, CV_32F);
255
cv::Mat resized_img = resizeImage(img);
258
splitSizes(FLAGS_crop, &height, &width);
259
if ((height <= 0) || (width <= 0)) {
260
height = resized_img.rows;
261
width = resized_img.cols;
263
cv::Mat crop = cropToRec(resized_img, &height, &width);
266
DCHECK(crop.isContinuous());
267
assert(crop.rows == height);
268
assert(crop.cols == width);
269
std::vector<float> one_image_values = convertToVector(crop);
270
*height_ptr = height;
272
double ts = timer.MicroSeconds();
273
reportTime("image_preprocess", ts, "convert", "us");
274
return one_image_values;
277
int getBatchSize(int num_items) {
278
int batch_size = FLAGS_batch_size;
279
if (batch_size < 0) {
280
batch_size = num_items;
282
assert(num_items % batch_size == 0);
288
std::vector<std::vector<std::vector<float>>>& values,
289
std::vector<std::vector<int>>& dims,
290
std::string output_file) {
295
assert(dims.size() == values.size());
296
int num_batches = dims.size();
299
for (int k = 0; k < num_batches; k++) {
301
data = protos.add_protos();
302
data->set_data_type(TensorProto::FLOAT);
303
auto one_dim = dims[k];
304
for (int dim : one_dim) {
307
int batch_size = one_dim[0];
308
long long int entry_size = 1;
309
for (int i = 1; i < one_dim.size(); i++) {
310
entry_size *= one_dim[i];
314
for (int i = 0; i < batch_size; i++) {
315
assert(values[k][i].size() == entry_size);
316
for (int j = 0; j < values[k][i].size(); j++) {
317
data->add_float_data(values[k][i][j]);
321
double ts = timer.MicroSeconds();
322
reportTime("preprocess", ts, "data_pack", "us");
324
if (FLAGS_text_output) {
325
caffe2::WriteProtoToTextFile(protos, output_file);
327
caffe2::WriteProtoToBinaryFile(protos, output_file);
331
void convertImages() {
332
vector<string> file_names;
333
if (FLAGS_input_images != "") {
334
file_names = caffe2::split(',', FLAGS_input_images);
335
} else if (FLAGS_input_image_file != "") {
336
std::ifstream infile(FLAGS_input_image_file);
338
while (std::getline(infile, line)) {
339
vector<string> file_name = caffe2::split(',', line);
341
if (file_name.size() == 3) {
346
file_names.push_back(name);
351
int batch_size = getBatchSize(file_names.size());
352
int num_batches = file_names.size() / batch_size;
353
assert(file_names.size() == batch_size * num_batches);
354
std::vector<std::vector<std::vector<float>>> values;
355
std::vector<std::vector<int>> dims;
356
int C = FLAGS_color ? 3 : 1;
357
for (int k = 0; k < num_batches; k++) {
358
std::vector<std::vector<float>> one_value;
361
for (int i = 0; i < batch_size; i++) {
362
int idx = k * batch_size + i;
363
int one_height, one_width;
364
std::vector<float> one_image_values =
365
convertOneImage(file_names[idx], &one_height, &one_width);
366
if (height < 0 && width < 0) {
370
assert(height == one_height);
371
assert(width == one_width);
373
one_value.push_back(one_image_values);
375
vector<int> one_dim = {batch_size, C, height, width};
376
dims.push_back(one_dim);
377
values.push_back(one_value);
379
writeValues(values, dims, FLAGS_output_tensor);
383
vector<TYPE> splitString(std::string& line) {
384
vector<string> vector_str = caffe2::split(',', line);
385
vector<TYPE> vector_int;
386
for (string str : vector_str) {
387
vector_int.push_back((TYPE)std::stod(str));
399
void convertValues() {
400
if (FLAGS_input_text_file == "") {
403
std::ifstream infile(FLAGS_input_text_file);
405
std::getline(infile, line);
406
vector<int> file_dims = splitString <int>(line);
407
assert(file_dims.size() >= 2);
409
int num_items = file_dims[0];
410
int batch_size = getBatchSize(num_items);
411
int num_batches = num_items / batch_size;
412
assert(num_items == batch_size * num_batches);
413
vector<string> lines;
414
while (std::getline(infile, line)) {
415
lines.push_back(line);
417
assert(lines.size() == num_items);
418
std::vector<std::vector<std::vector<float>>> values;
419
std::vector<std::vector<int>> dims;
420
for (int i = 0; i < num_batches; i++) {
421
std::vector<std::vector<float>> one_value;
423
for (int j = 0; j < batch_size; j++) {
424
int idx = i * batch_size + j;
425
std::string line = lines[idx];
426
vector<float> item = splitString<float>(line);
430
assert(num == item.size());
432
one_value.push_back(item);
434
vector<int> batch_dims = file_dims;
435
batch_dims[0] = batch_size;
436
dims.push_back(batch_dims);
437
values.push_back(one_value);
440
writeValues(values, dims, FLAGS_output_text_tensor);
445
int main(int argc, char** argv) {
446
caffe2::GlobalInit(&argc, &argv);
447
caffe2::convertImages();
448
caffe2::convertValues();