paralleization wip

This commit is contained in:
2023-06-30 00:48:56 +02:00
parent f5dad284e6
commit b3c2d585ae
5 changed files with 230 additions and 237 deletions

367
main.cpp
View File

@ -1,9 +1,13 @@
#include <filesystem>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/core/types.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <algorithm>
#include <execution>
#include <vector>
#include <numeric>
#include "yolo.h"
#include "log.h"
@ -17,10 +21,10 @@ const Yolo::Detection* pointInDetectionHoriz(int x, const std::vector<Yolo::Dete
const Yolo::Detection* inDetection = nullptr;
for(const Yolo::Detection& detection : detections)
{
if(!ignore || ignore != &detection)
if(ignore && ignore == &detection)
continue;
if(detection.box.x <= x && detection.box.x+detection.box.width <= x)
if(detection.box.x <= x && detection.box.x+detection.box.width >= x)
{
if(!inDetection || detection.box.br().x > inDetection->box.br().x)
inDetection = &detection;
@ -33,6 +37,8 @@ bool findRegionEndpointHoriz(int& x, const std::vector<Yolo::Detection>& detecti
{
const Yolo::Detection* inDetection = pointInDetectionHoriz(x, detections);
Log(Log::DEBUG, false)<<__func__<<" point "<<x;
if(!inDetection)
{
const Yolo::Detection* closest = nullptr;
@ -48,16 +54,25 @@ bool findRegionEndpointHoriz(int& x, const std::vector<Yolo::Detection>& detecti
x = closest->box.x;
else
x = imgSizeX;
Log(Log::DEBUG)<<" is not in any box and will be moved to "<<x<<" where the closest box ("<<(closest ? closest->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 "<<x<<" where ";
const Yolo::Detection* candidateDetection = pointInDetectionHoriz(x, detections, inDetection);
if(candidateDetection && candidateDetection->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;
}
}
}
@ -65,106 +80,25 @@ std::vector<std::pair<cv::Mat, bool>> cutImageIntoHorzRegions(cv::Mat& image, co
{
std::vector<std::pair<cv::Mat, bool>> out;
std::cout<<__func__<<' '<<image.cols<<'x'<<image.rows<<std::endl;
for(int x = 0; x < image.cols; ++x)
{
int start = x;
bool frozen = findRegionEndpointHoriz(x, detections, image.cols);
cv::Mat slice = image(cv::Rect(start, 0, x-start, image.rows));
int width = x-start;
if(x < image.cols)
++width;
cv::Rect rect(start, 0, width, image.rows);
Log(Log::DEBUG)<<__func__<<" region\t"<<rect;
cv::Mat slice = image(rect);
out.push_back({slice, frozen});
}
return out;
}
const Yolo::Detection* pointInDetectionVert(int y, const std::vector<Yolo::Detection>& 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.y <= y && detection.box.y+detection.box.height <= y)
{
if(!inDetection || detection.box.br().y > inDetection->box.br().y)
inDetection = &detection;
}
}
return inDetection;
}
bool findRegionEndpointVert(int& y, const std::vector<Yolo::Detection>& detections, int imgSizeY)
{
const Yolo::Detection* inDetection = pointInDetectionVert(y, detections);
if(!inDetection)
{
const Yolo::Detection* closest = nullptr;
for(const Yolo::Detection& detection : detections)
{
if(detection.box.y > y)
{
if(closest == nullptr || detection.box.y-y > closest->box.y-y)
closest = &detection;
}
}
if(closest)
y = closest->box.y;
else
y = imgSizeY;
return false;
}
else
{
y = inDetection->box.br().y;
const Yolo::Detection* candidateDetection = pointInDetectionVert(y, detections, inDetection);
if(candidateDetection && candidateDetection->box.br().y > y)
return findRegionEndpointVert(y, detections, imgSizeY);
else
return true;
}
}
std::vector<std::pair<cv::Mat, bool>> cutImageIntoVertRegions(cv::Mat& image, const std::vector<Yolo::Detection>& detections)
{
std::vector<std::pair<cv::Mat, bool>> out;
for(int y = 0; y < image.rows; ++y)
{
int start = y;
bool frozen = findRegionEndpointVert(y, detections, image.rows);
cv::Mat slice = image(cv::Rect(0, start, image.cols, y-start));
out.push_back({slice, frozen});
}
return out;
}
cv::Mat assembleFromSlicesVert(const std::vector<std::pair<cv::Mat, bool>>& slices)
{
assert(!slices.empty());
int rows = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
rows += slice.first.rows;
cv::Mat image(slices[0].first.cols, rows, slices[0].first.type());
Log(Log::DEBUG)<<__func__<<" assembled image size "<<image.size;
int row = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
{
cv::Rect rect(0, row, slice.first.cols, slice.first.rows);
slice.first.copyTo(image(rect));
row += slice.first.rows;
}
return image;
}
cv::Mat assembleFromSlicesHoriz(const std::vector<std::pair<cv::Mat, bool>>& slices)
{
assert(!slices.empty());
@ -173,19 +107,33 @@ cv::Mat assembleFromSlicesHoriz(const std::vector<std::pair<cv::Mat, bool>>& sli
for(const std::pair<cv::Mat, bool>& slice : slices)
cols += slice.first.cols;
cv::Mat image(cols, slices[0].first.rows, slices[0].first.type());
Log(Log::DEBUG)<<__func__<<' '<<image.size()<<' '<<cols<<' '<<slices[0].first.rows;
int col = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
{
cv::Rect rect(col, 0, slice.first.cols, slice.first.rows);
Log(Log::DEBUG)<<__func__<<' '<<rect;
slice.first.copyTo(image(rect));
col += slice.first.cols;
col += slice.first.cols-1;
}
return image;
}
void transposeRect(cv::Rect& rect)
{
int x = rect.x;
rect.x = rect.y;
rect.y = x;
int width = rect.width;
rect.width = rect.height;
rect.height = width;
}
bool seamCarveResize(cv::Mat& image, std::vector<Yolo::Detection> detections, double targetAspectRatio = 1.0)
{
detections.erase(std::remove_if(detections.begin(), detections.end(), [](const Yolo::Detection& detection){return detection.priority < 3;}), detections.end());
@ -206,66 +154,67 @@ bool seamCarveResize(cv::Mat& image, std::vector<Yolo::Detection> detections, do
Log(Log::DEBUG)<<__func__<<' '<<requiredLines<<" lines are required in "<<(vertical ? "vertical" : "horizontal")<<" direction";
if(!vertical)
if(vertical)
{
std::vector<std::pair<cv::Mat, bool>> slices = cutImageIntoHorzRegions(image, detections);
Log(Log::DEBUG)<<"Image has "<<slices.size()<<" slices";
int totalResizableSize = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
{
if(slice.second)
totalResizableSize += slice.first.cols;
}
cv::transpose(image, image);
for(Yolo::Detection& detection : detections)
transposeRect(detection.box);
}
if(totalResizableSize < requiredLines/10)
{
Log(Log::WARN)<<"Unable to seam carve as there are only "<<totalResizableSize<<" unfrozen cols";
return false;
}
std::vector<std::pair<cv::Mat, bool>> slices = cutImageIntoHorzRegions(image, detections);
Log(Log::DEBUG)<<"Image has "<<slices.size()<<" slices:";
int totalResizableSize = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
{
Log(Log::DEBUG)<<"a "<<(slice.second ? "frozen" : "unfrozen")<<" slice of size "<<slice.first.cols;
if(!slice.second)
totalResizableSize += slice.first.cols;
}
for(size_t i = 0; i < slices.size(); ++i)
if(totalResizableSize < requiredLines+1)
{
Log(Log::WARN)<<"Unable to seam carve as there are only "<<totalResizableSize<<" unfrozen cols";
if(vertical)
cv::transpose(image, image);
return false;
}
std::vector<int> seamsForSlice(slices.size(), 0);
for(size_t i = 0; i < slices.size(); ++i)
{
if(!slices[i].second)
seamsForSlice[i] = (static_cast<double>(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)
{
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)
{
int seamsForSlice = (static_cast<double>(slices[i].first.cols)/totalResizableSize)*requiredLines;
bool ret = SeamCarving::strechImage(image, seamsForSlice, true);
if(!ret)
return false;
if(vertical)
transpose(image, image);
return false;
}
}
image = assembleFromSlicesHoriz(slices);
}
else
{
std::vector<std::pair<cv::Mat, bool>> slices = cutImageIntoVertRegions(image, detections);
Log(Log::DEBUG)<<"Image has "<<slices.size()<<" slices";
int totalResizableSize = 0;
for(const std::pair<cv::Mat, bool>& slice : slices)
{
if(slice.second)
totalResizableSize += slice.first.rows;
}
if(totalResizableSize < requiredLines/10)
{
Log(Log::WARN)<<"Unable to seam carve as there are only "<<totalResizableSize<<" unfrozen rows";
return false;
}
image = assembleFromSlicesHoriz(slices);
for(size_t i = 0; i < slices.size(); ++i)
{
if(slices[i].second)
{
int seamsForSlice = (static_cast<double>(slices[i].first.rows)/totalResizableSize)*requiredLines;
bool ret = SeamCarving::strechImageVert(image, seamsForSlice, true);
if(!ret)
return false;
}
}
if(vertical)
cv::transpose(image, image);
image = assembleFromSlicesVert(slices);
}
return true;
}
@ -284,6 +233,85 @@ void drawDebugInfo(cv::Mat &image, const cv::Rect& rect, const std::vector<Yolo:
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<double>(longTargetSize)/image.cols;
cv::resize(image, image, {longTargetSize, static_cast<int>(image.rows*ratio)}, 0, 0, cv::INTER_CUBIC);
}
else
{
double ratio = static_cast<double>(longTargetSize)/image.rows;
cv::resize(image, image, {static_cast<int>(image.cols*ratio), longTargetSize}, 0, 0, cv::INTER_CUBIC);
}
}
}
void pipeline(const std::filesystem::path& path, const Config& config, Yolo& yolo, const std::filesystem::path& debugOutputPath)
{
InteligentRoi intRoi(yolo);
cv::Mat image = cv::imread(path);
if(!image.data)
{
Log(Log::WARN)<<"could not load image "<<path<<" skipping";
return;
}
reduceSize(image, config.targetSize);
std::vector<Yolo::Detection> detections = yolo.runInference(image);
Log(Log::DEBUG)<<"Got "<<detections.size()<<" detections for "<<path;
for(const Yolo::Detection& detection : detections)
Log(Log::DEBUG)<<detection.class_id<<": "<<detection.className<<" at "<<detection.box<<" with prio "<<detection.priority;
cv::Rect crop;
bool incompleate = intRoi.getCropRectangle(crop, detections, image.size());
if(config.seamCarving && incompleate)
{
bool ret = seamCarveResize(image, detections, config.targetSize.aspectRatio());
if(ret && image.size().aspectRatio() != config.targetSize.aspectRatio())
detections = yolo.runInference(image);
}
cv::Mat croppedImage;
if(image.size().aspectRatio() != config.targetSize.aspectRatio() && incompleate)
{
intRoi.getCropRectangle(crop, detections, image.size());
if(config.debug)
{
cv::Mat debugImage = image.clone();
drawDebugInfo(debugImage, crop, detections);
bool ret = cv::imwrite(debugOutputPath/path.filename(), debugImage);
if(!ret)
Log(Log::WARN)<<"could not save debug image to "<<debugOutputPath/path.filename()<<" skipping";
}
croppedImage = image(crop);
}
else if(!incompleate)
{
croppedImage = image(crop);
}
else
{
croppedImage = image;
}
cv::Mat resizedImage;
cv::resize(croppedImage, resizedImage, {512, 512}, 0, 0, cv::INTER_CUBIC);
bool ret = cv::imwrite(config.outputDir/path.filename(), resizedImage);
if(!ret)
Log(Log::WARN)<<"could not save image to "<<config.outputDir/path.filename()<<" skipping";
}
int main(int argc, char* argv[])
{
Log::level = Log::INFO;
@ -319,7 +347,6 @@ int main(int argc, char* argv[])
}
Yolo yolo(config.modelPath, {640, 480}, config.classesPath, false);
InteligentRoi intRoi(yolo);
if(!std::filesystem::exists(config.outputDir))
{
@ -337,60 +364,8 @@ int main(int argc, char* argv[])
std::filesystem::create_directory(debugOutputPath);
}
for(const std::filesystem::path& path : imagePaths)
{
cv::Mat image = cv::imread(path);
if(!image.data)
{
Log(Log::WARN)<<"could not load image "<<path<<" skipping";
continue;
}
std::for_each(std::execution::parallel_unsequenced_policy(),
imagePaths.begin(), imagePaths.end(), [&yolo, &debugOutputPath, &config](const std::filesystem::path& path){pipeline(path, config, yolo, debugOutputPath);});
if(std::max(image.cols, image.rows) > 1024)
{
Log(Log::DEBUG, false)<<"Image is "<<image.size();
if(image.cols > image.rows)
{
double ratio = 1024.0/image.cols;
cv::resize(image, image, {1024, static_cast<int>(image.rows*ratio)}, 0, 0, cv::INTER_CUBIC);
}
else
{
double ratio = 1024.0/image.rows;
cv::resize(image, image, {static_cast<int>(image.cols*ratio), 1024}, 0, 0, cv::INTER_CUBIC);
}
Log(Log::DEBUG)<<" resized to "<<image.size();
}
std::vector<Yolo::Detection> detections = yolo.runInference(image);
Log(Log::DEBUG)<<"Got "<<detections.size()<<" detections for "<<path;
for(const Yolo::Detection& detection : detections)
Log(Log::DEBUG)<<detection.class_id<<": "<<detection.className<<" at "<<detection.box<<" with prio "<<detection.priority;
if(config.seamCarving)
{
Log(Log::INFO)<<"Trying seam resize";
seamCarveResize(image, detections, 1);
}
cv::Rect crop = intRoi.getCropRectangle(detections, image.size());
if(config.debug)
{
cv::Mat debugImage = image.clone();
drawDebugInfo(debugImage, crop, detections);
bool ret = cv::imwrite(debugOutputPath/path.filename(), debugImage);
if(!ret)
Log(Log::WARN)<<"could not save debug image to "<<debugOutputPath/path.filename()<<" skipping";
}
cv::Mat croppedImage = image(crop);
cv::Mat resizedImage;
cv::resize(croppedImage, resizedImage, {512, 512}, 0, 0, cv::INTER_CUBIC);
bool ret = cv::imwrite(config.outputDir/path.filename(), resizedImage);
if(!ret)
Log(Log::WARN)<<"could not save image to "<<config.outputDir/path.filename()<<" skipping";
}
return 0;
}