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 isContourConvex, intersectConvexConvex #299

Merged
merged 6 commits into from
Nov 28, 2024
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -58,3 +58,4 @@ doc/api/

.flutter-plugins
.flutter-plugins-dependencies
Podfile.lock
26 changes: 26 additions & 0 deletions packages/dartcv/lib/src/imgproc/imgproc.dart
Original file line number Diff line number Diff line change
@@ -1758,3 +1758,29 @@ Mat accumulateWeighted(InputArray src, InputOutputArray dst, double alpha, {Inpu
}
return dst;
}

/// Tests a contour convexity.
///
/// The function tests whether the input contour is convex or not.
/// The contour must be simple, that is, without self-intersections.
/// Otherwise, the function output is undefined.
///
/// https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga8abf8010377b58cbc16db6734d92941b
bool isContourConvex(VecPoint contour) => cimgproc.cv_isContourConvex(contour.ref);

/// Finds intersection of two convex polygons.
///
/// https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga06eed475945f155030f2135a7f25f11d
(double rval, VecPoint p12) intersectConvexConvex(
VecPoint p1,
VecPoint p2, {
VecPoint? p12,
bool handleNested = true,
}) {
final r = calloc<ffi.Float>();
final pP12 = p12?.ptr ?? calloc<cvg.VecPoint>();
cvRun(() => cimgproc.cv_intersectConvexConvex(p1.ref, p2.ref, pP12, handleNested, r, ffi.nullptr));
final rval = (r.value, p12 ?? VecPoint.fromPointer(pP12));
calloc.free(r);
return rval;
}
21 changes: 21 additions & 0 deletions packages/dartcv/lib/src/imgproc/imgproc_async.dart
Original file line number Diff line number Diff line change
@@ -2109,3 +2109,24 @@ Future<Mat> accumulateWeightedAsync(
);
}
}

/// Finds intersection of two convex polygons.
///
/// https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga06eed475945f155030f2135a7f25f11d
Future<(double rval, VecPoint p12)> intersectConvexConvexAsync(
VecPoint p1,
VecPoint p2, {
VecPoint? p12,
bool handleNested = true,
}) {
final r = calloc<ffi.Float>();
final pP12 = p12?.ptr ?? calloc<cvg.VecPoint>();
return cvRunAsync0(
(callback) => cimgproc.cv_intersectConvexConvex(p1.ref, p2.ref, pP12, handleNested, r, callback),
(c) {
final rval = (r.value, p12 ?? VecPoint.fromPointer(pP12));
calloc.free(r);
return c.complete(rval);
},
);
}
4 changes: 1 addition & 3 deletions packages/dartcv/lib/src/native_lib.dart
Original file line number Diff line number Diff line change
@@ -6,8 +6,6 @@
import 'dart:ffi' as ffi;
import 'dart:io';

import 'package:logging/logging.dart';

import 'g/calib3d.g.dart' as calib3d;
import 'g/contrib.g.dart' as contrib;
import 'g/core.g.dart' as core;
@@ -42,7 +40,7 @@ ffi.DynamicLibrary loadNativeLibrary(String libName) {
try {
return ffi.DynamicLibrary.open(libPath);
} catch (e) {
Logger("dartcv").warning("$e");
print("Error loading $libPath, error: $e fallback to process.");
return ffi.DynamicLibrary.process();
}
}
1 change: 0 additions & 1 deletion packages/dartcv/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ environment:

dependencies:
ffi: ^2.1.3
logging: ^1.3.0

dev_dependencies:
ffigen: ">=13.0.0 <15.0.0"
2 changes: 1 addition & 1 deletion packages/dartcv/src
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 117 additions & 0 deletions packages/dartcv/test/imgproc/imgproc_async_test.dart
Original file line number Diff line number Diff line change
@@ -948,4 +948,121 @@ void main() async {
expect(p[0], isIn([0, 1]));
});
});

test('cv.intersectConvexConvexAsync', () async {
// helper functions
cv.VecPoint makeRectangle(cv.Point topLeft, cv.Point bottomRiht) =>
[topLeft, cv.Point(bottomRiht.x, topLeft.y), bottomRiht, cv.Point(topLeft.x, bottomRiht.y)].asVec();

Future<double> drawIntersection(cv.Mat image, cv.VecPoint p1, cv.VecPoint p2,
{bool handleNested = true}) async {
final (intersectArea, intersectionPolygon) =
await cv.intersectConvexConvexAsync(p1, p2, handleNested: handleNested);
if (intersectArea > 0) {
final fillColor =
!cv.isContourConvex(p1) || !cv.isContourConvex(p2) ? cv.Scalar(0, 0, 255) : cv.Scalar.all(200);
await cv.fillPolyAsync(image, cv.VecVecPoint.fromVecPoint(intersectionPolygon), fillColor);
}
await cv.polylinesAsync(image, cv.VecVecPoint.fromVecPoint(intersectionPolygon), true, cv.Scalar.black);
return intersectArea;
}

Future<void> drawDescription(
cv.Mat image, int intersectionArea, String description, cv.Point origin) async {
final caption = "Intersection area: $intersectionArea$description";
await cv.putTextAsync(image, caption, origin, cv.FONT_HERSHEY_SIMPLEX, 0.6, cv.Scalar.black);
}

// start testing
final image = cv.Mat.fromScalar(610, 550, cv.MatType.CV_8UC3, cv.Scalar.white);
double intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 10), cv.Point(50, 50)),
makeRectangle(cv.Point(20, 20), cv.Point(60, 60)),
);
await drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 40));

intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 70), cv.Point(35, 95)),
makeRectangle(cv.Point(35, 95), cv.Point(60, 120)),
);
await drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 100));

intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 130), cv.Point(60, 180)),
makeRectangle(cv.Point(20, 140), cv.Point(50, 170)),
handleNested: true,
);
await drawDescription(image, intersectionArea.toInt(), " (handleNested true)", cv.Point(70, 160));

intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 250), cv.Point(60, 300)),
makeRectangle(cv.Point(20, 250), cv.Point(50, 290)),
handleNested: true,
);

await drawDescription(image, intersectionArea.toInt(), " (handleNested true)", cv.Point(70, 280));

// These rectangles share an edge so handleNested can be false and an intersection is still found
intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 310), cv.Point(60, 360)),
makeRectangle(cv.Point(20, 310), cv.Point(50, 350)),
handleNested: false,
);

await drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 340));

intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 370), cv.Point(60, 420)),
makeRectangle(cv.Point(20, 371), cv.Point(50, 410)),
handleNested: false,
);

await drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 400));

// A vertex of the triangle lies on an edge of the rectangle so handleNested can be false and an intersection is still found
intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 430), cv.Point(60, 480)),
[cv.Point(35, 430), cv.Point(20, 470), cv.Point(50, 470)].asVec(),
handleNested: false,
);

await drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 460));

// Show intersection of overlapping rectangle and triangle
intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 490), cv.Point(40, 540)),
[cv.Point(25, 500), cv.Point(25, 530), cv.Point(60, 515)].asVec(),
handleNested: false,
);

await drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 520));

// This concave polygon is invalid input to intersectConvexConvex so it returns an invalid intersection
final cv.VecPoint notConvex = [
cv.Point(25, 560),
cv.Point(25, 590),
cv.Point(45, 580),
cv.Point(60, 600),
cv.Point(60, 550),
cv.Point(45, 570),
].asVec();
intersectionArea = await drawIntersection(
image,
makeRectangle(cv.Point(10, 550), cv.Point(50, 600)),
notConvex,
handleNested: false,
);

await drawDescription(image, intersectionArea.toInt(), " (invalid input: not convex)", cv.Point(70, 580));

await cv.imwriteAsync("test/images_out/intersections.png", image);
});
}
132 changes: 132 additions & 0 deletions packages/dartcv/test/imgproc/imgproc_test.dart
Original file line number Diff line number Diff line change
@@ -974,4 +974,136 @@ void main() async {
expect(p[0], isIn([0, 1]));
});
});

test('cv.isContourConvex', () {
final rectangle = [cv.Point(0, 0), cv.Point(100, 0), cv.Point(100, 100), cv.Point(0, 100)].asVec();
final res = cv.isContourConvex(rectangle);
expect(res, true);

final notConvex = [
cv.Point(25, 560),
cv.Point(25, 590),
cv.Point(45, 580),
cv.Point(60, 600),
cv.Point(60, 550),
cv.Point(45, 570),
].asVec();
expect(cv.isContourConvex(notConvex), false);
});

// https://docs.opencv.org/4.x/df/da5/samples_2cpp_2intersectExample_8cpp-example.html
test('cv.intersectConvexConvex', () {
// helper functions
cv.VecPoint makeRectangle(cv.Point topLeft, cv.Point bottomRiht) =>
[topLeft, cv.Point(bottomRiht.x, topLeft.y), bottomRiht, cv.Point(topLeft.x, bottomRiht.y)].asVec();

double drawIntersection(cv.Mat image, cv.VecPoint p1, cv.VecPoint p2, {bool handleNested = true}) {
final (intersectArea, intersectionPolygon) =
cv.intersectConvexConvex(p1, p2, handleNested: handleNested);
if (intersectArea > 0) {
final fillColor =
!cv.isContourConvex(p1) || !cv.isContourConvex(p2) ? cv.Scalar(0, 0, 255) : cv.Scalar.all(200);
cv.fillPoly(image, cv.VecVecPoint.fromVecPoint(intersectionPolygon), fillColor);
}
cv.polylines(image, cv.VecVecPoint.fromVecPoint(intersectionPolygon), true, cv.Scalar.black);
return intersectArea;
}

void drawDescription(cv.Mat image, int intersectionArea, String description, cv.Point origin) {
final caption = "Intersection area: $intersectionArea$description";
cv.putText(image, caption, origin, cv.FONT_HERSHEY_SIMPLEX, 0.6, cv.Scalar.black);
}

// start testing
final image = cv.Mat.fromScalar(610, 550, cv.MatType.CV_8UC3, cv.Scalar.white);
double intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 10), cv.Point(50, 50)),
makeRectangle(cv.Point(20, 20), cv.Point(60, 60)),
);
drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 40));

intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 70), cv.Point(35, 95)),
makeRectangle(cv.Point(35, 95), cv.Point(60, 120)),
);
drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 100));

intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 130), cv.Point(60, 180)),
makeRectangle(cv.Point(20, 140), cv.Point(50, 170)),
handleNested: true,
);
drawDescription(image, intersectionArea.toInt(), " (handleNested true)", cv.Point(70, 160));

intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 250), cv.Point(60, 300)),
makeRectangle(cv.Point(20, 250), cv.Point(50, 290)),
handleNested: true,
);

drawDescription(image, intersectionArea.toInt(), " (handleNested true)", cv.Point(70, 280));

// These rectangles share an edge so handleNested can be false and an intersection is still found
intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 310), cv.Point(60, 360)),
makeRectangle(cv.Point(20, 310), cv.Point(50, 350)),
handleNested: false,
);

drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 340));

intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 370), cv.Point(60, 420)),
makeRectangle(cv.Point(20, 371), cv.Point(50, 410)),
handleNested: false,
);

drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 400));

// A vertex of the triangle lies on an edge of the rectangle so handleNested can be false and an intersection is still found
intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 430), cv.Point(60, 480)),
[cv.Point(35, 430), cv.Point(20, 470), cv.Point(50, 470)].asVec(),
handleNested: false,
);

drawDescription(image, intersectionArea.toInt(), " (handleNested false)", cv.Point(70, 460));

// Show intersection of overlapping rectangle and triangle
intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 490), cv.Point(40, 540)),
[cv.Point(25, 500), cv.Point(25, 530), cv.Point(60, 515)].asVec(),
handleNested: false,
);

drawDescription(image, intersectionArea.toInt(), "", cv.Point(70, 520));

// This concave polygon is invalid input to intersectConvexConvex so it returns an invalid intersection
final cv.VecPoint notConvex = [
cv.Point(25, 560),
cv.Point(25, 590),
cv.Point(45, 580),
cv.Point(60, 600),
cv.Point(60, 550),
cv.Point(45, 570),
].asVec();
intersectionArea = drawIntersection(
image,
makeRectangle(cv.Point(10, 550), cv.Point(50, 600)),
notConvex,
handleNested: false,
);

drawDescription(image, intersectionArea.toInt(), " (invalid input: not convex)", cv.Point(70, 580));

cv.imwrite("test/images_out/intersections.png", image);
});
}
4 changes: 2 additions & 2 deletions packages/opencv_core/images/opencv_core_size_report.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/opencv_core/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ description: |
if you need them, please use `opencv_dart` instead.
version: 1.3.3
opencv_version: 4.10.0+10
dartcv_version: 4.10.0.5
dartcv_version: 4.10.0.6
repository: https://github.com/rainyl/opencv_dart
homepage: https://github.com/rainyl/opencv_dart/tree/main/packages/opencv_core

4 changes: 2 additions & 2 deletions packages/opencv_dart/images/opencv_dart_size_report.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/opencv_dart/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ description: |
please use `opencv_core` instead.
version: 1.3.3
opencv_version: 4.10.0+10
dartcv_version: 4.10.0.5
dartcv_version: 4.10.0.6
repository: https://github.com/rainyl/opencv_dart
homepage: https://github.com/rainyl/opencv_dart/tree/main/packages/opencv_dart