You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
835 lines
25 KiB
C++
835 lines
25 KiB
C++
/*! \file CVUtils.h
|
|
\brief Some functions extend opencv classes
|
|
|
|
Created: 2015/06/22, author: Jin Bingwen.
|
|
*/
|
|
|
|
#ifndef __CVUtils_h_
|
|
#define __CVUtils_h_
|
|
|
|
#include "StdUtils.h"
|
|
#include <opencv2/opencv.hpp>
|
|
#include <opencv2/opencv_modules.hpp>
|
|
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <map>
|
|
#include "pointpair.h"
|
|
|
|
#if defined(_DEBUG) && defined(_VIEW_INTERNAL_MAT)
|
|
#define DEBUG_VIEW_INTERNAL_MAT
|
|
#endif
|
|
|
|
#if defined(_DEBUG) && defined(_DETAIL_LOG)
|
|
#define DEBUG_DETAIL_LOG
|
|
#endif
|
|
|
|
#define M_PI CV_PI
|
|
|
|
#define INTER_POINT_Y(p0x, p0y, p1x, p1y, p2x) ((p1y - p0y)/(p1x - p0x)*(p2x - p0x) + p0y)
|
|
#define INTER_POINT_X(p0x, p0y, p1x, p1y, p2y) ((p1x - p0x)/(p1y - p0y)*(p2y - p0y) + p0x)
|
|
|
|
using std::vector;
|
|
using std::pair;
|
|
using namespace cv;
|
|
|
|
typedef Size_<double> Sized;
|
|
typedef Size_<float> Sizef;
|
|
|
|
namespace CyclopsUtils {
|
|
|
|
// Hold an empty dummy mat which is useful when you want to return a Mat() incase of some failure,
|
|
// or pass a Mat() as defualt function parameter.
|
|
// This will save some computation cost for initialization and deallocation of a local variable.
|
|
extern Mat gDummyMat;
|
|
|
|
// Hold an empty dummy Scalar which is useful when you want to return a Scalar() incase of some failure,
|
|
// or pass a Scalar() as defualt function parameter.
|
|
// This will save some computation cost for initialization and deallocation of a local variable.
|
|
extern Scalar gDummyScalar;
|
|
|
|
string templateMatchMethod2Str(int method);
|
|
|
|
// Carmack fast InvSqrt!
|
|
inline float InvSqrt(float x)
|
|
{
|
|
float xhalf = 0.5f*x;
|
|
int i = *(int*)&x;
|
|
i = 0x5f3759df - (i >> 1); // 计算第一个近似根
|
|
x = *(float*)&i;
|
|
x = x*(1.5f - xhalf*x*x); // 牛顿迭代法
|
|
return x;
|
|
}
|
|
|
|
void printIndent(int indentNum, int indent = 4);
|
|
|
|
template<typename _Ty>
|
|
void printProperty(int indentNum, string label, _Ty val)
|
|
{
|
|
printIndent(indentNum);
|
|
std::cout << label << ": " << val << "; " << std::endl;
|
|
}
|
|
|
|
void printMatInfo(const Mat& m, int baseIndentNum = 0, int indent = 4);
|
|
|
|
void alignEachRowRightBound(Mat& img, int tarCen, vector<int>& dxVec,
|
|
int gradientKernelWidth = 12,
|
|
int smoothBoundaryKernelWidth = 32,
|
|
float smoothBoundaryWeight = 3.0,
|
|
int backgroundThre = 40);
|
|
void autoAlignEachRowGloballyWithIndependentBoundaries(Mat& img);
|
|
void autoAlignEachRowWithWeightedXCen(Mat& img, int tarCenX, vector<int>& dxVec);
|
|
void autoAlignEachRowWithAutoThreshold(Mat& img, int tarCenX, vector<int>& dxVec);
|
|
void alignEachRow(Mat& img, const vector<int>& dxVec);
|
|
void autoAlignEachRowGloballyWithWidthConstraint(Mat& img, int tarCenX, vector<int>& dxVec);
|
|
void autoAlignEachRowLocally(Mat& img, int searchRadius = 5, int rowWidth = 1, const Mat& preImg = Mat());
|
|
|
|
void openOper(Mat& img, int w);
|
|
void openOper(Mat& img, const Mat& kernel);
|
|
void closeOper(Mat& img, int w);
|
|
void closeOper(Mat& img, const Mat& kernel);
|
|
void localCloseOper(Mat& img, vector< vector<Point> >& contours, float longerScale);
|
|
|
|
Mat genCircleMask(int rows, int cols, int type,
|
|
Point cen, double r,
|
|
const Scalar& color, int thickness, int lineType = 8, int shift = 0);
|
|
|
|
void closeShapesToConvex(const Mat& mask, Mat& closedMask);
|
|
|
|
double longShortRatio(const Size& s);
|
|
|
|
float lpContourArea(const vector<Point>& contour);
|
|
|
|
|
|
float circleDegree(const vector<Point>& contour);
|
|
|
|
|
|
enum LogicOper
|
|
{
|
|
LP_LOGIC_LARGER,
|
|
LP_LOGIC_SMALLER
|
|
};
|
|
|
|
Mat gridThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, LogicOper oper /*= LP_LOGIC_SMALLER*/, float majority /*= 0.8*/);
|
|
Mat gridWhiteThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority /*= 0.8*/);
|
|
Mat gridBlackThre(const Mat& img, int gridWidth, int gridHeight, float threStdDevScale, float majority = 0.8);
|
|
|
|
void converToType(cv::Mat& mat, int mattype);
|
|
|
|
void genScharrImage(Mat& img);
|
|
void genSobelImage(Mat& img, Mat* pSobelx = NULL, Mat* pSobely = NULL);
|
|
void genSobelDir(Mat& img);
|
|
Mat genSobelDir(const Mat& sobelX, const Mat& sobelY);
|
|
Mat genDirColorImg(const Mat& img, Mat* pValueMat = NULL);
|
|
|
|
template<typename _T>
|
|
void histMeanStddev(const Mat& hist, double* pMean, double* pStddev)
|
|
{
|
|
_T* pData = (_T*)hist.data;
|
|
double sum = 0;
|
|
int count = 0;
|
|
for (int i = 0; i < hist.cols; ++i)
|
|
{
|
|
if (!pData[i])
|
|
{
|
|
continue;
|
|
}
|
|
count += pData[i];
|
|
sum += pData[i]*i;
|
|
}
|
|
if (count == 1)
|
|
{
|
|
if (pMean)
|
|
{
|
|
*pMean = sum;
|
|
}
|
|
if (pStddev)
|
|
{
|
|
*pStddev = 0;
|
|
}
|
|
return;
|
|
}
|
|
*pMean = sum / (double)count;
|
|
sum = 0;
|
|
for (int i = 0; i < hist.cols; ++i)
|
|
{
|
|
if (!pData[i])
|
|
{
|
|
continue;
|
|
}
|
|
double d = i - *pMean;
|
|
sum += d*d*pData[i];
|
|
}
|
|
*pStddev = sqrt(sum / (double)(count - 1));
|
|
}
|
|
|
|
bool localMeanVarNorm(const Mat& srcMat, Mat& dstMat, int winRadius, double tarMean = 120, double tarStd = 30);
|
|
|
|
void meanvarnorm(Mat& src, Mat &dst, double avg, double var, Mat mask = Mat());
|
|
Mat genXDeriLineKernel(int w, int type, double absVal);
|
|
|
|
Mat genXDeriMat(const Mat& img, int w, double absVal);
|
|
|
|
/************************************************************************/
|
|
/* Normalize */
|
|
/************************************************************************/
|
|
Mat normEachRow(const Mat& img);
|
|
|
|
enum PixelSelectionMethod
|
|
{
|
|
LP_PIXEL_SELECT_ALL,
|
|
LP_PIXEL_SELECT_MAJORITY,
|
|
LP_PIXEL_SELECT_MAJORITY_FAST
|
|
};
|
|
|
|
Mat cocentricNorm(const Mat& img, Point2f center, const Mat& mask, float dstMeanVal);
|
|
|
|
Mat meanNorm(const Mat& img, const Mat& mask, float dstMeanVal,
|
|
float majority = 1.0,
|
|
PixelSelectionMethod method = LP_PIXEL_SELECT_ALL);
|
|
|
|
Mat minMaxNorm(const Mat& img, double tarMin, double tarMax, const Mat& mask = Mat());
|
|
|
|
Mat normCanvas(const Mat& img);
|
|
/**********************************************************************/
|
|
|
|
Mat genGradientDir4EachRow(const Mat& img);
|
|
|
|
void findMatElementsEquals(const Mat& img, vector<Point>& pointVec, float val, int xPadding);
|
|
|
|
double localMatSum(const Mat& mat, const Rect& roi);
|
|
|
|
void findEdgePointsEachRow(const Mat& img, vector<Point>& edgePointVec, int xPadding);
|
|
|
|
template<typename _Ty>
|
|
Mat sumEachRow(const Mat& iMat)
|
|
{
|
|
_ASSERTE(iMat.channels() == 1);
|
|
Mat oVec(iMat.rows, 1, DataType<_Ty>::type);
|
|
for (int i = 0; i < iMat.rows; ++i)
|
|
{
|
|
Mat rowVec = iMat.row(i);
|
|
Scalar s = cv::sum(rowVec);
|
|
oVec.at<_Ty>(i) = s.val[0]/rowVec.cols;
|
|
}
|
|
return oVec;
|
|
}
|
|
|
|
template<typename _Ty, int cn>
|
|
Mat sumEachRowN(const Mat& iMat)
|
|
{
|
|
_ASSERTE(iMat.channels() == cn);
|
|
Mat oVec(iMat.rows, 1, CV_MAKETYPE(DataType<_Ty>::type, cn));
|
|
for (int i = 0; i < iMat.rows; ++i)
|
|
{
|
|
Mat rowVec = iMat.row(i);
|
|
Scalar s = cv::sum(rowVec);
|
|
Vec<_Ty, cn>& d = oVec.at<Vec<_Ty, cn> >(i);
|
|
for (int j = 0; j < cn; ++j)
|
|
d[j] = s[j] / iMat.cols;
|
|
}
|
|
return oVec;
|
|
}
|
|
|
|
Mat sumEachRow2(const Mat& img);
|
|
|
|
template<typename _Ty>
|
|
Mat sumEachCol(const Mat& iMat)
|
|
{
|
|
_ASSERTE(iMat.channels() == 1);
|
|
Mat oVec(1, iMat.cols, DataType<_Ty>::type);
|
|
for (int i = 0; i < iMat.cols; ++i)
|
|
{
|
|
Mat colVec = iMat.col(i);
|
|
Scalar s = cv::sum(colVec);
|
|
oVec.at<_Ty>(i) = s.val[0]/colVec.rows;
|
|
}
|
|
return oVec;
|
|
}
|
|
|
|
template<typename _Ty, int cn>
|
|
Mat sumEachColN(const Mat& iMat)
|
|
{
|
|
_ASSERTE(iMat.channels() == cn);
|
|
Mat oVec(1, iMat.cols, CV_MAKETYPE(DataType<_Ty>::type, cn));
|
|
for (int i = 0; i < iMat.cols; ++i)
|
|
{
|
|
Mat colVec = iMat.col(i);
|
|
Scalar s = cv::sum(colVec);
|
|
Vec<_Ty, cn>& d = oVec.at<Vec<_Ty, cn> >(i);
|
|
for (int j = 0; j < cn; ++j)
|
|
d[j] = s[j] / colVec.rows;
|
|
}
|
|
return oVec;
|
|
}
|
|
|
|
Mat sumEachCol2(const Mat& img);
|
|
|
|
/************************************************************************/
|
|
/* Threshold */
|
|
/************************************************************************/
|
|
Mat thresholdEachRowLocally(const Mat& img, int localRange = 501, int C = 10);
|
|
|
|
Mat thresholdEachRow(const Mat& img, float threScale = 0.5);
|
|
|
|
Mat thresholdEachRow(const Mat& img, const Mat& rowThre, float threScale);
|
|
|
|
// As OpenCV doesn't provide any API to get automatic threshold value only without actually apply the threshold,
|
|
// however in some cases, we need this value only to apply our own threshold type.
|
|
// So, copied out the private getThreshVal_Otsu_8u and getThreshVal_Triangle_8u function here,
|
|
// and we provide the uniformed getAutoThresValue function to call the above two.
|
|
// Pass in THRESH_OTSU for Otsu threshold, and THRESH_TRIANGLE for the other.
|
|
double getAutoThresValue(const Mat& img, int type = THRESH_OTSU);
|
|
/**********************************************************************/
|
|
|
|
void segEachRow(const Mat& img, vector<PointPair>& pointPairVec, int xPadding);
|
|
|
|
void fillPointPairVal(const Mat& img, std::string filePath, vector<PointPair>& pointPairVec);
|
|
|
|
Mat getFirstChannel(const Mat& img);
|
|
Mat getChannel(const Mat& img, int i);
|
|
void setChannel(Mat& img, int i, Mat chnMat);
|
|
|
|
void mulEachRow(Mat& img, const Mat& scaleRow);
|
|
|
|
void genRandomPoints(const Mat& img, vector<Point>& points, RNG rng, int sampleCnt = 1000);
|
|
|
|
void plot8uVec(const Mat& vec, float scale = 1.0);
|
|
|
|
void plot32fVec(const Mat& vec, float scale = 1.0);
|
|
|
|
Mat calcHist(const Mat& img, const Mat& mask, int histSize = 256, int minVal = 0, int maxVal = 256);
|
|
|
|
template<typename T>
|
|
Mat calcHist(const vector<T>& vals, unsigned int histSize, T* pMinVal = nullptr, T* pMaxVal = nullptr)
|
|
{
|
|
_ASSERTE(histSize > 1);
|
|
if (histSize <= 1) return gDummyMat;
|
|
|
|
int minValIdx = -1;
|
|
int maxValIdx = -1;
|
|
int valSize = vals.size();
|
|
if (!pMinVal || !pMaxVal) {
|
|
for (int i = 0; i < valSize; ++i)
|
|
{
|
|
const T& v = vals[i];
|
|
if (minValIdx < 0 || v < vals[minValIdx]) {
|
|
minValIdx = i;
|
|
}
|
|
if (maxValIdx < 0 || v > vals[maxValIdx]) {
|
|
maxValIdx = i;
|
|
}
|
|
}
|
|
}
|
|
T minVal = pMinVal ? *pMinVal : vals[minValIdx];
|
|
T maxVal = pMaxVal ? *pMaxVal : vals[maxValIdx];
|
|
if (minVal == maxVal) return gDummyMat;
|
|
|
|
double histStep = (maxVal - minVal) / (histSize-1); // ensure max value is in analysis.
|
|
|
|
Mat hist(2, histSize, CV_32S, Scalar(0));
|
|
int* axis = (int*)hist.ptr(1);
|
|
for (int i = 0; i < histSize; ++i)
|
|
axis[i] = histStep * i + minVal;
|
|
|
|
int* data = (int*)hist.ptr(0);
|
|
|
|
for (int i = 0; i < valSize; ++i)
|
|
{
|
|
const T& v = vals[i];
|
|
int histIdx = (v - minVal) / histStep;
|
|
data[histIdx]++;
|
|
}
|
|
|
|
return hist;
|
|
}
|
|
|
|
Mat resize(const Mat& img, float s, int interMethod = cv::INTER_LINEAR);
|
|
|
|
void writeFile(const Mat& mat, std::string filePath, std::string matName = "mat");
|
|
|
|
Mat readFile(std::string filePath, std::string matName);
|
|
|
|
void gaussianBlurEachRow(const Mat& src, Mat& dst, int ksize = 3);
|
|
|
|
void medianBlurEachRow(const Mat& src, Mat& dst, int ksize = 3);
|
|
|
|
double maxLaplacianX(const Mat& rowMat, float scale = 1.0);
|
|
|
|
|
|
double minLaplacianX(const Mat& rowMat, float scale = 1.0);
|
|
|
|
void Laplacian1D(const Mat& src, Mat& dst, int ddepth, int ksize = 1);
|
|
|
|
void filterKeyPointsByNeighborDistance(vector<KeyPoint>& vec, float dis);
|
|
|
|
void filterKeyPointsByRotationInvariants(vector<KeyPoint>& vec, const Mat& img,
|
|
FeatureDetector* pDesExtractor, float tor);
|
|
|
|
double localIC_Angle(const Mat& img, Point2f center, int patchSize);
|
|
double localAngle_WeightedCen(const Mat& img, Point2f center);
|
|
double localAngle_(const Mat& img, Point2f center, Mat mask);
|
|
|
|
class GroupMatcher : public BFMatcher
|
|
{
|
|
public:
|
|
CV_WRAP GroupMatcher(int normType = NORM_L2, bool crossCheck = false);
|
|
virtual ~GroupMatcher() {}
|
|
|
|
virtual bool isMaskSupported() const { return false; }
|
|
|
|
virtual Ptr<DescriptorMatcher> clone(bool emptyTrainData = false) const;
|
|
|
|
// no longer supported in opencv3.4.1
|
|
#if (CV_MAJOR_VERSION < 3)
|
|
AlgorithmInfo* info() const;
|
|
#endif
|
|
protected:
|
|
virtual void knnMatchImpl(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, int k,
|
|
const vector<Mat>& masks = vector<Mat>(), bool compactResult = false);
|
|
virtual void radiusMatchImpl(const Mat& queryDescriptors, vector<vector<DMatch> >& matches, float maxDistance,
|
|
const vector<Mat>& masks = vector<Mat>(), bool compactResult = false);
|
|
|
|
int normType;
|
|
bool crossCheck;
|
|
};
|
|
|
|
void upperMajorityMask(const Mat& img, Mat& mask, float majority);
|
|
void majorityHistLower(const Mat& img, const Mat& mask, Mat& hist, float majority);
|
|
void majorityHistUpper(const Mat& img, const Mat& mask, Mat& hist, float majority);
|
|
Mat lowerMajorityMask(const Mat& img, const Mat& mask, float majority);
|
|
void meanStdDev(const Mat& img, const Mat& mask, double* pMean, double* pStdDev, float majority, int type = 0);
|
|
|
|
|
|
Mat getRoiImg(const Mat& img, Point2i cen, int radius);
|
|
|
|
|
|
Mat getRoiImg(const Mat& img, vector<Point2i> ptVec, int radius);
|
|
|
|
|
|
Mat filterY(const Mat& img, float* kernel, int size, int ddepth = CV_32FC1);
|
|
|
|
Mat getContourBoundedRoiImg(const Mat& src, const vector<Point>& contour, Rect* pRoiRect = NULL);
|
|
|
|
void filterContours(Mat hsvColorThresImg,
|
|
int minArea, double minLength, double minLongShortRatio,
|
|
double maxCircleDegree, vector<Point>* pAreaMaxContour = NULL);
|
|
void filterContours(Mat& img, double minArea);
|
|
void filterContours(const vector< vector<Point> >& contours, const Mat& srcImg,
|
|
vector< vector<Point> >& filteredContours, int minArea, double minLength,
|
|
double minLongShortRatio, double maxCircleDegree, vector<Point>* pAreaMaxContour = NULL);
|
|
int filterSmallAndFindMaxContours(vector< vector<Point> >& contours, double minArea);
|
|
Mat genInRangeMask(const Mat& src, std::map<int, pair<Scalar, Scalar>>& colorRanges);
|
|
|
|
void genContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat);
|
|
void genContrastImg(const Mat& img, Mat& contrastImg, int ksize);
|
|
void genYContrastImg(const Mat& img, Mat& constrastImg, int ksize);
|
|
|
|
void genHalfContrastImg(const Mat& img, Mat& contrastImg, const Mat& kernelMat);
|
|
void genHalfContrastImg(const Mat& img, Mat& contrastImg, int ksize);
|
|
|
|
void cvtColor(Mat& img, int t);
|
|
|
|
Mat rotateImage(const Mat& img, Point2f cen, float degree);
|
|
Mat normAngle(const Mat& img, Mat mask);
|
|
|
|
Mat genSimpleXGradient(const Mat& m);
|
|
|
|
void uniformLocalMinExtremas(const Mat& vec, Mat& localExtremaValVec,
|
|
vector<int>& idxVec, int n, int refineRange);
|
|
|
|
void equalizeHist(const Mat& src, Mat& dst, const Mat& mask = gDummyMat);
|
|
|
|
void removeHighlights(const Mat& src, Mat& dst, float thre);
|
|
|
|
void genSectorSumVec(const Mat& img, const Mat& weightMat, Mat& hist,
|
|
Mat& weightHist, const Point2f& cen, float angleStep,
|
|
Mat* pAngMat = NULL, Mat* pMagMat = NULL);
|
|
void applySectorScales(Mat& img, const Mat& hist, const Mat& angMat);
|
|
void normSectors(Mat& img, Mat weightMat, const Point2f& cen, float angleStep,
|
|
float tarVal);
|
|
void normSectors_tarImg(Mat& img, Mat weightMat, const Point2f& cen, float angleStep,
|
|
const Mat& tarImg);
|
|
void normSectorsMeans_tarHist(Mat& img, const Mat& hist, const Point2f& cen,
|
|
float angleStep, const Mat& tarHist, const Mat& angMat);
|
|
|
|
Point2f imgCen(const Mat& img);
|
|
|
|
class luffyCircle;
|
|
cv::Mat findCircleObject(const Mat &src, const Mat& backgroundImg,
|
|
int nThres = 20, luffyCircle *pCircle = NULL);
|
|
|
|
bool ensureGrayImg(const Mat& img, Mat& gray);
|
|
|
|
/************************************************************************/
|
|
/* Color Space */
|
|
/************************************************************************/
|
|
|
|
double hsvDis(const Vec3b& v0, const Vec3b& v1);
|
|
Mat genHSVColorDisMat(const Mat& m0, const Mat& m1);
|
|
Mat genHSVColorDisMat(const Mat& m, Vec3b baseColor);
|
|
Mat genHSVColorDisMat(const Mat& m);
|
|
Mat genBlueGreenRatioMat(const Mat& m, float alpha = 166.32, float beta = -16.414);
|
|
Mat genColorFuncDisMat(const Mat& m, float a = 0.001, float b = 0.4061, float c = -5.6845);
|
|
|
|
template<typename T>
|
|
float validate_diff(T result, T target, float err_tol)
|
|
{
|
|
T diff = result - target;
|
|
float normDiff = cv::norm(diff);
|
|
return normDiff <= err_tol ? 0 : normDiff;
|
|
}
|
|
|
|
template<typename T>
|
|
T getImgSubPix(const Mat& img, float x, float y)
|
|
{
|
|
_ASSERTE(!img.empty());
|
|
_ASSERTE(x >= 0 && y >= 0 && x <= img.cols - 1 && y <= img.rows - 1);
|
|
|
|
int x0 = floor(x);
|
|
int y0 = floor(y);
|
|
bool noSubX = x0 == x;
|
|
bool noSubY = y0 == y;
|
|
if (noSubX && noSubY) {
|
|
return img.at<T>(y0, x0);
|
|
}
|
|
else if (noSubX) {
|
|
int y1 = y0 + 1;
|
|
double c = y1 - y;
|
|
double d = 1. - c;
|
|
|
|
const T* p0 = (const T*)img.ptr(y0);
|
|
const T* p1 = (const T*)img.ptr(y1);
|
|
|
|
T ret = p0[x0] * c + p1[x0] * d;
|
|
return ret;
|
|
}
|
|
else if (noSubY) {
|
|
int x1 = x0 + 1;
|
|
double a = x1 - x;
|
|
double b = 1. - a;
|
|
|
|
const T* p0 = (const T*)img.ptr(y0);
|
|
|
|
T ret = a * p0[x0] + b * p0[x1];
|
|
return ret;
|
|
}
|
|
else {
|
|
int x1 = x0 + 1;
|
|
int y1 = y0 + 1;
|
|
|
|
double a = x1 - x;
|
|
double b = 1. - a;
|
|
double c = y1 - y;
|
|
double d = 1. - c;
|
|
|
|
const T* p0 = (const T*)img.ptr(y0);
|
|
const T* p1 = (const T*)img.ptr(y1);
|
|
|
|
T ret = (a * p0[x0] + b * p0[x1]) * c +
|
|
(a * p1[x0] + b * p1[x1]) * d;
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
vector<T> cvtMat2Vec(const Mat& img)
|
|
{
|
|
_ASSERTE(!img.empty());
|
|
int count = img.cols * img.rows;
|
|
|
|
std::vector<T> vals(count);
|
|
if (img.isContinuous()) {
|
|
memcpy((T*)vals.data(), (T*)img.data, sizeof(T)*count);
|
|
}
|
|
else {
|
|
int rows = img.rows;
|
|
T* vals_p = vals.data();
|
|
size_t rowStep = sizeof(T)*img.cols;
|
|
for (int i = 0; i < rows; ++i) {
|
|
memcpy(vals_p, img.ptr<T>(i), rowStep);
|
|
vals_p += img.cols;
|
|
}
|
|
}
|
|
|
|
return vals;
|
|
}
|
|
|
|
template<typename T, typename S>
|
|
std::list<T> cvtMat2List(const Mat& img)
|
|
{
|
|
_ASSERTE(!img.empty());
|
|
int rows = img.rows;
|
|
int cols = img.cols;
|
|
int count = cols * rows;
|
|
|
|
std::list<T> vals;
|
|
for (int i = 0; i < rows; ++i) {
|
|
const S* img_p = img.ptr<S>(i);
|
|
for (int j = 0; j < cols; ++j) {
|
|
vals.push_back((S)(img_p[j]));
|
|
}
|
|
}
|
|
|
|
return vals;
|
|
}
|
|
|
|
template<typename T>
|
|
T getNthElement(const Mat& img, int n)
|
|
{
|
|
_ASSERTE(!img.empty());
|
|
int count = img.cols * img.rows;
|
|
_ASSERTE(n < count);
|
|
|
|
std::vector<T> vals = cvtMat2Vec<T>(img);
|
|
|
|
std::nth_element(vals.begin(), vals.begin() + n, vals.end());
|
|
|
|
return vals[n];
|
|
}
|
|
|
|
int getLocalMaximun(const Mat& vec, double thres, std::vector<int>* pMaxIdxVec);
|
|
int getLocalMinimun(const Mat& vec, double thres, std::vector<int>* pMaxIdxVec);
|
|
|
|
enum EnumAbnormalType {
|
|
AbTypePositive = 0,
|
|
AbTypeNegative,
|
|
AbTypeBoth
|
|
};
|
|
int detectAbnormal(const Mat& vec, int kernelSize, EnumAbnormalType abnormalType, double abnormalThreshold,
|
|
Mat* pScores = nullptr, std::vector<int>* pAbIdxVec = nullptr);
|
|
|
|
|
|
|
|
using namespace cv;
|
|
|
|
/************************************************************************/
|
|
/* Miscellaneous */
|
|
/************************************************************************/
|
|
|
|
inline double distance(float x0, float y0, float x1, float y1)
|
|
{
|
|
return sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2));
|
|
}
|
|
|
|
inline double distance(Point2f p1, Point2f p2)
|
|
{
|
|
return distance(p1.x, p1.y, p2.x, p2.y);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Rect */
|
|
/************************************************************************/
|
|
|
|
typedef Rect_<double> Rectd;
|
|
typedef Rect_<float> Rectf;
|
|
|
|
template<typename _Ty>
|
|
bool isImageRoiRectValid(const Rect_<_Ty>& rect)
|
|
{
|
|
return rect.x >= 0 && rect.y >= 0 && rect.width > 0 && rect.height > 0;
|
|
}
|
|
|
|
|
|
template<typename _Ty>
|
|
string rect2str(const Rect_<_Ty>& rect)
|
|
{
|
|
stringstream ss;
|
|
ss << rect.x << ", " << rect.y << ", " << rect.width << ", " << rect.height;
|
|
return ss.str();
|
|
}
|
|
template<typename _Ty>
|
|
Rect_<_Ty> rectFromVec(vector<_Ty>& vec)
|
|
{
|
|
if (vec.size() < 4)
|
|
{
|
|
return Rect_<_Ty>();
|
|
}
|
|
Rect_<_Ty> rect;
|
|
rect.x = vec[0];
|
|
rect.y = vec[1];
|
|
rect.width = vec[2];
|
|
rect.height = vec[3];
|
|
return rect;
|
|
}
|
|
|
|
template<typename _Point>
|
|
Rect genRectCenRadius(_Point cen, int radius)
|
|
{
|
|
return Rect(cen.x - radius,
|
|
cen.y - radius,
|
|
radius * 2 + 1,
|
|
radius * 2 + 1);
|
|
}
|
|
template<typename _Point>
|
|
Rect genRectLeftTopRadius(_Point p, int radius)
|
|
{
|
|
return Rect(p.x,
|
|
p.y,
|
|
radius * 2,
|
|
radius * 2);
|
|
}
|
|
|
|
template<typename _Point>
|
|
Rect genRectCenRadius(const vector<_Point>& ptVec, int radius)
|
|
{
|
|
Rect br = cv::boundingRect(ptVec);
|
|
|
|
return Rect(br.x - radius, br.y - radius, br.width + 2 * radius, br.height + 2 * radius);
|
|
}
|
|
|
|
Rect loadRect(std::string filepath);
|
|
|
|
Rect rectInImage(const Mat& img, const Point& pt, const Size& rectSize,
|
|
int xPadding = 0, int yPadding = 0);
|
|
|
|
Rect extRectTopLeftFix(const Rect& r, int w);
|
|
|
|
Rect extRectCenFix(const Rect& r, int w);
|
|
|
|
inline void rotatedRect2PtVec(const RotatedRect& rr, vector<Point2f>& ptVec)
|
|
{
|
|
ptVec.resize(4);
|
|
rr.points(&ptVec[0]);
|
|
}
|
|
|
|
bool isPntInRotatedRect(const RotatedRect& rr, const Point2f& pnt);
|
|
|
|
|
|
/************************************************************************/
|
|
/* Intersection */
|
|
/************************************************************************/
|
|
|
|
bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2,
|
|
Point2f &r, bool bounded = false);
|
|
bool intersection(const Vec4f& l0, const Vec4f& l1, Point2f& r,
|
|
bool bounded = false);
|
|
bool intersection(const Vec4f& l, const vector<Point2f>& vertexes, vector<Point2f>& interPtVec,
|
|
bool bounded = false);
|
|
|
|
int intersection(const Vec4f& line, const Vec3f& circle, Point2f& r1, Point2f& r2);
|
|
int intersection(const Vec4d& line, const Vec3d& circle, Point2d& r1, Point2d& r2);
|
|
|
|
Point2f getNearestIntersectionOfMultiLines(const vector<Vec4f>& lines);
|
|
Point2f getNearestIntersectionOfMultiLines(const vector<Point2f>& linePnts, const vector<Vec2f>& lineNorms);
|
|
Point2f getNearestIntersectionOfMultiLines(const vector<Point2f>& linePnts, const vector<float>& lineAngles, bool useDegree = true);
|
|
|
|
|
|
/************************************************************************/
|
|
/* Angle */
|
|
/************************************************************************/
|
|
|
|
inline float normAngle(float x, float y)
|
|
{
|
|
float angle = atan2f(y, x);
|
|
return angle / CV_PI * 180.0;
|
|
}
|
|
|
|
// convert angle to (0, 360)
|
|
// inline float normAngle(float angle)
|
|
// {
|
|
// int c = angle / 360;
|
|
// float ret = angle - c * 360;
|
|
// return ret < 0 ? ret + 360 : ret;
|
|
// }
|
|
inline int normAngle(int angle)
|
|
{
|
|
int c = angle / 360;
|
|
int ret = angle - c * 360;
|
|
return ret < 0 ? ret + 360 : ret;
|
|
}
|
|
inline double normAngle(double angle)
|
|
{
|
|
int c = angle / 360;
|
|
double ret = angle - c * 360;
|
|
return ret < 0 ? ret + 360 : ret;
|
|
}
|
|
inline float normAngle180(float angle)
|
|
{
|
|
int c = angle / 360;
|
|
float ret = angle - c * 360;
|
|
return ret > 180 ? ret - 360 : (ret < -180 ? ret + 360 : ret);
|
|
}
|
|
|
|
inline float diffAngle(float angle1, float angle2)
|
|
{
|
|
float diff = abs(angle1 - angle2);
|
|
return diff > 180 ? 360 - diff : diff;
|
|
}
|
|
|
|
inline float diffAngle2(float angle1, float angle2)
|
|
{
|
|
Vec2f vp(cos(angle1 / 180 * CV_PI), sin(angle1 / 180 * CV_PI));
|
|
Vec2f vpp(cos(angle2 / 180 * CV_PI), sin(angle2 / 180 * CV_PI));
|
|
|
|
return vp.dot(vpp);
|
|
}
|
|
|
|
inline float diffAngleRaw(float angle1, float angle2)
|
|
{
|
|
return diffAngle(normAngle(angle1), normAngle(angle2));
|
|
}
|
|
|
|
inline float bisectAngle(float angle1, float angle2)
|
|
{
|
|
float diff = abs(angle1 - angle2);
|
|
return diff < 180 ? (angle1 + angle2) / 2 : (angle1 + angle2 + 360) / 2;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* Transform */
|
|
/************************************************************************/
|
|
|
|
void transPoints(vector<Point2d>& vec, const Mat& mat);
|
|
void transPoints(vector<Point2d>& vec, const Matx33d& mat);
|
|
void transPoints(const vector<Point2f>& vec, vector<Point2f>& oVec, const Matx23f& mat);
|
|
|
|
Matx23f getRotationMatrix23f(Point2f center, float angle, float scale,
|
|
float xOffset = 0, float yOffset = 0);
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, const Point2f& u2,
|
|
const Point2f& cen2, const Point2f& v1, const Point2f& v2,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
void getRigidTransform(const Point2f& u1, const Point2f& u2, const Point2f& v1, const Point2f& v2,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, float ua,
|
|
const Point2f& cen2, const Point2f& v1, float va,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
void getRigidTransform(const Point2f& u1, float ua, const Point2f& v1, float va,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
void getRigidTransform(const Point2f& cen1, const Point2f& u1, float ua,
|
|
const Point2f& cen2, const Point2f& v1, float va, float s,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
void getRigidTransform(const Point2f& u1, float ua, const Point2f& v1, float va, float s,
|
|
double& x0, double& y0, double& angle, double& scale);
|
|
|
|
Mat applyPerspectiveTransform(const Mat& img,
|
|
std::vector<Point2f>& transVertexes, // in order top-left, top-right, bottom-right, bottom-left
|
|
int flags = INTER_LINEAR);
|
|
|
|
/************************************************************************/
|
|
/* Shape */
|
|
/************************************************************************/
|
|
|
|
float isSameDir(Vec4f& l1, Vec4f& l2, float err_tol = 1e-9);
|
|
|
|
float isCollinear(Vec4f& l1, Vec4f& l2, float err_tol = 1e-9);
|
|
|
|
bool isLikeArch(const Mat& vec, double tor);
|
|
|
|
bool isLikeHorizontalLine(const Mat& vec, double tor);
|
|
|
|
};
|
|
using namespace CyclopsUtils;
|
|
#endif // __CVUtils_h_
|
|
|