From 879cbc4a320520def609ca70854e33cf655381a1 Mon Sep 17 00:00:00 2001 From: Jacob Date: Sat, 21 Sep 2024 19:32:31 -0400 Subject: [PATCH] Initial. --- .gitignore | 7 +++++++ Makefile | 29 +++++++++++++++++++++++++++ src/bound.cpp | 23 +++++++++++++++++++++ src/img.cpp | 5 +++++ src/include/bound.hpp | 17 ++++++++++++++++ src/include/img.hpp | 10 ++++++++++ src/include/main.hpp | 15 ++++++++++++++ src/include/preprocess.hpp | 10 ++++++++++ src/include/yolo.hpp | 28 ++++++++++++++++++++++++++ src/main.cpp | 41 ++++++++++++++++++++++++++++++++++++++ src/preprocess.cpp | 7 +++++++ src/yolo.cpp | 21 +++++++++++++++++++ 12 files changed, 213 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/bound.cpp create mode 100644 src/img.cpp create mode 100644 src/include/bound.hpp create mode 100644 src/include/img.hpp create mode 100644 src/include/main.hpp create mode 100644 src/include/preprocess.hpp create mode 100644 src/include/yolo.hpp create mode 100644 src/main.cpp create mode 100644 src/preprocess.cpp create mode 100644 src/yolo.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..391bf78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.o +obj/ +*.out +.cache/ +dataset/ +testimgs/ +compile_commands.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6653eed --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +NAME = imgboxer + +CXX = g++ +CXXFLAGS = -Wall -std=c++11 `pkg-config --cflags opencv4` +LDFLAGS = `pkg-config --libs opencv4` + +SRC_DIR = src +OBJ_DIR = obj +TARGET = $(NAME).out + +SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp) +OBJ_FILES = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC_FILES)) + +all: $(TARGET) + +$(TARGET): $(OBJ_FILES) + @ echo -e "\x1b[32;1mLinking \x1b[0m\x1b[32m$(TARGET)\x1b[32;1m...\x1b[0m\x1b[37m $(CXX) -o $(TARGET) $(OBJ_FILES) $(LDFLAGS)\x1b[0m" + @ $(CXX) -o $(TARGET) $(OBJ_FILES) $(LDFLAGS) + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp $(SRC_DIR)/include/%.hpp + @ mkdir -p $(OBJ_DIR) + @ echo -e "\x1b[32;1mCompiling \x1b[0m\x1b[32m$<\x1b[32;1m... \x1b[0m\x1b[37m$(CXX) $(CXXFLAGS) -c $< -o $@\x1b[0m" + @ $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + @ echo -e "\x1b[32;1mCleaning up...\x1b[0m" + @ rm -rf $(OBJ_DIR) $(TARGET) + +.PHONY: all clean diff --git a/src/bound.cpp b/src/bound.cpp new file mode 100644 index 0000000..ab999cc --- /dev/null +++ b/src/bound.cpp @@ -0,0 +1,23 @@ +#include "include/bound.hpp" + +std::vector> bound_contours(cv::Mat img) { + std::vector> contours; + cv::findContours(img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); + + return contours; +} + +std::vector +bound_boxes(std::vector> contours) { + std::vector boxes;; + + for (size_t i = 0; i < contours.size(); i++) { + cv::Rect box = cv::boundingRect(contours[i]); + + if (box.width < BOUND_MIN_W && box.height < BOUND_MIN_H) continue; + + boxes.push_back(box); + } + + return boxes; +} diff --git a/src/img.cpp b/src/img.cpp new file mode 100644 index 0000000..cb644f1 --- /dev/null +++ b/src/img.cpp @@ -0,0 +1,5 @@ +#include "include/img.hpp" + +cv::Mat img_get(char* fpath) { + return cv::imread(fpath, cv::IMREAD_GRAYSCALE); +} diff --git a/src/include/bound.hpp b/src/include/bound.hpp new file mode 100644 index 0000000..31803cd --- /dev/null +++ b/src/include/bound.hpp @@ -0,0 +1,17 @@ +#ifndef BOUND_H +#define BOUND_H + +// Create bounding boxes and contours for images. + +#include + +#define BOUND_MIN_W 90 +#define BOUND_MIN_H BOUND_MIN_W + +// Return list of coutours. +std::vector> bound_contours(cv::Mat img); + +// Return list of boxes. +std::vector bound_boxes(std::vector> contours); + +#endif diff --git a/src/include/img.hpp b/src/include/img.hpp new file mode 100644 index 0000000..1458cca --- /dev/null +++ b/src/include/img.hpp @@ -0,0 +1,10 @@ +#ifndef IMG_H +#define IMG_H + +// Read in images. + +#include + +cv::Mat img_get(char* fpath); + +#endif diff --git a/src/include/main.hpp b/src/include/main.hpp new file mode 100644 index 0000000..ef68e3c --- /dev/null +++ b/src/include/main.hpp @@ -0,0 +1,15 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +#include + +#include "img.hpp" +#include "preprocess.hpp" +#include "bound.hpp" +#include "yolo.hpp" + +int main(int argc, char** argv); + +#endif diff --git a/src/include/preprocess.hpp b/src/include/preprocess.hpp new file mode 100644 index 0000000..dd2394b --- /dev/null +++ b/src/include/preprocess.hpp @@ -0,0 +1,10 @@ +#ifndef PREPROCESS_H +#define PREPROCESS_H + +// Pre-process images (apply filters, grayscale, &c.) + +#include + +cv::Mat preprocess_threshold(cv::Mat img); + +#endif diff --git a/src/include/yolo.hpp b/src/include/yolo.hpp new file mode 100644 index 0000000..dc57323 --- /dev/null +++ b/src/include/yolo.hpp @@ -0,0 +1,28 @@ +#ifndef YOLO_H +#define YOLO_H + +// Create the YOLOv8 image labels. + +#include +#include + +#include + +// Name to assign to all labels. +#define LABEL_NAME "tooth" + +typedef struct Label { + int confidence; + int xmin; + int ymin; + int xmax; + int ymax; +} label_t; + +label_t* yolo_mklabel(cv::Rect box); + +// Write labels to file from cv::Rect. +void yolo_write_labels(char* fname, std::vector boxes); + + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2b3550c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,41 @@ +#include "include/main.hpp" + +using namespace std; +using namespace cv; + +int main(int argc, char** argv) { + // Check for invalid invocation. + if (argc != 2) { + printf("%s IMAGE", argv[0]); + return -1; + } + + // Read in image. + cv::Mat img = img_get(argv[1]); + + // Check the image actually exists. + if (img.empty()) { + std::cout << "File not found: " << argv[1] << std::endl; + return -1; + } + + // Threshold the image (in case it's not already binary). + cv::Mat binimg = preprocess_threshold(img); + + vector> contours = bound_contours(binimg); + vector boxes = bound_boxes(contours); + + for (size_t i = 0; i < contours.size(); i++) { + rectangle(img, boxes[i], Scalar(255, 255, 255), 1); + drawContours(img, contours, (int)i, Scalar(255, 255, 255), 1, LINE_8); + } + + yolo_write_labels("asdf", boxes); + + // Show image with bounding boxes. + imshow("Bounding Boxes", img); + waitKey(0); + + return 0; +} + diff --git a/src/preprocess.cpp b/src/preprocess.cpp new file mode 100644 index 0000000..9e6297e --- /dev/null +++ b/src/preprocess.cpp @@ -0,0 +1,7 @@ +#include "include/preprocess.hpp" + +cv::Mat preprocess_threshold(cv::Mat img) { + cv::Mat binimg; + cv::threshold(img, binimg, 128, 255, cv::THRESH_BINARY); + return binimg; +} diff --git a/src/yolo.cpp b/src/yolo.cpp new file mode 100644 index 0000000..5c4e27e --- /dev/null +++ b/src/yolo.cpp @@ -0,0 +1,21 @@ +#include "include/yolo.hpp" + +label_t* yolo_mklabel(cv::Rect box) { + // why is c++ like this + label_t* label = (label_t*)malloc(sizeof(label_t)); + + label->confidence = 1; + label->xmin = box.x; + label->ymin = box.y; + label->xmax = box.x + box.width; + label->ymax = box.y + box.height; + + return label; +} + +void yolo_write_labels(char* fname, std::vector boxes) { + for (size_t i = 0; i < boxes.size(); i++) { + auto box = boxes[i]; + printf("%s 1 %d %d %d %d\n", LABEL_NAME, box.x, box.y, box.x + box.width, box.y + box.height); + } +}