Skip to content

Commit 6195d2d

Browse files
committed
Implement OBB
1 parent e84e3e7 commit 6195d2d

File tree

10 files changed

+59
-19
lines changed

10 files changed

+59
-19
lines changed

docs/source/docs/objectDetection/opi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ PhotonVision runs object detection on the Orange Pi 5 by use of the RKNN model a
66

77
## Supported models
88

9-
PhotonVision currently ONLY supports 640x640 Ultralytics YOLOv5, YOLOv8, and YOLOv11 models trained and converted to `.rknn` format for RK3588 SOCs! Other models require different post-processing code and will NOT work.
9+
PhotonVision currently ONLY supports 640x640 Ultralytics YOLOv5, YOLOv8, YOLOv11, and YOLOv11OBB models trained and converted to `.rknn` format for RK3588 SOCs! Other models require different post-processing code and will NOT work.
1010

1111
## Converting Custom Models
1212

photon-client/src/components/settings/ObjectDetectionCard.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,8 @@ const handleBulkImport = () => {
304304
<v-card-text>
305305
<span v-if="useSettingsStore().general.supportedBackends?.includes('RKNN')"
306306
>Upload a new object detection model to this device that can be used in a pipeline. Note that ONLY
307-
640x640 YOLOv5, YOLOv8, and YOLOv11 models trained and converted to `.rknn` format for RK3588 SOCs are
308-
currently supporter!</span
307+
640x640 YOLOv5, YOLOv8, YOLOv11, and YOLOv11 OBB models trained and converted to `.rknn` format for RK3588 SOCs are
308+
currently supported!</span
309309
>
310310
<span v-else-if="useSettingsStore().general.supportedBackends?.includes('RUBIK')"
311311
>Upload a new object detection model to this device that can be used in a pipeline. Note that ONLY
@@ -344,7 +344,7 @@ const handleBulkImport = () => {
344344
label="Model Version"
345345
:items="
346346
useSettingsStore().general.supportedBackends?.includes('RKNN')
347-
? ['YOLOv5', 'YOLOv8', 'YOLO11']
347+
? ['YOLOv5', 'YOLOv8', 'YOLO11', 'YOLO11OBB']
348348
: ['YOLOv8', 'YOLO11']
349349
"
350350
/>

photon-client/src/types/SettingTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface ObjectDetectionModelProperties {
2121
resolutionWidth: number;
2222
resolutionHeight: number;
2323
family: "RKNN" | "RUBIK";
24-
version: "YOLOV5" | "YOLOV8" | "YOLOV11";
24+
version: "YOLOV5" | "YOLOV8" | "YOLOV11" | "YOLOV11OBB";
2525
}
2626

2727
export interface MetricData {

photon-core/src/main/java/org/photonvision/common/configuration/NeuralNetworkModelManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ public String extension() {
244244
public enum Version {
245245
YOLOV5,
246246
YOLOV8,
247-
YOLOV11
247+
YOLOV11,
248+
YOLOV11OBB
248249
}
249250

250251
/**

photon-core/src/main/java/org/photonvision/vision/objects/Letterbox.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import java.util.List;
2222
import org.opencv.core.Core;
2323
import org.opencv.core.Mat;
24+
import org.opencv.core.Point;
2425
import org.opencv.core.Rect2d;
26+
import org.opencv.core.RotatedRect;
2527
import org.opencv.core.Scalar;
2628
import org.opencv.core.Size;
2729
import org.opencv.imgproc.Imgproc;
@@ -86,19 +88,25 @@ public static Letterbox letterbox(Mat frame, Mat letterboxed, Size newShape, Sca
8688
* @return The resized detections
8789
*/
8890
public List<NeuralNetworkPipeResult> resizeDetections(List<NeuralNetworkPipeResult> unscaled) {
89-
var ret = new ArrayList<NeuralNetworkPipeResult>();
91+
var ret = new ArrayList<NeuralNetworkPipeResult>(unscaled.size());
9092

9193
for (var t : unscaled) {
9294
var scale = 1.0 / this.scale;
9395
var boundingBox = t.bbox();
94-
double x = (boundingBox.x - this.dx) * scale;
95-
double y = (boundingBox.y - this.dy) * scale;
96-
double width = boundingBox.width * scale;
97-
double height = boundingBox.height * scale;
96+
97+
double cx = (boundingBox.center.x - this.dx) * scale;
98+
double cy = (boundingBox.center.y - this.dy) * scale;
99+
double width = boundingBox.size.width * scale;
100+
double height = boundingBox.size.height * scale;
101+
102+
Point center = new Point(cx, cy);
103+
Size size = new Size(width, height);
104+
105+
// angle is unchanged from letterbox transformation
98106

99107
ret.add(
100108
new NeuralNetworkPipeResult(
101-
new Rect2d(x, y, width, height), t.classIdx(), t.confidence()));
109+
new RotatedRect(center, size, boundingBox.angle), t.classIdx(), t.confidence()));
102110
}
103111

104112
return ret;

photon-core/src/main/java/org/photonvision/vision/objects/RknnModel.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ public RknnModel(ModelProperties properties) throws IllegalArgumentException {
5353

5454
if (properties.version() != Version.YOLOV5
5555
&& properties.version() != Version.YOLOV8
56-
&& properties.version() != Version.YOLOV11) {
57-
throw new IllegalArgumentException("Model version must be YOLOV5, YOLOV8, or YOLOV11");
56+
&& properties.version() != Version.YOLOV11
57+
&& properties.version() != Version.YOLOV11OBB) {
58+
throw new IllegalArgumentException("Model version must be YOLOV5, YOLOV8, YOLOV11, or YOLOV11OBB");
5859
}
5960

6061
this.properties = properties;

photon-core/src/main/java/org/photonvision/vision/opencv/Contour.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ public Contour(Rect2d box) {
5757
new Point(box.x, box.y + box.height));
5858
}
5959

60+
public Contour(RotatedRect obb) {
61+
Point[] pts = new Point[4];
62+
for (int i = 0; i < 4; ++i) pts[i] = new Point();
63+
64+
obb.points(pts);
65+
66+
// target: tl tr br bl
67+
// pts array: "The order is bottomLeft, topLeft, topRight, bottomRight."
68+
this.mat = new MatOfPoint(pts[1], pts[2], pts[3], pts[0]);
69+
}
70+
6071
public MatOfPoint2f getMat2f() {
6172
if (mat2f == null) {
6273
mat2f = new MatOfPoint2f(mat.toArray());

photon-core/src/main/java/org/photonvision/vision/pipe/impl/FilterObjectDetectionsPipe.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.photonvision.common.util.numbers.DoubleCouple;
2323
import org.photonvision.vision.frame.FrameStaticProperties;
2424
import org.photonvision.vision.pipe.CVPipe;
25+
import org.photonvision.vision.target.TargetCalculations;
2526

2627
public class FilterObjectDetectionsPipe
2728
extends CVPipe<
@@ -42,15 +43,16 @@ protected List<NeuralNetworkPipeResult> process(List<NeuralNetworkPipeResult> in
4243

4344
private void filterContour(NeuralNetworkPipeResult contour) {
4445
var boc = contour.bbox();
45-
46+
4647
// Area filtering
47-
double areaPercentage = boc.area() / params.frameStaticProperties().imageArea * 100.0;
48+
double areaPercentage = boc.size.area() / params.frameStaticProperties().imageArea * 100.0;
4849
double minAreaPercentage = params.area().getFirst();
4950
double maxAreaPercentage = params.area().getSecond();
5051
if (areaPercentage < minAreaPercentage || areaPercentage > maxAreaPercentage) return;
5152

52-
// Aspect ratio filtering; much simpler since always axis-aligned
53-
double aspectRatio = boc.width / boc.height;
53+
// Aspect Ratio Filtering.
54+
double aspectRatio =
55+
TargetCalculations.getAspectRatio(boc, params.isLandscape());
5456
if (aspectRatio < params.ratio().getFirst() || aspectRatio > params.ratio().getSecond()) return;
5557

5658
m_filteredContours.add(contour);

photon-core/src/main/java/org/photonvision/vision/pipe/impl/NeuralNetworkPipeResult.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@
1717

1818
package org.photonvision.vision.pipe.impl;
1919

20+
import org.opencv.core.Point;
2021
import org.opencv.core.Rect2d;
22+
import org.opencv.core.RotatedRect;
23+
import org.opencv.core.Size;
2124

22-
public record NeuralNetworkPipeResult(Rect2d bbox, int classIdx, double confidence) {}
25+
public record NeuralNetworkPipeResult(RotatedRect bbox, int classIdx, double confidence) {
26+
public NeuralNetworkPipeResult(Rect2d rect, int classIdx, double confidence) {
27+
// turn the axis-aligned rect into a RotatedRect with angle 0 degrees
28+
this(
29+
new RotatedRect(
30+
new Point(rect.x + (rect.width) / 2, rect.y + (rect.height) / 2),
31+
new Size(rect.width, rect.height),
32+
0.0
33+
),
34+
classIdx,
35+
confidence
36+
);
37+
}
38+
}

photon-server/src/main/java/org/photonvision/server/RequestHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ public static void onImportObjectDetectionModelRequest(Context ctx) {
570570
case "YOLOv5" -> NeuralNetworkModelManager.Version.YOLOV5;
571571
case "YOLOv8" -> NeuralNetworkModelManager.Version.YOLOV8;
572572
case "YOLO11" -> NeuralNetworkModelManager.Version.YOLOV11;
573+
case "YOLO11OBB" -> NeuralNetworkModelManager.Version.YOLOV11OBB;
573574
// Add more versions as necessary for new models
574575
default -> {
575576
ctx.status(400);

0 commit comments

Comments
 (0)