mirror of
https://github.com/PhotonVision/photonvision
synced 2026-07-01 02:41:42 +00:00
Add rotatedrect detections for OD and bump rubik JNI (#2325)
## Description <!-- What changed? Why? (the code + comments should speak for itself on the "how") --> <!-- Fun screenshots or a cool video or something are super helpful as well. If this touches platform-specific behavior, this is where test evidence should be collected. --> <!-- Any issues this pull request closes or pull requests this supersedes should be linked with `Closes #issuenumber`. --> Pursuant to PhotonVision/rubik_jni#21 modify the neuralnetworkresult and related code to use a rotatedrect. This is scaffolding for implementing OBB -- overall this shouldn't change behavior of existing normal object detection models. This PR also bumps the rubik_jni version. ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2025.3.2 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added Co-authored-by: Matt Morley <matthew.morley.ca@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@ ext {
|
||||
javalinVersion = "6.7.0"
|
||||
libcameraDriverVersion = "v2026.0.0"
|
||||
rknnVersion = "v2026.0.1"
|
||||
rubikVersion = "v2026.0.1"
|
||||
rubikVersion = "dev-v2026.0.1-3-g977bb2e"
|
||||
frcYear = "2026"
|
||||
mrcalVersion = "v2026.0.0";
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect2d;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.RotatedRect;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
@@ -86,21 +87,26 @@ public class Letterbox {
|
||||
* @return The resized detections
|
||||
*/
|
||||
public List<NeuralNetworkPipeResult> resizeDetections(List<NeuralNetworkPipeResult> unscaled) {
|
||||
var ret = new ArrayList<NeuralNetworkPipeResult>();
|
||||
var ret = new ArrayList<NeuralNetworkPipeResult>(unscaled.size());
|
||||
|
||||
for (var t : unscaled) {
|
||||
var scale = 1.0 / this.scale;
|
||||
var boundingBox = t.bbox();
|
||||
double x = (boundingBox.x - this.dx) * scale;
|
||||
double y = (boundingBox.y - this.dy) * scale;
|
||||
double width = boundingBox.width * scale;
|
||||
double height = boundingBox.height * scale;
|
||||
|
||||
double cx = (boundingBox.center.x - this.dx) * scale;
|
||||
double cy = (boundingBox.center.y - this.dy) * scale;
|
||||
double width = boundingBox.size.width * scale;
|
||||
double height = boundingBox.size.height * scale;
|
||||
|
||||
Point center = new Point(cx, cy);
|
||||
Size size = new Size(width, height);
|
||||
|
||||
// angle is unchanged from letterbox transformation
|
||||
|
||||
ret.add(
|
||||
new NeuralNetworkPipeResult(
|
||||
new Rect2d(x, y, width, height), t.classIdx(), t.confidence()));
|
||||
new RotatedRect(center, size, boundingBox.angle), t.classIdx(), t.confidence()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,9 @@ public class RubikObjectDetector implements ObjectDetector {
|
||||
|
||||
// Create the detector
|
||||
try {
|
||||
ptr = RubikJNI.create(model.modelFile.getPath().toString());
|
||||
ptr =
|
||||
RubikJNI.create(
|
||||
model.modelFile.getPath().toString(), model.properties.version().ordinal());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to create detector from path " + model.modelFile.getPath(), e);
|
||||
throw new RuntimeException(
|
||||
|
||||
@@ -57,6 +57,17 @@ public class Contour implements Releasable {
|
||||
new Point(box.x, box.y + box.height));
|
||||
}
|
||||
|
||||
public Contour(RotatedRect obb) {
|
||||
Point[] pts = new Point[4];
|
||||
for (int i = 0; i < 4; ++i) pts[i] = new Point();
|
||||
|
||||
obb.points(pts);
|
||||
|
||||
// target: tl tr br bl
|
||||
// pts array: "The order is bottomLeft, topLeft, topRight, bottomRight."
|
||||
this.mat = new MatOfPoint(pts[1], pts[2], pts[3], pts[0]);
|
||||
}
|
||||
|
||||
public MatOfPoint2f getMat2f() {
|
||||
if (mat2f == null) {
|
||||
mat2f = new MatOfPoint2f(mat.toArray());
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
import org.photonvision.common.util.numbers.DoubleCouple;
|
||||
import org.photonvision.vision.frame.FrameStaticProperties;
|
||||
import org.photonvision.vision.pipe.CVPipe;
|
||||
import org.photonvision.vision.target.TargetCalculations;
|
||||
|
||||
public class FilterObjectDetectionsPipe
|
||||
extends CVPipe<
|
||||
@@ -44,13 +45,13 @@ public class FilterObjectDetectionsPipe
|
||||
var boc = contour.bbox();
|
||||
|
||||
// Area filtering
|
||||
double areaPercentage = boc.area() / params.frameStaticProperties().imageArea * 100.0;
|
||||
double areaPercentage = boc.size.area() / params.frameStaticProperties().imageArea * 100.0;
|
||||
double minAreaPercentage = params.area().getFirst();
|
||||
double maxAreaPercentage = params.area().getSecond();
|
||||
if (areaPercentage < minAreaPercentage || areaPercentage > maxAreaPercentage) return;
|
||||
|
||||
// Aspect ratio filtering; much simpler since always axis-aligned
|
||||
double aspectRatio = boc.width / boc.height;
|
||||
// Aspect ratio filtering
|
||||
double aspectRatio = TargetCalculations.getAspectRatio(boc, params.isLandscape());
|
||||
if (aspectRatio < params.ratio().getFirst() || aspectRatio > params.ratio().getSecond()) return;
|
||||
|
||||
m_filteredContours.add(contour);
|
||||
|
||||
@@ -17,6 +17,20 @@
|
||||
|
||||
package org.photonvision.vision.pipe.impl;
|
||||
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Rect2d;
|
||||
import org.opencv.core.RotatedRect;
|
||||
import org.opencv.core.Size;
|
||||
|
||||
public record NeuralNetworkPipeResult(Rect2d bbox, int classIdx, double confidence) {}
|
||||
public record NeuralNetworkPipeResult(RotatedRect bbox, int classIdx, double confidence) {
|
||||
public NeuralNetworkPipeResult(Rect2d rect, int classIdx, double confidence) {
|
||||
// turn the axis-aligned rect into a RotatedRect with angle 0 degrees
|
||||
this(
|
||||
new RotatedRect(
|
||||
new Point(rect.x + (rect.width) / 2, rect.y + (rect.height) / 2),
|
||||
new Size(rect.width, rect.height),
|
||||
0.0),
|
||||
classIdx,
|
||||
confidence);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user