Skip to content

Commit

Permalink
Add Bounding Box Crop Function
Browse files Browse the repository at this point in the history
  • Loading branch information
ppmzhang2 committed Aug 25, 2024
1 parent 07caf5e commit 414d79f
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -fuse-ld=lld")
set(SOURCES
"${FusswegDatentools_SOURCE_DIR}/src/main.cpp"
"${FusswegDatentools_SOURCE_DIR}/src/annot.cpp"
"${FusswegDatentools_SOURCE_DIR}/src/img.cpp"
"${FusswegDatentools_SOURCE_DIR}/src/via.cpp"
"${FusswegDatentools_SOURCE_DIR}/src/crs.cpp"
"${FusswegDatentools_SOURCE_DIR}/src/cv.cpp"
Expand All @@ -34,6 +35,7 @@ set(SOURCES
)
set(HEADERS
"${FusswegDatentools_SOURCE_DIR}/include/annot.hpp"
"${FusswegDatentools_SOURCE_DIR}/include/img.hpp"
"${FusswegDatentools_SOURCE_DIR}/include/via.hpp"
"${FusswegDatentools_SOURCE_DIR}/include/crs.hpp"
"${FusswegDatentools_SOURCE_DIR}/include/cv.hpp"
Expand Down
14 changes: 14 additions & 0 deletions include/img.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string>

namespace fdt {

namespace img {

void bboxCrop(const std::string &, const std::string &,
const std::string &, const int, const int);

} // namespace img

} // namespace fdt
1 change: 1 addition & 0 deletions include/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#define MAX2(x, y) ((x) > (y) ? (x) : (y))
#define MIN2(x, y) ((x) < (y) ? (x) : (y))

typedef std::vector<std::string> Paths;

Expand Down
81 changes: 81 additions & 0 deletions src/img.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "img.hpp"
#include "csv.hpp"
#include "utils.hpp"
#include <future>
#include <iostream>
#include <opencv2/opencv.hpp>

// Function to process each record and save the bounding box as an image
// Handles one TSV file at a time
void crop_bbox(const std::string &root_dir, const std::string &tsv_file,
const std::string &output_dir, const int width,
const int height) {
// Create a TSV reader
csv::CSVFormat format;
format.delimiter('\t').header_row(0);
csv::CSVReader reader(tsv_file, format);

// Iterate over each row
for (auto &row : reader) {
const auto prefix = row["prefix"].get<std::string>();
const auto image_name = row["image"].get<std::string>();
const auto category = row["cate"].get<std::string>();
const auto level = row["level"].get<std::string>();

// Assertion:
// - x = max(0, x); y = max(0, y)
// - w = min(w, width - x); h = min(h, height - y)
const int x = MAX2(0, row["x"].get<int>());
const int y = MAX2(0, row["y"].get<int>());
const int w = MIN2(row["w"].get<int>(), width - x);
const int h = MIN2(row["h"].get<int>(), height - y);

// Load the image with std::filesystem by combining the root dir,
// prefix, and image name
std::string image_path =
std::filesystem::path(root_dir) / prefix / image_name;

cv::Mat image = cv::imread(image_path);
// NOTE: cv::imread() silently returns an empty matrix if it fails to
// load the image, rendering try-catch blocks useless
if (image.empty()) {
std::cerr << "Could not open or find the image: " << image_path
<< std::endl;
continue;
}

// Extract the bounding box
cv::Rect bounding_box(x, y, w, h);
cv::Mat cropped_image = image(bounding_box);

// Create the output image name
std::string output_name =
"_" + category + "_" + level + "_" + prefix + "_" + image_name;

// Save the cropped image
std::string output_path = output_dir + "/" + output_name;
imwrite(output_path, cropped_image);
}
}

// Crop the bounding box and save the image.
// TSV files in `tsv_dir` will all be read first and combined, before being
// shuffled and split to different threads, each of which will then process
// the images and save them to the output directory.
void fdt::img::bboxCrop(const std::string &root_dir, const std::string &tsv_dir,
const std::string &output_path, const int width,
const int height) {
// Vector to hold future objects
std::vector<std::future<void>> futures;

// Launch a thread for each TSV file
for (const auto &f : fdt::utils::listAllFiles(tsv_dir, ".tsv")) {
futures.push_back(std::async(std::launch::async, crop_bbox, root_dir, f,
output_path, width, height));
}

// Wait for all threads to finish
for (auto &f : futures) {
f.get();
}
}
22 changes: 18 additions & 4 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "cv.hpp"
#include "exif.hpp"
#include "gis.hpp"
#include "img.hpp"
#include "utils.hpp"
#include "via.hpp"

Expand All @@ -30,6 +31,9 @@ int parse_args(int argc, char *argv[]) {
<< "<label_dir>" << std::endl;
std::cout << " " << argv[0] << " annot-to-coco "
<< "<annot_dir> <exif_dir> <output_file>" << std::endl;
std::cout << " " << argv[0] << " crop-bbox "
<< "<root_dir> <tsv_dir> <output_dir> <width> <height>"
<< std::endl;
std::cout << " " << argv[0] << " bbox-draw "
<< "<label_dir> <src_dir> <dst_dir> <ext>" << std::endl;
std::cout << " " << argv[0] << " crs-to-nzgd2000 "
Expand All @@ -55,9 +59,9 @@ int parse_args(int argc, char *argv[]) {
if (op != "exif-export-json" && op != "exif-export-csv" &&
op != "displacement" && op != "via-export" && op != "via-print-stats" &&
op != "via-export-stats" && op != "annot-to-coco" &&
op != "bbox-draw" && op != "pov-roi" && op != "pov-transform" &&
op != "crs-to-nzgd2000" && op != "crs-from-nzgd2000" &&
op != "geojson-to-tsv") {
op != "crop-bbox" && op != "bbox-draw" && op != "pov-roi" &&
op != "pov-transform" && op != "crs-to-nzgd2000" &&
op != "crs-from-nzgd2000" && op != "geojson-to-tsv") {
throw std::runtime_error("Unknown operation. ");
}
if ((op == "exif-export-json" && argc != 4) ||
Expand All @@ -67,7 +71,7 @@ int parse_args(int argc, char *argv[]) {
(op == "via-export-stats" && argc != 4) ||
(op == "via-print-stats" && argc != 3) ||
(op == "annot-to-coco" && argc != 5) ||
(op == "bbox-draw" && argc != 6) ||
(op == "crop-bbox" && argc != 7) || (op == "bbox-draw" && argc != 6) ||
(op == "geojson-to-tsv" && argc != 4) ||
(op == "crs-to-nzgd2000" && argc != 4) ||
(op == "crs-from-nzgd2000" && argc != 4) ||
Expand Down Expand Up @@ -132,6 +136,16 @@ int parse_args(int argc, char *argv[]) {
fdt::via::printStats(dir_lab);
return 0;
}
if (op == "crop-bbox") {
std::string root_dir = argv[2];
std::string tsv_dir = argv[3];
std::string out_dir = argv[4];
int width = std::strtol(argv[5], nullptr, 10);
int height = std::strtol(argv[6], nullptr, 10);
fdt::img::bboxCrop(root_dir, tsv_dir, out_dir, width, height);
return 0;
}

if (op == "annot-to-coco") {
std::string dir_annot = argv[2];
std::string dir_exif = argv[3];
Expand Down

0 comments on commit 414d79f

Please sign in to comment.