#include #include #include #include #include #include #include #include #include #include #include #include "yolo.h" #include "log.h" #include "options.h" #include "utils.h" #include "intelligentroi.h" #include "seamcarving.h" #include "facerecognizer.h" const Yolo::Detection* pointInDetectionHoriz(int x, const std::vector& detections, const Yolo::Detection* ignore = nullptr) { const Yolo::Detection* inDetection = nullptr; for(const Yolo::Detection& detection : detections) { if(ignore && ignore == &detection) continue; if(detection.box.x <= x && detection.box.x+detection.box.width >= x) { if(!inDetection || detection.box.br().x > inDetection->box.br().x) inDetection = &detection; } } return inDetection; } bool findRegionEndpointHoriz(int& x, const std::vector& detections, int imgSizeX) { const Yolo::Detection* inDetection = pointInDetectionHoriz(x, detections); Log(Log::DEBUG, false)<<__func__<<" point "< x) { if(closest == nullptr || detection.box.x-x > closest->box.x-x) closest = &detection; } } if(closest) x = closest->box.x; else x = imgSizeX; Log(Log::DEBUG)<<" is not in any box and will be moved to "<className : "null")<<") is"; return false; } else { x = inDetection->box.br().x; Log(Log::DEBUG, false)<<" is in a box and will be moved to its end "<box.br().x > x) { Log(Log::DEBUG)<<"it is again in a box"; return findRegionEndpointHoriz(x, detections, imgSizeX); } else { Log(Log::DEBUG)<<"it is not in a box"; return true; } } } std::vector> cutImageIntoHorzRegions(cv::Mat& image, const std::vector& detections) { std::vector> out; std::cout<<__func__<<' '<>& slices) { assert(!slices.empty()); int cols = 0; for(const std::pair& slice : slices) cols += slice.first.cols; cv::Mat image(cols, slices[0].first.rows, slices[0].first.type()); Log(Log::DEBUG)<<__func__<<' '<& slice : slices) { cv::Rect rect(col, 0, slice.first.cols, slice.first.rows); Log(Log::DEBUG)<<__func__<<' '< detections, double targetAspectRatio = 1.0) { detections.erase(std::remove_if(detections.begin(), detections.end(), [](const Yolo::Detection& detection){return detection.priority < 3;}), detections.end()); double aspectRatio = image.cols/static_cast(image.rows); Log(Log::DEBUG)<<"Image size "< targetAspectRatio) vertical = true; int requiredLines = 0; if(!vertical) requiredLines = image.rows*targetAspectRatio - image.cols; else requiredLines = image.cols/targetAspectRatio - image.rows; Log(Log::DEBUG)<<__func__<<' '<> slices = cutImageIntoHorzRegions(image, detections); Log(Log::DEBUG)<<"Image has "<& slice : slices) { Log(Log::DEBUG)<<"a "<<(slice.second ? "frozen" : "unfrozen")<<" slice of size "< seamsForSlice(slices.size(), 0); for(size_t i = 0; i < slices.size(); ++i) { if(!slices[i].second) seamsForSlice[i] = (static_cast(slices[i].first.cols)/totalResizableSize)*requiredLines; } int residual = requiredLines - std::accumulate(seamsForSlice.begin(), seamsForSlice.end(), decltype(seamsForSlice)::value_type(0));; for(ssize_t i = slices.size()-1; i >= 0; --i) { if(!slices[i].second) { seamsForSlice[i] += residual; break; } } for(size_t i = 0; i < slices.size(); ++i) { if(seamsForSlice[i] != 0) { bool ret = SeamCarving::strechImage(slices[i].first, seamsForSlice[i], true); if(!ret) { if(vertical) transpose(image, image); return false; } } } image = assembleFromSlicesHoriz(slices); if(vertical) cv::transpose(image, image); return true; } void drawDebugInfo(cv::Mat &image, const cv::Rect& rect, const std::vector& detections) { for(const Yolo::Detection& detection : detections) { cv::rectangle(image, detection.box, detection.color, 3); std::string label = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4) + ' ' + std::to_string(detection.priority); cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_DUPLEX, 1, 1, 0); cv::Rect textBox(detection.box.x, detection.box.y - 40, labelSize.width + 10, labelSize.height + 20); cv::rectangle(image, textBox, detection.color, cv::FILLED); cv::putText(image, label, cv::Point(detection.box.x + 5, detection.box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 1, 0); } cv::rectangle(image, rect, cv::Scalar(0, 0, 255), 8); } static void reduceSize(cv::Mat& image, const cv::Size& targetSize) { int longTargetSize = std::max(targetSize.width, targetSize.height)*2; if(std::max(image.cols, image.rows) > longTargetSize) { if(image.cols > image.rows) { double ratio = static_cast(longTargetSize)/image.cols; cv::resize(image, image, {longTargetSize, static_cast(image.rows*ratio)}, 0, 0, cv::INTER_CUBIC); } else { double ratio = static_cast(longTargetSize)/image.rows; cv::resize(image, image, {static_cast(image.cols*ratio), longTargetSize}, 0, 0, cv::INTER_CUBIC); } } } void pipeline(const std::filesystem::path& path, const Config& config, Yolo& yolo, FaceRecognizer* recognizer, std::mutex& reconizerMutex, const std::filesystem::path& debugOutputPath) { InteligentRoi intRoi(yolo); cv::Mat image = cv::imread(path); if(!image.data) { Log(Log::WARN)<<"could not load image "< detections = yolo.runInference(image); Log(Log::DEBUG)<<"Got "< match = recognizer->isMatch(person); reconizerMutex.unlock(); if(match.first >= 0) { detection.priority += 10; hasmatch = true; } } Log(Log::DEBUG)<& images, const Config& config, FaceRecognizer* recognizer, std::mutex& reconizerMutex, const std::filesystem::path& debugOutputPath) { Yolo yolo(config.modelPath, {640, 480}, config.classesPath, false); for(std::filesystem::path path : images) pipeline(path, config, yolo, recognizer, reconizerMutex, debugOutputPath); } template std::vector> splitVector(const std::vector& vec, size_t parts) { std::vector> out; size_t length = vec.size()/parts; size_t remain = vec.size() % parts; size_t begin = 0; size_t end = 0; for (size_t i = 0; i < std::min(parts, vec.size()); ++i) { end += (remain > 0) ? (length + !!(remain--)) : length; out.push_back(std::vector(vec.begin() + begin, vec.begin() + end)); begin = end; } return out; } int main(int argc, char* argv[]) { Log::level = Log::INFO; Config config; argp_parse(&argp, argc, argv, 0, 0, &config); if(config.outputDir.empty()) { Log(Log::ERROR)<<"a output path \"-o\" is required"; return 1; } if(config.imagePaths.empty()) { Log(Log::ERROR)<<"at least one input image or directory is required"; return 1; } std::vector imagePaths; for(const std::filesystem::path& path : config.imagePaths) getImageFiles(path, imagePaths); Log(Log::DEBUG)<<"Images:"; for(const::std::filesystem::path& path: imagePaths) Log(Log::DEBUG)<addReferances({personImage}); recognizer->setThreshold(config.threshold); } std::vector threads; std::vector> imagePathParts = splitVector(imagePaths, std::thread::hardware_concurrency()); for(size_t i = 0; i < std::thread::hardware_concurrency(); ++i) threads.push_back(std::thread(threadFn, imagePathParts[i], std::ref(config), recognizer, std::ref(recognizerMutex), std::ref(debugOutputPath))); for(std::thread& thread : threads) thread.join(); return 0; }