Recover git state after .git was lost
This commit is contained in:
		
							parent
							
								
									d4d7418cb2
								
							
						
					
					
						commit
						5e3c26e763
					
				
					 29 changed files with 1610 additions and 1065 deletions
				
			
		| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
cmake_minimum_required(VERSION 2.4)
 | 
					cmake_minimum_required(VERSION 3.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
project(unwap)
 | 
					project(unwrap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(SRC_FILES main.cpp unwrap.cpp drawing.cpp matutils.cpp bgremoval.cpp charuco.cpp)
 | 
					set(SRC_FILES src/main.cpp src/unwrap.cpp src/drawing.cpp src/matutils.cpp src/bgremoval.cpp src/charuco.cpp src/harris.cpp)
 | 
				
			||||||
set(LIBS -lX11 -lrt)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
find_package( OpenCV REQUIRED )
 | 
					find_package( OpenCV REQUIRED )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_executable(${PROJECT_NAME} ${SRC_FILES})
 | 
					add_executable(${PROJECT_NAME} ${SRC_FILES})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_link_libraries( ${PROJECT_NAME} ${LIBS}  -lopencv_core -lopencv_aruco -lopencv_imgcodecs -lopencv_highgui -lopencv_features2d -lopencv_imgcodecs -lopencv_imgproc -lopencv_video)
 | 
					target_link_libraries( ${PROJECT_NAME} ${LIBS}  -lopencv_core -lopencv_aruco -lopencv_imgcodecs -lopencv_highgui -lopencv_features2d -lopencv_imgcodecs -lopencv_imgproc -lopencv_video -lopencv_stitching)
 | 
				
			||||||
target_include_directories(${PROJECT_NAME} PRIVATE  "/usr/include/opencv4")
 | 
					target_include_directories(${PROJECT_NAME} PRIVATE  "/usr/include/opencv4")
 | 
				
			||||||
add_definitions(" -std=c++17 -Wall -O2 -flto -fno-strict-aliasing")
 | 
					add_definitions(" -std=c++17 -Wall -O2 -fno-strict-aliasing")
 | 
				
			||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
 | 
					set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
#include "bgremoval.h"
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
#include <opencv2/highgui.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/imgproc.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/bgsegm.hpp>
 | 
					 | 
				
			||||||
#include "log.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool createMask(const cv::Mat& in, cv::Mat& mask, const cv::Mat& bg)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(in.size != bg.size || in.type() != bg.type())
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<"input image and backgournd image size and type needs to be the same";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	cv::Ptr<cv::BackgroundSubtractorMOG2> bgremv = cv::createBackgroundSubtractorMOG2(2,10,false);
 | 
					 | 
				
			||||||
	bgremv->apply(bg, mask, 1);
 | 
					 | 
				
			||||||
	bgremv->apply(in, mask, 0);
 | 
					 | 
				
			||||||
	cv::GaussianBlur(mask,mask,cv::Size(49,49), 15);
 | 
					 | 
				
			||||||
	cv::threshold(mask, mask, 70, 255, cv::THRESH_BINARY);
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,5 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool createMask(const cv::Mat& in, cv::Mat& mask, const cv::Mat& bg);
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,8 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void createCharucoBoard(unsigned int size, const std::string& fileName);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Point2f> detectCharucoPoints(cv::Mat image, std::vector<cv::Point2i>* coordiantes = nullptr, bool verbose = true);
 | 
					 | 
				
			||||||
							
								
								
									
										19
									
								
								drawing.cpp
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								drawing.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -1,19 +0,0 @@
 | 
				
			||||||
#include "drawing.h"
 | 
					 | 
				
			||||||
#include <opencv2/viz/types.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/imgproc.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void drawRows(cv::Mat& image, const std::vector< std::vector<cv::Point2f > >& rows)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < rows.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		for(size_t y = 0; y < rows[i].size(); ++y)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			cv::circle(image, rows[i][y], 5, cv::viz::Color(128 * (i%3), 128 * ((i+1)%3), 128 * ((i+2)%3)));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void drawEllipses(cv::Mat& image, const std::vector<cv::RotatedRect>& ellipses )
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	 for(const auto& ellipse : ellipses)cv::ellipse(image, ellipse, cv::viz::Color(128,128,128));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void drawRows(cv::Mat& image, const std::vector< std::vector<cv::Point2f > >& rows);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void drawEllipses(cv::Mat& image, const std::vector<cv::RotatedRect>& ellipses );
 | 
					 | 
				
			||||||
							
								
								
									
										44
									
								
								harris.h
									
										
									
									
									
								
							
							
						
						
									
										44
									
								
								harris.h
									
										
									
									
									
								
							| 
						 | 
					@ -1,44 +0,0 @@
 | 
				
			||||||
#include <opencv2/imgproc.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/features2d.hpp>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::vector<cv::Point2f> harrisDetectPoints(cv::Mat& image, const cv::Mat& mask, 
 | 
					 | 
				
			||||||
											 int blockSize = 5, int apature = 5, float detectorParameter = 0.01,
 | 
					 | 
				
			||||||
											 float minSize = 7, bool verbose = false)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cv::Mat gray;
 | 
					 | 
				
			||||||
	cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	//detect corners
 | 
					 | 
				
			||||||
	cv::Mat corners;
 | 
					 | 
				
			||||||
	cv::cornerHarris(gray, corners, blockSize, apature, detectorParameter);
 | 
					 | 
				
			||||||
	cv::normalize(corners, corners, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
 | 
					 | 
				
			||||||
	cv::convertScaleAbs( corners, corners );
 | 
					 | 
				
			||||||
	cv::threshold(corners, corners, 50, 255, cv::THRESH_BINARY);
 | 
					 | 
				
			||||||
	cv::Mat cornersMasked;
 | 
					 | 
				
			||||||
	if(mask.data && mask.size == corners.size) corners.copyTo(cornersMasked, mask);
 | 
					 | 
				
			||||||
	else corners.copyTo(cornersMasked);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(verbose)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::imshow( "Viewer", cornersMasked );
 | 
					 | 
				
			||||||
		cv::waitKey(0);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	//get middle of corners
 | 
					 | 
				
			||||||
	cv::SimpleBlobDetector::Params blobParams;
 | 
					 | 
				
			||||||
	blobParams.filterByArea = true;
 | 
					 | 
				
			||||||
	blobParams.minArea = minSize;
 | 
					 | 
				
			||||||
	blobParams.maxArea = 500;
 | 
					 | 
				
			||||||
	blobParams.filterByColor = false;
 | 
					 | 
				
			||||||
	blobParams.blobColor = 255;
 | 
					 | 
				
			||||||
	blobParams.filterByInertia = false;
 | 
					 | 
				
			||||||
	blobParams.filterByConvexity = false;
 | 
					 | 
				
			||||||
	cv::Ptr<cv::SimpleBlobDetector> blobDetector = cv::SimpleBlobDetector::create(blobParams);
 | 
					 | 
				
			||||||
	std::vector<cv::KeyPoint> keypoints;
 | 
					 | 
				
			||||||
	blobDetector->detect(cornersMasked, keypoints);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector<cv::Point2f> points;
 | 
					 | 
				
			||||||
	cv::KeyPoint::convert(keypoints, points);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	return points;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										171
									
								
								main.cpp
									
										
									
									
									
								
							
							
						
						
									
										171
									
								
								main.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -1,171 +0,0 @@
 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
#include <opencv2/highgui.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
#include <unistd.h>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
#include <string> 
 | 
					 | 
				
			||||||
#include "argpopt.h"
 | 
					 | 
				
			||||||
#include "unwrap.h"
 | 
					 | 
				
			||||||
#include "bgremoval.h"
 | 
					 | 
				
			||||||
#include "normalize.h"
 | 
					 | 
				
			||||||
#include "log.h"
 | 
					 | 
				
			||||||
#include "charuco.h"
 | 
					 | 
				
			||||||
#include "harris.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void cd_to_exe_dir( char *argv[] )
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	std::string path = argv[0];
 | 
					 | 
				
			||||||
	int ii = path.length();
 | 
					 | 
				
			||||||
	while ( !( path[ii] == '/' || path[ii] == '\\' ) && ii > 0 )
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		ii--;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	path.erase( ii, 100 );
 | 
					 | 
				
			||||||
	chdir( path.c_str() );
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Mat> loadImages(char** fileNames)
 | 
					 | 
				
			||||||
{ 
 | 
					 | 
				
			||||||
	std::vector<cv::Mat> images;
 | 
					 | 
				
			||||||
	for(size_t i = 0; fileNames[i]; ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::Mat tmpImage = cv::imread(fileNames[i]);
 | 
					 | 
				
			||||||
		if(tmpImage.data)images.push_back(tmpImage);
 | 
					 | 
				
			||||||
		else std::cout<<"can not read image "<<i<<" from "<<fileNames[i]<<'\n';
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return images;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main(int argc, char* argv[])
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cv::ocl::setUseOpenCL(false);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	cd_to_exe_dir(argv);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	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.charuco.empty())
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		createCharucoBoard(config.size, config.charuco);
 | 
					 | 
				
			||||||
		Log(Log::INFO)<<"exporting charuco map";
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector<cv::Mat> inImages = loadImages(config.inFileNames);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(inImages.empty()) 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<"Input images must be provided!";
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(config.verbose) 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::namedWindow( "Viewer", cv::WINDOW_NORMAL );
 | 
					 | 
				
			||||||
		cv::resizeWindow("Viewer", 960, 500);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(config.maps.empty())
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		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<cv::Point2f > points;
 | 
					 | 
				
			||||||
		std::vector<cv::Point2i> coordiantes;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(config.harris)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			points = harrisDetectPoints(inImages[0], mask);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			points = detectCharucoPoints(inImages[0], &coordiantes);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		Log(Log::INFO)<<"Found "<<points.size()<<" points";
 | 
					 | 
				
			||||||
		if(points.size() < 8) 
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Log(Log::ERROR)<<"Error creating map, insufficant points detected";
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		createRemapMap(inImages[0], points, coordiantes, config.inFileNames[0], config.verbose);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::FileStorage fs(config.maps, cv::FileStorage::READ);
 | 
					 | 
				
			||||||
		if (!fs.isOpened())
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Log(Log::ERROR)<<"could not open maps file " <<config.maps;
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		cv::Mat xMat;
 | 
					 | 
				
			||||||
		cv::Mat yMat;
 | 
					 | 
				
			||||||
		fs["xmat"]>>xMat;
 | 
					 | 
				
			||||||
		fs["ymat"]>>yMat;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		cv::Mat 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;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			applyRemap(tmp, norm, xMat, yMat, config.size);
 | 
					 | 
				
			||||||
			if(config.verbose)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				cv::imshow("Viewer", norm );
 | 
					 | 
				
			||||||
				cv::waitKey(0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		for(auto& image : inImages)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(config.verbose)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				cv::imshow( "Viewer", image );
 | 
					 | 
				
			||||||
				cv::waitKey(0);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			cv::Mat remaped;
 | 
					 | 
				
			||||||
			applyRemap(image, remaped, xMat, yMat, config.size);
 | 
					 | 
				
			||||||
			if(norm.data) normalize(remaped, norm);
 | 
					 | 
				
			||||||
			cv::imshow( "Viewer", remaped );
 | 
					 | 
				
			||||||
			cv::waitKey(0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(config.verbose)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::destroyWindow("Viewer");  
 | 
					 | 
				
			||||||
		cv::waitKey(0);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										166
									
								
								matutils.cpp
									
										
									
									
									
								
							
							
						
						
									
										166
									
								
								matutils.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -1,166 +0,0 @@
 | 
				
			||||||
#include "matutils.h"
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void sanityCheckMap(cv::Mat& mat, const float min, const float max, float minValue, float maxValue)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for(int y = 0; y < mat.rows; y++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float* col = mat.ptr<float>(y);
 | 
					 | 
				
			||||||
		for(int x = 0; x < mat.cols; ++x)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(col[x] < min) 
 | 
					 | 
				
			||||||
				col[x] = minValue;
 | 
					 | 
				
			||||||
			else if(col[x] > max)
 | 
					 | 
				
			||||||
				col[x] = maxValue;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Point2f>::iterator getTopLeft(std::vector<cv::Point2f>& points)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::min_element(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){
 | 
					 | 
				
			||||||
		return sqrt(a.y*a.y+a.x*a.x) < sqrt(b.y*b.y+b.x*b.x);
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Point2f>::iterator getBottomRight(std::vector<cv::Point2f>& points)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::max_element(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){
 | 
					 | 
				
			||||||
		return sqrt(a.y*a.y+a.x*a.x) < sqrt(b.y*b.y+b.x*b.x);
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
double distance(const cv::Point2f& a, const cv::Point2f& b)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return sqrt((a.y-b.y)*(a.y-b.y)+(a.x-b.x)*(a.x-b.x));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool findClosest(size_t& xIndex, size_t& yIndex, 
 | 
					 | 
				
			||||||
				 cv::Point2f point, const std::vector< std::vector<cv::Point2f> >& array, 
 | 
					 | 
				
			||||||
				 float xTolerance, float yTolerance)
 | 
					 | 
				
			||||||
{	
 | 
					 | 
				
			||||||
	size_t rowBelow = 0;
 | 
					 | 
				
			||||||
	while(rowBelow < array.size() && !array[rowBelow].empty() && array[rowBelow][0].y < point.y) ++rowBelow;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(rowBelow == array.size()) rowBelow = array.size()-1;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	size_t rightAbove = 0;
 | 
					 | 
				
			||||||
	size_t rightBelow = 0;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	while(rightBelow < array[rowBelow].size() && array[rowBelow][rightBelow].x < point.x ) ++rightBelow;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float distRB = distance(point, array[rowBelow][rightBelow]);
 | 
					 | 
				
			||||||
	float distLB = rightBelow > 0 ? distance(point, array[rowBelow][rightBelow-1]) : std::numeric_limits<float>::max();
 | 
					 | 
				
			||||||
	float distRA = std::numeric_limits<float>::max();
 | 
					 | 
				
			||||||
	float distLA = std::numeric_limits<float>::max();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(rowBelow > 0)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		while(rightAbove < array[rowBelow-1].size() && array[rowBelow-1][rightAbove].x < point.x ) ++rightAbove;
 | 
					 | 
				
			||||||
		distRA = distance(point, array[rowBelow-1][rightAbove]);
 | 
					 | 
				
			||||||
		if(rightAbove > 0)  distLA = distance(point, array[rowBelow-1][rightAbove-1]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float* min = &distRB;
 | 
					 | 
				
			||||||
	if(distLB < *min) min = &distLB;
 | 
					 | 
				
			||||||
	if(distRA < *min) min = &distRA;
 | 
					 | 
				
			||||||
	if(distLA < *min) min = &distLA;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(min == &distRB)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		yIndex = rowBelow;
 | 
					 | 
				
			||||||
		xIndex = rightBelow;
 | 
					 | 
				
			||||||
	} 
 | 
					 | 
				
			||||||
	else if(min == &distLB)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		yIndex = rowBelow;
 | 
					 | 
				
			||||||
		xIndex = rightBelow-1;
 | 
					 | 
				
			||||||
	} 
 | 
					 | 
				
			||||||
	else if(min == &distRA)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		yIndex = rowBelow-1;
 | 
					 | 
				
			||||||
		xIndex = rightBelow;
 | 
					 | 
				
			||||||
	} 
 | 
					 | 
				
			||||||
	else if(min == &distLA)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		yIndex = rowBelow-1;
 | 
					 | 
				
			||||||
		xIndex = rightBelow-1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else return false;
 | 
					 | 
				
			||||||
	return abs(array[yIndex][xIndex].x - point.x) < xTolerance && abs(array[yIndex][xIndex].y - point.y) < yTolerance;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void interpolateMissing(cv::Mat& mat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for(int y = 0; y < mat.rows; y++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float* col = mat.ptr<float>(y);
 | 
					 | 
				
			||||||
		for(int x = 0; x < mat.cols; ++x)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(col[x] < 0) 
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				int closestA = -1;
 | 
					 | 
				
			||||||
				int closestB = -1;
 | 
					 | 
				
			||||||
				int dist = std::numeric_limits<int>::max();
 | 
					 | 
				
			||||||
				for(int i = 0; i < mat.cols; ++i)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					if(i != closestA && col[i] >= 0 && abs(i-x) <= dist)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						closestB = closestA;
 | 
					 | 
				
			||||||
						closestA = i;
 | 
					 | 
				
			||||||
						dist = abs(i-x);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if(closestA < 0 || closestB < 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					closestA = -1;
 | 
					 | 
				
			||||||
					closestB = -1;
 | 
					 | 
				
			||||||
					dist = std::numeric_limits<int>::max();
 | 
					 | 
				
			||||||
					for(int i = mat.cols-1; i >= 0; --i)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						if(i != closestA && col[i] >= 0 && abs(i-x) <= dist)
 | 
					 | 
				
			||||||
						{
 | 
					 | 
				
			||||||
							closestB = closestA;
 | 
					 | 
				
			||||||
							closestA = i;
 | 
					 | 
				
			||||||
							dist = abs(i-x);
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				float slope = (col[closestB] - col[closestA])/(closestB-closestA);
 | 
					 | 
				
			||||||
				col[x] = col[closestA] - (closestA-x)*slope;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void fillMissing(cv::Mat& mat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	bool finished = true;
 | 
					 | 
				
			||||||
	for(int y = 0; y < mat.rows; y++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float* col = mat.ptr<float>(y);
 | 
					 | 
				
			||||||
		for(int x = 0; x < mat.cols; ++x)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(col[x] < 0 && col[x] > -2) 
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if(y > 0 && mat.at<float>(y-1,x) >= 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					col[x] = mat.at<float>(y-1,x);
 | 
					 | 
				
			||||||
					finished = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				else if(y < mat.rows-1 && mat.at<float>(y+1,x) >= 0)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					col[x] = mat.at<float>(y+1,x);
 | 
					 | 
				
			||||||
					finished = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if(col[x] > 0 && ((x+1 < mat.cols && col[x] > col[x+1]) || (x > 0 && col[x] < col[x-1])))
 | 
					 | 
				
			||||||
					col[x] = -2;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if(!finished) fillMissing(mat);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										22
									
								
								matutils.h
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								matutils.h
									
										
									
									
									
								
							| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void sanityCheckMap(cv::Mat& mat, const float min, const float max, const float minValue, const float maxValue);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Point2f>::iterator getTopLeft(std::vector<cv::Point2f>& points);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::vector<cv::Point2f>::iterator getBottomRight(std::vector<cv::Point2f>& points);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
double distance(const cv::Point2f& a, const cv::Point2f& b);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool findClosest(size_t& xIndex, size_t& yIndex, 
 | 
					 | 
				
			||||||
				 cv::Point2f point, const std::vector< std::vector<cv::Point2f> >& array, 
 | 
					 | 
				
			||||||
				 float xTolerance, float yTolerance);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void interpolateMissing(cv::Mat& mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void fillMissing(cv::Mat& mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
* Sigstoped
 | 
					* Lubricant Detecter
 | 
				
			||||||
* Copyright (C) 2020 Carl Klemm
 | 
					* Copyright (C) 2021 Carl Klemm
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This program is free software; you can redistribute it and/or
 | 
					* This program is free software; you can redistribute it and/or
 | 
				
			||||||
* modify it under the terms of the GNU General Public License
 | 
					* modify it under the terms of the GNU General Public License
 | 
				
			||||||
| 
						 | 
					@ -23,16 +23,17 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Config
 | 
					struct Config
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char** inFileNames = NULL;
 | 
						char** commandsFiles = NULL;
 | 
				
			||||||
 | 
						std::string output = "";
 | 
				
			||||||
	std::string norm = "";
 | 
						std::string norm = "";
 | 
				
			||||||
	std::string maps = "";
 | 
					 | 
				
			||||||
	std::string bg = "";
 | 
						std::string bg = "";
 | 
				
			||||||
	std::string charuco = "";
 | 
					 | 
				
			||||||
	bool harris = false;
 | 
						bool harris = false;
 | 
				
			||||||
	float minSize = 7; 
 | 
						float minSize = 7; 
 | 
				
			||||||
	unsigned int size = 600;
 | 
						unsigned int size = 50;
 | 
				
			||||||
	bool verbose = false;
 | 
						bool verbose = false;
 | 
				
			||||||
	bool quiet = false;
 | 
						bool quiet = false;
 | 
				
			||||||
 | 
						bool interactive = false;
 | 
				
			||||||
 | 
						bool simpleStich = false;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const char *argp_program_version = "0.2";
 | 
					const char *argp_program_version = "0.2";
 | 
				
			||||||
| 
						 | 
					@ -44,13 +45,14 @@ static struct argp_option options[] =
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
{"verbose",  'v', 0,      0,  "Enable verbose logging" },
 | 
					{"verbose",  'v', 0,      0,  "Enable verbose logging" },
 | 
				
			||||||
{"quiet",  'q', 0,      0,  "Disable info messages" },
 | 
					{"quiet",  'q', 0,      0,  "Disable info messages" },
 | 
				
			||||||
{"map", 'm', "File Name",      0,  "remap maps file" },
 | 
					 | 
				
			||||||
{"bg", 'b', "File Name",      0,  "background image file" },
 | 
					{"bg", 'b', "File Name",      0,  "background image file" },
 | 
				
			||||||
{"normalize", 'n', "File Name",      0,  "image to use as a normalization source" },
 | 
					{"normalize", 'n', "File Name",      0,  "image to use as a normalization source" },
 | 
				
			||||||
{"min-size", 's', "Value",      0,  "Smallest feature accepted as a corner" },
 | 
					{"min-size", 's', "Value",      0,  "Smallest feature accepted as a corner" },
 | 
				
			||||||
{"create", 'c', "File Name",      0,  "Create charuco board" },
 | 
					{"size", 'x', "Value",      0,  "Output cell size" },
 | 
				
			||||||
{"x-size", 'x', "Value",      0,  "Output image width" },
 | 
					 | 
				
			||||||
{"harris", 'r', 0,      0,  "Use harris to detect points" },
 | 
					{"harris", 'r', 0,      0,  "Use harris to detect points" },
 | 
				
			||||||
 | 
					{"output", 'o', "File Name",      0,  "output file name" },
 | 
				
			||||||
 | 
					{"interactive", 'i', 0,      0,  "interactivly process multiple commands" },
 | 
				
			||||||
 | 
					{"simpe-stich", 'a', 0,      0,  "Use non blending sticher" },
 | 
				
			||||||
{ 0 }
 | 
					{ 0 }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,9 +68,6 @@ error_t parse_opt (int key, char *arg, struct argp_state *state)
 | 
				
			||||||
		case 'q':
 | 
							case 'q':
 | 
				
			||||||
			config->quiet = true;
 | 
								config->quiet = true;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
		case 'm':
 | 
					 | 
				
			||||||
			config->maps.assign(arg);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
		case 'b':
 | 
							case 'b':
 | 
				
			||||||
			config->bg.assign(arg);
 | 
								config->bg.assign(arg);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -81,14 +80,20 @@ error_t parse_opt (int key, char *arg, struct argp_state *state)
 | 
				
			||||||
		case 'r':
 | 
							case 'r':
 | 
				
			||||||
			config->harris = true;
 | 
								config->harris = true;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
		case 'c':
 | 
					 | 
				
			||||||
			config->charuco.assign(arg);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
		case 'x':
 | 
							case 'x':
 | 
				
			||||||
			config->size=atol(arg);
 | 
								config->size=atol(arg);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
							case 'a':
 | 
				
			||||||
 | 
								config->simpleStich = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
							case 'i':
 | 
				
			||||||
 | 
								config->interactive = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
							case 'o':
 | 
				
			||||||
 | 
								config->output.assign(arg);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
		case ARGP_KEY_ARG:
 | 
							case ARGP_KEY_ARG:
 | 
				
			||||||
			config->inFileNames = &state->argv[state->next-1];
 | 
								config->commandsFiles = &state->argv[state->next-1];
 | 
				
			||||||
			state->next = state->argc;
 | 
								state->next = state->argc;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/bgremoval.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/bgremoval.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "bgremoval.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <opencv2/highgui.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/imgproc.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/bgsegm.hpp>
 | 
				
			||||||
 | 
					#include "log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool createMask(const cv::Mat& in, cv::Mat& mask, const cv::Mat& bg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if(in.size != bg.size || in.type() != bg.type())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<"input image and backgournd image size and type needs to be the same";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						cv::Ptr<cv::BackgroundSubtractorMOG2> bgremv = cv::createBackgroundSubtractorMOG2(2,10,false);
 | 
				
			||||||
 | 
						bgremv->apply(bg, mask, 1);
 | 
				
			||||||
 | 
						bgremv->apply(in, mask, 0);
 | 
				
			||||||
 | 
						cv::GaussianBlur(mask,mask,cv::Size(49,49), 15);
 | 
				
			||||||
 | 
						cv::threshold(mask, mask, 70, 255, cv::THRESH_BINARY);
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/bgremoval.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/bgremoval.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool createMask(const cv::Mat& in, cv::Mat& mask, const cv::Mat& bg);
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,22 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "charuco.h"
 | 
					#include "charuco.h"
 | 
				
			||||||
#include <opencv2/aruco/charuco.hpp>
 | 
					#include <opencv2/aruco/charuco.hpp>
 | 
				
			||||||
#include <opencv2/highgui.hpp>
 | 
					#include <opencv2/highgui.hpp>
 | 
				
			||||||
| 
						 | 
					@ -5,6 +24,7 @@
 | 
				
			||||||
static constexpr unsigned int X_BOARD_SIZE = 18;
 | 
					static constexpr unsigned int X_BOARD_SIZE = 18;
 | 
				
			||||||
static constexpr unsigned int Y_BOARD_SIZE = 10;
 | 
					static constexpr unsigned int Y_BOARD_SIZE = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void createCharucoBoard(unsigned int size, const std::string& fileName)
 | 
					void createCharucoBoard(unsigned int size, const std::string& fileName)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    cv::Ptr<cv::aruco::CharucoBoard> board = 
 | 
					    cv::Ptr<cv::aruco::CharucoBoard> board = 
 | 
				
			||||||
| 
						 | 
					@ -15,7 +35,7 @@ void createCharucoBoard(unsigned int size, const std::string& fileName)
 | 
				
			||||||
    cv::imwrite(fileName, charucoImage);
 | 
					    cv::imwrite(fileName, charucoImage);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<cv::Point2f> detectCharucoPoints(cv::Mat image, std::vector<cv::Point2i>* coordiantes, bool verbose)
 | 
					std::vector<DetectedPoint> detectCharucoPoints(cv::Mat image, bool verbose)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(X_BOARD_SIZE, Y_BOARD_SIZE, 0.03f, 0.02f, 
 | 
						cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(X_BOARD_SIZE, Y_BOARD_SIZE, 0.03f, 0.02f, 
 | 
				
			||||||
												cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_250 ));
 | 
																	cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_250 ));
 | 
				
			||||||
| 
						 | 
					@ -58,10 +78,15 @@ std::vector<cv::Point2f> detectCharucoPoints(cv::Mat image, std::vector<cv::Poin
 | 
				
			||||||
			cv::waitKey(0);
 | 
								cv::waitKey(0);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		if(coordiantes) for( auto id : charucoIds)
 | 
							std::vector<DetectedPoint> detections;
 | 
				
			||||||
			coordiantes->push_back(cv::Point2i(id % (X_BOARD_SIZE-1), (Y_BOARD_SIZE-2) - id/(X_BOARD_SIZE-1)));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return charucoCorners;
 | 
							for(size_t i = 0; i < charucoIds.size(); ++i)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								cv::Point2i coordiante(charucoIds[i] % (X_BOARD_SIZE-1), (Y_BOARD_SIZE-2) - charucoIds[i]/(X_BOARD_SIZE-1));
 | 
				
			||||||
 | 
								detections.push_back(DetectedPoint(charucoCorners[i], coordiante));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							return detections;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return std::vector<cv::Point2f>();
 | 
						return std::vector<DetectedPoint>();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/charuco.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/charuco.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					#include "detectedpoint.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void createCharucoBoard(unsigned int size, const std::string& fileName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<DetectedPoint> detectCharucoPoints(cv::Mat image, bool verbose = true);
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/detectedpoint.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/detectedpoint.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DetectedPoint 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						cv::Point2f point;
 | 
				
			||||||
 | 
						cv::Point2i coordinate;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						DetectedPoint(const cv::Point2f& pointI, const cv::Point2f& coordinateI): 
 | 
				
			||||||
 | 
						point(pointI), coordinate(coordinateI)
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										38
									
								
								src/drawing.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/drawing.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "drawing.h"
 | 
				
			||||||
 | 
					#include <opencv2/viz/types.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/imgproc.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void drawRows(cv::Mat& image, const std::vector< std::vector<cv::Point2f > >& rows)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for(size_t i = 0; i < rows.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							for(size_t y = 0; y < rows[i].size(); ++y)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								cv::circle(image, rows[i][y], 5, cv::viz::Color(128 * (i%3), 128 * ((i+1)%3), 128 * ((i+2)%3)));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void drawEllipses(cv::Mat& image, const std::vector<cv::RotatedRect>& ellipses )
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						 for(const auto& ellipse : ellipses)cv::ellipse(image, ellipse, cv::viz::Color(128,128,128));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/drawing.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/drawing.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void drawRows(cv::Mat& image, const std::vector< std::vector<cv::Point2f > >& rows);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void drawEllipses(cv::Mat& image, const std::vector<cv::RotatedRect>& ellipses );
 | 
				
			||||||
							
								
								
									
										294
									
								
								src/harris.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								src/harris.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,294 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 <opencv2/imgproc.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/features2d.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/highgui.hpp>
 | 
				
			||||||
 | 
					#include "harris.h"
 | 
				
			||||||
 | 
					#include "log.h"
 | 
				
			||||||
 | 
					#include "matutils.h"
 | 
				
			||||||
 | 
					#include "drawing.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static float detimineXPitch(const std::vector<cv::Point2f>& row, float fuzz = 1.3f)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<float> xRowDists;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < row.size()-1; ++i)
 | 
				
			||||||
 | 
							xRowDists.push_back(abs(row[i+1].x-row[i].x));
 | 
				
			||||||
 | 
						float xMinDist = *std::min(xRowDists.begin(), xRowDists.end());
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": xMinDist "<<xMinDist;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float meanXDistAccum = 0;
 | 
				
			||||||
 | 
						size_t validCount = 0;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < xRowDists.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(xRowDists[i] < xMinDist*fuzz)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								++validCount;
 | 
				
			||||||
 | 
								meanXDistAccum+=xRowDists[i];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return meanXDistAccum/validCount;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::vector<cv::RotatedRect> fitEllipses(std::vector< std::vector<cv::Point2f > >& rows, bool remove = true)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if(rows.empty()) return std::vector<cv::RotatedRect>();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector<cv::RotatedRect> ellipses;
 | 
				
			||||||
 | 
						ellipses.reserve(rows.size());
 | 
				
			||||||
 | 
						for(size_t i = 0; i < rows.size(); ++i) 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(rows[i].size() > 4) ellipses.push_back(cv::fitEllipse(rows[i]));
 | 
				
			||||||
 | 
							else 
 | 
				
			||||||
 | 
							{ 
 | 
				
			||||||
 | 
								rows.erase(rows.begin()+i);
 | 
				
			||||||
 | 
								i--;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ellipses;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool sanityCheckElipses(std::vector< std::vector<cv::Point2f > >& rows, std::vector<cv::RotatedRect>& elipses)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if(rows.size() != elipses.size() && elipses.size() > 1) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for(size_t i = 0; i < elipses.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float angDiff = fmod(elipses[i].angle,90);
 | 
				
			||||||
 | 
							if(angDiff < 90-5 && angDiff > 5)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								elipses.erase(elipses.begin()+i);
 | 
				
			||||||
 | 
								rows.erase(rows.begin()+i);
 | 
				
			||||||
 | 
								--i;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector<float> widths;
 | 
				
			||||||
 | 
						std::vector<float> heights;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(auto& elipse : elipses)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							widths.push_back(elipse.size.width);
 | 
				
			||||||
 | 
							heights.push_back(elipse.size.height);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector<size_t> outliersW;
 | 
				
			||||||
 | 
						std::vector<size_t> outliersH;
 | 
				
			||||||
 | 
						thompsonTauTest(widths, outliersW, 2);
 | 
				
			||||||
 | 
						thompsonTauTest(heights, outliersH, 2);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector< std::vector<cv::Point2f > > rowsReduced;
 | 
				
			||||||
 | 
						std::vector<cv::RotatedRect> elipsesReduced;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for(size_t i = 0; i < elipses.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool found = false;
 | 
				
			||||||
 | 
							for(size_t j = 0; j < outliersW.size() && !found; ++j) 
 | 
				
			||||||
 | 
								if(outliersW[j]==i) found = true;
 | 
				
			||||||
 | 
							for(size_t j = 0; j < outliersH.size() && !found; ++j) 
 | 
				
			||||||
 | 
								if(outliersH[j]==i) found = true;
 | 
				
			||||||
 | 
							if(!found)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								rowsReduced.push_back(rows[i]);
 | 
				
			||||||
 | 
								elipsesReduced.push_back(elipses[i]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						elipses = elipsesReduced;
 | 
				
			||||||
 | 
						rows = rowsReduced;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::vector< std::vector<cv::Point2f > > sortPointsIntoRows(std::vector<cv::Point2f>& points)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(points.size() < 6) return std::vector< std::vector<cv::Point2f> >();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						cv::Point2f topLeft(*getTopLeft(points));
 | 
				
			||||||
 | 
						cv::Point2f bottomRight(*getBottomRight(points));
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<"topLeft "<<topLeft.x<<' '<<topLeft.y;
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<"bottomRight "<<bottomRight.x<<' '<<bottomRight.y;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float fuzz = (bottomRight.x-topLeft.x)*0.05f;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(size_t i = 0; i < points.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(points[i].x < topLeft.x-fuzz || points[i].y < topLeft.y-fuzz || 
 | 
				
			||||||
 | 
								points[i].x > bottomRight.x+fuzz || points[i].y > bottomRight.y+fuzz)
 | 
				
			||||||
 | 
								points.erase(points.begin()+i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::sort(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){return a.y < b.y;});
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						double accumulator = points[0].y+points[1].y;
 | 
				
			||||||
 | 
						size_t accuCount = 2;
 | 
				
			||||||
 | 
						float sigDist = (bottomRight.y-topLeft.y) / 20;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector< std::vector<cv::Point2f> > result(1);
 | 
				
			||||||
 | 
						result.back().push_back(points[0]);
 | 
				
			||||||
 | 
						result.back().push_back(points[1]);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(size_t i = 2; i < points.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if( points[i].y - accumulator/accuCount > sigDist )
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(points.size() - i - 3 < 0) break;
 | 
				
			||||||
 | 
								else 
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									accumulator = points[i+1].y+points[i+2].y;
 | 
				
			||||||
 | 
									accuCount = 2;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								result.push_back(std::vector<cv::Point2f>());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							result.back().push_back(points[i]);
 | 
				
			||||||
 | 
							accumulator += points[i].y;
 | 
				
			||||||
 | 
							++accuCount;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(auto& row : result)  
 | 
				
			||||||
 | 
							std::sort(row.begin(), row.end(), [](const cv::Point2f& a, const cv::Point2f& b){return a.x < b.x;});
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static std::vector<DetectedPoint> harrisGenerateCoordinates(std::vector<cv::Point2f>& points, bool verbose)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector< std::vector<cv::Point2f > > rows = sortPointsIntoRows(points);
 | 
				
			||||||
 | 
						Log(Log::INFO)<<"Found "<<rows.size()<<" rows";
 | 
				
			||||||
 | 
						if(rows.size() < 2)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<"Error creating map, insufficant rows detected";
 | 
				
			||||||
 | 
							return std::vector<DetectedPoint>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector< cv::RotatedRect > ellipses = fitEllipses(rows);
 | 
				
			||||||
 | 
						Log(Log::INFO)<<"Found "<<ellipses.size()<<" ellipses. rows reduced to "<<rows.size();
 | 
				
			||||||
 | 
						if(ellipses.size() < 3)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<"Error creating map, insufficant ellipses detected";
 | 
				
			||||||
 | 
							return std::vector<DetectedPoint>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sanityCheckElipses(rows, ellipses);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(rows.size() < 2 || rows.size() != ellipses.size()) {
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<__func__<<": rows < 2 or rows != ellipses";
 | 
				
			||||||
 | 
							return std::vector<DetectedPoint>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<cv::Point2i> coordinates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						coordinates.assign(points.size(), cv::Point2i());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float meanYdist = 0;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < ellipses.size()-1; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							meanYdist += ellipses[i+1].center.y-ellipses[i].center.y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						meanYdist = meanYdist/ellipses.size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float meanXdist = 0;
 | 
				
			||||||
 | 
						float xMin = std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
						float xMeanMin = 0;
 | 
				
			||||||
 | 
						float xMax = std::numeric_limits<float>::min();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(auto& row : rows)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							meanXdist+=detimineXPitch(row);
 | 
				
			||||||
 | 
							xMeanMin+=row.front().x;
 | 
				
			||||||
 | 
							if(xMin > row.front().x )
 | 
				
			||||||
 | 
								xMin = row.front().x;
 | 
				
			||||||
 | 
							if(xMax < row.back().x)
 | 
				
			||||||
 | 
								xMax = row.back().x;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						meanXdist/=rows.size();
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": meanXdist "<<meanXdist;
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": meanYdist "<<meanYdist;
 | 
				
			||||||
 | 
						xMeanMin = xMeanMin / rows.size();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": Grid: xMin "<<xMin;
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": Grid: grid xMax "<<xMax;
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": Grid: grid xMeanMin "<<xMeanMin;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						size_t xGridSize = static_cast<size_t>(std::lround(abs((xMax-xMin)/meanXdist))+1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector<DetectedPoint> dpoints;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(size_t y = 0; y < rows.size(); ++y)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::DEBUG)<<__func__<<": Proc row "<<y;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							for(size_t x = 0; x < xGridSize; ++x)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								cv::Point2f tmp(x*meanXdist+xMin, y*meanYdist);
 | 
				
			||||||
 | 
								size_t closest;
 | 
				
			||||||
 | 
								bool found = 	findClosest(closest, tmp, 
 | 
				
			||||||
 | 
																   points, 
 | 
				
			||||||
 | 
																   (2*meanXdist)/5, (2*meanYdist)/5);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								Log(Log::DEBUG)<<__func__<<": found: "<<found<<' '<<x<<'x'<<y;
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if(found)
 | 
				
			||||||
 | 
									dpoints.push_back(DetectedPoint(points[closest], cv::Point2i(x,y)));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dpoints;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<DetectedPoint> harrisDetectPoints(cv::Mat& image, const cv::Mat& mask, 
 | 
				
			||||||
 | 
																 int blockSize, int apature, float detectorParameter,
 | 
				
			||||||
 | 
																 float minSize, bool verbose)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cv::Mat gray;
 | 
				
			||||||
 | 
						cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//detect corners
 | 
				
			||||||
 | 
						cv::Mat corners;
 | 
				
			||||||
 | 
						cv::cornerHarris(gray, corners, blockSize, apature, detectorParameter);
 | 
				
			||||||
 | 
						cv::normalize(corners, corners, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
 | 
				
			||||||
 | 
						cv::convertScaleAbs( corners, corners );
 | 
				
			||||||
 | 
						cv::threshold(corners, corners, 50, 255, cv::THRESH_BINARY);
 | 
				
			||||||
 | 
						cv::Mat cornersMasked;
 | 
				
			||||||
 | 
						if(mask.data && mask.size == corners.size) corners.copyTo(cornersMasked, mask);
 | 
				
			||||||
 | 
						else corners.copyTo(cornersMasked);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(verbose)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							cv::imshow( "Viewer", cornersMasked );
 | 
				
			||||||
 | 
							cv::waitKey(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						//get middle of corners
 | 
				
			||||||
 | 
						cv::SimpleBlobDetector::Params blobParams;
 | 
				
			||||||
 | 
						blobParams.filterByArea = true;
 | 
				
			||||||
 | 
						blobParams.minArea = minSize;
 | 
				
			||||||
 | 
						blobParams.maxArea = 500;
 | 
				
			||||||
 | 
						blobParams.filterByColor = false;
 | 
				
			||||||
 | 
						blobParams.blobColor = 255;
 | 
				
			||||||
 | 
						blobParams.filterByInertia = false;
 | 
				
			||||||
 | 
						blobParams.filterByConvexity = false;
 | 
				
			||||||
 | 
						cv::Ptr<cv::SimpleBlobDetector> blobDetector = cv::SimpleBlobDetector::create(blobParams);
 | 
				
			||||||
 | 
						std::vector<cv::KeyPoint> keypoints;
 | 
				
			||||||
 | 
						blobDetector->detect(cornersMasked, keypoints);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::vector<cv::Point2f> points;
 | 
				
			||||||
 | 
						cv::KeyPoint::convert(keypoints, points);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return harrisGenerateCoordinates(points, verbose);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										25
									
								
								src/harris.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/harris.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					#include "detectedpoint.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<DetectedPoint> harrisDetectPoints(cv::Mat& image, const cv::Mat& mask, 
 | 
				
			||||||
 | 
																 int blockSize = 5, int apature = 5, float detectorParameter = 0.01,
 | 
				
			||||||
 | 
																 float minSize = 7, bool verbose = false);
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,22 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
							
								
								
									
										296
									
								
								src/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								src/main.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,296 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "unwrap.h"
 | 
				
			||||||
 | 
					#include "bgremoval.h"
 | 
				
			||||||
 | 
					#include "normalize.h"
 | 
				
			||||||
 | 
					#include "log.h"
 | 
				
			||||||
 | 
					#include "charuco.h"
 | 
				
			||||||
 | 
					#include "harris.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IMREAD_SIZE pow(2, 20)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						CREATE_CHARUCO,
 | 
				
			||||||
 | 
						CREATE_MAP,
 | 
				
			||||||
 | 
						APPLY_MAP,
 | 
				
			||||||
 | 
						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], "makeboard" ) == 0)
 | 
				
			||||||
 | 
							return CREATE_CHARUCO;
 | 
				
			||||||
 | 
						else if(strcmp(opt[0], "exit" ) == 0)
 | 
				
			||||||
 | 
							return EXIT;
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<cv::Mat> loadImages(char** fileNames)
 | 
				
			||||||
 | 
					{ 
 | 
				
			||||||
 | 
						std::vector<cv::Mat> images;
 | 
				
			||||||
 | 
						for(size_t i = 0; fileNames[i]; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int fd = open(fileNames[i], O_RDONLY);
 | 
				
			||||||
 | 
							if(fd < 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Log(Log::WARN)<<"could not open "<<fileNames[i]<< "ignoreing";
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							cv::Mat tmpImage = cv::imdecode(buffer, cv::IMREAD_UNCHANGED);
 | 
				
			||||||
 | 
							if(tmpImage.data)
 | 
				
			||||||
 | 
								images.push_back(tmpImage);
 | 
				
			||||||
 | 
							else 
 | 
				
			||||||
 | 
								Log(Log::WARN)<<"can not read image "<<i<<" from "<<fileNames[i]<<'\n';
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return images;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int perfromOperation(int operation, char** fileNames, const Config& config)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<cv::Mat> inImages;
 | 
				
			||||||
 | 
						if(operation == CREATE_MAP || operation == APPLY_MAP)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							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*14, 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, fileNames[0]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
									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);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								remapedImages.push_back(remaped);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							cv::Mat out;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							Log(Log::INFO)<<"Stiching images";
 | 
				
			||||||
 | 
							if(config.simpleStich)
 | 
				
			||||||
 | 
								out = simpleStich(remapedImages);
 | 
				
			||||||
 | 
							else 
 | 
				
			||||||
 | 
								out = stich(remapedImages);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if(config.verbose)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								cv::imshow( "Viewer", out );
 | 
				
			||||||
 | 
								cv::waitKey(0);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							cv::imwrite(!config.output.empty() ? config.output : "out.png", out);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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";
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										353
									
								
								src/matutils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								src/matutils.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,353 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "matutils.h"
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include "log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sanityCheckMap(cv::Mat& mat, const float min, const float max, float minValue, float maxValue)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for(int y = 0; y < mat.rows; y++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float* col = mat.ptr<float>(y);
 | 
				
			||||||
 | 
							for(int x = 0; x < mat.cols; ++x)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(col[x] < min) 
 | 
				
			||||||
 | 
									col[x] = minValue;
 | 
				
			||||||
 | 
								else if(col[x] > max)
 | 
				
			||||||
 | 
									col[x] = maxValue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thompsonTauTest(const std::vector<float>& in, std::vector<size_t>& outliers, float criticalValue)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						float mean = 0;
 | 
				
			||||||
 | 
						size_t n = 0;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < in.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool found = false;
 | 
				
			||||||
 | 
							for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
				
			||||||
 | 
								if(outliers[j]==i) found = true;
 | 
				
			||||||
 | 
							if(!found)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								mean+=in[i];
 | 
				
			||||||
 | 
								++n;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mean/=n;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float sd = 0;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < in.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool found = false;
 | 
				
			||||||
 | 
							for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
				
			||||||
 | 
								if(outliers[j]==i) found = true;
 | 
				
			||||||
 | 
							if(!found) sd+=pow(in[i]-mean,2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sd = sqrt(sd/(n-1));
 | 
				
			||||||
 | 
						float rej = (criticalValue*(n-1))/(sqrt(n)*sqrt(n-2+criticalValue));
 | 
				
			||||||
 | 
						bool removed = false;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < in.size() && !removed; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							bool found = false;
 | 
				
			||||||
 | 
							for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
				
			||||||
 | 
								if(outliers[j]==i) found = true;
 | 
				
			||||||
 | 
							if(!found)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(abs((in[i]-mean)/sd) > rej)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Log(Log::DEBUG)<<__func__<<": "<<i<<" is outlier mean: "<<mean<<" sd: "<<sd<<" n: "<<n<<'\n';
 | 
				
			||||||
 | 
									outliers.push_back(i);
 | 
				
			||||||
 | 
									removed = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(removed) thompsonTauTest(in, outliers, criticalValue);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<cv::Point2f>::iterator getTopLeft(std::vector<cv::Point2f>& points)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return std::min_element(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){
 | 
				
			||||||
 | 
							return sqrt(a.y*a.y+a.x*a.x) < sqrt(b.y*b.y+b.x*b.x);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<cv::Point2f>::iterator getBottomRight(std::vector<cv::Point2f>& points)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return std::max_element(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){
 | 
				
			||||||
 | 
							return sqrt(a.y*a.y+a.x*a.x) < sqrt(b.y*b.y+b.x*b.x);
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double distance(const cv::Point2f& a, const cv::Point2f& b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return sqrt((a.y-b.y)*(a.y-b.y)+(a.x-b.x)*(a.x-b.x));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findClosest(size_t& xIndex, size_t& yIndex, 
 | 
				
			||||||
 | 
									 const cv::Point2f point, const std::vector< std::vector<cv::Point2f> >& array, 
 | 
				
			||||||
 | 
									 float xTolerance, float yTolerance)
 | 
				
			||||||
 | 
					{	
 | 
				
			||||||
 | 
						size_t rowBelow = 0;
 | 
				
			||||||
 | 
						while(rowBelow < array.size() && !array[rowBelow].empty() && array[rowBelow][0].y < point.y) ++rowBelow;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(rowBelow == array.size()) rowBelow = array.size()-1;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						size_t rightAbove = 0;
 | 
				
			||||||
 | 
						size_t rightBelow = 0;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						while(rightBelow < array[rowBelow].size() && array[rowBelow][rightBelow].x < point.x ) ++rightBelow;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float distRB = distance(point, array[rowBelow][rightBelow]);
 | 
				
			||||||
 | 
						float distLB = rightBelow > 0 ? distance(point, array[rowBelow][rightBelow-1]) : std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
						float distRA = std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
						float distLA = std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(rowBelow > 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							while(rightAbove < array[rowBelow-1].size() && array[rowBelow-1][rightAbove].x < point.x ) ++rightAbove;
 | 
				
			||||||
 | 
							distRA = distance(point, array[rowBelow-1][rightAbove]);
 | 
				
			||||||
 | 
							if(rightAbove > 0)  distLA = distance(point, array[rowBelow-1][rightAbove-1]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float* min = &distRB;
 | 
				
			||||||
 | 
						if(distLB < *min) min = &distLB;
 | 
				
			||||||
 | 
						if(distRA < *min) min = &distRA;
 | 
				
			||||||
 | 
						if(distLA < *min) min = &distLA;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(min == &distRB)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							yIndex = rowBelow;
 | 
				
			||||||
 | 
							xIndex = rightBelow;
 | 
				
			||||||
 | 
						} 
 | 
				
			||||||
 | 
						else if(min == &distLB)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							yIndex = rowBelow;
 | 
				
			||||||
 | 
							xIndex = rightBelow-1;
 | 
				
			||||||
 | 
						} 
 | 
				
			||||||
 | 
						else if(min == &distRA)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							yIndex = rowBelow-1;
 | 
				
			||||||
 | 
							xIndex = rightBelow;
 | 
				
			||||||
 | 
						} 
 | 
				
			||||||
 | 
						else if(min == &distLA)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							yIndex = rowBelow-1;
 | 
				
			||||||
 | 
							xIndex = rightBelow-1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else return false;
 | 
				
			||||||
 | 
						return abs(array[yIndex][xIndex].x - point.x) < xTolerance && abs(array[yIndex][xIndex].y - point.y) < yTolerance;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findClosest(size_t& index, const cv::Point2f point, const std::vector<cv::Point2f>& array, float xTolerance, float yTolerance)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						float minDistance = std::numeric_limits<float>::max();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool found;
 | 
				
			||||||
 | 
						for(size_t i = 0; i < array.size(); ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							const auto& arrayPoint = array[i];
 | 
				
			||||||
 | 
							if(abs(arrayPoint.x - point.x < xTolerance) && 
 | 
				
			||||||
 | 
							   abs(arrayPoint.y - point.y < yTolerance) &&
 | 
				
			||||||
 | 
							   distance(point, arrayPoint) < minDistance)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								index = i;
 | 
				
			||||||
 | 
								found = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return found;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void interpolateMissing(cv::Mat& mat)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(mat.type() == CV_32FC1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(int y = 0; y < mat.rows; y++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float* col = mat.ptr<float>(y);
 | 
				
			||||||
 | 
							for(int x = 0; x < mat.cols; ++x)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(col[x] < 0) 
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									int closestA = -1;
 | 
				
			||||||
 | 
									int closestB = -1;
 | 
				
			||||||
 | 
									int dist = std::numeric_limits<int>::max();
 | 
				
			||||||
 | 
									for(int i = 0; i < mat.cols; ++i)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										if(i != closestA && col[i] >= 0 && abs(i-x) <= dist)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											closestB = closestA;
 | 
				
			||||||
 | 
											closestA = i;
 | 
				
			||||||
 | 
											dist = abs(i-x);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if(closestA < 0 || closestB < 0)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										closestA = -1;
 | 
				
			||||||
 | 
										closestB = -1;
 | 
				
			||||||
 | 
										dist = std::numeric_limits<int>::max();
 | 
				
			||||||
 | 
										for(int i = mat.cols-1; i >= 0; --i)
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											if(i != closestA && col[i] >= 0 && abs(i-x) <= dist)
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												closestB = closestA;
 | 
				
			||||||
 | 
												closestA = i;
 | 
				
			||||||
 | 
												dist = abs(i-x);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									float slope = (col[closestB] - col[closestA])/(closestB-closestA);
 | 
				
			||||||
 | 
									col[x] = col[closestA] - (closestA-x)*slope;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fillMissing(cv::Mat& mat)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(mat.type() == CV_32FC1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						bool finished = true;
 | 
				
			||||||
 | 
						for(int y = 0; y < mat.rows; y++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float* col = mat.ptr<float>(y);
 | 
				
			||||||
 | 
							for(int x = 0; x < mat.cols; ++x)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if(col[x] < 0 && col[x] > -2) 
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if(y > 0 && mat.at<float>(y-1,x) >= 0)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										col[x] = mat.at<float>(y-1,x);
 | 
				
			||||||
 | 
										finished = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else if(y < mat.rows-1 && mat.at<float>(y+1,x) >= 0)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										col[x] = mat.at<float>(y+1,x);
 | 
				
			||||||
 | 
										finished = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if(col[x] > 0 && ((x+1 < mat.cols && col[x] > col[x+1]) || (x > 0 && col[x] < col[x-1])))
 | 
				
			||||||
 | 
										col[x] = -2;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(!finished) fillMissing(mat);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(mat.type() == CV_32FC1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(mat.cols < 2 || mat.rows < 2) 
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						float centerTop = mat.at<float>(0,mat.cols/2);
 | 
				
			||||||
 | 
						float centerLeft = mat.at<float>(mat.rows/2,0);
 | 
				
			||||||
 | 
						float centerBottom = mat.at<float>(mat.rows-1,mat.cols/2);
 | 
				
			||||||
 | 
						float centerRight = mat.at<float>(mat.rows/2,mat.cols-1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						int left = 0;
 | 
				
			||||||
 | 
						int right = mat.cols-1;
 | 
				
			||||||
 | 
						int top = 0;
 | 
				
			||||||
 | 
						int bottom = mat.rows-1;
 | 
				
			||||||
 | 
						for(; left < mat.cols && mat.at<float>(mat.rows/2,left) == centerLeft; ++left);
 | 
				
			||||||
 | 
						for(; top < mat.rows && mat.at<float>(top,mat.cols/2) == centerTop; ++top);
 | 
				
			||||||
 | 
						for(; right > left && mat.at<float>(mat.rows/2,right) == centerRight; --right);
 | 
				
			||||||
 | 
						for(; bottom > top && mat.at<float>(bottom,mat.cols/2) == centerBottom; --bottom);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						roi.x=left > 0 ? left : 0;
 | 
				
			||||||
 | 
						roi.y=top > 0 ? top : 0;
 | 
				
			||||||
 | 
						roi.width = right-roi.x;
 | 
				
			||||||
 | 
						roi.height = bottom-roi.y;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(roi.width < 0 || roi.height < 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<"Reduced Roi: "<<roi;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool deleteEmptyCols(cv::Mat& mat)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(mat.type() == CV_32FC1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<size_t> 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<float>(x,y) > 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									empty = false;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if(!empty)
 | 
				
			||||||
 | 
								cols.push_back(x);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(mat.cols < static_cast<long int>(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 removeEmptyFrontBackCols(cv::Mat& mat)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						assert(mat.type() == CV_32FC1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int front = 0;
 | 
				
			||||||
 | 
						int back = 0;
 | 
				
			||||||
 | 
						for(int y = 0; y < mat.rows; ++y)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(mat.at<float>(y,0) >= 0) ++front;
 | 
				
			||||||
 | 
							if(mat.at<float>(y,mat.cols-1) >= 0) ++back;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<" front: "<<front<<" back "<<back;
 | 
				
			||||||
 | 
						cv::Rect roi;
 | 
				
			||||||
 | 
						bool frontRej = (front < mat.rows/2);
 | 
				
			||||||
 | 
						bool backRej  = (back < mat.rows/2);
 | 
				
			||||||
 | 
						roi.x=frontRej;
 | 
				
			||||||
 | 
						roi.y=0;
 | 
				
			||||||
 | 
						roi.width = mat.cols - backRej - frontRej;
 | 
				
			||||||
 | 
						roi.height = mat.rows;
 | 
				
			||||||
 | 
						mat = mat(roi);
 | 
				
			||||||
 | 
						if(frontRej || backRej) removeEmptyFrontBackCols(mat);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/matutils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/matutils.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sanityCheckMap(cv::Mat& mat, const float min, const float max, const float minValue, const float maxValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<cv::Point2f>::iterator getTopLeft(std::vector<cv::Point2f>& points);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<cv::Point2f>::iterator getBottomRight(std::vector<cv::Point2f>& points);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double distance(const cv::Point2f& a, const cv::Point2f& b);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findClosest(size_t& xIndex, size_t& yIndex, 
 | 
				
			||||||
 | 
									 const cv::Point2f point, const std::vector< std::vector<cv::Point2f> >& array, 
 | 
				
			||||||
 | 
									 float xTolerance, float yTolerance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findClosest(size_t& index, const cv::Point2f point, const std::vector<cv::Point2f>& array, float xTolerance, float yTolerance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void thompsonTauTest(const std::vector<float>& in, std::vector<size_t>& outliers, float criticalValue);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void interpolateMissing(cv::Mat& mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void fillMissing(cv::Mat& mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool deleteEmptyCols(cv::Mat& mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void removeEmptyFrontBackCols(cv::Mat& mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,22 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
#include <opencv2/imgproc.hpp>
 | 
					#include <opencv2/imgproc.hpp>
 | 
				
			||||||
							
								
								
									
										263
									
								
								src/unwrap.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/unwrap.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,263 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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 "unwrap.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <opencv2/highgui.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/stitching/detail/seam_finders.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/stitching/detail/blenders.hpp>
 | 
				
			||||||
 | 
					#include <opencv2/imgproc.hpp>
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "matutils.h"
 | 
				
			||||||
 | 
					#include "drawing.h"
 | 
				
			||||||
 | 
					#include "log.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void sortIntoRemapMaps(const std::vector<DetectedPoint>& points, cv::Mat& xMat, cv::Mat& yMat, cv::Point2i& topLeftCoordinate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if(points.size() < 6) 
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<__func__<<": at least 6 points are needed";
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						int xMin = std::numeric_limits<int>::max();
 | 
				
			||||||
 | 
						int xMax = std::numeric_limits<int>::min();
 | 
				
			||||||
 | 
						int yMin = std::numeric_limits<int>::max();
 | 
				
			||||||
 | 
						int yMax = std::numeric_limits<int>::min();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(auto& point : points)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::DEBUG)<<"point: "<<point.coordinate.x<<'x'<<point.coordinate.y;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							if(point.coordinate.x > xMax) xMax = point.coordinate.x;
 | 
				
			||||||
 | 
							else if(point.coordinate.x < xMin) xMin = point.coordinate.x;
 | 
				
			||||||
 | 
							if(point.coordinate.y > yMax) yMax = point.coordinate.y;
 | 
				
			||||||
 | 
							else if(point.coordinate.y < yMin) yMin = point.coordinate.y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						topLeftCoordinate.x = xMin;
 | 
				
			||||||
 | 
						topLeftCoordinate.y = yMin;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						size_t xGridSize = xMax-xMin+1;
 | 
				
			||||||
 | 
						size_t yGridSize = yMax-yMin+1;
 | 
				
			||||||
 | 
						xMat.create(cv::Size(xGridSize, yGridSize), CV_32FC1);
 | 
				
			||||||
 | 
						yMat.create(cv::Size(xGridSize, yGridSize), CV_32FC1);
 | 
				
			||||||
 | 
						xMat = -1;
 | 
				
			||||||
 | 
						yMat = -1;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<"Grid: "<<xGridSize<<'x'<<yGridSize;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(int y = 0; y < xMat.rows; y++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							float* colx = xMat.ptr<float>(y);
 | 
				
			||||||
 | 
							float* coly = yMat.ptr<float>(y);
 | 
				
			||||||
 | 
							for(int x = 0; x < xMat.cols; x++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								for(size_t i = 0; i < points.size(); ++i)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if(points[i].coordinate == cv::Point2i(x+xMin,y+yMin))
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										colx[x] = points[i].point.x;
 | 
				
			||||||
 | 
										coly[x] = points[i].point.y;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
									
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool createRemapMap(const cv::Mat& image, RemapMap& out, const std::vector<DetectedPoint>& points, bool verbose)
 | 
				
			||||||
 | 
					{	
 | 
				
			||||||
 | 
						sortIntoRemapMaps(points, out.xMat, out.yMat, out.topLeftCoordinate);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": xMat raw\n"<<out.xMat;
 | 
				
			||||||
 | 
						removeEmptyFrontBackCols(out.xMat);
 | 
				
			||||||
 | 
						removeEmptyFrontBackCols(out.yMat);
 | 
				
			||||||
 | 
						deleteEmptyCols(out.xMat);
 | 
				
			||||||
 | 
						deleteEmptyCols(out.yMat);
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": xMat rejcted\n"<<out.xMat;
 | 
				
			||||||
 | 
						fillMissing(out.xMat);
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<__func__<<": xMat filled\n"<<out.xMat;
 | 
				
			||||||
 | 
						interpolateMissing(out.xMat);
 | 
				
			||||||
 | 
						interpolateMissing(out.yMat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sanityCheckMap(out.xMat, 0, image.cols-1, -1, -1);
 | 
				
			||||||
 | 
						sanityCheckMap(out.yMat, 0, image.rows-1, -1, -1);
 | 
				
			||||||
 | 
						fillMissing(out.xMat);
 | 
				
			||||||
 | 
						interpolateMissing(out.xMat);
 | 
				
			||||||
 | 
						interpolateMissing(out.yMat);
 | 
				
			||||||
 | 
						sanityCheckMap(out.xMat, 0, image.cols-1, 0, image.cols-1);
 | 
				
			||||||
 | 
						sanityCheckMap(out.yMat, 0, image.rows-1, 0, image.rows-1);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::INFO)<<__func__<<": xMat \n"<<out.xMat;
 | 
				
			||||||
 | 
						Log(Log::INFO)<<__func__<<": yMat \n"<<out.yMat;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(out.xMat.cols < 3 || out.xMat.rows < 3)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<"Error creating map, to few points with high confidence";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(verbose)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							RemapedImage remaped = applyRemap(image, out);
 | 
				
			||||||
 | 
							cv::imshow( "Viewer", remaped.image );
 | 
				
			||||||
 | 
							cv::waitKey(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool saveRemapMap(const RemapMap& map, const std::string& fileName)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cv::FileStorage matf(fileName+".mat", cv::FileStorage::WRITE );
 | 
				
			||||||
 | 
						matf<<"xmat"<<map.xMat<<"ymat"<<map.yMat<<"origin"<<map.topLeftCoordinate;
 | 
				
			||||||
 | 
						matf.release();
 | 
				
			||||||
 | 
						Log(Log::INFO)<<"Unwrap maps saved to "<<fileName<<".mat";
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RemapMap loadRemapMap(const std::string& fileName)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cv::FileStorage fs(fileName, cv::FileStorage::READ);
 | 
				
			||||||
 | 
						if (!fs.isOpened())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Log(Log::ERROR)<<"could not open maps file "<<fileName;
 | 
				
			||||||
 | 
							return RemapMap();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RemapMap map;
 | 
				
			||||||
 | 
						fs["xmat"]>>map.xMat;
 | 
				
			||||||
 | 
						fs["ymat"]>>map.yMat;
 | 
				
			||||||
 | 
						fs["origin"]>>map.topLeftCoordinate;
 | 
				
			||||||
 | 
						return map;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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::resize(map.xMat, xMapResized, outputSize, cv::INTER_LINEAR);
 | 
				
			||||||
 | 
						cv::resize(map.yMat, yMapResized, outputSize, cv::INTER_LINEAR);
 | 
				
			||||||
 | 
						cv::Rect roi;
 | 
				
			||||||
 | 
						cv::Mat xMapRed;
 | 
				
			||||||
 | 
						cv::Mat yMapRed;
 | 
				
			||||||
 | 
						if(findDeadSpace(xMapResized, roi))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							xMapRed = xMapResized(roi);
 | 
				
			||||||
 | 
							yMapRed = yMapResized(roi);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							xMapRed = xMapResized;
 | 
				
			||||||
 | 
							yMapRed = yMapResized;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cv::remap(image, out.image,  xMapRed, yMapRed, cv::INTER_LINEAR);
 | 
				
			||||||
 | 
						out.origin = cv::Point2i(map.topLeftCoordinate.x*map.outputCellSize, map.topLeftCoordinate.y*map.outputCellSize);
 | 
				
			||||||
 | 
						return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cv::Mat simpleStich(const std::vector<RemapedImage>& images)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if(images.size() < 1)
 | 
				
			||||||
 | 
						return cv::Mat();
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						cv::Size outputSize(0,0);
 | 
				
			||||||
 | 
						for(auto& image : images)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if(outputSize.width < image.image.cols+image.origin.x)
 | 
				
			||||||
 | 
								outputSize.width = image.image.cols+image.origin.x;
 | 
				
			||||||
 | 
							if(outputSize.height < image.image.rows+image.origin.y)
 | 
				
			||||||
 | 
								outputSize.height = image.image.rows+image.origin.y;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							Log(Log::DEBUG)<<"image: "<<image.image.rows<<'x'<<image.image.cols<<" at "<<image.origin.x<<'x'<<image.origin.y;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						Log(Log::DEBUG)<<"outputSize: "<<outputSize;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						cv::Mat out(outputSize, images[0].image.type(), cv::Scalar::all(0));
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						for(auto& image : images)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							cv::Rect roi(image.origin, image.image.size());
 | 
				
			||||||
 | 
							image.image.copyTo(out(roi));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cv::Mat stich(const std::vector<RemapedImage>& images, bool seamAdjust)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<cv::Mat> masks(images.size());
 | 
				
			||||||
 | 
						std::vector<cv::Point> corners(images.size());
 | 
				
			||||||
 | 
						std::vector<cv::Size> sizes(images.size());
 | 
				
			||||||
 | 
						for (size_t i = 0; i < images.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							masks[i].create(images[i].image.size(), CV_8U);
 | 
				
			||||||
 | 
							masks[i].setTo(cv::Scalar::all(255));
 | 
				
			||||||
 | 
							corners[i] = images[i].origin;
 | 
				
			||||||
 | 
							sizes[i] = images[i].image.size();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if(seamAdjust)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::vector<cv::UMat> images32f(images.size());
 | 
				
			||||||
 | 
							std::vector<cv::UMat> masksUmat(images.size());
 | 
				
			||||||
 | 
							for (size_t i = 0; i < images.size(); i++) 
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								images[i].image.convertTo(images32f[i], CV_32F);
 | 
				
			||||||
 | 
								masks[i].copyTo(masksUmat[i]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							cv::detail::VoronoiSeamFinder seamFinder;
 | 
				
			||||||
 | 
							seamFinder.find(images32f, corners, masksUmat);
 | 
				
			||||||
 | 
							for (size_t i = 0; i < images.size(); i++) 
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								masksUmat[i].copyTo(masks[i]);
 | 
				
			||||||
 | 
								masksUmat[i].release();
 | 
				
			||||||
 | 
								images32f[i].release();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							images32f.clear();
 | 
				
			||||||
 | 
							masksUmat.clear();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cv::Ptr<cv::detail::Blender> blender;
 | 
				
			||||||
 | 
						blender = cv::detail::Blender::createDefault(cv::detail::Blender::Blender::MULTI_BAND, false);
 | 
				
			||||||
 | 
						cv::detail::MultiBandBlender* mb = dynamic_cast<cv::detail::MultiBandBlender*>(blender.get());
 | 
				
			||||||
 | 
						mb->setNumBands(5);
 | 
				
			||||||
 | 
						blender->prepare(corners, sizes);
 | 
				
			||||||
 | 
						for (size_t i = 0; i < images.size(); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							blender->feed(images[i].image, masks[i], corners[i]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cv::Mat result;
 | 
				
			||||||
 | 
						cv::Mat result_mask;
 | 
				
			||||||
 | 
						blender->blend(result, result_mask);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/unwrap.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/unwrap.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					* 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.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					#include <opencv2/core/ocl.hpp>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include "detectedpoint.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RemapedImage
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cv::Mat image;
 | 
				
			||||||
 | 
						cv::Point2i origin;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RemapMap
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cv::Mat xMat;
 | 
				
			||||||
 | 
						cv::Mat yMat;
 | 
				
			||||||
 | 
						cv::Point2i topLeftCoordinate;
 | 
				
			||||||
 | 
						unsigned int outputCellSize;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool saveRemapMap(const RemapMap& map, const std::string& fileName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RemapMap loadRemapMap(const std::string& fileName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool createRemapMap(const cv::Mat& image, RemapMap& out, const std::vector<DetectedPoint>& points, bool verbose = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RemapedImage applyRemap(const cv::Mat& image, const RemapMap &map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cv::Mat stich(const std::vector<RemapedImage>& images, bool seamAdjust = false);
 | 
				
			||||||
 | 
					cv::Mat simpleStich(const std::vector<RemapedImage>& images);
 | 
				
			||||||
							
								
								
									
										568
									
								
								unwrap.cpp
									
										
									
									
									
								
							
							
						
						
									
										568
									
								
								unwrap.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -1,568 +0,0 @@
 | 
				
			||||||
#include "unwrap.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <opencv2/highgui.hpp>
 | 
					 | 
				
			||||||
#include <opencv2/imgproc.hpp>
 | 
					 | 
				
			||||||
#include <math.h>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <iostream>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "matutils.h"
 | 
					 | 
				
			||||||
#include "drawing.h"
 | 
					 | 
				
			||||||
#include "log.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct DisplacmentMap
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	std::vector< std::vector<cv::Point2f> > destination;
 | 
					 | 
				
			||||||
	std::vector< std::vector<cv::Point2f> > source;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::vector< std::vector<cv::Point2f > > sortPointsIntoRows(std::vector<cv::Point2f>& points)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(points.size() < 6) return std::vector< std::vector<cv::Point2f> >();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	cv::Point2f topLeft(*getTopLeft(points));
 | 
					 | 
				
			||||||
	cv::Point2f bottomRight(*getBottomRight(points));
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<"topLeft "<<topLeft.x<<' '<<topLeft.y;
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<"bottomRight "<<bottomRight.x<<' '<<bottomRight.y;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float fuzz = (bottomRight.x-topLeft.x)*0.05f;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < points.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(points[i].x < topLeft.x-fuzz || points[i].y < topLeft.y-fuzz || 
 | 
					 | 
				
			||||||
			points[i].x > bottomRight.x+fuzz || points[i].y > bottomRight.y+fuzz)
 | 
					 | 
				
			||||||
			points.erase(points.begin()+i);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::sort(points.begin(), points.end(), [](const cv::Point2f& a, const cv::Point2f& b){return a.y < b.y;});
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	double accumulator = points[0].y+points[1].y;
 | 
					 | 
				
			||||||
	size_t accuCount = 2;
 | 
					 | 
				
			||||||
	float sigDist = (bottomRight.y-topLeft.y) / 20;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector< std::vector<cv::Point2f> > result(1);
 | 
					 | 
				
			||||||
	result.back().push_back(points[0]);
 | 
					 | 
				
			||||||
	result.back().push_back(points[1]);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(size_t i = 2; i < points.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if( points[i].y - accumulator/accuCount > sigDist )
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(points.size() - i - 3 < 0) break;
 | 
					 | 
				
			||||||
			else 
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				accumulator = points[i+1].y+points[i+2].y;
 | 
					 | 
				
			||||||
				accuCount = 2;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			result.push_back(std::vector<cv::Point2f>());
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		result.back().push_back(points[i]);
 | 
					 | 
				
			||||||
		accumulator += points[i].y;
 | 
					 | 
				
			||||||
		++accuCount;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(auto& row : result)  std::sort(row.begin(), row.end(), [](const cv::Point2f& a, const cv::Point2f& b){return a.x < b.x;});
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static float detimineXPitch(const std::vector<cv::Point2f>& row, float fuzz = 1.3f)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	std::vector<float> xRowDists;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < row.size()-1; ++i)
 | 
					 | 
				
			||||||
		xRowDists.push_back(abs(row[i+1].x-row[i].x));
 | 
					 | 
				
			||||||
	float xMinDist = *std::min(xRowDists.begin(), xRowDists.end());
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMinDist "<<xMinDist;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float meanXDistAccum = 0;
 | 
					 | 
				
			||||||
	size_t validCount = 0;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < xRowDists.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(xRowDists[i] < xMinDist*fuzz)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			++validCount;
 | 
					 | 
				
			||||||
			meanXDistAccum+=xRowDists[i];
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return meanXDistAccum/validCount;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::vector<cv::RotatedRect> fitEllipses(std::vector< std::vector<cv::Point2f > >& rows, bool remove = true)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(rows.empty()) return std::vector<cv::RotatedRect>();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector<cv::RotatedRect> ellipses;
 | 
					 | 
				
			||||||
	ellipses.reserve(rows.size());
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < rows.size(); ++i) 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(rows[i].size() > 4) ellipses.push_back(cv::fitEllipse(rows[i]));
 | 
					 | 
				
			||||||
		else 
 | 
					 | 
				
			||||||
		{ 
 | 
					 | 
				
			||||||
			rows.erase(rows.begin()+i);
 | 
					 | 
				
			||||||
			i--;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ellipses;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void thompsonTauTest(const std::vector<float>& in, std::vector<size_t>& outliers, float criticalValue)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	float mean = 0;
 | 
					 | 
				
			||||||
	size_t n = 0;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < in.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
		for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
					 | 
				
			||||||
			if(outliers[j]==i) found = true;
 | 
					 | 
				
			||||||
		if(!found)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			mean+=in[i];
 | 
					 | 
				
			||||||
			++n;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	mean/=n;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float sd = 0;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < in.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
		for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
					 | 
				
			||||||
			if(outliers[j]==i) found = true;
 | 
					 | 
				
			||||||
		if(!found) sd+=pow(in[i]-mean,2);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	sd = sqrt(sd/(n-1));
 | 
					 | 
				
			||||||
	float rej = (criticalValue*(n-1))/(sqrt(n)*sqrt(n-2+criticalValue));
 | 
					 | 
				
			||||||
	bool removed = false;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < in.size() && !removed; ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
		for(size_t j = 0; j < outliers.size() && !found; ++j) 
 | 
					 | 
				
			||||||
			if(outliers[j]==i) found = true;
 | 
					 | 
				
			||||||
		if(!found)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if(abs((in[i]-mean)/sd) > rej)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Log(Log::DEBUG)<<__func__<<": "<<i<<" is outlier mean: "<<mean<<" sd: "<<sd<<" n: "<<n<<'\n';
 | 
					 | 
				
			||||||
				outliers.push_back(i);
 | 
					 | 
				
			||||||
				removed = true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if(removed) thompsonTauTest(in, outliers, criticalValue);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool sanityCheckElipses(std::vector< std::vector<cv::Point2f > >& rows, std::vector<cv::RotatedRect>& elipses)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(rows.size() != elipses.size() && elipses.size() > 1) return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < elipses.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float angDiff = fmod(elipses[i].angle,90);
 | 
					 | 
				
			||||||
		if(angDiff < 90-5 && angDiff > 5)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			elipses.erase(elipses.begin()+i);
 | 
					 | 
				
			||||||
			rows.erase(rows.begin()+i);
 | 
					 | 
				
			||||||
			--i;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector<float> widths;
 | 
					 | 
				
			||||||
	std::vector<float> heights;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(auto& elipse : elipses)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		widths.push_back(elipse.size.width);
 | 
					 | 
				
			||||||
		heights.push_back(elipse.size.height);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector<size_t> outliersW;
 | 
					 | 
				
			||||||
	std::vector<size_t> outliersH;
 | 
					 | 
				
			||||||
	thompsonTauTest(widths, outliersW, 2);
 | 
					 | 
				
			||||||
	thompsonTauTest(heights, outliersH, 2);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	std::vector< std::vector<cv::Point2f > > rowsReduced;
 | 
					 | 
				
			||||||
	std::vector<cv::RotatedRect> elipsesReduced;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < elipses.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bool found = false;
 | 
					 | 
				
			||||||
		for(size_t j = 0; j < outliersW.size() && !found; ++j) 
 | 
					 | 
				
			||||||
			if(outliersW[j]==i) found = true;
 | 
					 | 
				
			||||||
		for(size_t j = 0; j < outliersH.size() && !found; ++j) 
 | 
					 | 
				
			||||||
			if(outliersH[j]==i) found = true;
 | 
					 | 
				
			||||||
		if(!found)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			rowsReduced.push_back(rows[i]);
 | 
					 | 
				
			||||||
			elipsesReduced.push_back(elipses[i]);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	elipses = elipsesReduced;
 | 
					 | 
				
			||||||
	rows = rowsReduced;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static DisplacmentMap calcDisplacementMap(const std::vector< std::vector<cv::Point2f > >& rows, 
 | 
					 | 
				
			||||||
											   const std::vector<cv::RotatedRect>& elipses)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(rows.size() < 2 || rows.size() != elipses.size()) {
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<__func__<<": rows < 2 or rows != elipses";
 | 
					 | 
				
			||||||
		return DisplacmentMap();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	DisplacmentMap displacmentmap;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	displacmentmap.destination.assign(rows.size(), std::vector<cv::Point2f>());
 | 
					 | 
				
			||||||
	displacmentmap.source = rows;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < displacmentmap.destination.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		displacmentmap.destination[i].reserve(rows[i].size());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float meanYdist = 0;
 | 
					 | 
				
			||||||
	size_t j = 0;
 | 
					 | 
				
			||||||
	while(j < elipses.size()-1)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		meanYdist += elipses[j+1].center.y-elipses[j].center.y;
 | 
					 | 
				
			||||||
		++j;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	meanYdist = meanYdist/elipses.size();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": meanYdist "<<meanYdist;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(size_t rowCnt = 0; rowCnt < rows.size(); ++rowCnt)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		const std::vector<cv::Point2f>& row = rows[rowCnt];
 | 
					 | 
				
			||||||
		const cv::RotatedRect& elipse = elipses[rowCnt];
 | 
					 | 
				
			||||||
				
 | 
					 | 
				
			||||||
		cv::Rect_<float> boundingRect = elipse.boundingRect2f();
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		Log(Log::DEBUG)<<__func__<<": Proc row "<<rowCnt;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		for(size_t i = 0; i < row.size(); ++i)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			float yDest = rowCnt*meanYdist;
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
			double normDist = ((row[i].x - boundingRect.x)/boundingRect.width-0.5)*2;
 | 
					 | 
				
			||||||
			double tau = asin(normDist);
 | 
					 | 
				
			||||||
			float xDest = (((2*tau)/M_PI)*500)+500;
 | 
					 | 
				
			||||||
			Log(Log::DEBUG)<<__func__<<": normDist "<<normDist<<" tau "<<tau<<" xDest "<<xDest;
 | 
					 | 
				
			||||||
			displacmentmap.destination[rowCnt].push_back(cv::Point2f(xDest,yDest));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return displacmentmap;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void removeSparseCollums(cv::Mat& mat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int front = 0;
 | 
					 | 
				
			||||||
	int back = 0;
 | 
					 | 
				
			||||||
	for(int y = 0; y < mat.rows; ++y)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if(mat.at<float>(y,0) >= 0) ++front;
 | 
					 | 
				
			||||||
		if(mat.at<float>(y,mat.cols-1) >= 0) ++back;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<" front: "<<front<<" back "<<back;
 | 
					 | 
				
			||||||
	cv::Rect roi;
 | 
					 | 
				
			||||||
	bool frontRej = (front < mat.rows/2);
 | 
					 | 
				
			||||||
	bool backRej  = (back < mat.rows/2);
 | 
					 | 
				
			||||||
	roi.x=frontRej;
 | 
					 | 
				
			||||||
	roi.y=0;
 | 
					 | 
				
			||||||
	roi.width = mat.cols - backRej - frontRej;
 | 
					 | 
				
			||||||
	roi.height = mat.rows;
 | 
					 | 
				
			||||||
	mat = mat(roi);
 | 
					 | 
				
			||||||
	if(frontRej || backRej) removeSparseCollums(mat);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void generateRemapMaps(const DisplacmentMap& map, cv::Mat& xMat, cv::Mat& yMat, float fuzz = 1.3f)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(map.destination.size() < 2) 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<__func__<<": at least 2 rows are needed";
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float yMeanDist = map.destination[1][0].y-map.destination[0][0].y;
 | 
					 | 
				
			||||||
	float xMeanDist = 0;
 | 
					 | 
				
			||||||
	for(size_t i = 0; i < map.destination.size(); ++i)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		xMeanDist+=detimineXPitch(map.destination[i]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	xMeanDist/=map.destination.size();
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMeanDist "<<xMeanDist;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float xMin = std::numeric_limits<float>::max();
 | 
					 | 
				
			||||||
	float xMeanMin = 0;
 | 
					 | 
				
			||||||
	float xMax = std::numeric_limits<float>::min();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(auto& row: map.destination )
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		xMeanMin+=row.front().x;
 | 
					 | 
				
			||||||
		if(xMin > row.front().x )
 | 
					 | 
				
			||||||
			xMin = row.front().x;
 | 
					 | 
				
			||||||
		if(xMax < row.back().x)
 | 
					 | 
				
			||||||
			xMax = row.back().x;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	xMeanMin = xMeanMin / map.destination.size();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": Grid: xMin "<<xMin;
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": Grid: grid xMax "<<xMax;
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": Grid: grid xMeanMin "<<xMeanMin;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	size_t xGridSize = static_cast<size_t>(std::lround(abs((xMax-xMin)/xMeanDist))+1);
 | 
					 | 
				
			||||||
	xMat = cv::Mat::zeros(cv::Size(xGridSize, map.destination.size()), CV_32FC1);
 | 
					 | 
				
			||||||
	yMat = cv::Mat::zeros(cv::Size(xGridSize, map.destination.size()), CV_32FC1);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<"Grid: "<<xGridSize<<'x'<<map.destination.size();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(int y = 0; y < xMat.rows; y++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float* colx = xMat.ptr<float>(y);
 | 
					 | 
				
			||||||
		float* coly = yMat.ptr<float>(y);
 | 
					 | 
				
			||||||
		for(int x = 0; x < xMat.cols; x++)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size_t xIndex = 0;
 | 
					 | 
				
			||||||
			size_t yIndex = 0;
 | 
					 | 
				
			||||||
			bool found = findClosest(xIndex, yIndex, 
 | 
					 | 
				
			||||||
									 cv::Point2f((x)*xMeanDist+xMin, (y)*yMeanDist), 
 | 
					 | 
				
			||||||
									 map.destination, (2*xMeanDist)/5, (2*yMeanDist)/5);
 | 
					 | 
				
			||||||
			Log(Log::DEBUG)<<__func__<<": found: "<<found<<' '<<xIndex<<'x'<<yIndex<<" at: "<<(x)*xMeanDist+xMin<<'x'<<(y)*yMeanDist;
 | 
					 | 
				
			||||||
			colx[x] = found ? map.source[yIndex][xIndex].x : -1;
 | 
					 | 
				
			||||||
			coly[x] = found ? map.source[yIndex][xIndex].y : -1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat raw\n"<<xMat;
 | 
					 | 
				
			||||||
	removeSparseCollums(xMat);
 | 
					 | 
				
			||||||
	removeSparseCollums(yMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat rejcted\n"<<xMat;
 | 
					 | 
				
			||||||
	fillMissing(xMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat filled\n"<<xMat;
 | 
					 | 
				
			||||||
	interpolateMissing(xMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": yMat raw \n"<<yMat;
 | 
					 | 
				
			||||||
	interpolateMissing(yMat);
 | 
					 | 
				
			||||||
	Log(Log::INFO)<<__func__<<": xMat \n"<<xMat;
 | 
					 | 
				
			||||||
	Log(Log::INFO)<<__func__<<": yMat \n"<<yMat;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void generateRemapMaps(const std::vector<cv::Point2f>& points, const std::vector<cv::Point2i>& coordinates, cv::Mat& xMat, cv::Mat& yMat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(points.size() < 6 || coordinates.size() != points.size()) 
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<__func__<<": at least 6 points are needed";
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	int xMin = std::numeric_limits<int>::max();
 | 
					 | 
				
			||||||
	int xMax = std::numeric_limits<int>::min();
 | 
					 | 
				
			||||||
	int yMin = std::numeric_limits<int>::max();
 | 
					 | 
				
			||||||
	int yMax = std::numeric_limits<int>::min();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(auto& point : coordinates)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::DEBUG)<<"point: "<<point.x<<'x'<<point.y;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(point.x > xMax) xMax = point.x;
 | 
					 | 
				
			||||||
		else if(point.x < xMin) xMin = point.x;
 | 
					 | 
				
			||||||
		if(point.y > yMax) yMax = point.y;
 | 
					 | 
				
			||||||
		else if(point.y < yMin) yMin = point.y;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	size_t xGridSize = xMax-xMin+1;
 | 
					 | 
				
			||||||
	size_t yGridSize = yMax-yMin+1;
 | 
					 | 
				
			||||||
	xMat = cv::Mat::zeros(cv::Size(xGridSize, yGridSize), CV_32FC1);
 | 
					 | 
				
			||||||
	yMat = cv::Mat::zeros(cv::Size(xGridSize, yGridSize), CV_32FC1);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<"Grid: "<<xGridSize<<'x'<<yGridSize;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for(int y = 0; y < xMat.rows; y++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		float* colx = xMat.ptr<float>(y);
 | 
					 | 
				
			||||||
		float* coly = yMat.ptr<float>(y);
 | 
					 | 
				
			||||||
		for(int x = 0; x < xMat.cols; x++)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			colx[x] = -1;
 | 
					 | 
				
			||||||
			coly[x] = -1;
 | 
					 | 
				
			||||||
			for(size_t i = 0; i < coordinates.size(); ++i)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if(coordinates[i] == cv::Point2i(x+xMin,y+yMin))
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					colx[x] = points[i].x;
 | 
					 | 
				
			||||||
					coly[x] = points[i].y;
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
				
 | 
					 | 
				
			||||||
			
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat raw\n"<<xMat;
 | 
					 | 
				
			||||||
	removeSparseCollums(xMat);
 | 
					 | 
				
			||||||
	removeSparseCollums(yMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat rejcted\n"<<xMat;
 | 
					 | 
				
			||||||
	fillMissing(xMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": xMat filled\n"<<xMat;
 | 
					 | 
				
			||||||
	interpolateMissing(xMat);
 | 
					 | 
				
			||||||
	Log(Log::DEBUG)<<__func__<<": yMat raw \n"<<yMat;
 | 
					 | 
				
			||||||
	interpolateMissing(yMat);
 | 
					 | 
				
			||||||
	Log(Log::INFO)<<__func__<<": xMat \n"<<xMat;
 | 
					 | 
				
			||||||
	Log(Log::INFO)<<__func__<<": yMat \n"<<yMat;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool createRemapMap(cv::Mat& image, std::vector<cv::Point2f> points, const std::vector<cv::Point2i>& coordinates, 
 | 
					 | 
				
			||||||
					const std::string& fileName, bool verbose)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cv::Mat xMat;
 | 
					 | 
				
			||||||
	cv::Mat yMat;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(coordinates.empty())
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		std::vector< std::vector<cv::Point2f > > rows = sortPointsIntoRows(points);
 | 
					 | 
				
			||||||
		Log(Log::INFO)<<"Found "<<rows.size()<<" rows";
 | 
					 | 
				
			||||||
		if(rows.size() < 2)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Log(Log::ERROR)<<"Error creating map, insufficant rows detected";
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(verbose)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			cv::Mat pointsMat = cv::Mat::zeros(image.size(), CV_8UC3);
 | 
					 | 
				
			||||||
			drawRows(pointsMat, rows);
 | 
					 | 
				
			||||||
			cv::imshow( "Viewer", pointsMat );
 | 
					 | 
				
			||||||
			cv::waitKey(0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		std::vector< cv::RotatedRect > ellipses = fitEllipses(rows);
 | 
					 | 
				
			||||||
		Log(Log::INFO)<<"Found "<<ellipses.size()<<" ellipses. rows reduced to "<<rows.size();
 | 
					 | 
				
			||||||
		if(ellipses.size() < 3)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Log(Log::ERROR)<<"Error creating map, insufficant ellipses detected";
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(verbose)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			cv::Mat pointsMat = cv::Mat::zeros(image.size(), CV_8UC3);
 | 
					 | 
				
			||||||
			drawRows(pointsMat, rows);
 | 
					 | 
				
			||||||
			drawEllipses(pointsMat, ellipses);
 | 
					 | 
				
			||||||
			cv::imshow( "Viewer", pointsMat );
 | 
					 | 
				
			||||||
			cv::waitKey(0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		sanityCheckElipses(rows, ellipses);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(verbose)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			cv::Mat pointsMat = cv::Mat::zeros(image.size(), CV_8UC3);
 | 
					 | 
				
			||||||
			drawRows(pointsMat, rows);
 | 
					 | 
				
			||||||
			drawEllipses(pointsMat, ellipses);
 | 
					 | 
				
			||||||
			cv::imshow( "Viewer", pointsMat );
 | 
					 | 
				
			||||||
			cv::waitKey(0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		DisplacmentMap dispMap = calcDisplacementMap(rows, ellipses);
 | 
					 | 
				
			||||||
		if(dispMap.destination.size() < 2)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			Log(Log::ERROR)<<"Error creating map, unable to calculate destination points";
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		cv::Mat dispPointsDest = cv::Mat::zeros(cv::Size(cv::Size(1000,1000)), CV_8UC3);
 | 
					 | 
				
			||||||
		drawRows(dispPointsDest, dispMap.destination);
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
		if(verbose)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			cv::imshow( "Viewer", dispPointsDest );
 | 
					 | 
				
			||||||
			cv::waitKey(0);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		generateRemapMaps(dispMap, xMat, yMat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		generateRemapMaps(points, coordinates, xMat, yMat);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	sanityCheckMap(xMat, 0, image.cols-1, -1, -1);
 | 
					 | 
				
			||||||
	sanityCheckMap(yMat, 0, image.rows-1, -1, -1);
 | 
					 | 
				
			||||||
	fillMissing(xMat);
 | 
					 | 
				
			||||||
	interpolateMissing(xMat);
 | 
					 | 
				
			||||||
	interpolateMissing(yMat);
 | 
					 | 
				
			||||||
	sanityCheckMap(xMat, 0, image.cols-1, 0, image.cols-1);
 | 
					 | 
				
			||||||
	sanityCheckMap(yMat, 0, image.rows-1, 0, image.rows-1);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(xMat.cols < 3 || xMat.rows < 3)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Log(Log::ERROR)<<"Error creating map, to few points with high confidence";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	cv::FileStorage matf(fileName+".mat", cv::FileStorage::WRITE );
 | 
					 | 
				
			||||||
	matf<<"xmat"<<xMat<<"ymat"<<yMat;
 | 
					 | 
				
			||||||
	matf.release();
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	Log(Log::INFO)<<"Unwrap maps saved to "<<fileName<<".mat";
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	if(verbose)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		cv::Mat remaped;
 | 
					 | 
				
			||||||
		applyRemap(image, remaped, xMat, yMat, 800);
 | 
					 | 
				
			||||||
		cv::imshow( "Viewer", remaped );
 | 
					 | 
				
			||||||
		cv::waitKey(0);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool findDeadSpace(const cv::Mat& mat, cv::Rect& roi)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if(mat.cols < 2 || mat.rows < 2) return false;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	float centerTop = mat.at<float>(0,mat.cols/2);
 | 
					 | 
				
			||||||
	float centerLeft = mat.at<float>(mat.rows/2,0);
 | 
					 | 
				
			||||||
	float centerBottom = mat.at<float>(mat.rows-1,mat.cols/2);
 | 
					 | 
				
			||||||
	float centerRight = mat.at<float>(mat.rows/2,mat.cols-1);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	int left = 0;
 | 
					 | 
				
			||||||
	int right = mat.cols-1;
 | 
					 | 
				
			||||||
	int top = 0;
 | 
					 | 
				
			||||||
	int bottom = mat.rows-1;
 | 
					 | 
				
			||||||
	for(; left < mat.cols && mat.at<float>(mat.rows/2,left) == centerLeft; ++left);
 | 
					 | 
				
			||||||
	for(; top < mat.rows && mat.at<float>(top,mat.cols/2) == centerTop; ++top);
 | 
					 | 
				
			||||||
	for(; right > left && mat.at<float>(mat.rows/2,right) == centerRight; --right);
 | 
					 | 
				
			||||||
	for(; bottom > top && mat.at<float>(bottom,mat.cols/2) == centerBottom; --bottom);
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	roi.x=left;
 | 
					 | 
				
			||||||
	roi.y=top;
 | 
					 | 
				
			||||||
	roi.width = right-left;
 | 
					 | 
				
			||||||
	roi.height = bottom-top;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void applyRemap(cv::Mat& image, cv::Mat& out, const cv::Mat& xmap, const cv::Mat& ymap, unsigned int outputSize)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cv::Mat xMapResized;
 | 
					 | 
				
			||||||
	cv::Mat yMapResized;
 | 
					 | 
				
			||||||
	cv::resize(xmap, xMapResized, cv::Size(outputSize,outputSize*(xmap.rows/(double)xmap.cols)), cv::INTER_CUBIC);
 | 
					 | 
				
			||||||
	cv::resize(ymap, yMapResized, cv::Size(outputSize,outputSize*(xmap.rows/(double)xmap.cols)), cv::INTER_CUBIC);
 | 
					 | 
				
			||||||
	cv::Rect roi;
 | 
					 | 
				
			||||||
	cv::Mat xMapRed;
 | 
					 | 
				
			||||||
	cv::Mat yMapRed;
 | 
					 | 
				
			||||||
	if(!findDeadSpace(xMapResized, roi))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		xMapRed = xMapResized(roi);
 | 
					 | 
				
			||||||
		yMapRed = yMapResized(roi);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		xMapRed = xMapResized;
 | 
					 | 
				
			||||||
		yMapRed = yMapResized;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	cv::remap(image, out,  xMapRed, yMapRed, cv::INTER_LINEAR);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										7
									
								
								unwrap.h
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								unwrap.h
									
										
									
									
									
								
							| 
						 | 
					@ -1,7 +0,0 @@
 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
#include <opencv2/core/ocl.hpp>
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool createRemapMap(cv::Mat& image, std::vector<cv::Point2f> points, const std::vector<cv::Point2i>& coordinates, const std::string& fileName, bool verbose = false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void applyRemap(cv::Mat& image, cv::Mat& out, const cv::Mat& xmap, const cv::Mat& ymap, unsigned int size);
 | 
					 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue