/** * 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 #include #include #include #include "uvosunwrap/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(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& in, std::vector& 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__<<": "<::iterator getTopLeft(std::vector& 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::iterator getBottomRight(std::vector& 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 >& 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::max(); float distRA = std::numeric_limits::max(); float distLA = std::numeric_limits::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& array, float xTolerance, float yTolerance) { float minDistance = std::numeric_limits::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 interpolateMissingOnX(cv::Mat& mat) { assert(mat.type() == CV_32FC1); for(int y = 0; y < mat.rows; y++) { float* col = mat.ptr(y); for(int x = 0; x < mat.cols; ++x) { if(col[x] < 0) { int closestA = -1; int closestB = -1; int dist = std::numeric_limits::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::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 interpolateMissingOnY(cv::Mat& mat) { assert(mat.type() == CV_32FC1); for(int x = 0; x < mat.cols; ++x) { for(int y = 0; y < mat.rows; y++) { if(mat.at(y,x) < 0) { int closestA = -1; int closestB = -1; int dist = std::numeric_limits::max(); for(int i = 0; i < mat.rows; i++) { if(i != closestA && mat.at(i,x) >= 0 && abs(i-y) <= dist) { closestB = closestA; closestA = i; dist = abs(i-y); } } if(closestA < 0 || closestB < 0) { closestA = -1; closestB = -1; dist = std::numeric_limits::max(); for(int i = mat.rows-1; i >= 0; --i) { if(i != closestA && mat.at(i,x) >= 0 && abs(i-y) <= dist) { closestB = closestA; closestA = i; dist = abs(i-y); } } } float slope = (mat.at(closestB,x) - mat.at(closestA,x))/(closestB-closestA); mat.at(y,x) = mat.at(closestA,x) - (closestA-y)*slope; } } } } 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(0,mat.cols/2); float centerLeft = mat.at(mat.rows/2,0); float centerBottom = mat.at(mat.rows-1,mat.cols/2); float centerRight = mat.at(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(mat.rows/2,left) == centerLeft; ++left); for(; top < mat.rows && mat.at(top,mat.cols/2) == centerTop; ++top); for(; right > left && mat.at(mat.rows/2,right) == centerRight; --right); for(; bottom > top && mat.at(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: "<(y,0) >= 0) || (!front && mat.at(y,mat.cols-1) >= 0)) ++count; } cv::Rect roi; bool rej = (count < mat.rows*reject); roi.x=front ? rej : 0; roi.y=0; roi.width = mat.cols - rej; roi.height = mat.rows; mat = mat(roi); if(rej) removeSparseCols(mat, reject, front); } static float linInterpolate(float A, float B, float x) { return A * (1.0f - x) + B * x; } template static Type resizePointBilinear(const cv::Mat& inImage, float scaledX, float scaledY, float xFrac, float yFrac) { int u0 = static_cast(scaledX); int v0 = static_cast(scaledY); int u1 = std::min(inImage.cols-1, static_cast(scaledX)+1); int v1 = v0; int u2 = u0; int v2 = std::min(inImage.rows-1, static_cast(scaledY)+1); int u3 = std::min(inImage.cols-1, static_cast(scaledX)+1); int v3 = std::min(inImage.rows-1, static_cast(scaledY)+1); float col0 = linInterpolate(inImage.at(v0, u0), inImage.at(v1, u1), xFrac); float col1 = linInterpolate(inImage.at(v2, u2), inImage.at(v3, u3), xFrac); float value = linInterpolate(col0, col1, yFrac); return cv::saturate_cast(value); } template static void resize(const cv::Mat& inImage, cv::Mat& outImage) { float scaleY = (inImage.rows - 1) / static_cast(outImage.rows - 1); float scaleX = (inImage.cols - 1) / static_cast(outImage.cols - 1); for (int y = 0; y < outImage.rows; ++y) { float scaledY = y * scaleY; float yFrac = scaledY - static_cast(scaledY); for (int x = 0; x < outImage.cols; x++) { float scaledX = x * scaleX; float xFrac = scaledX - (static_cast(scaledX)); outImage.at(y, x) = resizePointBilinear(inImage, scaledX, scaledY, xFrac, yFrac); } } } bool bilinearResize(const cv::Mat& inImage, cv::Mat& outImage, const cv::Size size) { if (!(inImage.type() == CV_8U || inImage.type() == CV_32F) || size.width < 2 || size.height < 2 || inImage.cols < 2 || inImage.rows < 2) return false; std::cout<<"size: "<(inImage, outImage); else if(inImage.type() == CV_32F) resize(inImage, outImage); return true; }