Files
libuvosunwrap/src/main.cpp
2021-07-26 10:34:47 +02:00

445 lines
10 KiB
C++

/**
* Lubricant Detecter
* Copyright (C) 2021 Carl Klemm
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core/ocl.hpp>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <vector>
#include <string>
#include "argpopt.h"
#include "uvosunwrap/unwrap.h"
#include "uvosunwrap/bgremoval.h"
#include "uvosunwrap/normalize.h"
#include "uvosunwrap/log.h"
#include "uvosunwrap/charuco.h"
#include "uvosunwrap/harris.h"
#include "uvosunwrap/curve.h"
#define IMREAD_SIZE pow(2, 20)
enum {
CREATE_CHARUCO,
CREATE_MAP,
APPLY_MAP,
APPLY_CURVE,
CREATE_CURVE,
SHOW_IMAGE,
EXIT
};
int selectOperation(char** opt)
{
if(!opt || !opt[0])
return -1;
else if(strcmp(opt[0], "create" ) == 0)
return CREATE_MAP;
else if(strcmp(opt[0], "apply" ) == 0)
return APPLY_MAP;
else if(strcmp(opt[0], "curve" ) == 0)
return APPLY_CURVE;
else if(strcmp(opt[0], "mkcurve" ) == 0)
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;
}
cv::Mat openImageImg(char* fileName)
{
int fd = open(fileName, O_RDONLY);
if(fd < 0)
{
Log(Log::WARN)<<"could not open "<<fileName<< "ignoreing";
return cv::Mat();
}
size_t pos = 0;
std::vector<char> buffer(IMREAD_SIZE);
size_t count;
while((count = read(fd, buffer.data()+pos, IMREAD_SIZE)) > 0)
{
pos+=count;
buffer.resize(pos+IMREAD_SIZE);
Log(Log::WARN)<<pos<<" "<<IMREAD_SIZE;
}
close(fd);
return cv::imdecode(buffer, cv::IMREAD_UNCHANGED);
}
cv::Mat openImageYml(char* fileName)
{
cv::Mat image;
cv::FileStorage matf(fileName, cv::FileStorage::READ);
matf["image"]>>image;
if(matf.isOpened() && (!image.data || image.type() != CV_32FC1))
{
Log(Log::WARN)<<fileName<<" dose not contain a valid image";
matf.release();
return cv::Mat();
}
else if(!image.data)
{
Log(Log::WARN)<<"could not open "<<fileName<< "ignoreing";
matf.release();
return cv::Mat();
}
return image;
}
std::vector<cv::Mat> loadImages(char** fileNames)
{
std::vector<cv::Mat> images;
for(size_t i = 0; fileNames[i]; ++i)
{
cv::Mat tmpImage;
const std::string str(fileNames[i]);
if(str.find(".mat") != std::string::npos)
{
Log(Log::DEBUG)<<__func__<<": "<<fileNames[i]<<" as YAML image";
tmpImage = openImageYml(fileNames[i]);
}
else
{
Log(Log::DEBUG)<<__func__<<": "<<fileNames[i]<<" as png image";
tmpImage = openImageImg(fileNames[i]);
}
if(tmpImage.data)
images.push_back(tmpImage);
else
Log(Log::WARN)<<"can not read image "<<i<<" from "<<fileNames[i];
}
return images;
}
int perfromOperation(int operation, char** fileNames, const Config& config)
{
std::vector<cv::Mat> inImages;
if(operation == CREATE_MAP || operation == APPLY_MAP || operation == APPLY_CURVE || operation == SHOW_IMAGE)
{
inImages = loadImages(fileNames);
if(inImages.empty())
{
Log(Log::ERROR)<<"Input images must be provided";
return -1;
}
}
if(operation == CREATE_CHARUCO)
{
std::string fileName = config.output.empty() ? "out.png" : config.output;
createCharucoBoard(config.size*X_BOARD_SIZE, fileName);
Log(Log::INFO)<<"Exported charuco map of size "<<config.size*14<<" to "<<fileName;
return 0;
}
else if(operation == CREATE_MAP)
{
cv::Mat mask;
if(config.verbose)
{
cv::imshow( "Viewer", inImages[0] );
cv::waitKey(0);
}
if(!config.bg.empty())
{
cv::Mat bg = cv::imread(config.bg);
if(bg.data)
{
createMask(inImages[0], mask, bg);
if(config.verbose)
{
cv::Mat masked;
inImages[0].copyTo(masked, mask);
cv::imshow( "Viewer", masked );
cv::waitKey(0);
}
}
else Log(Log::WARN)<<"can not read background image from "<<config.bg;
}
std::vector<DetectedPoint> points;
if(config.harris)
points = harrisDetectPoints(inImages[0], mask);
else
points = detectCharucoPoints(inImages[0], config.verbose);
Log(Log::INFO)<<"Found "<<points.size()<<" points";
if(points.size() < 8)
{
Log(Log::ERROR)<<"Error creating map, insufficant points detected";
return -1;
}
RemapMap map;
map.outputCellSize = 50;
createRemapMap(inImages[0], map, points, config.verbose);
saveRemapMap(map, std::string(fileNames[0]) + ".mat");
}
else if(operation == APPLY_MAP)
{
std::vector<RemapedImage> remapedImages;
for(size_t i = 0; i<inImages.size(); ++i)
{
Log(Log::INFO)<<"Processing image "<<i;
RemapMap map = loadRemapMap(std::string(fileNames[i])+".mat");
map.outputCellSize = config.size;
if(!map.xMat.data)
{
Log(Log::ERROR)<<"could not load remap map from "<<std::string(fileNames[i])+".mat";
return -1;
}
RemapedImage norm;
if(!config.norm.empty())
{
cv::Mat tmp = cv::imread(config.norm);
if(!tmp.data)
{
Log(Log::WARN)<<"could not open normalize file "<<config.norm;
}
norm = applyRemap(tmp, map);
if(config.verbose)
{
cv::imshow("Viewer", norm.image);
cv::waitKey(0);
}
}
Log(Log::INFO)<<"Remaping image";
RemapedImage remaped = applyRemap(inImages[i], map);
Log(Log::INFO)<<"Normalizeing image";
if(norm.image.data) normalize(remaped.image, norm.image);
if(config.verbose)
{
cv::imshow( "Viewer", remaped.image );
cv::waitKey(0);
cv::imshow( "Viewer", remaped.angle);
cv::waitKey(0);
}
applyKfactor(remaped.image, remaped.angle, config.kFactor);
if(config.verbose)
{
cv::imshow("Viewer", remaped.image );
cv::waitKey(0);
}
remapedImages.push_back(remaped);
}
cv::Mat out;
Log(Log::INFO)<<"Stiching images";
if(config.simpleStich)
out = simpleStich(remapedImages);
else
out = stich(remapedImages, true);
if(config.verbose)
{
cv::imshow( "Viewer", out );
cv::waitKey(0);
}
cv::imwrite(!config.output.empty() ? config.output : "out.png", out);
}
else if(operation == APPLY_CURVE)
{
if(config.curve.empty())
{
Log(Log::INFO)<<"a curve must be supplied";
return -1;
}
cv::FileStorage fs(config.curve, cv::FileStorage::READ);
cv::Mat curve;
fs["cal"]>>curve;
if(!curve.data || curve.type() != CV_32FC1 || curve.rows != 2 || curve.cols < 3)
{
Log(Log::INFO)<<"invalid curve";
return -1;
}
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"<<inImages[0];
fsO.release();
}
else if(operation == CREATE_CURVE)
{
std::cout<<"how many coordinate pares are required?\n> ";
int num = 0;
std::cin>>num;
if(std::cin.fail())
{
std::cin.clear();
std::cout<<"invalid number";
return -1;
}
cv::Mat curve = cv::Mat::zeros(2, num, CV_32FC1);
float* keys = curve.ptr<float>(0);
float* values = curve.ptr<float>(1);
std::cout<<"Please type "<<num<<" coordinate pairs\n";
for(int i = 0; i < curve.cols; ++i)
{
std::cout<<i<<"> ";
double key;
double value;
std::cin>>key;
if(std::cin.fail())
{
std::cin.clear();
--i;
continue;
}
std::cin>>value;
if(std::cin.fail())
{
std::cin.clear();
--i;
continue;
}
keys[i] = key;
values[i] = value;
}
for(int i = 0; i < curve.cols; ++i)
std::cout<<keys[i]<<' '<<values[i]<<'\n';
cv::FileStorage fs(!config.output.empty() ? config.output : "curve.mat", cv::FileStorage::WRITE);
fs<<"cal"<<curve;
fs.release();
std::cout<<"Curve saved to "<<(!config.output.empty() ? config.output : "curve.mat")<<'\n';
}
else if(operation == SHOW_IMAGE)
{
cv::namedWindow("Show Image", cv::WINDOW_NORMAL );
for(size_t i = 0; i < inImages.size(); ++i)
{
cv::imshow("Show Image", inImages[i]);
cv::waitKey(0);
}
cv::destroyWindow("Show Image");
}
else if(operation == EXIT)
return -1;
return 0;
}
int main(int argc, char* argv[])
{
cv::ocl::setUseOpenCL(false);
Config config;
argp_parse(&argp, argc, argv, 0, 0, &config);
Log::level = config.quiet ? Log::WARN : config.verbose ? Log::DEBUG : Log::INFO;
Log(Log::INFO)<<"UVOS Optical lubricant thikness mapper "<<argp_program_version;
if(config.verbose)
{
cv::namedWindow( "Viewer", cv::WINDOW_NORMAL );
cv::resizeWindow("Viewer", 960, 500);
}
int operation = selectOperation(config.commandsFiles);
int ret = 0;
if(!config.interactive)
{
if(operation < 0)
{
Log(Log::ERROR)<<"An operation must be selected";
Log(Log::INFO)<<"Possible operations: apply create curve mkcurve mkboard";
return -1;
}
ret = perfromOperation(operation, config.commandsFiles+1, config);
}
else
{
while(true)
{
std::cout<<"> ";
char cmdline[1024];
char** tokens = new char*[256];
char *token;
char *r = cmdline;
for(size_t i = 0; i < 256/sizeof(char*); ++i)
tokens[i] = nullptr;
std::cin.getline(cmdline, sizeof(cmdline), '\n');
for(size_t i = 0; (token = strtok_r(r, " ", &r)) && i < 256; ++i)
{
tokens[i] = new char[strlen(token)+1];
strcpy(tokens[i], token);
}
int operation = selectOperation(tokens);
if(operation < 0)
{
Log(Log::ERROR)<<"A operation must be selected";
continue;
}
if(perfromOperation(operation, tokens+1, config) < 0)
break;
for(size_t i = 0; i < 256/sizeof(char*) && tokens[i]; ++i)
delete[] tokens[i];
delete[] tokens;
}
}
if(config.verbose)
{
cv::destroyWindow("Viewer");
cv::waitKey(0);
}
return ret;
}