/*! * \file GeomUtils.h * \date 2018/08/24 * * \author Lin, Chi * Contact: lin.chi@hzleaper.com * * * \note */ #ifndef __GeomUtils_h_ #define __GeomUtils_h_ #include "StdUtils.h" #include #include #include "CyclopsVersion.h" namespace GeomUtils { using namespace cv; /************************************************************************/ /* Miscellaneous */ /************************************************************************/ template inline T distance(T x0, T y0, T z0, T x1, T y1, T z1) { return sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2) + pow(z0 - z1, 2)); } template inline T distance(const Point3_ &p1, const Point3_ & p2) { return distance(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); } template inline T distance(T x0, T y0, T x1, T y1) { return sqrt(pow(x0 - x1, 2) + pow(y0 - y1, 2)); } template inline T distance(const Point_& p1, const Point_& p2) { return distance(p1.x, p1.y, p2.x, p2.y); } double distance(Point2f p1, float a1, Point2f p2, float a2); inline double distance(float x0, float y0, float a0, float x1, float y1, float a1) { return distance(Point2f(x0, y0), a0, Point2f(x1, y1), a1); } template inline T distance(const Vec& line) { return distance(line[0], line[1], line[2], line[3]); } template inline T distance2(T x0, T y0, T x1, T y1) { return pow(x0 - x1, 2) + pow(y0 - y1, 2); } template inline T distance2(const Point_& p1, const Point_& p2) { return distance2(p1.x, p1.y, p2.x, p2.y); } template inline T distance2(const Vec& line) { return distance2(line[0], line[1], line[2], line[3]); } template inline T manhattanDistance(T x0, T y0, T x1, T y1) { return abs(x0 - x1) + abs(y0 - y1); } template inline T hypotApprox(T x, T y) { x = abs(x); y = abs(y); T r1 = std::max(x, y); T r2 = 0.7071067811865 * (x + y); // sqrt(0.5) return saturate_cast(1.04119610015 * std::max(r1, r2)); // (1 + sqrt(4 - 2 * sqrt(2))) / 2 } template inline Point2f pntsCenter(const vector >& pnts) { double cx = 0, cy = 0; int pntCount = pnts.size(); for (int i = 0; i < pntCount; ++i) { const Point_& pnt = pnts[i]; cx += pnt.x; cy += pnt.y; } return Point2f(cx / pntCount, cy / pntCount); } template inline Point2f pntsCenter(const vector >& pnts, int startIdx, int endIdx) { // start and end point is excluded int c = endIdx - startIdx - 1; double x = 0, y = 0; if (c <= 0) { x = (pnts[startIdx].x + pnts[endIdx].x) / 2; y = (pnts[startIdx].y + pnts[endIdx].y) / 2; } else { for (int i = startIdx + 1; i < endIdx; ++i) { const Point_& p = pnts[i]; x += p.x; y += p.y; } x /= c; y /= c; } return Point2f(x, y); } template inline bool isZeroPoint(const Point3_& pnt) { return pnt.x == 0 && pnt.y == 0 && pnt.z == 0; } template inline bool isZeroPoint(const Point_& pnt) { return pnt.x == 0 && pnt.y == 0; } /************************************************************************/ /* Rect */ /************************************************************************/ typedef Rect_ Rectd; typedef Rect_ Rectf; template bool isImageRoiRectValid(const Rect_<_Ty>& rect) { return rect.x >= 0 && rect.y >= 0 && rect.width > 0 && rect.height > 0; } template string rect2str(const Rect_<_Ty>& rect) { stringstream ss; ss << rect.x << ", " << rect.y << ", " << rect.width << ", " << rect.height; return ss.str(); } template 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 Rect genRectCenRadius(_Point cen, int radius) { return Rect(cen.x - radius, cen.y - radius, radius * 2 + 1, radius * 2 + 1); } template Rect genRectLeftTopRadius(_Point p, int radius) { return Rect(p.x, p.y, radius * 2, radius * 2); } template 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); int intersectRatio(RotatedRect& rect1, RotatedRect& rect2, double& dRatio1, double& dRatio2); CYCLOPS_GEOM_DLLSPEC void rotatedRect2PtVec(const RotatedRect& rr, vector& ptVec); inline Point2f rotateRectNthPnt(const RotatedRect& rr, int i) { _ASSERTE(i >= 0 && i < 4); if (i >= 4 || i < 0) return Point2f(); vector ptVec; rotatedRect2PtVec(rr, ptVec); return ptVec[i]; } template inline vector > rect2PtVec(const Rect_<_Tx>& rect) { vector > ret(4, rect.tl()); ret[1].x += rect.width; ret[2].x += rect.width; ret[2].y += rect.height; ret[3].y += rect.height; return ret; } inline void rect2PtVec(const Rect& br, vector& ptVec) { ptVec = rect2PtVec(br); } bool isPntInRotatedRect(const RotatedRect& rr, const Point2f& pnt); bool isPntInRotatedRect(const vector& ptVec, const Point2f& pnt); template inline Rect_<_Ty> scaleRect(const Rect_<_Ty>& r, _Ty val) { Rect_<_Ty> ret = r; ret.x -= val; ret.y -= val; ret.width += val * 2; ret.height += val * 2; return ret; } inline Rect2i scaleRect(const Rect2i& r, cv::Size orgSize, cv::Size dstSize) { float xScale = (float)dstSize.width / orgSize.width; float yScale = (float)dstSize.height / orgSize.height; int x = r.tl().x*xScale; int y = r.tl().y*yScale; int xend = r.br().x*xScale; int yend = r.br().y*yScale; Rect rescalerect(Point2i(x, y), Point2i(xend, yend)); rescalerect &= Rect(0, 0, dstSize.width, dstSize.height); return rescalerect; } template inline Rect_<_Ty> cropRect(const Rect_<_Ty>& r, const Mat& img) { Rect_<_Ty> ret = r; _Ty x1 = ret.x + ret.width, y1 = ret.y + ret.height; if (ret.x < 0) ret.x = 0; if (ret.y < 0) ret.y = 0; if (x1 > img.cols) x1 = img.cols; if (y1 > img.rows) y1 = img.rows; ret.width = x1 - ret.x; ret.height = y1 - ret.y; return ret; } template inline Rect2f boundingRect2fFromPtVec(const vector >& ptVec) { size_t ptCount = ptVec.size(); if (ptCount == 0) return Rect(); const Point_<_Ty>& firstPnt = ptVec[0]; _Ty minX = firstPnt.x, maxX = firstPnt.x, minY = firstPnt.y, maxY = firstPnt.y; for (int i = 1; i < ptCount; ++i) { const Point_<_Ty>& pnt = ptVec[i]; if (pnt.x < minX) minX = pnt.x; else if (pnt.x > maxX) maxX = pnt.x; if (pnt.y < minY) minY = pnt.y; else if (pnt.y > maxY) maxY = pnt.y; } return Rect2f(minX, minY, maxX - minX, maxY - minY); } template inline Point2f tl2fFromPtVec(const vector >& ptVec) { size_t ptCount = ptVec.size(); if (ptCount == 0) return Point2f(); const Point_<_Ty>& firstPnt = ptVec[0]; _Ty minX = firstPnt.x, minY = firstPnt.y; for (int i = 1; i < ptCount; ++i) { const Point_<_Ty>& pnt = ptVec[i]; if (pnt.x < minX) minX = pnt.x; if (pnt.y < minY) minY = pnt.y; } return Point2f(minX, minY); } inline Rect boundingRectFromDiagPnts(float x0, float y0, float x1, float y1) { Rect r(floor(x0), floor(y0), ceil(x1), ceil(y1)); r.width -= r.x - 1; r.height -= r.y - 1; return r; } template inline Rect boundingRectFromPtVec(vector >& ptVec) { Rect2f r = boundingRect2fFromPtVec(ptVec); return boundingRectFromDiagPnts(r.x, r.y, r.x + r.width, r.y + r.height); } inline Rect boundingRectFromRR(const RotatedRect& rr) { vector pnts; rotatedRect2PtVec(rr, pnts); return boundingRectFromPtVec(pnts); } template inline Rect_<_Ty> resizeRect(const Rect_<_Ty>& r, float w, float h) { Rect_<_Ty> newr; newr.width = w; newr.height = h; newr.x = r.x + r.width / 2. - w / 2.; newr.y = r.y + r.height / 2. - h / 2.; return newr; } /************************************************************************/ /* Intersection */ /************************************************************************/ enum IntersectType { IntersectUnbounded = 0, IntersectPartialBounded = 1, IntersectBounded = 2, IntersectPartialBoundedRight = 3, IntersectNone = 4 }; CYCLOPS_GEOM_DLLSPEC bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2, Point2f &r, int intersectType = IntersectUnbounded); CYCLOPS_GEOM_DLLSPEC bool intersection(const Vec4f& l0, const Vec4f& l1, Point2f& r, int intersectType = IntersectUnbounded); CYCLOPS_GEOM_DLLSPEC bool intersection(const Vec4f& l, const vector& vertexes, vector& interPtVec, int intersectType = IntersectUnbounded); CYCLOPS_GEOM_DLLSPEC int intersection(const Vec4f& line, const Vec3f& circle, Point2f& r1, Point2f& r2); CYCLOPS_GEOM_DLLSPEC int intersection(const Vec4d& line, const Vec3d& circle, Point2d& r1, Point2d& r2); CYCLOPS_GEOM_DLLSPEC int intersection(const Vec3f& circle1, const Vec3f& circle2, Point2f& r1, Point2f& r2); CYCLOPS_GEOM_DLLSPEC int intersection(const Vec3d& circle1, const Vec3d& circle2, Point2d& r1, Point2d& r2); template inline bool isOnLineSegment(const Point_& pt, const Vec& line) { T dx0 = pt.x - line.val[0]; T dy0 = pt.y - line.val[1]; if (abs(dx0) > abs(dy0)) { T dx1 = line.val[2] - pt.x; if (dx0 >= 0 && dx1 >= 0 || dx0 <= 0 && dx1 <= 0) { return true; } else { return false; } } else { T dy1 = line.val[3] - pt.y; if (dy0 >= 0 && dy1 >= 0 || dy0 <= 0 && dy1 <= 0) { return true; } else { return false; } } } Point2f getNearestIntersectionOfMultiLines(const vector& lines); Point2f getNearestIntersectionOfMultiLines(const vector& linePnts, const vector& lineNorms); Point2f getNearestIntersectionOfMultiLines(const vector& linePnts, const vector& lineAngles, bool useDegree = true); CYCLOPS_GEOM_DLLSPEC Point2f pedalRoot(const Point2f& pnt, const Vec4f& line); CYCLOPS_GEOM_DLLSPEC float pedalDistance(const Point2f& pnt, const Vec4f& line); CYCLOPS_GEOM_DLLSPEC void nearestPntToPoly(const Point2f& pnt, const vector& poly, int& mxIdx, double& mxDist, bool getMin); CYCLOPS_GEOM_DLLSPEC Point2f nearestRootToPoly(const Point2f& pnt, const vector& poly, int mxIdx, double mxDist); CYCLOPS_GEOM_DLLSPEC bool nearestPntPairInPolys(const vector& poly1, const vector& poly2, int& mxIdx1, int& mxIdx2, bool getMin); CYCLOPS_GEOM_DLLSPEC void maxDisPolyToPoly(const vector& poly1, const vector& poly2, Point2f& pnt1, Point2f& pnt2); enum { CALIPERS_MAXHEIGHT = 0, CALIPERS_MINAREARECT = 1, CALIPERS_MAXDIST = 2, CALIPERS_CSLINE = 3 }; void getMaxDisByRotCaliper(const vector& hullVec, int &index1, int &index2, float * maxDis = nullptr); void csVerticeByRotCaliper(const vector& hullVec1, const vector& hullVec2, vector& sepIndexPoly1, vector& sepIndexPoly2); CYCLOPS_GEOM_DLLSPEC bool slicePolyToPnt(vector& poly, const Point2f& pnt); // remove points in polygon's backside to the given point CYCLOPS_GEOM_DLLSPEC bool slicePolytoLine(vector& poly, const Vec4f& line); // remove points in polygon's backside to the given line CYCLOPS_GEOM_DLLSPEC bool slicePolyToPoly(vector& poly1, vector& poly2); // remove the outside parts of the two polygons /************************************************************************/ /* Angle */ /************************************************************************/ inline float degreeToRadian(float d) { return (float)(d * CV_PI / 180.); } inline float radianToDegree(float r) { return (float)(r * 180. / CV_PI); } inline float normAngle(float x, float y) { float angle = atan2f(y, x); return radianToDegree(angle); } // convert angle to [0, 360] // call this if you are sure the input angle is in [-360, 360] inline float normAngleQuick(float angle) { return angle < 0 ? angle + 360 : angle; } // convert angle to [0, 360] inline float normAngle(float angle) { float ret = fmod(angle, 360); return ret < 0 ? ret + 360 : ret; } // convert angle to [-180, 180] // call this if you are sure the input angle is in [-360, 360] inline float normAngle180Quick(float angle) { return angle > 180 ? angle - 360 : (angle < -180 ? angle + 360 : angle); } // convert angle to [-180, 180] inline float normAngle180(float angle) { float ret = fmod(angle, 360); return normAngle180Quick(ret); } // convert angle to [-90, 90] inline float normAngle90(float angle) { float ret = fmod(angle, 180); return ret > 90 ? ret - 180 : (ret < -90 ? ret + 180 : 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) { float r1 = degreeToRadian(angle1); Vec2f vp(cos(r1), sin(r1)); float r2 = degreeToRadian(angle2); Vec2f vpp(cos(r2), sin(r2)); 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; } inline float getLineAngle(const Vec4f& l) { float r = atan2(l[1] - l[3], l[0] - l[2]); return radianToDegree(r); } // convert angle to [0, 90], indicate Horizontal or Vertical inline float getNorm90LineAngle(const Vec4f& l) { float r = atan2(abs(l[1] - l[3]), abs(l[0] - l[2])); return radianToDegree(r); } inline float getPointAngle(const Point2f& p) { return fastAtan2(p.y, p.x); } inline Point2f getPointAngleNormalized(const Point2f& p) { double n = norm(p); return n < DBL_EPSILON ? p : p / n; } inline float getCrossAngle(const Point2f& p) { Point2f tarDir(0, 1.0); float dx = p.dot(tarDir); float dy = p.cross(tarDir); return fastAtan2(dy, dx); } inline float getRayAngle(const Point2f& ori, const Point2f& pnt) { Point2f d = pnt - ori; return radianToDegree(atan2(d.y, d.x)); } inline float getInterAngle(const Point2f& ori, const Point2f& p1, const Point2f& p2) { float a1 = getRayAngle(ori, p1); float a2 = getRayAngle(ori, p2); float diff = abs(a1 - a2); return diff > 180 ? 360 - diff : diff; } inline float getInterAngle360(const Point2f& ori, const Point2f& p1, const Point2f& p2) { float a1 = getRayAngle(ori, p1); float a2 = getRayAngle(ori, p2); float diff = a1 - a2; return diff < 0 ? 360 + diff : diff; } inline Vec2f getAngleVec(float a) { float r = degreeToRadian(a); return Vec2f(cos(r), sin(r)); } // @param angles a collection of angles in [-180, 180] or [0, 360] // @param rngStart start of search range // @param rngEnd end of search range, should not be more than 360 degree away from rngStart // @param binSize bin size of analysis histogram // @param pWeights optional weights for angles // @return major angle in [-360, 360] float getMajorAngle(const vector& angles, float rngStart = 0, float rngEnd = 360, float binSize = 10, const vector* pWeights = nullptr); // precise cosine double cosd(double angle); double sind(double angle); /************************************************************************/ /* Transform */ /************************************************************************/ DEPRECATED void transPoints(vector& vec, const Mat& mat); DEPRECATED void transPoints(vector& vec, const Matx33d& mat); DEPRECATED void transPoints(const vector& vec, vector& oVec, const Matx23f& mat); void getRotationMatrix23f(float* data, Point2f center, float angle, float scale, float newCenterX, float newCenterY); inline void getRotationMatrix23f(float* data, Point2f center, float angle, float scale) { return getRotationMatrix23f(data, center, angle, scale, center.x, center.y); } inline Matx23f getRotationMatrix23f(Point2f center, float angle, float scale, float newCenterX, float newCenterY) { Matx23f rotMat; getRotationMatrix23f(rotMat.val, center, angle, scale, newCenterX, newCenterY); return rotMat; } inline Matx23f getRotationMatrix23f(Point2f center, float angle, float scale) { return getRotationMatrix23f(center, angle, scale, center.x, center.y); } template inline void getRotationMatrix23(T* data, Point_ center, double angle, double scale, double newCenterX, double newCenterY) { double a_pi = degreeToRadian(angle); double alpha = cos(a_pi)*scale; double beta = -sin(a_pi)*scale; data[0] = saturate_cast(alpha); data[1] = saturate_cast(beta); data[2] = saturate_cast(-alpha * center.x - beta * center.y + newCenterX); // move to a new center data[3] = saturate_cast(-beta); data[4] = saturate_cast(alpha); data[5] = saturate_cast(beta * center.x - alpha * center.y + newCenterY); // move to a new center } template inline void getRotationMatrix34f(T* data, Point3_ center, Point3_ angle, Point3_ offset) { float r = degreeToRadian(angle.x); float sinv = sin(r); float cosv = cos(r); Mat rx = (Mat_(3, 3) << 1, 0, 0, 0, cosv, -sinv, 0, sinv, cosv); r = degreeToRadian(angle.y); sinv = sin(r); cosv = cos(r); Mat ry = (Mat_(3, 3) << cosv, 0, sinv, 0, 1, 0, -sinv, 0, cosv); r = degreeToRadian(angle.z); sinv = sin(r); cosv = cos(r); Mat rz = (Mat_(3, 3) << cosv, -sinv, 0, sinv, cosv, 0, 0, 0, 1); Mat rMat = rz * ry * rx; T* pdata = (T*)rMat.data; data[0] = pdata[0], data[1] = pdata[1], data[2] = pdata[2]; data[3] = center.x - data[0] * center.x - data[1] * center.y - data[2] * center.z + offset.x; data[4] = pdata[3], data[5] = pdata[4], data[6] = pdata[5]; data[7] = center.y - data[3] * center.x - data[4] * center.y - data[5] * center.z + offset.y; data[8] = pdata[6], data[9] = pdata[7], data[10] = pdata[8]; data[11] = center.z - data[6] * center.x - data[7] * center.y - data[8] * center.z + offset.z; } template inline void getRotationMatrix23(T* data, Point_ center, double angle, double scale) { return getRotationMatrix23(data, center, angle, scale, center.x, center.y); } Point2f transPoint23f(const Point2f& p, float* rotMat); void transPoint23f(vector& pnts, float* rotMat); void transPoint23f(vector& pnts, float* rotMat); template inline void transPoint23(S& x, S& y, const T* rotMat) { S a = saturate_cast(rotMat[0] * x + rotMat[1] * y + rotMat[2]); y = saturate_cast(rotMat[3] * x + rotMat[4] * y + rotMat[5]); x = a; } template inline void transPoint23(Vec4f& line, const T* rotMat) { transPoint23(line[0], line[1], rotMat); transPoint23(line[2], line[3], rotMat); } template inline void transPoint23(vector >& pnts, T* rotMat) { size_t pcount = pnts.size(); for (size_t i = 0; i < pcount; ++i) { Point_& p = pnts[i]; transPoint23(p.x, p.y, rotMat); } } template void transPoint23(vector >& pnts, T* rotMat) { size_t pcount = pnts.size(); for (size_t i = 0; i < pcount; ++i) { Point3_& p = pnts[i]; float x = saturate_cast(rotMat[0] * p.x + rotMat[1] * p.y + rotMat[2]); float y = saturate_cast(rotMat[3] * p.x + rotMat[4] * p.y + rotMat[5]); p.x = x, p.y = y; } } template void transRotateRect23(RotatedRect& rr, T* rotMat) { float r = degreeToRadian(rr.angle); Point2f angleDir = rr.center + Point2f(cos(r), sin(r)); transPoint23(rr.center.x, rr.center.y, rotMat); transPoint23(angleDir.x, angleDir.y, rotMat); rr.angle = radianToDegree(atan2(angleDir.y - rr.center.y, angleDir.x - rr.center.x)); } Matx34f getRotationMatrix34f(Point3f center, Point3f angle, Point3f offset); Point3f transPoint34f(const Point3f& p, float* rotMat); void transPoint34f(vector& pnts, float* rotMat); template inline Point3_ transPoint34(const Point3_& p, T* rotMat) { return Point3_(saturate_cast(rotMat[0] * p.x + rotMat[1] * p.y + rotMat[2] * p.z + rotMat[3]), saturate_cast(rotMat[4] * p.x + rotMat[5] * p.y + rotMat[6] * p.z + rotMat[7]), saturate_cast(rotMat[8] * p.x + rotMat[9] * p.y + rotMat[10] * p.z + rotMat[11])); } template void transPoint34(vector>& pnts, T* rotMat) { size_t pcount = pnts.size(); for (size_t i = 0; i < pcount; ++i) { pnts[i] = transPoint34(pnts[i], rotMat); } } template void addScalePoints(vector >& pnts, const Point_& scale, const Point_& shift) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point_& p = pnts[i]; p.x = p.x * scale.x + shift.x; p.y = p.y * scale.y + shift.y; } } template void addPoints(vector >& pnts, const Point_& shift) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point_& p = pnts[i]; p.x = p.x + shift.x; p.y = p.y + shift.y; } } template void scalePoints(vector >& pnts, const Point_& scale) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point_& p = pnts[i]; p.x = p.x * scale.x; p.y = p.y * scale.y; } } template void addScalePoints(vector >& pnts, const Point3_& scale, const Point3_& shift) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point3_& p = pnts[i]; p.x = p.x * scale.x + shift.x; p.y = p.y * scale.y + shift.y; p.z = p.z * scale.z + shift.z; } } template void addPoints(vector >& pnts, const Point3_& shift) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point3_& p = pnts[i]; p.x = p.x + shift.x; p.y = p.y + shift.y; p.z = p.z + shift.z; } } template void scalePoints(vector >& pnts, const Point3_& scale) { size_t c = pnts.size(); for (size_t i = 0; i < c; ++i) { Point3_& p = pnts[i]; p.x = p.x * scale.x; p.y = p.y * scale.y; p.z = p.z * scale.z; } } 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); CYCLOPS_GEOM_DLLSPEC Mat applyPerspectiveTransform(const Mat& img, std::vector& transVertexes, // in order top-left, top-right, bottom-right, bottom-left int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar()); template inline Mat transVecToMat3D(const Mat& tVec) { _ASSERTE(tVec.total() == 3); Mat tMat = Mat::zeros(3, 4, CV_MAKETYPE(DataType<_Ty>::type, 1)); _Ty* tVecData = (_Ty*)(tVec.data); tMat.at<_Ty>(0, 0) = 1.0; tMat.at<_Ty>(1, 1) = 1.0; tMat.at<_Ty>(2, 2) = 1.0; tMat.at<_Ty>(0, 3) = tVecData[0]; tMat.at<_Ty>(1, 3) = tVecData[1]; tMat.at<_Ty>(2, 3) = tVecData[2]; return tMat; } template inline void applyHomography(S& x, S& y, const T* homoMat) { double z = x * homoMat[6] + y * homoMat[7] + homoMat[8]; S x0 = saturate_cast((x * homoMat[0] + y * homoMat[1] + homoMat[2]) / z); y = saturate_cast((x * homoMat[3] + y * homoMat[4] + homoMat[5]) / z); x = x0; } template void applyHomography(Point_& pnt, T* homoMat) { applyHomography(pnt.x, pnt.y, homoMat); } enum EnumPntsMappingType { PntMapTypeAffine = 0, PntMapTypeRigid, PntMapTypeHomo, }; CYCLOPS_GEOM_DLLSPEC Mat findPntsMapping(vector& src, vector& dst, EnumPntsMappingType type); /************************************************************************/ /* Shape */ /************************************************************************/ CYCLOPS_GEOM_DLLSPEC float isParallel(const Vec4f& l1, const Vec4f& l2, float err_tol = 1e-9); // returns area/avg(l1.length + l2.length), smaller value if the two lines are more likely collinear CYCLOPS_GEOM_DLLSPEC float isCollinear(const Vec4f& l1, const Vec4f& l2, float err_tol = 1e-9); // return cosine of the biggest shape angle, bigger value if the three points are more likely collinear CYCLOPS_GEOM_DLLSPEC float isCollinear(const Point2f& p1, const Point2f& p2, const Point2f& p3, float err_tol = 1e-9); // returns (rmin/rmax), [0, 1], bigger value if the two circles are more likely concentric. // return 0 if they are intersected or no overlap. CYCLOPS_GEOM_DLLSPEC float isConcentric(const Vec3f& c1, const Vec3f& c2, float err_tol = 1e-9); bool isLikeArch(const Mat& vec, double tor); bool isLikeHorizontalLine(const Mat& vec, double tor); double isLikeLine(const vector& pnts, int distType, Vec4f& line); bool isPntInCircle(const Vec3f& circle, const Point2f& p); Vec3f makeCircle(const Point2f& p1, const Point2f& p2); Vec3f makeCircle(const Point2f& p1, const Point2f& p2, const Point2f& p3); CYCLOPS_GEOM_DLLSPEC void minAreaCircle(const vector& pnts, Vec3f& circle); CYCLOPS_GEOM_DLLSPEC RotatedRect minAreaRect2(InputArray pnts); bool approxMinAreaQuadix(const vector& pnts, vector& corners, bool isClockWise = false); bool approxMinAreaQuadix(const vector& pnts, vector& corners, bool isClockWise = false); CYCLOPS_GEOM_DLLSPEC bool approxMinAreaPoly(const vector& pnts, vector& corners, bool isClockWise = false, int maxVertex = 0); bool approxMinAreaPoly(const vector& pnts, vector& corners, bool isClockWise = false, int maxVertex = 0); void approxPolyVSV(const vector& pnts, vector& corners, int number, bool closed); void approxPolyVSV(const vector& pnts, vector& corners, int number, bool closed); vector reorderToAlignPnt(const vector& pnts, const Point2f& alignPnt); vector reorderToAlignPnt(const vector& pnts, const Point& alignPnt); template inline T getTriangleArea(T ax, T ay, T bx, T by, T cx, T cy) { double area = ((bx - ax)*(cy - ay) - (cx - ax)*(by - ay)) / 2.; return saturate_cast(abs(area)); } template inline T getTriangleArea(const Point_& a, const Point_& b, const Point_& c) { return getTriangleArea(a.x, a.y, b.x, b.y, c.x, c.y); } template inline float getConvexHullArea(const vector >& contour) { vector > hull; convexHull(contour, hull); return contourArea(hull); } CYCLOPS_GEOM_DLLSPEC float getInertia(const Moments& moms); float getOrientation(const Moments& moms); template inline bool circumcenter(T x1, T y1, T x2, T y2, T x3, T y3, T& px, T& py) { T a = x2 - x1; T b = y2 - y1; T c = x3 - x1; T d = y3 - y1; T e = a * (x1 + x2) + b * (y1 + y2); T f = c * (x1 + x3) + d * (y1 + y3); T g = T(2.0) * (a * (y3 - y2) - b * (x3 - x2)); if (abs(g) < FLT_EPSILON) { return false; // it means the 3 points may be collinear } else { px = (d * e - b * f) / g; py = (a * f - c * e) / g; return true; } } template inline bool circumcenter(const Point_& p1, const Point_& p2, const Point_& p3, Point_& p) { return circumcenter(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p.x, p.y); } Vec4f midLine(const Vec4f& l1, const Vec4f& l2); //*****************by reference equation from web************* //http://www.ambrsoft.com/TrigoCalc/Circles2/Circles2Tangent_.htm //the func generates two pairs of outer tangent over two circles //each pair could be formed as line which is tangent line on the //one side of two circles CYCLOPS_GEOM_DLLSPEC void outerTangentLine2Circle(const Vec3d& circle1, const Vec3d& circle2, std::pair& pp1, std::pair& pp2); /***************** reference paper *************/ //"poles of Inaccessibility:A calculation algorithm for the //remotest places on earth" CYCLOPS_GEOM_DLLSPEC void getInscribedCircleOnPolygonBySequency(const vector& poly, Vec3f & circle); // count pixels inside the polygon contour, works only for contour of dense points (out of CHAIN_APPROX_NONE) int countPixelInPolygon(const vector& poly, bool countBorder = true); }; using namespace GeomUtils; /************************************************************************/ /* Equal, Less, Hash injection to STD */ /************************************************************************/ struct RectHasher { size_t operator()(Rect const& r) const noexcept { return std::hash{}(r.x) ^ (std::hash{}(r.y) << 1) ^ (std::hash{}(r.width) << 2) ^ (std::hash{}(r.height) << 3); } }; struct RectEqualFunc { bool operator() (const Rect& r1, const Rect& r2) const { return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height; } }; #endif // GeomUtils_h_