/*! \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 #include #include #include #include #include #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_ Sized; typedef Size_ 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 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& 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& dxVec); void autoAlignEachRowWithAutoThreshold(Mat& img, int tarCenX, vector& dxVec); void alignEachRow(Mat& img, const vector& dxVec); void autoAlignEachRowGloballyWithWidthConstraint(Mat& img, int tarCenX, vector& 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 >& 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& contour); float circleDegree(const vector& 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 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& pointVec, float val, int xPadding); double localMatSum(const Mat& mat, const Rect& roi); void findEdgePointsEachRow(const Mat& img, vector& edgePointVec, int xPadding); template 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 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 >(i); for (int j = 0; j < cn; ++j) d[j] = s[j] / iMat.cols; } return oVec; } Mat sumEachRow2(const Mat& img); template 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 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 >(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& pointPairVec, int xPadding); void fillPointPairVal(const Mat& img, std::string filePath, vector& 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& 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 Mat calcHist(const vector& 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& vec, float dis); void filterKeyPointsByRotationInvariants(vector& 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 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 >& matches, int k, const vector& masks = vector(), bool compactResult = false); virtual void radiusMatchImpl(const Mat& queryDescriptors, vector >& matches, float maxDistance, const vector& masks = vector(), 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 ptVec, int radius); Mat filterY(const Mat& img, float* kernel, int size, int ddepth = CV_32FC1); Mat getContourBoundedRoiImg(const Mat& src, const vector& contour, Rect* pRoiRect = NULL); void filterContours(Mat hsvColorThresImg, int minArea, double minLength, double minLongShortRatio, double maxCircleDegree, vector* pAreaMaxContour = NULL); void filterContours(Mat& img, double minArea); void filterContours(const vector< vector >& contours, const Mat& srcImg, vector< vector >& filteredContours, int minArea, double minLength, double minLongShortRatio, double maxCircleDegree, vector* pAreaMaxContour = NULL); int filterSmallAndFindMaxContours(vector< vector >& contours, double minArea); Mat genInRangeMask(const Mat& src, std::map>& 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& 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 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 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(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 vector cvtMat2Vec(const Mat& img) { _ASSERTE(!img.empty()); int count = img.cols * img.rows; std::vector 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(i), rowStep); vals_p += img.cols; } } return vals; } template std::list cvtMat2List(const Mat& img) { _ASSERTE(!img.empty()); int rows = img.rows; int cols = img.cols; int count = cols * rows; std::list vals; for (int i = 0; i < rows; ++i) { const S* img_p = img.ptr(i); for (int j = 0; j < cols; ++j) { vals.push_back((S)(img_p[j])); } } return vals; } template T getNthElement(const Mat& img, int n) { _ASSERTE(!img.empty()); int count = img.cols * img.rows; _ASSERTE(n < count); std::vector vals = cvtMat2Vec(img); std::nth_element(vals.begin(), vals.begin() + n, vals.end()); return vals[n]; } int getLocalMaximun(const Mat& vec, double thres, std::vector* pMaxIdxVec); int getLocalMinimun(const Mat& vec, double thres, std::vector* pMaxIdxVec); enum EnumAbnormalType { AbTypePositive = 0, AbTypeNegative, AbTypeBoth }; int detectAbnormal(const Mat& vec, int kernelSize, EnumAbnormalType abnormalType, double abnormalThreshold, Mat* pScores = nullptr, std::vector* pAbIdxVec = nullptr); /************************************************************************/ /* 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& vec, const Mat& mat); void transPoints(vector& vec, const Matx33d& mat); void transPoints(const vector& vec, vector& 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& transVertexes, // in order top-left, top-right, bottom-right, bottom-left int flags = INTER_LINEAR); }; using namespace CyclopsUtils; #endif // __CVUtils_h_