diff --git a/src/argpopt.h b/src/argpopt.h index 1a40e30..7a1828e 100644 --- a/src/argpopt.h +++ b/src/argpopt.h @@ -35,12 +35,13 @@ struct Config bool quiet = false; bool interactive = false; bool simpleStich = false; + float kFactor = 0; }; const char *argp_program_version = "0.2"; const char *argp_program_bug_address = ""; static char doc[] = "Program to determine the lubricant thikness on a curved surface.\n\ -Possible operations: apply create curve mkcurve mkboard"; +Possible operations: apply create curve mkcurve mkboard show"; static char args_doc[] = "[OPERATION] IMAGE1 IMAGE2 ..."; static struct argp_option options[] = @@ -56,6 +57,7 @@ static struct argp_option options[] = {"interactive", 'i', 0, 0, "interactivly process multiple commands" }, {"simpe-stich", 'a', 0, 0, "Use non blending sticher" }, {"curve", 'c', "File Name", 0, "curve file name" }, +{"kfactor", 'k', "Value", 0, "set the kfactor" }, { 0 } }; @@ -78,7 +80,7 @@ error_t parse_opt (int key, char *arg, struct argp_state *state) config->norm.assign(arg); break; case 's': - config->minSize=atol(arg); + config->minSize=strtod(arg, nullptr); break; case 'r': config->harris = true; @@ -98,6 +100,9 @@ error_t parse_opt (int key, char *arg, struct argp_state *state) case 'c': config->curve.assign(arg); break; + case 'k': + config->kFactor=strtod(arg, nullptr); + break; case ARGP_KEY_ARG: config->commandsFiles = &state->argv[state->next-1]; state->next = state->argc; diff --git a/src/charuco.cpp b/src/charuco.cpp index 14f8b2b..cd58672 100644 --- a/src/charuco.cpp +++ b/src/charuco.cpp @@ -20,19 +20,76 @@ #include "uvosunwrap/charuco.h" #include #include - -static constexpr unsigned int X_BOARD_SIZE = 18; -static constexpr unsigned int Y_BOARD_SIZE = 10; - +#include "uvosunwrap/log.h" void createCharucoBoard(unsigned int size, const std::string& fileName) { cv::Ptr board = cv::aruco::CharucoBoard::create(X_BOARD_SIZE, Y_BOARD_SIZE, 0.03f, 0.02f, cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_250 )); - cv::Mat charucoImage; - board->draw(cv::Size((size*18)/10, size), charucoImage, 0, 1); - cv::imwrite(fileName, charucoImage); + cv::Mat charucoImage; + double cellSize = size/(double)Y_BOARD_SIZE; + board->draw(cv::Size((size*X_BOARD_SIZE)/Y_BOARD_SIZE, size), charucoImage, 0, 1); + cv::Mat charucoImageWithBorder = + cv::Mat::zeros(cv::Size(cellSize*(X_BOARD_SIZE+1), cellSize*(Y_BOARD_SIZE+2)), CV_8UC1); + cv::Rect roi(cellSize, cellSize, charucoImage.cols, charucoImage.rows); + charucoImage.copyTo(charucoImageWithBorder(roi)); + cv::imwrite(fileName, charucoImageWithBorder); +} + +static void seamAjust(std::vector& detections) +{ + int xMin = std::numeric_limits::max(); + int xMax = std::numeric_limits::min(); + + for(auto& point : detections) + { + if(point.coordinate.x > xMax) + xMax = point.coordinate.x; + else if(point.coordinate.x < xMin) + xMin = point.coordinate.x; + } + + if(xMax - xMin < static_cast(X_BOARD_SIZE/2)) + return; + + Log(Log::DEBUG)<<"Image contains seam"; + + int rightMostPoint = 0; + bool extantCol[X_BOARD_SIZE] = {0}; + for(auto& point : detections) + { + if(!extantCol[point.coordinate.x]) + extantCol[point.coordinate.x] = true; + if(point.coordinate.x > rightMostPoint) + rightMostPoint = point.coordinate.x; + } + + int leftCoordinate = 0; + int maxDeadrange = 0; + int deadRange = 0; + for(unsigned int i = 0; i < X_BOARD_SIZE-1; ++i) + { + if(!extantCol[i]) + { + ++deadRange; + if(extantCol[i+1] && maxDeadrange < deadRange) + { + leftCoordinate = i+1; + maxDeadrange = deadRange; + deadRange = 0; + } + } + } + + Log(Log::DEBUG)<<"Left coordinate before seam "< detectCharucoPoints(cv::Mat image, bool verbose) @@ -86,6 +143,8 @@ std::vector detectCharucoPoints(cv::Mat image, bool verbose) detections.push_back(DetectedPoint(charucoCorners[i], coordiante)); } + seamAjust(detections); + return detections; } return std::vector(); diff --git a/src/main.cpp b/src/main.cpp index 3ec1c41..e61d8bd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -42,6 +42,7 @@ enum { APPLY_MAP, APPLY_CURVE, CREATE_CURVE, + SHOW_IMAGE, EXIT }; @@ -59,6 +60,8 @@ int selectOperation(char** opt) return CREATE_CURVE; else if(strcmp(opt[0], "mkboard" ) == 0) return CREATE_CHARUCO; + else if(strcmp(opt[0], "show" ) == 0) + return SHOW_IMAGE; else if(strcmp(opt[0], "exit" ) == 0) return EXIT; return -1; @@ -92,7 +95,7 @@ cv::Mat openImageYml(char* fileName) cv::FileStorage matf(fileName, cv::FileStorage::READ); matf["image"]>>image; - if(matf.isOpened() && (!image.data || image.type() != CV_32FC1)) + if(matf.isOpened() && !image.data) { Log(Log::WARN)< loadImages(char** fileNames) cv::Mat tmpImage; const std::string str(fileNames[i]); if(str.find(".mat") != std::string::npos) - openImageYml(fileNames[i]); - else - tmpImage = openImageImg(fileNames[i]); + { + Log(Log::DEBUG)<<__func__<<": "< loadImages(char** fileNames) int perfromOperation(int operation, char** fileNames, const Config& config) { std::vector inImages; - if(operation == CREATE_MAP || operation == APPLY_MAP || operation == APPLY_CURVE) + if(operation == CREATE_MAP || operation == APPLY_MAP || operation == APPLY_CURVE || operation == SHOW_IMAGE) { inImages = loadImages(fileNames); @@ -143,7 +152,7 @@ int perfromOperation(int operation, char** fileNames, const Config& config) if(operation == CREATE_CHARUCO) { std::string fileName = config.output.empty() ? "out.png" : config.output; - createCharucoBoard(config.size*14, fileName); + createCharucoBoard(config.size*X_BOARD_SIZE, fileName); Log(Log::INFO)<<"Exported charuco map of size "<>curve; + fs["cal"]>>curve; if(!curve.data || curve.type() != CV_32FC1 || curve.rows != 2 || curve.cols < 3) { Log(Log::INFO)<<"invalid curve"; return -1; } - cvtColor(inImages[0], inImages[0], cv::COLOR_BGR2GRAY); + if(inImages[0].channels() > 1) + cvtColor(inImages[0], inImages[0], cv::COLOR_BGR2GRAY); + if(inImages[0].type() != CV_32FC1) inImages[0].convertTo(inImages[0], CV_32F); + Log(Log::DEBUG)<<"applyCurve"; applyCurve(inImages[0], curve); + cv::FileStorage fsO("out.mat", cv::FileStorage::WRITE); + fsO<<"image"<(y); - for(int x = 0; x < mat.cols; ++x) + for(int y = 0; y < mat.rows; y++) { - if(col[x] < 0 && col[x] > -2) + if(mat.at(y,x) < 0) { - if(y > 0 && mat.at(y-1,x) >= 0) + int closestA = -1; + int closestB = -1; + int dist = std::numeric_limits::max(); + for(int i = 0; i < mat.rows; i++) { - col[x] = mat.at(y-1,x); - finished = false; + if(i != closestA && mat.at(i,x) >= 0 && abs(i-y) <= dist) + { + closestB = closestA; + closestA = i; + dist = abs(i-y); + } } - else if(y < mat.rows-1 && mat.at(y+1,x) >= 0) + if(closestA < 0 || closestB < 0) { - col[x] = mat.at(y+1,x); - finished = false; + closestA = -1; + closestB = -1; + dist = std::numeric_limits::max(); + for(int i = mat.rows-1; i >= 0; --i) + { + if(i != closestA && mat.at(i,x) >= 0 && abs(i-y) <= dist) + { + closestB = closestA; + closestA = i; + dist = abs(i-y); + } + } } - if(col[x] > 0 && ((x+1 < mat.cols && col[x] > col[x+1]) || (x > 0 && col[x] < col[x-1]))) - col[x] = -2; + float slope = (mat.at(closestB,x) - mat.at(closestA,x))/(closestB-closestA); + mat.at(y,x) = mat.at(closestA,x) - (closestA-y)*slope; } } } - if(!finished) fillMissing(mat); } bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi) @@ -287,66 +301,25 @@ bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi) return true; } -bool deleteEmptyCols(cv::Mat& mat) -{ - assert(mat.type() == CV_32FC1); - - std::vector cols; - cols.reserve(mat.cols); - - for(int x = 0; x < mat.cols; x++) - { - bool empty = true; - for(int y = 0; y < mat.rows; y++) - { - if(mat.at(x,y) > 0) - { - empty = false; - break; - } - } - - if(!empty) - cols.push_back(x); - } - - if(mat.cols < static_cast(cols.size())) - { - cv::Mat tmp(cv::Size(cols.size(), mat.rows), CV_32FC1); - - for(auto& col : cols) - { - cv::Rect roi(cv::Point2i(col, 0), cv::Size(1, mat.rows)); - mat.copyTo(tmp(roi)); - } - mat.release(); - mat = tmp; - - return true; - } - - return false; -} - -void removeSparseCols(cv::Mat& mat, bool front) +void removeSparseCols(cv::Mat& mat, float reject, bool front) { assert(mat.type() == CV_32FC1); int count = 0; for(int y = 0; y < mat.rows; ++y) { - if(front && mat.at(y,0) >= 0) ++count; - else if(mat.at(y,mat.cols-1) >= 0) ++count; + if((front && mat.at(y,0) >= 0) || (!front && mat.at(y,mat.cols-1) >= 0)) + ++count; } cv::Rect roi; - bool rej = (count < mat.rows/2); + bool rej = (count < mat.rows*reject); roi.x=front ? rej : 0; roi.y=0; roi.width = mat.cols - rej; roi.height = mat.rows; mat = mat(roi); if(rej) - removeSparseCols(mat, front); + removeSparseCols(mat, reject, front); } static float linInterpolate(float A, float B, float x) diff --git a/src/matutils.h b/src/matutils.h index 3e96024..6020967 100644 --- a/src/matutils.h +++ b/src/matutils.h @@ -37,15 +37,13 @@ bool findClosest(size_t& index, const cv::Point2f point, const std::vector& in, std::vector& outliers, float criticalValue); -void interpolateMissing(cv::Mat& mat); +void interpolateMissingOnX(cv::Mat& mat); -void fillMissing(cv::Mat& mat); +void interpolateMissingOnY(cv::Mat& mat); bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi); -bool deleteEmptyCols(cv::Mat& mat); - -void removeSparseCols(cv::Mat& mat, bool front); +void removeSparseCols(cv::Mat& mat, float reject, bool front); bool bilinearResize(const cv::Mat& inImage, cv::Mat& outImage, cv::Size size); diff --git a/src/unwrap.cpp b/src/unwrap.cpp index 707ba5d..3b7b113 100644 --- a/src/unwrap.cpp +++ b/src/unwrap.cpp @@ -66,7 +66,7 @@ static void sortIntoRemapMaps(const std::vector& points, cv::Mat& xMat = -1; yMat = -1; - Log(Log::DEBUG)<<"Grid: "<(y); + const float* anglex = angleMat.ptr(y); + for(int x = 0; x < image.cols; x++) + { + int value = colx[x]*(1-(anglex[x]*kFactor)); + if(value < 0) + value = 0; + else if(value > 255) + value = 255; + colx[x] = value; + } + } +} + +static void generateAngleMats(const cv::Mat& xMat, const cv::Mat& yMat, cv::Mat& greatestAngle) +{ + std::cout<(y,x), yMat.at(y,x)); + cv::Point2f pB(xMat.at(y,x+1), yMat.at(y,x+1)); + cv::Point2f pC(xMat.at(y+1,x), yMat.at(y+1,x)); + double normX = cv::norm(pA-pB); + double normY = cv::norm(pA-pC); + + if(normX > max) + max = normX; + if(normY > max) + max = normY; + } + } + + for(int y = 0; y < xMat.rows-1; y++) + { + for(int x = 0; x < xMat.cols-1; x++) + { + cv::Point2f pA(xMat.at(y,x), yMat.at(y,x)); + cv::Point2f pB(xMat.at(y,x+1), yMat.at(y,x+1)); + cv::Point2f pC(xMat.at(y+1,x), yMat.at(y+1,x)); + double normX = cv::norm(pA-pB); + double normY = cv::norm(pA-pC); + + greatestAngle.at(y,x) = 1-(std::min(normX, normY)/max); + + if(y == 0) + Log(Log::DEBUG)<(y,x); + } + } +} + RemapedImage applyRemap(const cv::Mat& image, const RemapMap &map) { RemapedImage out; cv::Mat xMapResized; cv::Mat yMapResized; const cv::Size outputSize(map.outputCellSize*map.xMat.cols,map.outputCellSize*map.xMat.rows); + cv::Rect noBorderRoi(map.outputCellSize/2, map.outputCellSize/2, + outputSize.width-map.outputCellSize, outputSize.height-map.outputCellSize); + + generateAngleMats(map.xMat, map.yMat, out.angle); + cv::Mat angleResized; + cv::resize(out.angle, angleResized, outputSize, cv::INTER_LINEAR); + out.angle = angleResized(noBorderRoi); cv::resize(map.xMat, xMapResized, outputSize, cv::INTER_LINEAR); cv::resize(map.yMat, yMapResized, outputSize, cv::INTER_LINEAR); - cv::Rect noBorderRoi(map.outputCellSize/2, map.outputCellSize/2, - outputSize.width-map.outputCellSize, outputSize.height-map.outputCellSize); + cv::Mat xMapRed = xMapResized(noBorderRoi); cv::Mat yMapRed = yMapResized(noBorderRoi); cv::remap(image, out.image, xMapRed, yMapRed, cv::INTER_LINEAR); @@ -198,10 +261,10 @@ cv::Mat simpleStich(const std::vector& images) if(topLeft.y > image.origin.y) topLeft.y = image.origin.y; - Log(Log::DEBUG)<<"image: "<& images) cv::Mat stich(std::vector& images, bool seamAdjust) { + assert(images.size() > 0); + for(auto& image : images) assert(image.image.type() == CV_8UC3 || image.image.type() == CV_8UC1); + Log(Log::DEBUG)<<__func__<<" got "< #include "detectedpoint.h" +static constexpr unsigned int X_BOARD_SIZE = 20; +static constexpr unsigned int Y_BOARD_SIZE = 12; + void createCharucoBoard(unsigned int size, const std::string& fileName); std::vector detectCharucoPoints(cv::Mat image, bool verbose = true); diff --git a/src/uvosunwrap/unwrap.h b/src/uvosunwrap/unwrap.h index 0bcc383..185abbf 100644 --- a/src/uvosunwrap/unwrap.h +++ b/src/uvosunwrap/unwrap.h @@ -25,6 +25,7 @@ struct RemapedImage { cv::Mat image; + cv::Mat angle; cv::Point2i origin; }; @@ -44,5 +45,7 @@ bool createRemapMap(const cv::Mat& image, RemapMap& out, const std::vector& images, bool seamAdjust = false); cv::Mat simpleStich(const std::vector& images);