From 378a4d8fd68ff0b698e84ddabdc370c85db71e59 Mon Sep 17 00:00:00 2001 From: TeCHiScy <741195+TeCHiScy@users.noreply.github.com> Date: Fri, 29 Mar 2024 21:34:23 +0800 Subject: [PATCH] Add float version of BoxPoints and MinAreaRect --- core.cpp | 10 ++++++ core.h | 17 +++++++++++ imgproc.cpp | 30 ++++++++++++++++++ imgproc.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ imgproc.h | 2 ++ imgproc_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+) diff --git a/core.cpp b/core.cpp index 7c9f141f..949affdf 100644 --- a/core.cpp +++ b/core.cpp @@ -865,6 +865,16 @@ void Points_Close(Points ps) { void Point_Close(Point p) {} +void Points2f_Close(Points2f ps) { + for (size_t i = 0; i < ps.length; i++) { + Point2f_Close(ps.points[i]); + } + + delete[] ps.points; +} + +void Point2f_Close(Point2f p) {} + void Rects_Close(struct Rects rs) { delete[] rs.rects; } diff --git a/core.h b/core.h index c939b552..f0c81375 100644 --- a/core.h +++ b/core.h @@ -121,6 +121,12 @@ typedef struct Size { int height; } Size; +// Wrapper for an individual cv::cvSize +typedef struct Size2f { + float width; + float height; +} Size2f; + // Wrapper for an individual cv::RotatedRect typedef struct RotatedRect { Points pts; @@ -130,6 +136,15 @@ typedef struct RotatedRect { double angle; } RotatedRect; +// Wrapper for an individual cv::RotatedRect +typedef struct RotatedRect2f { + Points2f pts; + Rect boundingRect; + Point2f center; + Size2f size; + double angle; +} RotatedRect2f; + // Wrapper for an individual cv::cvScalar typedef struct Scalar { double val1; @@ -268,6 +283,8 @@ void Rects_Close(struct Rects rs); void Mats_Close(struct Mats mats); void Point_Close(struct Point p); void Points_Close(struct Points ps); +void Point2f_Close(struct Point2f p); +void Points2f_Close(struct Points2f ps); void DMatches_Close(struct DMatches ds); void MultiDMatches_Close(struct MultiDMatches mds); diff --git a/imgproc.cpp b/imgproc.cpp index ae89fbfc..4b35fa36 100644 --- a/imgproc.cpp +++ b/imgproc.cpp @@ -198,6 +198,13 @@ void BoxPoints(RotatedRect rect, Mat boxPts){ cv::boxPoints(rotatedRectangle, *boxPts); } +void BoxPoints2f(RotatedRect2f rect, Mat boxPts){ + cv::Point2f centerPt(rect.center.x , rect.center.y); + cv::Size2f rSize(rect.size.width, rect.size.height); + cv::RotatedRect rotatedRectangle(centerPt, rSize, rect.angle); + cv::boxPoints(rotatedRectangle, *boxPts); +} + double ContourArea(PointVector pts) { return cv::contourArea(*pts); } @@ -225,6 +232,29 @@ struct RotatedRect MinAreaRect(PointVector pts){ return retrect; } +struct RotatedRect2f MinAreaRect2f(PointVector pts){ + cv::RotatedRect cvrect = cv::minAreaRect(*pts); + + Point2f* rpts = new Point2f[4]; + cv::Point2f* pts4 = new cv::Point2f[4]; + cvrect.points(pts4); + + for (size_t j = 0; j < 4; j++) { + Point2f pt = {pts4[j].x, pts4[j].y}; + rpts[j] = pt; + } + + delete[] pts4; + + cv::Rect bRect = cvrect.boundingRect(); + Rect r = {bRect.x, bRect.y, bRect.width, bRect.height}; + Point2f centrpt = {cvrect.center.x, cvrect.center.y}; + Size2f szsz = {cvrect.size.width, cvrect.size.height}; + + RotatedRect2f retrect = {(Contour2f){rpts, 4}, r, centrpt, szsz, cvrect.angle}; + return retrect; +} + void MinEnclosingCircle(PointVector pts, Point2f* center, float* radius){ cv::Point2f center2f; cv::minEnclosingCircle(*pts, center2f, *radius); diff --git a/imgproc.go b/imgproc.go index 98692f22..46e31349 100644 --- a/imgproc.go +++ b/imgproc.go @@ -458,6 +458,41 @@ func BoxPoints(rect RotatedRect, pts *Mat) { C.BoxPoints(r, pts.p) } +// BoxPoints finds the four vertices of a rotated rect. Useful to draw the rotated rectangle. +// +// For further Details, please see: +// https://docs.opencv.org/3.3.0/d3/dc0/group__imgproc__shape.html#gaf78d467e024b4d7936cf9397185d2f5c +func BoxPoints2f(rect RotatedRect2f, pts *Mat) { + rPoints := toCPoints2f(rect.Points) + + rRect := C.struct_Rect{ + x: C.int(rect.BoundingRect.Min.X), + y: C.int(rect.BoundingRect.Min.Y), + width: C.int(rect.BoundingRect.Max.X - rect.BoundingRect.Min.X), + height: C.int(rect.BoundingRect.Max.Y - rect.BoundingRect.Min.Y), + } + + rCenter := C.struct_Point2f{ + x: C.float(rect.Center.X), + y: C.float(rect.Center.Y), + } + + rSize := C.struct_Size2f{ + width: C.float(rect.Width), + height: C.float(rect.Height), + } + + r := C.struct_RotatedRect2f{ + pts: rPoints, + boundingRect: rRect, + center: rCenter, + size: rSize, + angle: C.double(rect.Angle), + } + + C.BoxPoints2f(r, pts.p) +} + // ContourArea calculates a contour area. // // For further details, please see: @@ -476,6 +511,15 @@ type RotatedRect struct { Angle float64 } +type RotatedRect2f struct { + Points []Point2f + BoundingRect image.Rectangle + Center Point2f + Width float32 + Height float32 + Angle float64 +} + // toPoints converts C.Contour to []image.Points func toPoints(points C.Contour) []image.Point { pArray := points.points @@ -495,6 +539,25 @@ func toPoints(points C.Contour) []image.Point { return points4 } +// toPoints2f converts C.Contour2f to []Point2f +func toPoints2f(points C.Contour2f) []Point2f { + pArray := points.points + pLength := int(points.length) + + pHdr := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(pArray)), + Len: pLength, + Cap: pLength, + } + sPoints := *(*[]C.Point)(unsafe.Pointer(&pHdr)) + + points4 := make([]Point2f, pLength) + for j, pt := range sPoints { + points4[j] = NewPoint2f(float32(pt.x), float32(pt.y)) + } + return points4 +} + // MinAreaRect finds a rotated rectangle of the minimum area enclosing the input 2D point set. // // For further details, please see: @@ -513,6 +576,24 @@ func MinAreaRect(points PointVector) RotatedRect { } } +// MinAreaRect finds a rotated rectangle of the minimum area enclosing the input 2D point set. +// +// For further details, please see: +// https://docs.opencv.org/master/d3/dc0/group__imgproc__shape.html#ga3d476a3417130ae5154aea421ca7ead9 +func MinAreaRect2f(points PointVector) RotatedRect2f { + result := C.MinAreaRect2f(points.p) + defer C.Points2f_Close(result.pts) + + return RotatedRect2f{ + Points: toPoints2f(result.pts), + BoundingRect: image.Rect(int(result.boundingRect.x), int(result.boundingRect.y), int(result.boundingRect.x)+int(result.boundingRect.width), int(result.boundingRect.y)+int(result.boundingRect.height)), + Center: NewPoint2f(float32(result.center.x), float32(result.center.y)), + Width: float32(result.size.width), + Height: float32(result.size.height), + Angle: float64(result.angle), + } +} + // FitEllipse Fits an ellipse around a set of 2D points. // // For further details, please see: diff --git a/imgproc.h b/imgproc.h index 46386eda..0b7ccd58 100644 --- a/imgproc.h +++ b/imgproc.h @@ -42,8 +42,10 @@ void PyrDown(Mat src, Mat dst, Size dstsize, int borderType); void PyrUp(Mat src, Mat dst, Size dstsize, int borderType); struct Rect BoundingRect(PointVector pts); void BoxPoints(RotatedRect rect, Mat boxPts); +void BoxPoints2f(RotatedRect2f rect, Mat boxPts); double ContourArea(PointVector pts); struct RotatedRect MinAreaRect(PointVector pts); +struct RotatedRect2f MinAreaRect2f(PointVector pts); struct RotatedRect FitEllipse(PointVector pts); void MinEnclosingCircle(PointVector pts, Point2f* center, float* radius); PointsVector FindContours(Mat src, Mat hierarchy, int mode, int method); diff --git a/imgproc_test.go b/imgproc_test.go index 2210485d..04a032e6 100644 --- a/imgproc_test.go +++ b/imgproc_test.go @@ -417,6 +417,77 @@ func TestMinAreaRect(t *testing.T) { } } +func TestBoxPoints2f(t *testing.T) { + img := IMRead("images/face-detect.jpg", IMReadGrayScale) + if img.Empty() { + t.Error("Invalid read of Mat in BoxPoints2f test") + } + defer img.Close() + + threshImg := NewMat() + defer threshImg.Close() + + Threshold(img, &threshImg, 25, 255, ThresholdBinary) + + contours := FindContours(threshImg, RetrievalExternal, ChainApproxSimple) + defer contours.Close() + + contour := contours.At(0) + + hull := NewMat() + defer hull.Close() + ConvexHull(contour, &hull, false, false) + hullPoints := []image.Point{} + for i := 0; i < hull.Cols(); i++ { + for j := 0; j < hull.Rows(); j++ { + p := hull.GetIntAt(j, i) + hullPoints = append(hullPoints, contour.At(int(p))) + } + } + + pvhp := NewPointVectorFromPoints(hullPoints) + defer pvhp.Close() + + rect := MinAreaRect2f(pvhp) + pts := NewMat() + defer pts.Close() + BoxPoints2f(rect, &pts) + + if pts.Empty() || pts.Rows() != 4 || pts.Cols() != 2 { + t.Error("Invalid BoxPoints2f test") + } +} + +func TestMinAreaRect2f(t *testing.T) { + src := []image.Point{ + image.Pt(0, 2), + image.Pt(2, 0), + image.Pt(8, 4), + image.Pt(4, 8), + } + + pv := NewPointVectorFromPoints(src) + defer pv.Close() + + m := MinAreaRect2f(pv) + + if m.Center.X != 3.5 { + t.Errorf("TestMinAreaRect2f(): unexpected center.X = %v, want = %v", m.Center.X, 3.5) + } + if m.Center.Y != 3.5 { + t.Errorf("TestMinAreaRect2f(): unexpected center.Y = %v, want = %v", m.Center.Y, 3.5) + } + if m.Width != 7.071067810058594 { + t.Errorf("TestMinAreaRect2f(): unexpected width = %v, want = %v", m.Width, 7.071067810058594) + } + if m.Height != 5.656853675842285 { + t.Errorf("TestMinAreaRect2f(): unexpected height = %v, want = %v", m.Height, 5.656853675842285) + } + if m.Angle != 45.0 { + t.Errorf("TestMinAreaRect2f(): unexpected angle = %v, want = %v", m.Angle, 45.0) + } +} + func TestFitEllipse(t *testing.T) { src := []image.Point{ image.Pt(1, 1),