Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add float version of BoxPoints and MinAreaRect #1164

Merged
merged 1 commit into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading