int tile_dataset(const command_line_parser& parser) { if (parser.number_of_arguments() != 1) { cerr << "The --tile option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } string out_image = parser.option("tile").argument(); string ext = right_substr(out_image,"."); if (ext != "png" && ext != "jpg") { cerr << "The output image file must have either .png or .jpg extension." << endl; return EXIT_FAILURE; } const unsigned long chip_size = get_option(parser, "size", 8000); dlib::image_dataset_metadata::dataset data; load_image_dataset_metadata(data, parser[0]); locally_change_current_dir chdir(get_parent_directory(file(parser[0]))); dlib::array<array2d<rgb_pixel> > images; console_progress_indicator pbar(data.images.size()); for (unsigned long i = 0; i < data.images.size(); ++i) { // don't even bother loading images that don't have objects. if (data.images[i].boxes.size() == 0) continue; pbar.print_status(i); array2d<rgb_pixel> img; load_image(img, data.images[i].filename); // figure out what chips we want to take from this image std::vector<chip_details> dets; for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) { if (data.images[i].boxes[j].ignore) continue; rectangle rect = data.images[i].boxes[j].rect; dets.push_back(chip_details(rect, chip_size)); } // Now grab all those chips at once. dlib::array<array2d<rgb_pixel> > chips; extract_image_chips(img, dets, chips); // and put the chips into the output. for (unsigned long j = 0; j < chips.size(); ++j) images.push_back(chips[j]); } chdir.revert(); if (ext == "png") save_png(tile_images(images), out_image); else save_jpeg(tile_images(images), out_image); return EXIT_SUCCESS; }
int make_train_test_splits ( const command_line_parser& parser ) { if (parser.number_of_arguments() != 1) { cerr << "The --split-train-test option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } const double train_frac = get_option(parser, "split-train-test", 0.5); dlib::image_dataset_metadata::dataset data, data_train, data_test; load_image_dataset_metadata(data, parser[0]); data_train.name = data.name; data_train.comment = data.comment; data_test.name = data.name; data_test.comment = data.comment; const unsigned long num_train_images = static_cast<unsigned long>(std::round(train_frac*data.images.size())); for (unsigned long i = 0; i < data.images.size(); ++i) { if (i < num_train_images) data_train.images.push_back(data.images[i]); else data_test.images.push_back(data.images[i]); } save_image_dataset_metadata(data_train, left_substr(parser[0],".") + "_train.xml"); save_image_dataset_metadata(data_test, left_substr(parser[0],".") + "_test.xml"); return EXIT_SUCCESS; }
void convert_idl( const command_line_parser& parser ) { cout << "Convert from IDL annotation format..." << endl; dlib::image_dataset_metadata::dataset dataset; for (unsigned long i = 0; i < parser.number_of_arguments(); ++i) { parse_annotation_file(parser[i], dataset); } const std::string filename = parser.option("c").argument(); save_image_dataset_metadata(dataset, filename); }
int split_dataset ( const command_line_parser& parser ) { if (parser.number_of_arguments() != 1) { cerr << "The --split option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } const std::string label = parser.option("split").argument(); dlib::image_dataset_metadata::dataset data, data_with, data_without; load_image_dataset_metadata(data, parser[0]); data_with.name = data.name; data_with.comment = data.comment; data_without.name = data.name; data_without.comment = data.comment; for (unsigned long i = 0; i < data.images.size(); ++i) { auto&& temp = data.images[i]; bool has_the_label = false; // check for the label we are looking for for (unsigned long j = 0; j < temp.boxes.size(); ++j) { if (temp.boxes[j].label == label) { has_the_label = true; break; } } if (has_the_label) data_with.images.push_back(temp); else data_without.images.push_back(temp); } save_image_dataset_metadata(data_with, left_substr(parser[0],".") + "_with_"+label + ".xml"); save_image_dataset_metadata(data_without, left_substr(parser[0],".") + "_without_"+label + ".xml"); return EXIT_SUCCESS; }
void create_new_dataset ( const command_line_parser& parser ) { using namespace dlib::image_dataset_metadata; const std::string filename = parser.option("c").argument(); // make sure the file exists so we can use the get_parent_directory() command to // figure out it's parent directory. make_empty_file(filename); const std::string parent_dir = get_parent_directory(file(filename)); unsigned long depth = 0; if (parser.option("r")) depth = 30; dataset meta; meta.name = "imglab dataset"; meta.comment = "Created by imglab tool."; for (unsigned long i = 0; i < parser.number_of_arguments(); ++i) { try { const string temp = strip_path(file(parser[i]), parent_dir); meta.images.push_back(image(temp)); } catch (dlib::file::file_not_found&) { // then parser[i] should be a directory std::vector<file> files = get_files_in_directory_tree(parser[i], match_endings(".png .PNG .jpeg .JPEG .jpg .JPG .bmp .BMP .dng .DNG"), depth); sort(files.begin(), files.end()); for (unsigned long j = 0; j < files.size(); ++j) { meta.images.push_back(image(strip_path(files[j], parent_dir))); } } } save_image_dataset_metadata(meta, filename); }
void convert_pascal_xml( const command_line_parser& parser ) { cout << "Convert from PASCAL XML annotation format..." << endl; dlib::image_dataset_metadata::dataset dataset; std::string name; dlib::image_dataset_metadata::image img; const std::string filename = parser.option("c").argument(); // make sure the file exists so we can use the get_parent_directory() command to // figure out it's parent directory. make_empty_file(filename); const std::string parent_dir = get_parent_directory(file(filename)).full_name(); for (unsigned long i = 0; i < parser.number_of_arguments(); ++i) { try { parse_annotation_file(parser[i], img, name); const string root = get_parent_directory(get_parent_directory(file(parser[i]))).full_name(); const string img_path = root + directory::get_separator() + "JPEGImages" + directory::get_separator(); dataset.name = name; img.filename = strip_path(img_path + img.filename, parent_dir); dataset.images.push_back(img); } catch (exception& e) { cout << "Error while processing file " << parser[i] << endl << endl; throw; } } save_image_dataset_metadata(dataset, filename); }
int resample_dataset(const command_line_parser& parser) { if (parser.number_of_arguments() != 1) { cerr << "The --resample option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } const size_t obj_size = get_option(parser,"resample",100*100); const double margin_scale = 2.5; // cropped image will be this times wider than the object. const size_t image_size = obj_size*margin_scale*margin_scale; dlib::image_dataset_metadata::dataset data, resampled_data; resampled_data.comment = data.comment; resampled_data.name = data.name + " RESAMPLED"; load_image_dataset_metadata(data, parser[0]); locally_change_current_dir chdir(get_parent_directory(file(parser[0]))); console_progress_indicator pbar(data.images.size()); for (unsigned long i = 0; i < data.images.size(); ++i) { // don't even bother loading images that don't have objects. if (data.images[i].boxes.size() == 0) continue; pbar.print_status(i); array2d<rgb_pixel> img, chip; load_image(img, data.images[i].filename); // figure out what chips we want to take from this image for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) { const rectangle rect = data.images[i].boxes[j].rect; if (data.images[i].boxes[j].ignore || !get_rect(img).contains(rect)) continue; const rectangle crop_rect = centered_rect(rect, rect.width()*margin_scale, rect.height()*margin_scale); // skip crops that have a lot of border pixels if (get_rect(img).intersect(crop_rect).area() < crop_rect.area()*0.8) continue; const rectangle_transform tform = get_mapping_to_chip(chip_details(crop_rect, image_size)); extract_image_chip(img, chip_details(crop_rect, image_size), chip); image_dataset_metadata::image dimg; // Now transform the boxes to the crop and also mark them as ignored if they // have already been cropped out or are outside the crop. for (size_t k = 0; k < data.images[i].boxes.size(); ++k) { image_dataset_metadata::box box = data.images[i].boxes[k]; // ignore boxes outside the cropped image if (crop_rect.intersect(box.rect).area() == 0) continue; // mark boxes we include in the crop as ignored. Also mark boxes that // aren't totally within the crop as ignored. if (crop_rect.contains(grow_rect(box.rect,10))) data.images[i].boxes[k].ignore = true; else box.ignore = true; box.rect = tform(box.rect); for (auto&& p : box.parts) p.second = tform.get_tform()(p.second); dimg.boxes.push_back(box); } dimg.filename = data.images[i].filename + "RESAMPLED"+cast_to_string(j)+".jpg"; save_jpeg(chip,dimg.filename, 98); resampled_data.images.push_back(dimg); } } save_image_dataset_metadata(resampled_data, parser[0] + ".RESAMPLED.xml"); return EXIT_SUCCESS; }
int extract_chips (const command_line_parser& parser) { if (parser.number_of_arguments() != 1) { cerr << "The --extract-chips option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } const size_t obj_size = get_option(parser,"extract-chips",100*100); dlib::image_dataset_metadata::dataset data; load_image_dataset_metadata(data, parser[0]); // figure out the average box size so we can make all the chips have the same exact // dimensions running_stats<double> rs; for (auto&& img : data.images) { for (auto&& box : img.boxes) { if (box.rect.height() != 0) rs.add(box.rect.width()/(double)box.rect.height()); } } if (rs.current_n() == 0) { cerr << "Dataset doesn't contain any non-empty and non-ignored boxes!" << endl; return EXIT_FAILURE; } const double dobj_nr = std::sqrt(obj_size/rs.mean()); const double dobj_nc = obj_size/dobj_nr; const chip_dims cdims(std::round(dobj_nr), std::round(dobj_nc)); locally_change_current_dir chdir(get_parent_directory(file(parser[0]))); cout << "Writing image chips to image_chips.dat. It is a file containing serialized images" << endl; cout << "Written like this: " << endl; cout << " ofstream fout(\"image_chips.dat\", ios::bianry); " << endl; cout << " bool is_not_background; " << endl; cout << " array2d<rgb_pixel> the_image_chip; " << endl; cout << " while(more images) { " << endl; cout << " ... load chip ... " << endl; cout << " serialize(is_not_background, fout);" << endl; cout << " serialize(the_image_chip, fout);" << endl; cout << " }" << endl; cout << endl; ofstream fout("image_chips.dat", ios::binary); dlib::rand rnd; unsigned long count = 0; console_progress_indicator pbar(data.images.size()); for (unsigned long i = 0; i < data.images.size(); ++i) { // don't even bother loading images that don't have objects. if (data.images[i].boxes.size() == 0) continue; pbar.print_status(i); array2d<rgb_pixel> img, chip; load_image(img, data.images[i].filename); std::vector<chip_details> chips; std::vector<rectangle> used_rects; for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) { const rectangle rect = data.images[i].boxes[j].rect; used_rects.push_back(rect); if (data.images[i].boxes[j].ignore) continue; chips.push_back(chip_details(rect, cdims)); } const auto num_good_chps = chips.size(); // Now grab some bad chips, being careful not to grab things that overlap with // annotated boxes in the dataset. for (unsigned long j = 0; j < num_good_chps*5; ++j) { // pick two random points that make a box of the correct aspect ratio // pick a point so that our rectangle will fit within the point p1(rnd.get_random_32bit_number()%img.nc(), rnd.get_random_32bit_number()%img.nr()); // make the random box between 0.5 and 1.5 times the size of the truth boxes. double box_size = rnd.get_random_double() + 0.5; point p2 = p1 + point(dobj_nc*box_size, dobj_nr*box_size); rectangle rect(p1,p2); if (overlaps_any_box(used_rects, rect) || !get_rect(img).contains(rect)) continue; used_rects.push_back(rect); chips.push_back(chip_details(rect, cdims)); } // now save these chips to disk. dlib::array<array2d<rgb_pixel>> image_chips; extract_image_chips(img, chips, image_chips); bool is_not_background = true; unsigned long j; for (j = 0; j < num_good_chps; ++j) { serialize(is_not_background, fout); serialize(image_chips[j], fout); } is_not_background = false; for (; j < image_chips.size(); ++j) { serialize(is_not_background, fout); serialize(image_chips[j], fout); } count += image_chips.size(); } cout << "\nSaved " << count << " chips." << endl; return EXIT_SUCCESS; }
int resample_dataset(const command_line_parser& parser) { if (parser.number_of_arguments() != 1) { cerr << "The --resample option requires you to give one XML file on the command line." << endl; return EXIT_FAILURE; } const size_t obj_size = get_option(parser,"cropped-object-size",100*100); const double margin_scale = get_option(parser,"crop-size",2.5); // cropped image will be this times wider than the object. const unsigned long min_object_size = get_option(parser,"min-object-size",1); const bool one_object_per_image = parser.option("one-object-per-image"); dlib::image_dataset_metadata::dataset data, resampled_data; std::ostringstream sout; sout << "\nThe --resample parameters which generated this dataset were:" << endl; sout << " cropped-object-size: "<< obj_size << endl; sout << " crop-size: "<< margin_scale << endl; sout << " min-object-size: "<< min_object_size << endl; if (one_object_per_image) sout << " one_object_per_image: true" << endl; resampled_data.comment = data.comment + sout.str(); resampled_data.name = data.name + " RESAMPLED"; load_image_dataset_metadata(data, parser[0]); locally_change_current_dir chdir(get_parent_directory(file(parser[0]))); dlib::rand rnd; const size_t image_size = std::round(std::sqrt(obj_size*margin_scale*margin_scale)); const chip_dims cdims(image_size, image_size); console_progress_indicator pbar(data.images.size()); for (unsigned long i = 0; i < data.images.size(); ++i) { // don't even bother loading images that don't have objects. if (data.images[i].boxes.size() == 0) continue; pbar.print_status(i); array2d<rgb_pixel> img, chip; load_image(img, data.images[i].filename); // figure out what chips we want to take from this image for (unsigned long j = 0; j < data.images[i].boxes.size(); ++j) { const rectangle rect = data.images[i].boxes[j].rect; if (data.images[i].boxes[j].ignore || rect.area() < min_object_size) continue; const auto max_dim = std::max(rect.width(), rect.height()); const double rand_scale_perturb = 1 - 0.3*(rnd.get_random_double()-0.5); const rectangle crop_rect = centered_rect(rect, max_dim*margin_scale*rand_scale_perturb, max_dim*margin_scale*rand_scale_perturb); const rectangle_transform tform = get_mapping_to_chip(chip_details(crop_rect, cdims)); extract_image_chip(img, chip_details(crop_rect, cdims), chip); image_dataset_metadata::image dimg; // Now transform the boxes to the crop and also mark them as ignored if they // have already been cropped out or are outside the crop. for (size_t k = 0; k < data.images[i].boxes.size(); ++k) { image_dataset_metadata::box box = data.images[i].boxes[k]; // ignore boxes outside the cropped image if (crop_rect.intersect(box.rect).area() == 0) continue; // mark boxes we include in the crop as ignored. Also mark boxes that // aren't totally within the crop as ignored. if (crop_rect.contains(grow_rect(box.rect,10)) && (!one_object_per_image || k==j)) data.images[i].boxes[k].ignore = true; else box.ignore = true; if (box.rect.area() < min_object_size) box.ignore = true; box.rect = tform(box.rect); for (auto&& p : box.parts) p.second = tform.get_tform()(p.second); dimg.boxes.push_back(box); } // Put a 64bit hash of the image data into the name to make sure there are no // file name conflicts. std::ostringstream sout; sout << hex << murmur_hash3_128bit(&chip[0][0], chip.size()*sizeof(chip[0][0])).second; dimg.filename = data.images[i].filename + "_RESAMPLED_"+sout.str()+".png"; if (parser.option("jpg")) { dimg.filename = to_jpg_name(dimg.filename); save_jpeg(chip,dimg.filename, JPEG_QUALITY); } else { save_png(chip,dimg.filename); } resampled_data.images.push_back(dimg); } } save_image_dataset_metadata(resampled_data, parser[0] + ".RESAMPLED.xml"); return EXIT_SUCCESS; }