From c836ba1247581b90e93dbe0cb66aca018d14fd1f Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:16:01 +0000 Subject: [PATCH 01/17] .gitignore change --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6a3ddc0b6f..a709d075e1 100644 --- a/.gitignore +++ b/.gitignore @@ -156,3 +156,4 @@ photon-client/playwright-report/ photon-client/blob-report/ photon-client/playwright/.cache/ photon-client/playwright/.auth/ +photon-client/package-lock.json From b3093481d9f2386b940c9931dc6793fd007d6fbc Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:17:32 +0000 Subject: [PATCH 02/17] Add JsonTypeName annotation and initialize targetModel in ObjectDetectionPipelineSettings --- .../vision/pipeline/ObjectDetectionPipelineSettings.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java index 66caa2cf96..efa0e0384a 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java @@ -17,10 +17,13 @@ package org.photonvision.vision.pipeline; +import com.fasterxml.jackson.annotation.JsonTypeName; import org.photonvision.common.configuration.NeuralNetworkModelManager; import org.photonvision.common.configuration.NeuralNetworkModelsSettings; import org.photonvision.vision.objects.Model; +import org.photonvision.vision.target.TargetModel; +@JsonTypeName("ObjectDetectionPipelineSettings") public class ObjectDetectionPipelineSettings extends AdvancedPipelineSettings { public double confidence; public double nms; // non maximal suppression @@ -35,10 +38,11 @@ public ObjectDetectionPipelineSettings() { ledMode = false; confidence = .9; nms = .45; + targetModel = TargetModel.k2025Algae; model = NeuralNetworkModelManager.getInstance() .getDefaultModel() .map(Model::getProperties) .orElse(null); } -} +} \ No newline at end of file From f5c2b4e8d7d7f2cf6818492f0fa698b7bda152b8 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:18:04 +0000 Subject: [PATCH 03/17] Add 3D target solving functionality to ObjectDetectionPipeline --- .../pipeline/ObjectDetectionPipeline.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java index a009d2cc9e..8653e7ddae 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java @@ -17,8 +17,10 @@ package org.photonvision.vision.pipeline; +import java.util.Arrays; import java.util.List; import java.util.Optional; +import org.opencv.core.Point; import org.photonvision.common.configuration.NeuralNetworkModelManager; import org.photonvision.vision.frame.Frame; import org.photonvision.vision.frame.FrameThresholdType; @@ -40,6 +42,9 @@ public class ObjectDetectionPipeline private final SortContoursPipe sortContoursPipe = new SortContoursPipe(); private final Collect2dTargetsPipe collect2dTargetsPipe = new Collect2dTargetsPipe(); private final FilterObjectDetectionsPipe filterContoursPipe = new FilterObjectDetectionsPipe(); + private final SolvePNPPipe solvePNPPipe = new SolvePNPPipe(); + + private final Point[] rectPoints = new Point[4]; private static final FrameThresholdType PROCESSING_TYPE = FrameThresholdType.NONE; @@ -99,11 +104,15 @@ protected void setPipeParamsImpl() { settings.contourTargetOffsetPointEdge, settings.contourTargetOrientation, frameStaticProperties)); + + solvePNPPipe.setParams( + new SolvePNPPipe.SolvePNPPipeParams( + frameStaticProperties.cameraCalibration, settings.targetModel)); } @Override protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings settings) { - long sumPipeNanosElapsed = 0; + long sumPipeNanosElapsed = 0L; CVPipeResult> neuralNetworkResult = objectDetectorPipe.run(frame.colorImage); @@ -125,11 +134,29 @@ protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings collect2dTargetsPipe.run(sortContoursResult.output); sumPipeNanosElapsed += collect2dTargetsResult.nanosElapsed; + List targetList; + + // 3d stuff + if (settings.solvePNPEnabled) { + collect2dTargetsResult.output.forEach( + target -> { + target.getMinAreaRect().points(rectPoints); + target.setTargetCorners(Arrays.asList(rectPoints)); + }); + + var solvePNPResult = solvePNPPipe.run(collect2dTargetsResult.output); + sumPipeNanosElapsed += solvePNPResult.nanosElapsed; + + targetList = solvePNPResult.output; + } else { + targetList = collect2dTargetsResult.output; + } + var fpsResult = calculateFPSPipe.run(null); var fps = fpsResult.output; return new CVPipelineResult( - frame.sequenceID, sumPipeNanosElapsed, fps, collect2dTargetsResult.output, frame, names); + frame.sequenceID, sumPipeNanosElapsed, fps, targetList, frame, names); } @Override @@ -137,4 +164,4 @@ public void release() { objectDetectorPipe.release(); super.release(); } -} +} \ No newline at end of file From e051e84922f871319e6c3c13d61fde2457843928 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:20:11 +0000 Subject: [PATCH 04/17] Reorder TargetModel enum values and update default target model in ObjectDetectionPipelineSettings --- photon-client/src/types/PipelineTypes.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/photon-client/src/types/PipelineTypes.ts b/photon-client/src/types/PipelineTypes.ts index 17a9f1cbdf..2f362b3359 100644 --- a/photon-client/src/types/PipelineTypes.ts +++ b/photon-client/src/types/PipelineTypes.ts @@ -30,9 +30,9 @@ export enum TargetModel { InfiniteRechargeHighGoalOuter = 2, CircularPowerCell7in = 3, RapidReactCircularCargoBall = 4, - AprilTag6in_16h5 = 5, - AprilTag6p5in_36h11 = 6, - ReefscapeAlgae = 7 + ReefscapeAlgae = 5, + AprilTag6in_16h5 = 6, + AprilTag6p5in_36h11 = 7 } export interface PipelineSettings { @@ -313,8 +313,8 @@ export const DefaultObjectDetectionPipelineSettings: ObjectDetectionPipelineSett ...DefaultPipelineSettings, pipelineType: PipelineType.ObjectDetection, cameraGain: 20, - targetModel: TargetModel.InfiniteRechargeHighGoalOuter, - ledMode: true, + targetModel: TargetModel.ReefscapeAlgae, + ledMode: false, outputMaximumTargets: 20, cameraExposureRaw: 6, confidence: 0.9, @@ -354,3 +354,4 @@ export type ActiveConfigurablePipelineSettings = | ConfigurableArucoPipelineSettings | ConfigurableObjectDetectionPipelineSettings | ConfigurableCalibration3dPipelineSettings; + \ No newline at end of file From 3d11b75e05ea17ba9cd16294689936f4201a6c0f Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:20:44 +0000 Subject: [PATCH 05/17] Remove pipeline type check for ObjectDetection in StreamConfigCard --- photon-client/src/components/dashboard/StreamConfigCard.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/photon-client/src/components/dashboard/StreamConfigCard.vue b/photon-client/src/components/dashboard/StreamConfigCard.vue index 38e031e934..f368525230 100644 --- a/photon-client/src/components/dashboard/StreamConfigCard.vue +++ b/photon-client/src/components/dashboard/StreamConfigCard.vue @@ -48,7 +48,6 @@ const processingMode = computed({ :disabled=" !useCameraSettingsStore().hasConnected || !useCameraSettingsStore().isCurrentVideoFormatCalibrated || - useCameraSettingsStore().currentPipelineSettings.pipelineType == PipelineType.ObjectDetection || useCameraSettingsStore().currentPipelineSettings.pipelineType == PipelineType.ColoredShape " :variant="theme.global.name.value === 'LightTheme' ? 'elevated' : 'outlined'" From 61ca96d5d29c602d0a082a0f5bfee06ad674ce3c Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 00:21:30 +0000 Subject: [PATCH 06/17] Fix PnP tab filtering logic in ConfigOptions to exclude when 3D is unavailable --- photon-client/src/components/dashboard/ConfigOptions.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/photon-client/src/components/dashboard/ConfigOptions.vue b/photon-client/src/components/dashboard/ConfigOptions.vue index be8e017233..8fe5d6fcbe 100644 --- a/photon-client/src/components/dashboard/ConfigOptions.vue +++ b/photon-client/src/components/dashboard/ConfigOptions.vue @@ -95,7 +95,7 @@ const tabGroups = computed(() => { tabGroup.filter( (tabConfig) => !(!allow3d && tabConfig.tabName === "3D") && //Filter out 3D tab any time 3D isn't calibrated - !((!allow3d || isAprilTag || isAruco || isObjectDetection) && tabConfig.tabName === "PnP") && //Filter out the PnP config tab if 3D isn't available, or we're doing AprilTags + !((!allow3d || isAprilTag || isAruco) && tabConfig.tabName === "PnP") && //Filter out the PnP config tab if 3D isn't available, or we're doing AprilTags !((isAprilTag || isAruco || isObjectDetection) && tabConfig.tabName === "Threshold") && //Filter out threshold tab if we're doing AprilTags !((isAprilTag || isAruco || isObjectDetection) && tabConfig.tabName === "Contours") && //Filter out contours if we're doing AprilTags !(!isAprilTag && tabConfig.tabName === "AprilTag") && //Filter out apriltag unless we actually are doing AprilTags From e26c4cc94496be0850695364f5f115d371a71205 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:35:10 +0000 Subject: [PATCH 07/17] Add object detection check to PnPTab for conditional rendering of slider --- photon-client/src/components/dashboard/tabs/PnPTab.vue | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/photon-client/src/components/dashboard/tabs/PnPTab.vue b/photon-client/src/components/dashboard/tabs/PnPTab.vue index 0ec9b5d668..af14d587ac 100644 --- a/photon-client/src/components/dashboard/tabs/PnPTab.vue +++ b/photon-client/src/components/dashboard/tabs/PnPTab.vue @@ -1,7 +1,7 @@ + \ No newline at end of file From 2d3d923aa14b994b85929b17324036a58cc5b41c Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:38:45 +0000 Subject: [PATCH 08/17] Refactor RknnObjectDetector: update Cleaner instance to static and optimize result processing --- .../vision/objects/RknnObjectDetector.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java b/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java index 3f9ef1ebb8..f00b3e4834 100644 --- a/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java @@ -19,6 +19,7 @@ import java.awt.Color; import java.lang.ref.Cleaner; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.opencv.core.Mat; @@ -33,8 +34,11 @@ public class RknnObjectDetector implements ObjectDetector { private static final Logger logger = new Logger(RknnObjectDetector.class, LogGroup.General); - /** Cleaner instance to release the detector when it goes out of scope */ - private final Cleaner cleaner = Cleaner.create(); + /** + * Shared Cleaner instance for all RknnObjectDetector instances. Using a single static Cleaner + * avoids spawning a new daemon thread per detector object. + */ + private static final Cleaner cleaner = Cleaner.create(); /** Atomic boolean to ensure that the native object can only be released once. */ private AtomicBoolean released = new AtomicBoolean(false); @@ -128,10 +132,11 @@ public List detect(Mat in, double nmsThresh, double box return List.of(); } - return scale.resizeDetections( - List.of(results).stream() - .map(it -> new NeuralNetworkPipeResult(it.rect, it.class_id, it.conf)) - .toList()); + var pipeResults = new ArrayList(results.length); + for (var it : results) { + pipeResults.add(new NeuralNetworkPipeResult(it.rect, it.class_id, it.conf)); + } + return scale.resizeDetections(pipeResults); } /** Thread-safe method to release the detector. */ @@ -150,4 +155,4 @@ public void release() { logger.debug("Released detector for model " + model.modelFile.getName()); } } -} +} \ No newline at end of file From c7531de0a77da5c40f07f4472275ee3f02e9e17f Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:39:57 +0000 Subject: [PATCH 09/17] Refactor RubikObjectDetector: update Cleaner instance to static and improve detection error handling --- .../vision/objects/RubikObjectDetector.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java b/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java index 38502ba8b4..a1b8325be4 100644 --- a/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java @@ -19,6 +19,7 @@ import java.awt.Color; import java.lang.ref.Cleaner; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.opencv.core.Mat; @@ -33,8 +34,11 @@ public class RubikObjectDetector implements ObjectDetector { private static final Logger logger = new Logger(RubikObjectDetector.class, LogGroup.General); - /** Cleaner instance to release the detector when it goes out of scope */ - private final Cleaner cleaner = Cleaner.create(); + /** + * Shared Cleaner instance for all RubikObjectDetector instances. Using a single static Cleaner + * avoids spawning a new daemon thread per detector object. + */ + private static final Cleaner cleaner = Cleaner.create(); /** Atomic boolean to ensure that the native object can only be released once. */ private AtomicBoolean released = new AtomicBoolean(false); @@ -113,10 +117,8 @@ public List getClasses() { @Override public List detect(Mat in, double nmsThresh, double boxThresh) { if (!isValid()) { - logger.error( - "Detector is not initialized, and so it can't be released! Model: " - + model.modelFile.getName()); - return null; + logger.error("Detector is not initialized! Model: " + model.modelFile.getName()); + return List.of(); } // Resize the frame to the input size of the model @@ -137,10 +139,11 @@ public List detect(Mat in, double nmsThresh, double box return List.of(); } - return scale.resizeDetections( - List.of(results).stream() - .map(it -> new NeuralNetworkPipeResult(it.rect, it.class_id, it.conf)) - .toList()); + var pipeResults = new ArrayList(results.length); + for (var it : results) { + pipeResults.add(new NeuralNetworkPipeResult(it.rect, it.class_id, it.conf)); + } + return scale.resizeDetections(pipeResults); } /** Thread-safe method to release the detector. */ @@ -162,4 +165,4 @@ public void release() { private boolean isValid() { return ptr != 0; } -} +} \ No newline at end of file From 08412fd8d03cded275b9c14ff3f945c41b4cb15d Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:42:54 +0000 Subject: [PATCH 10/17] Fix ObjectDetectionPipe: update frame variable in detect method for consistency --- .../photonvision/vision/pipe/impl/ObjectDetectionPipe.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java index 079ee7926c..7ec0cdbdbd 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java @@ -52,7 +52,7 @@ protected List process(CVMat in) { return List.of(); } - return detector.detect(in.getMat(), params.nms(), params.confidence()); + return detector.detect(frame, params.nms(), params.confidence()); } public static record ObjectDetectionPipeParams(double confidence, double nms, Model model) {} @@ -65,4 +65,4 @@ public List getClassNames() { public void release() { detector.release(); } -} +} \ No newline at end of file From e0d6a0d9b76f40aeb6b65978d5391d08da770f6e Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:44:25 +0000 Subject: [PATCH 11/17] Refactor ObjectDetectionPipeline: streamline target corner handling for spherical and non-symmetric targets --- .../pipeline/ObjectDetectionPipeline.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java index 8653e7ddae..a401ef0c2d 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java @@ -44,8 +44,6 @@ public class ObjectDetectionPipeline private final FilterObjectDetectionsPipe filterContoursPipe = new FilterObjectDetectionsPipe(); private final SolvePNPPipe solvePNPPipe = new SolvePNPPipe(); - private final Point[] rectPoints = new Point[4]; - private static final FrameThresholdType PROCESSING_TYPE = FrameThresholdType.NONE; public ObjectDetectionPipeline() { @@ -112,7 +110,7 @@ protected void setPipeParamsImpl() { @Override protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings settings) { - long sumPipeNanosElapsed = 0L; + long sumPipeNanosElapsed = 0; CVPipeResult> neuralNetworkResult = objectDetectorPipe.run(frame.colorImage); @@ -138,10 +136,37 @@ protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings // 3d stuff if (settings.solvePNPEnabled) { + var rectPoints = new Point[4]; collect2dTargetsResult.output.forEach( target -> { target.getMinAreaRect().points(rectPoints); - target.setTargetCorners(Arrays.asList(rectPoints)); + if (settings.targetModel.isSpherical()) { + // For symmetric (spherical) targets such as balls: the model presents + // the same circular silhouette from every angle, so any mapping of the + // 4 OBB corners to the 4 equatorial model points yields the same pose. + // Just hand the corners through as-is. + target.setTargetCorners( + Arrays.asList( + rectPoints[0], + rectPoints[1], + rectPoints[2], + rectPoints[3])); + } else { + // For non-symmetric targets the OBB side ratio indicates which face of + // the 3D object is visible. RotatedRect.points() returns corners in + // order (bottom-left, top-left, top-right, bottom-right); reorder to + // (bottom-left, bottom-right, top-right, top-left) to match the + // solvePNP model convention expected by CornerDetectionPipe. + // TODO: when TargetModel gains multi-face support for 3D non-symmetric + // objects, select the appropriate face's 3D corners based on the ratio + // of the OBB's width to its height before calling solvePNP. + target.setTargetCorners( + Arrays.asList( + rectPoints[0], + rectPoints[3], + rectPoints[2], + rectPoints[1])); + } }); var solvePNPResult = solvePNPPipe.run(collect2dTargetsResult.output); From 82882f50fb68dd63e152be7373f895a2634e8714 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:46:18 +0000 Subject: [PATCH 12/17] Refactor ObjectDetectionPipelineSettings: move targetModel assignment to constructor for clarity --- .../vision/pipeline/ObjectDetectionPipelineSettings.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java index efa0e0384a..4a0d26ef18 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java @@ -36,9 +36,11 @@ public ObjectDetectionPipelineSettings() { cameraExposureRaw = 20; cameraAutoExposure = false; ledMode = false; + // Use a spherical ball model by default: YOLO primarily detects game pieces like + // balls/cargo, which are symmetric from all sides and work well with solvePNP. + targetModel = TargetModel.k2025Algae; confidence = .9; nms = .45; - targetModel = TargetModel.k2025Algae; model = NeuralNetworkModelManager.getInstance() .getDefaultModel() From 83be30f5c4376bec2010255def831f56bf841d4f Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:47:22 +0000 Subject: [PATCH 13/17] Add isSpherical method to TargetModel for spherical target detection --- .../vision/target/TargetModel.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java index 65b7ec22c6..cf3bea0c92 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java @@ -217,6 +217,33 @@ public MatOfPoint3f getVisualizationBoxTop() { return visualizationBoxTop; } + /** + * Returns true if this model represents a spherical/symmetric target (such as a ball or cargo + * game piece). Spherical models have equal absolute x and y extents (a square cross-section) and + * boxHeight == 0. Because a sphere looks identical from any viewing angle, pose estimation can + * use the 4 bounding-box corners directly without any face-selection logic. + * + *

For non-spherical models (planar reflective targets, etc.) the OBB side-ratio can be used + * to determine which face of the 3D object is visible before running solvePNP. + * + * @return true if all four model points share the same absolute x and y distances from the + * origin, and boxHeight is 0. + */ + @JsonIgnore + public boolean isSpherical() { + if (realWorldCoordinatesArray == null || realWorldCoordinatesArray.size() != 4) return false; + if (boxHeight != 0) return false; + double absX = Math.abs(realWorldCoordinatesArray.get(0).x); + double absY = Math.abs(realWorldCoordinatesArray.get(0).y); + if (Math.abs(absX - absY) > 1e-9) return false; + for (Point3 p : realWorldCoordinatesArray) { + if (Math.abs(Math.abs(p.x) - absX) > 1e-9 || Math.abs(Math.abs(p.y) - absY) > 1e-9) { + return false; + } + } + return true; + } + // public static TargetModel getCircleTarget(double Units.inchesToMeters(7)) { // var corners = // List.of( @@ -233,4 +260,4 @@ public void release() { visualizationBoxBottom.release(); visualizationBoxTop.release(); } -} +} \ No newline at end of file From 567100076584ae794f52c497b3f4926c0807b04e Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 01:56:04 +0000 Subject: [PATCH 14/17] lint frontend --- .../src/components/dashboard/tabs/PnPTab.vue | 2 +- photon-client/src/types/PipelineTypes.ts | 1 - .../vision/target/TargetModel.java | 29 +------------------ 3 files changed, 2 insertions(+), 30 deletions(-) diff --git a/photon-client/src/components/dashboard/tabs/PnPTab.vue b/photon-client/src/components/dashboard/tabs/PnPTab.vue index af14d587ac..9f8998f767 100644 --- a/photon-client/src/components/dashboard/tabs/PnPTab.vue +++ b/photon-client/src/components/dashboard/tabs/PnPTab.vue @@ -51,4 +51,4 @@ const isObjectDetection = computed( " /> - \ No newline at end of file + diff --git a/photon-client/src/types/PipelineTypes.ts b/photon-client/src/types/PipelineTypes.ts index 2f362b3359..28e6bb92e7 100644 --- a/photon-client/src/types/PipelineTypes.ts +++ b/photon-client/src/types/PipelineTypes.ts @@ -354,4 +354,3 @@ export type ActiveConfigurablePipelineSettings = | ConfigurableArucoPipelineSettings | ConfigurableObjectDetectionPipelineSettings | ConfigurableCalibration3dPipelineSettings; - \ No newline at end of file diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java index cf3bea0c92..65b7ec22c6 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java @@ -217,33 +217,6 @@ public MatOfPoint3f getVisualizationBoxTop() { return visualizationBoxTop; } - /** - * Returns true if this model represents a spherical/symmetric target (such as a ball or cargo - * game piece). Spherical models have equal absolute x and y extents (a square cross-section) and - * boxHeight == 0. Because a sphere looks identical from any viewing angle, pose estimation can - * use the 4 bounding-box corners directly without any face-selection logic. - * - *

For non-spherical models (planar reflective targets, etc.) the OBB side-ratio can be used - * to determine which face of the 3D object is visible before running solvePNP. - * - * @return true if all four model points share the same absolute x and y distances from the - * origin, and boxHeight is 0. - */ - @JsonIgnore - public boolean isSpherical() { - if (realWorldCoordinatesArray == null || realWorldCoordinatesArray.size() != 4) return false; - if (boxHeight != 0) return false; - double absX = Math.abs(realWorldCoordinatesArray.get(0).x); - double absY = Math.abs(realWorldCoordinatesArray.get(0).y); - if (Math.abs(absX - absY) > 1e-9) return false; - for (Point3 p : realWorldCoordinatesArray) { - if (Math.abs(Math.abs(p.x) - absX) > 1e-9 || Math.abs(Math.abs(p.y) - absY) > 1e-9) { - return false; - } - } - return true; - } - // public static TargetModel getCircleTarget(double Units.inchesToMeters(7)) { // var corners = // List.of( @@ -260,4 +233,4 @@ public void release() { visualizationBoxBottom.release(); visualizationBoxTop.release(); } -} \ No newline at end of file +} From 0584d286e79ea6a9a067b9d90f3add4712d3db65 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 02:00:47 +0000 Subject: [PATCH 15/17] Fix: add missing newline at end of file in multiple detector and pipeline classes --- .../vision/objects/RknnObjectDetector.java | 2 +- .../vision/objects/RubikObjectDetector.java | 2 +- .../vision/pipe/impl/ObjectDetectionPipe.java | 2 +- .../vision/pipeline/ObjectDetectionPipeline.java | 14 +++----------- .../pipeline/ObjectDetectionPipelineSettings.java | 2 +- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java b/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java index f00b3e4834..7ab785ef0f 100644 --- a/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/objects/RknnObjectDetector.java @@ -155,4 +155,4 @@ public void release() { logger.debug("Released detector for model " + model.modelFile.getName()); } } -} \ No newline at end of file +} diff --git a/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java b/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java index a1b8325be4..d7664b3681 100644 --- a/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/objects/RubikObjectDetector.java @@ -165,4 +165,4 @@ public void release() { private boolean isValid() { return ptr != 0; } -} \ No newline at end of file +} diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java index 7ec0cdbdbd..7fe479b5f2 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ObjectDetectionPipe.java @@ -65,4 +65,4 @@ public List getClassNames() { public void release() { detector.release(); } -} \ No newline at end of file +} diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java index a401ef0c2d..8f9e53e2f4 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipeline.java @@ -146,11 +146,7 @@ protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings // 4 OBB corners to the 4 equatorial model points yields the same pose. // Just hand the corners through as-is. target.setTargetCorners( - Arrays.asList( - rectPoints[0], - rectPoints[1], - rectPoints[2], - rectPoints[3])); + Arrays.asList(rectPoints[0], rectPoints[1], rectPoints[2], rectPoints[3])); } else { // For non-symmetric targets the OBB side ratio indicates which face of // the 3D object is visible. RotatedRect.points() returns corners in @@ -161,11 +157,7 @@ protected CVPipelineResult process(Frame frame, ObjectDetectionPipelineSettings // objects, select the appropriate face's 3D corners based on the ratio // of the OBB's width to its height before calling solvePNP. target.setTargetCorners( - Arrays.asList( - rectPoints[0], - rectPoints[3], - rectPoints[2], - rectPoints[1])); + Arrays.asList(rectPoints[0], rectPoints[3], rectPoints[2], rectPoints[1])); } }); @@ -189,4 +181,4 @@ public void release() { objectDetectorPipe.release(); super.release(); } -} \ No newline at end of file +} diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java index 4a0d26ef18..541953b2b1 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ObjectDetectionPipelineSettings.java @@ -47,4 +47,4 @@ public ObjectDetectionPipelineSettings() { .map(Model::getProperties) .orElse(null); } -} \ No newline at end of file +} From a06bde674cd0d7c364d77cc8f8fd99880386e2a2 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 02:22:26 +0000 Subject: [PATCH 16/17] Add isSpherical method to TargetModel for spherical target detection --- .../java/org/photonvision/vision/target/TargetModel.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java index 65b7ec22c6..7dd199fdf6 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java @@ -217,6 +217,13 @@ public MatOfPoint3f getVisualizationBoxTop() { return visualizationBoxTop; } + public boolean isSpherical() { + return switch (this) { + case kCircularPowerCell7in, k2022CircularCargoBall, k2025Algae -> true; + default -> false; + }; + } + // public static TargetModel getCircleTarget(double Units.inchesToMeters(7)) { // var corners = // List.of( From bb1ef6d4f78c1fb44b614d64e3a8595ffb252c95 Mon Sep 17 00:00:00 2001 From: Ruthie Date: Sun, 15 Mar 2026 02:29:21 +0000 Subject: [PATCH 17/17] Refactor isSpherical method in TargetModel for improved readability --- .../org/photonvision/vision/target/TargetModel.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java index 7dd199fdf6..381d046480 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetModel.java @@ -217,12 +217,12 @@ public MatOfPoint3f getVisualizationBoxTop() { return visualizationBoxTop; } - public boolean isSpherical() { - return switch (this) { - case kCircularPowerCell7in, k2022CircularCargoBall, k2025Algae -> true; - default -> false; - }; - } + public boolean isSpherical() { + return switch (this) { + case kCircularPowerCell7in, k2022CircularCargoBall, k2025Algae -> true; + default -> false; + }; + } // public static TargetModel getCircleTarget(double Units.inchesToMeters(7)) { // var corners =