Skip to content

Commit

Permalink
Add float version of BoxPoints and MinAreaRect
Browse files Browse the repository at this point in the history
  • Loading branch information
TeCHiScy committed Mar 30, 2024
1 parent 4b4d1d8 commit 378a4d8
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 0 deletions.
10 changes: 10 additions & 0 deletions core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
17 changes: 17 additions & 0 deletions core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
30 changes: 30 additions & 0 deletions imgproc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
81 changes: 81 additions & 0 deletions imgproc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions imgproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 71 additions & 0 deletions imgproc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down

0 comments on commit 378a4d8

Please sign in to comment.