mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-22 01:11:40 +00:00
Allow configuring maximum target count (#2338)
This commit is contained in:
committed by
GitHub
parent
77457219c7
commit
09e6d45e77
@@ -3,6 +3,7 @@ import PvSelect from "@/components/common/pv-select.vue";
|
||||
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
|
||||
import { type ActivePipelineSettings, PipelineType, RobotOffsetPointMode } from "@/types/PipelineTypes";
|
||||
import PvSwitch from "@/components/common/pv-switch.vue";
|
||||
import PvSlider from "@/components/common/pv-slider.vue";
|
||||
import { computed } from "vue";
|
||||
import { RobotOffsetType } from "@/types/SettingTypes";
|
||||
import { useStateStore } from "@/stores/StateStore";
|
||||
@@ -58,14 +59,17 @@ const interactiveCols = computed(() =>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<pv-switch
|
||||
v-model="useCameraSettingsStore().currentPipelineSettings.outputShowMultipleTargets"
|
||||
label="Show Multiple Targets"
|
||||
tooltip="If enabled, up to five targets will be displayed and sent via PhotonLib, instead of just one"
|
||||
:disabled="isTagPipeline"
|
||||
<pv-slider
|
||||
v-model="useCameraSettingsStore().currentPipelineSettings.outputMaximumTargets"
|
||||
label="Maximum Targets"
|
||||
tooltip="The maximum number of targets to display and send."
|
||||
:hidden="isTagPipeline"
|
||||
:min="1"
|
||||
:max="127"
|
||||
:step="1"
|
||||
:switch-cols="interactiveCols"
|
||||
@update:modelValue="
|
||||
(value) => useCameraSettingsStore().changeCurrentPipelineSetting({ outputShowMultipleTargets: value }, false)
|
||||
(value) => useCameraSettingsStore().changeCurrentPipelineSetting({ outputMaximumTargets: value }, false)
|
||||
"
|
||||
/>
|
||||
<pv-switch
|
||||
|
||||
@@ -66,7 +66,7 @@ export interface PipelineSettings {
|
||||
hsvHue: WebsocketNumberPair | [number, number];
|
||||
ledMode: boolean;
|
||||
hueInverted: boolean;
|
||||
outputShowMultipleTargets: boolean;
|
||||
outputMaximumTargets: number;
|
||||
contourSortMode: number;
|
||||
cameraExposureRaw: number;
|
||||
cameraMinExposureRaw: number;
|
||||
@@ -108,7 +108,7 @@ export type ConfigurablePipelineSettings = Partial<
|
||||
// Omitted settings are changed for all pipeline types
|
||||
export const DefaultPipelineSettings: Omit<
|
||||
PipelineSettings,
|
||||
"cameraGain" | "targetModel" | "ledMode" | "outputShowMultipleTargets" | "cameraExposureRaw" | "pipelineType"
|
||||
"cameraGain" | "targetModel" | "ledMode" | "cameraExposureRaw" | "pipelineType"
|
||||
> = {
|
||||
offsetRobotOffsetMode: RobotOffsetPointMode.None,
|
||||
streamingFrameDivisor: 0,
|
||||
@@ -137,6 +137,7 @@ export const DefaultPipelineSettings: Omit<
|
||||
offsetDualPointB: { x: 0, y: 0 },
|
||||
hsvHue: { first: 50, second: 180 },
|
||||
hueInverted: false,
|
||||
outputMaximumTargets: 20,
|
||||
contourSortMode: 0,
|
||||
offsetSinglePoint: { x: 0, y: 0 },
|
||||
cameraBrightness: 50,
|
||||
@@ -166,7 +167,7 @@ export const DefaultReflectivePipelineSettings: ReflectivePipelineSettings = {
|
||||
cameraGain: 20,
|
||||
targetModel: TargetModel.InfiniteRechargeHighGoalOuter,
|
||||
ledMode: true,
|
||||
outputShowMultipleTargets: false,
|
||||
outputMaximumTargets: 20,
|
||||
cameraExposureRaw: 6,
|
||||
pipelineType: PipelineType.Reflective,
|
||||
|
||||
@@ -197,7 +198,7 @@ export const DefaultColoredShapePipelineSettings: ColoredShapePipelineSettings =
|
||||
cameraGain: 75,
|
||||
targetModel: TargetModel.InfiniteRechargeHighGoalOuter,
|
||||
ledMode: true,
|
||||
outputShowMultipleTargets: false,
|
||||
outputMaximumTargets: 20,
|
||||
cameraExposureRaw: 20,
|
||||
pipelineType: PipelineType.ColoredShape,
|
||||
|
||||
@@ -237,10 +238,9 @@ export const DefaultAprilTagPipelineSettings: AprilTagPipelineSettings = {
|
||||
cameraGain: 75,
|
||||
targetModel: TargetModel.AprilTag6p5in_36h11,
|
||||
ledMode: false,
|
||||
outputShowMultipleTargets: true,
|
||||
outputMaximumTargets: 127,
|
||||
cameraExposureRaw: 20,
|
||||
pipelineType: PipelineType.AprilTag,
|
||||
|
||||
hammingDist: 0,
|
||||
numIterations: 40,
|
||||
decimate: 1,
|
||||
@@ -278,13 +278,12 @@ export type ConfigurableArucoPipelineSettings = Partial<Omit<ArucoPipelineSettin
|
||||
export const DefaultArucoPipelineSettings: ArucoPipelineSettings = {
|
||||
...DefaultPipelineSettings,
|
||||
cameraGain: 75,
|
||||
outputShowMultipleTargets: true,
|
||||
outputMaximumTargets: 127,
|
||||
targetModel: TargetModel.AprilTag6p5in_36h11,
|
||||
cameraExposureRaw: -1,
|
||||
cameraAutoExposure: true,
|
||||
ledMode: false,
|
||||
pipelineType: PipelineType.Aruco,
|
||||
|
||||
tagFamily: AprilTagFamily.Family36h11,
|
||||
threshWinSizes: { first: 11, second: 91 },
|
||||
threshStepSize: 40,
|
||||
@@ -316,7 +315,7 @@ export const DefaultObjectDetectionPipelineSettings: ObjectDetectionPipelineSett
|
||||
cameraGain: 20,
|
||||
targetModel: TargetModel.InfiniteRechargeHighGoalOuter,
|
||||
ledMode: true,
|
||||
outputShowMultipleTargets: false,
|
||||
outputMaximumTargets: 20,
|
||||
cameraExposureRaw: 6,
|
||||
confidence: 0.9,
|
||||
nms: 0.45,
|
||||
@@ -335,7 +334,7 @@ export const DefaultCalibration3dPipelineSettings: Calibration3dPipelineSettings
|
||||
cameraGain: 20,
|
||||
targetModel: TargetModel.InfiniteRechargeHighGoalOuter,
|
||||
ledMode: true,
|
||||
outputShowMultipleTargets: false,
|
||||
outputMaximumTargets: 1,
|
||||
cameraExposureRaw: 6,
|
||||
drawAllSnapshots: false
|
||||
};
|
||||
|
||||
@@ -228,7 +228,8 @@ public class TestUtils {
|
||||
kRobots,
|
||||
kTag1_640_480,
|
||||
kTag1_16h5_1280,
|
||||
kTag_corner_1280;
|
||||
kTag_corner_1280,
|
||||
k36h11_stress_test;
|
||||
|
||||
public final Path path;
|
||||
|
||||
@@ -237,6 +238,7 @@ public class TestUtils {
|
||||
var filename = this.toString().substring(1).toLowerCase();
|
||||
var extension = ".jpg";
|
||||
if (filename.equals("tag1_16h5_1280")) extension = ".png";
|
||||
if (filename.equals("36h11_stress_test")) extension = ".png";
|
||||
return Path.of("apriltag", filename + extension);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ import org.photonvision.vision.frame.FrameDivisor;
|
||||
public class Draw2dAprilTagsPipe extends Draw2dTargetsPipe {
|
||||
public static class Draw2dAprilTagsParams extends Draw2dTargetsPipe.Draw2dTargetsParams {
|
||||
public Draw2dAprilTagsParams(
|
||||
boolean shouldDraw, boolean showMultipleTargets, FrameDivisor divisor) {
|
||||
super(shouldDraw, showMultipleTargets, divisor);
|
||||
boolean shouldDraw, int outputMaximumTargets, FrameDivisor divisor) {
|
||||
super(shouldDraw, outputMaximumTargets, divisor);
|
||||
// We want to show the polygon, not the rotated box
|
||||
this.showRotatedBox = false;
|
||||
this.showMaximumBox = false;
|
||||
|
||||
@@ -22,9 +22,8 @@ import org.photonvision.vision.frame.FrameDivisor;
|
||||
|
||||
public class Draw2dArucoPipe extends Draw2dTargetsPipe {
|
||||
public static class Draw2dArucoParams extends Draw2dTargetsPipe.Draw2dTargetsParams {
|
||||
public Draw2dArucoParams(
|
||||
boolean shouldDraw, boolean showMultipleTargets, FrameDivisor divisor) {
|
||||
super(shouldDraw, showMultipleTargets, divisor);
|
||||
public Draw2dArucoParams(boolean shouldDraw, int outputMaximumTargets, FrameDivisor divisor) {
|
||||
super(shouldDraw, outputMaximumTargets, divisor);
|
||||
// We want to show the polygon, not the rotated box
|
||||
this.showRotatedBox = false;
|
||||
this.showMaximumBox = false;
|
||||
|
||||
@@ -58,14 +58,10 @@ public class Draw2dTargetsPipe
|
||||
var circleColor = ColorHelper.colorToScalar(params.circleColor);
|
||||
var shapeColour = ColorHelper.colorToScalar(params.shapeOutlineColour);
|
||||
|
||||
for (int i = 0; i < (params.showMultipleTargets ? in.getSecond().size() : 1); i++) {
|
||||
for (int i = 0; i < Math.min(params.outputMaximumTargets, in.getSecond().size()); i++) {
|
||||
Point[] vertices = new Point[4];
|
||||
MatOfPoint contour = new MatOfPoint();
|
||||
|
||||
if (i != 0 && !params.showMultipleTargets) {
|
||||
break;
|
||||
}
|
||||
|
||||
TrackedTarget target = in.getSecond().get(i);
|
||||
RotatedRect r = target.getMinAreaRect();
|
||||
|
||||
@@ -233,8 +229,7 @@ public class Draw2dTargetsPipe
|
||||
public Color shapeOutlineColour = Color.MAGENTA;
|
||||
public Color textColor = Color.GREEN;
|
||||
public Color circleColor = Color.RED;
|
||||
|
||||
public final boolean showMultipleTargets;
|
||||
public int outputMaximumTargets;
|
||||
public final boolean shouldDraw;
|
||||
|
||||
public final FrameDivisor divisor;
|
||||
@@ -247,10 +242,9 @@ public class Draw2dTargetsPipe
|
||||
return shape != null && shape.shape.equals(ContourShape.Circle);
|
||||
}
|
||||
|
||||
public Draw2dTargetsParams(
|
||||
boolean shouldDraw, boolean showMultipleTargets, FrameDivisor divisor) {
|
||||
public Draw2dTargetsParams(boolean shouldDraw, int outputMaximumTargets, FrameDivisor divisor) {
|
||||
this.shouldDraw = shouldDraw;
|
||||
this.showMultipleTargets = showMultipleTargets;
|
||||
this.outputMaximumTargets = outputMaximumTargets;
|
||||
this.divisor = divisor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.photonvision.vision.pipeline;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import java.util.Objects;
|
||||
import org.opencv.core.Point;
|
||||
import org.photonvision.common.util.numbers.DoubleCouple;
|
||||
@@ -41,7 +42,7 @@ public class AdvancedPipelineSettings extends CVPipelineSettings {
|
||||
public boolean hueInverted = false;
|
||||
|
||||
public boolean outputShouldDraw = true;
|
||||
public boolean outputShowMultipleTargets = false;
|
||||
public int outputMaximumTargets = 20;
|
||||
|
||||
public DoubleCouple contourArea = new DoubleCouple(0.0, 100.0);
|
||||
public DoubleCouple contourRatio = new DoubleCouple(0.0, 20.0);
|
||||
@@ -90,6 +91,22 @@ public class AdvancedPipelineSettings extends CVPipelineSettings {
|
||||
public int cornerDetectionSideCount = 4;
|
||||
public double cornerDetectionAccuracyPercentage = 10;
|
||||
|
||||
/**
|
||||
* Handles backward compatibility for the deprecated outputShowMultipleTargets property. When
|
||||
* outputShowMultipleTargets is encountered during deserialization, it sets outputMaximumTargets
|
||||
* appropriately. If outputShowMultipleTargets is false, outputMaximumTargets is set to 1.
|
||||
*/
|
||||
@JsonAnySetter
|
||||
public void handleUnknownProperty(String name, Object value) {
|
||||
// Handle the old showMultipleTargets property for backward compatibility
|
||||
if ("outputShowMultipleTargets".equals(name) && value instanceof Boolean showMultipleTargets) {
|
||||
if (!showMultipleTargets) {
|
||||
// If showMultipleTargets is false, limit to 1 target
|
||||
outputMaximumTargets = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -97,7 +114,7 @@ public class AdvancedPipelineSettings extends CVPipelineSettings {
|
||||
if (!super.equals(o)) return false;
|
||||
AdvancedPipelineSettings that = (AdvancedPipelineSettings) o;
|
||||
return outputShouldDraw == that.outputShouldDraw
|
||||
&& outputShowMultipleTargets == that.outputShowMultipleTargets
|
||||
&& outputMaximumTargets == that.outputMaximumTargets
|
||||
&& contourSpecklePercentage == that.contourSpecklePercentage
|
||||
&& Double.compare(that.offsetDualPointAArea, offsetDualPointAArea) == 0
|
||||
&& Double.compare(that.offsetDualPointBArea, offsetDualPointBArea) == 0
|
||||
@@ -136,7 +153,7 @@ public class AdvancedPipelineSettings extends CVPipelineSettings {
|
||||
hsvValue,
|
||||
hueInverted,
|
||||
outputShouldDraw,
|
||||
outputShowMultipleTargets,
|
||||
outputMaximumTargets,
|
||||
contourArea,
|
||||
contourRatio,
|
||||
contourFullness,
|
||||
|
||||
@@ -30,6 +30,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.dataflow.structures.Packet;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.math.MathUtils;
|
||||
import org.photonvision.estimation.TargetModel;
|
||||
import org.photonvision.targeting.MultiTargetPNPResult;
|
||||
@@ -49,6 +52,8 @@ import org.photonvision.vision.target.TrackedTarget;
|
||||
import org.photonvision.vision.target.TrackedTarget.TargetCalculationParameters;
|
||||
|
||||
public class AprilTagPipeline extends CVPipeline<CVPipelineResult, AprilTagPipelineSettings> {
|
||||
private static final Logger logger = new Logger(AprilTagPipeline.class, LogGroup.VisionModule);
|
||||
|
||||
private final AprilTagDetectionPipe aprilTagDetectionPipe = new AprilTagDetectionPipe();
|
||||
private final AprilTagPoseEstimatorPipe singleTagPoseEstimatorPipe =
|
||||
new AprilTagPoseEstimatorPipe();
|
||||
@@ -232,6 +237,12 @@ public class AprilTagPipeline extends CVPipeline<CVPipelineResult, AprilTagPipel
|
||||
}
|
||||
}
|
||||
|
||||
if (targetList.size() > Packet.MAX_ARRAY_LEN) {
|
||||
logger.error(
|
||||
"We have " + targetList.size() + " targets! Arbitrarily dropping some on the floor");
|
||||
targetList = targetList.subList(0, Packet.MAX_ARRAY_LEN);
|
||||
}
|
||||
|
||||
var fpsResult = calculateFPSPipe.run(null);
|
||||
var fps = fpsResult.output;
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ public class AprilTagPipelineSettings extends AdvancedPipelineSettings {
|
||||
public AprilTagPipelineSettings() {
|
||||
super();
|
||||
pipelineType = PipelineType.AprilTag;
|
||||
outputShowMultipleTargets = true;
|
||||
targetModel = TargetModel.kAprilTag6p5in_36h11;
|
||||
cameraExposureRaw = 20;
|
||||
cameraAutoExposure = false;
|
||||
|
||||
@@ -15,23 +15,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipeline;
|
||||
|
||||
import edu.wpi.first.apriltag.AprilTagPoseEstimate;
|
||||
@@ -46,6 +29,9 @@ import org.opencv.core.Mat;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.objdetect.Objdetect;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.dataflow.structures.Packet;
|
||||
import org.photonvision.common.logging.LogGroup;
|
||||
import org.photonvision.common.logging.Logger;
|
||||
import org.photonvision.common.util.math.MathUtils;
|
||||
import org.photonvision.estimation.TargetModel;
|
||||
import org.photonvision.targeting.MultiTargetPNPResult;
|
||||
@@ -61,6 +47,8 @@ import org.photonvision.vision.target.TrackedTarget;
|
||||
import org.photonvision.vision.target.TrackedTarget.TargetCalculationParameters;
|
||||
|
||||
public class ArucoPipeline extends CVPipeline<CVPipelineResult, ArucoPipelineSettings> {
|
||||
private static final Logger logger = new Logger(ArucoPipeline.class, LogGroup.VisionModule);
|
||||
|
||||
private ArucoDetectionPipe arucoDetectionPipe = new ArucoDetectionPipe();
|
||||
private ArucoPoseEstimatorPipe singleTagPoseEstimatorPipe = new ArucoPoseEstimatorPipe();
|
||||
private final MultiTargetPNPPipe multiTagPNPPipe = new MultiTargetPNPPipe();
|
||||
@@ -237,6 +225,12 @@ public class ArucoPipeline extends CVPipeline<CVPipelineResult, ArucoPipelineSet
|
||||
}
|
||||
}
|
||||
|
||||
if (targetList.size() > Packet.MAX_ARRAY_LEN) {
|
||||
logger.error(
|
||||
"We have " + targetList.size() + " targets! Arbitrarily dropping some on the floor");
|
||||
targetList = targetList.subList(0, Packet.MAX_ARRAY_LEN);
|
||||
}
|
||||
|
||||
var fpsResult = calculateFPSPipe.run(null);
|
||||
var fps = fpsResult.output;
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ public class ArucoPipelineSettings extends AdvancedPipelineSettings {
|
||||
public ArucoPipelineSettings() {
|
||||
super();
|
||||
pipelineType = PipelineType.Aruco;
|
||||
outputShowMultipleTargets = true;
|
||||
targetModel = TargetModel.kAprilTag6p5in_36h11;
|
||||
cameraExposureRaw = 20;
|
||||
cameraAutoExposure = true;
|
||||
|
||||
@@ -26,8 +26,6 @@ import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
|
||||
public abstract class CVPipeline<R extends CVPipelineResult, S extends CVPipelineSettings>
|
||||
implements Releasable {
|
||||
static final int MAX_MULTI_TARGET_RESULTS = 50;
|
||||
|
||||
protected S settings;
|
||||
protected FrameStaticProperties frameStaticProperties;
|
||||
protected QuirkyCamera cameraQuirks;
|
||||
|
||||
@@ -101,11 +101,7 @@ public class ColoredShapePipeline
|
||||
|
||||
sortContoursPipe.setParams(
|
||||
new SortContoursPipe.SortContoursParams(
|
||||
settings.contourSortMode,
|
||||
settings.outputShowMultipleTargets
|
||||
? MAX_MULTI_TARGET_RESULTS // TODO don't hardcode?
|
||||
: 1,
|
||||
frameStaticProperties));
|
||||
settings.contourSortMode, settings.outputMaximumTargets, frameStaticProperties));
|
||||
|
||||
collect2dTargetsPipe.setParams(
|
||||
new Collect2dTargetsPipe.Collect2dTargetsParams(
|
||||
@@ -131,7 +127,7 @@ public class ColoredShapePipeline
|
||||
Draw2dTargetsPipe.Draw2dTargetsParams draw2DTargetsParams =
|
||||
new Draw2dTargetsPipe.Draw2dTargetsParams(
|
||||
settings.outputShouldDraw,
|
||||
settings.outputShowMultipleTargets,
|
||||
settings.outputMaximumTargets,
|
||||
settings.streamingFrameDivisor);
|
||||
draw2DTargetsParams.showShape = true;
|
||||
draw2DTargetsParams.showMaximumBox = false;
|
||||
|
||||
@@ -82,9 +82,7 @@ public class ObjectDetectionPipeline
|
||||
|
||||
sortContoursPipe.setParams(
|
||||
new SortContoursPipe.SortContoursParams(
|
||||
settings.contourSortMode,
|
||||
settings.outputShowMultipleTargets ? MAX_MULTI_TARGET_RESULTS : 1,
|
||||
frameStaticProperties));
|
||||
settings.contourSortMode, settings.outputMaximumTargets, frameStaticProperties));
|
||||
|
||||
filterContoursPipe.setParams(
|
||||
new FilterObjectDetectionsPipe.FilterContoursParams(
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ObjectDetectionPipelineSettings extends AdvancedPipelineSettings {
|
||||
public ObjectDetectionPipelineSettings() {
|
||||
super();
|
||||
this.pipelineType = PipelineType.ObjectDetection; // TODO: FIX this
|
||||
this.outputShowMultipleTargets = true;
|
||||
this.outputMaximumTargets = 20;
|
||||
cameraExposureRaw = 20;
|
||||
cameraAutoExposure = false;
|
||||
ledMode = false;
|
||||
|
||||
@@ -58,19 +58,19 @@ public class OutputStreamPipeline {
|
||||
draw2dTargetsPipe.setParams(
|
||||
new Draw2dTargetsPipe.Draw2dTargetsParams(
|
||||
settings.outputShouldDraw,
|
||||
settings.outputShowMultipleTargets,
|
||||
settings.outputMaximumTargets,
|
||||
settings.streamingFrameDivisor));
|
||||
|
||||
draw2dAprilTagsPipe.setParams(
|
||||
new Draw2dAprilTagsPipe.Draw2dAprilTagsParams(
|
||||
settings.outputShouldDraw,
|
||||
settings.outputShowMultipleTargets,
|
||||
settings.outputMaximumTargets,
|
||||
settings.streamingFrameDivisor));
|
||||
|
||||
draw2dArucoPipe.setParams(
|
||||
new Draw2dArucoPipe.Draw2dArucoParams(
|
||||
settings.outputShouldDraw,
|
||||
settings.outputShowMultipleTargets,
|
||||
settings.outputMaximumTargets,
|
||||
settings.streamingFrameDivisor));
|
||||
|
||||
draw2dCrosshairPipe.setParams(
|
||||
|
||||
@@ -85,9 +85,7 @@ public class ReflectivePipeline extends CVPipeline<CVPipelineResult, ReflectiveP
|
||||
|
||||
sortContoursPipe.setParams(
|
||||
new SortContoursPipe.SortContoursParams(
|
||||
settings.contourSortMode,
|
||||
settings.outputShowMultipleTargets ? MAX_MULTI_TARGET_RESULTS : 1,
|
||||
frameStaticProperties));
|
||||
settings.contourSortMode, settings.outputMaximumTargets, frameStaticProperties));
|
||||
|
||||
collect2dTargetsPipe.setParams(
|
||||
new Collect2dTargetsPipe.Collect2dTargetsParams(
|
||||
|
||||
@@ -51,7 +51,7 @@ public class BenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
@@ -105,7 +105,7 @@ public class BenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ public class ShapeBenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
pipeline.getSettings().contourShape = ContourShape.Custom;
|
||||
@@ -87,7 +87,7 @@ public class ShapeBenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
pipeline.getSettings().contourShape = ContourShape.Custom;
|
||||
@@ -109,7 +109,7 @@ public class ShapeBenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
pipeline.getSettings().contourShape = ContourShape.Custom;
|
||||
@@ -131,7 +131,7 @@ public class ShapeBenchmarkTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
pipeline.getSettings().contourShape = ContourShape.Custom;
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import edu.wpi.first.cscore.UsbCameraInfo;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Order;
|
||||
@@ -33,9 +34,12 @@ import org.photonvision.common.configuration.NeuralNetworkModelManager.Family;
|
||||
import org.photonvision.common.util.TestUtils;
|
||||
import org.photonvision.vision.camera.CameraQuirk;
|
||||
import org.photonvision.vision.camera.PVCameraInfo;
|
||||
import org.photonvision.vision.pipeline.AdvancedPipelineSettings;
|
||||
import org.photonvision.vision.pipeline.AprilTagPipelineSettings;
|
||||
import org.photonvision.vision.pipeline.CVPipelineSettings;
|
||||
import org.photonvision.vision.pipeline.ColoredShapePipelineSettings;
|
||||
import org.photonvision.vision.pipeline.ObjectDetectionPipelineSettings;
|
||||
import org.photonvision.vision.pipeline.PipelineType;
|
||||
import org.photonvision.vision.pipeline.ReflectivePipelineSettings;
|
||||
|
||||
public class SQLConfigTest {
|
||||
@@ -155,4 +159,41 @@ public class SQLConfigTest {
|
||||
|
||||
ConfigManager.INSTANCE = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxDetectionsMigration() {
|
||||
var folder = TestUtils.getConfigDirectoriesPath(false).resolve("2025.3.1-old-nnmm");
|
||||
var cfgManager = new ConfigManager(folder, new SqlConfigProvider(folder));
|
||||
|
||||
// Replace global configmanager
|
||||
ConfigManager.INSTANCE = cfgManager;
|
||||
|
||||
assertDoesNotThrow(cfgManager::load);
|
||||
|
||||
Collection<CameraConfiguration> cameraConfigs =
|
||||
cfgManager.getConfig().getCameraConfigurations().values();
|
||||
|
||||
for (CameraConfiguration cc : cameraConfigs) {
|
||||
for (CVPipelineSettings ps : cc.pipelineSettings) {
|
||||
if (ps instanceof AdvancedPipelineSettings adps) {
|
||||
AdvancedPipelineSettings finalPs = adps;
|
||||
if (finalPs.pipelineType.equals(PipelineType.AprilTag)
|
||||
|| finalPs.pipelineType.equals(PipelineType.Aruco)) {
|
||||
// Tag pipelines don't have max detections, so skip
|
||||
continue;
|
||||
} else if (finalPs.pipelineNickname.equals("TEST MIGRATION")) {
|
||||
// This is our colored shape pipeline that we set to 1 before saving
|
||||
assertEquals(1, finalPs.outputMaximumTargets);
|
||||
} else {
|
||||
// All others should be at default 20
|
||||
assertEquals(20, finalPs.outputMaximumTargets);
|
||||
}
|
||||
} else {
|
||||
System.out.println("Skipping pipeline settings type: " + ps.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigManager.INSTANCE = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,4 +137,31 @@ public class AprilTagTest {
|
||||
assertEquals(2, pose.getTranslation().getY(), 0.2);
|
||||
assertEquals(0.0, pose.getTranslation().getZ(), 0.2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyDetections() {
|
||||
// Given a 36h11 pipeline
|
||||
var pipeline = new AprilTagPipeline();
|
||||
pipeline.getSettings().inputShouldShow = true;
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().solvePNPEnabled = true;
|
||||
pipeline.getSettings().cornerDetectionAccuracyPercentage = 4;
|
||||
pipeline.getSettings().cornerDetectionUseConvexHulls = true;
|
||||
pipeline.getSettings().tagFamily = AprilTagFamily.kTag36h11;
|
||||
pipeline.getSettings().outputMaximumTargets = 3; // bogus
|
||||
|
||||
// when we have a picture with 280 targets
|
||||
var frameProvider =
|
||||
new FileFrameProvider(
|
||||
TestUtils.getApriltagImagePath(TestUtils.ApriltagTestImages.k36h11_stress_test, false),
|
||||
TestUtils.WPI2020Image.FOV,
|
||||
TestUtils.getCoeffs(TestUtils.LIMELIGHT_480P_CAL_FILE, false));
|
||||
frameProvider.requestFrameThresholdType(pipeline.getThresholdType());
|
||||
|
||||
CVPipelineResult pipelineResult;
|
||||
pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
|
||||
|
||||
// the pipeline will only give us Byte.MAX_VALUE many
|
||||
assertEquals(Byte.MAX_VALUE, pipelineResult.targets.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class CirclePNPTest {
|
||||
pipeline.getSettings().cameraCalibration = getCoeffs(LIFECAM_480P_CAL_FILE);
|
||||
pipeline.getSettings().targetModel = TargetModel.kCircularPowerCell7in;
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = false;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Single;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
pipeline.getSettings().contourShape = ContourShape.Circle;
|
||||
@@ -144,7 +144,7 @@ public class CirclePNPTest {
|
||||
settings.hsvSaturation.set(100, 255);
|
||||
settings.hsvValue.set(190, 255);
|
||||
settings.outputShouldDraw = true;
|
||||
settings.outputShowMultipleTargets = true;
|
||||
settings.outputMaximumTargets = 20;
|
||||
settings.contourGroupingMode = ContourGroupingMode.Dual;
|
||||
settings.contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ public class ColoredShapePipelineTest {
|
||||
settings.hsvSaturation.set(100, 255);
|
||||
settings.hsvValue.set(100, 255);
|
||||
settings.outputShouldDraw = true;
|
||||
settings.outputShowMultipleTargets = true;
|
||||
settings.outputMaximumTargets = 20;
|
||||
settings.contourGroupingMode = ContourGroupingMode.Single;
|
||||
settings.contourIntersection = ContourIntersectionDirection.Up;
|
||||
settings.contourShape = ContourShape.Triangle;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) Photon Vision.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.photonvision.vision.pipeline;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.photonvision.common.LoadJNI;
|
||||
import org.photonvision.common.configuration.ConfigManager;
|
||||
import org.photonvision.common.util.TestUtils;
|
||||
import org.photonvision.vision.camera.QuirkyCamera;
|
||||
import org.photonvision.vision.frame.provider.FileFrameProvider;
|
||||
import org.photonvision.vision.opencv.ContourShape;
|
||||
import org.photonvision.vision.pipe.impl.HSVPipe;
|
||||
import org.photonvision.vision.pipeline.result.CVPipelineResult;
|
||||
|
||||
public class MaxDetectionsTest {
|
||||
@Test
|
||||
public void testMaxDetections() {
|
||||
LoadJNI.loadLibraries();
|
||||
ConfigManager.getInstance().load();
|
||||
|
||||
ColoredShapePipeline pipeline = new ColoredShapePipeline();
|
||||
|
||||
pipeline.settings.contourShape = ContourShape.Circle;
|
||||
pipeline.settings.hsvHue.set(140, 160);
|
||||
pipeline.settings.hsvSaturation.set(226, 246);
|
||||
pipeline.settings.hsvValue.set(188, 208);
|
||||
pipeline.settings.maxCannyThresh = 90;
|
||||
pipeline.settings.circleAccuracy = 20;
|
||||
pipeline.settings.circleDetectThreshold = 5;
|
||||
|
||||
Path path =
|
||||
TestUtils.getResourcesFolderPath(false).resolve("testimages/polygons/ColoredShapeTest.png");
|
||||
|
||||
var frameProvider = new FileFrameProvider(path, TestUtils.WPI2019Image.FOV);
|
||||
|
||||
// VisionRunner normally does this
|
||||
var hsvParams =
|
||||
new HSVPipe.HSVParams(
|
||||
pipeline.getSettings().hsvHue,
|
||||
pipeline.getSettings().hsvSaturation,
|
||||
pipeline.getSettings().hsvValue,
|
||||
pipeline.getSettings().hueInverted);
|
||||
frameProvider.requestHsvSettings(hsvParams);
|
||||
frameProvider.requestFrameThresholdType(pipeline.getThresholdType());
|
||||
|
||||
CVPipelineResult result = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
|
||||
TestUtils.showImage(result.inputAndOutputFrame.processedImage.getMat(), "Max Detections Test");
|
||||
|
||||
assertEquals(20, result.targets.size());
|
||||
|
||||
pipeline.settings.outputMaximumTargets = 5;
|
||||
result = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
|
||||
assertEquals(5, result.targets.size());
|
||||
|
||||
pipeline.settings.outputMaximumTargets = 50;
|
||||
result = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera);
|
||||
// 24 circles, but we only detect 22
|
||||
assertEquals(22, result.targets.size());
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class ReflectivePipelineTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().outputMaximumTargets = 20;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
@@ -120,7 +120,7 @@ public class ReflectivePipelineTest {
|
||||
settings.hsvSaturation.set(100, 255);
|
||||
settings.hsvValue.set(190, 255);
|
||||
settings.outputShouldDraw = true;
|
||||
settings.outputShowMultipleTargets = true;
|
||||
settings.outputMaximumTargets = 20;
|
||||
settings.contourGroupingMode = ContourGroupingMode.Dual;
|
||||
settings.contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
|
||||
@@ -89,7 +89,6 @@ public class SolvePNPTest {
|
||||
pipeline.getSettings().hsvSaturation.set(100, 255);
|
||||
pipeline.getSettings().hsvValue.set(190, 255);
|
||||
pipeline.getSettings().outputShouldDraw = true;
|
||||
pipeline.getSettings().outputShowMultipleTargets = true;
|
||||
pipeline.getSettings().solvePNPEnabled = true;
|
||||
pipeline.getSettings().contourGroupingMode = ContourGroupingMode.Dual;
|
||||
pipeline.getSettings().contourIntersection = ContourIntersectionDirection.Up;
|
||||
@@ -225,7 +224,6 @@ public class SolvePNPTest {
|
||||
settings.hsvSaturation.set(100, 255);
|
||||
settings.hsvValue.set(190, 255);
|
||||
settings.outputShouldDraw = true;
|
||||
settings.outputShowMultipleTargets = true;
|
||||
settings.contourGroupingMode = ContourGroupingMode.Dual;
|
||||
settings.contourIntersection = ContourIntersectionDirection.Up;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ public class Packet {
|
||||
// Read and write positions.
|
||||
int readPos, writePos;
|
||||
|
||||
public static final int MAX_ARRAY_LEN = Byte.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* Constructs an empty packet. This buffer will dynamically expand if we need more data space.
|
||||
*
|
||||
|
||||
Binary file not shown.
BIN
test-resources/testimages/apriltag/36h11_stress_test.png
Normal file
BIN
test-resources/testimages/apriltag/36h11_stress_test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 178 KiB |
BIN
test-resources/testimages/polygons/ColoredShapeTest.png
Normal file
BIN
test-resources/testimages/polygons/ColoredShapeTest.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
Reference in New Issue
Block a user