Make pipe params into records

This commit is contained in:
Gold856
2025-03-30 01:43:03 -04:00
committed by Matt Morley
parent 20e2fe46ba
commit a42aed1e7f
30 changed files with 292 additions and 682 deletions

View File

@@ -100,7 +100,7 @@ public abstract class CpuImageProcessor extends FrameProvider {
m_processType,
input.captureTimestamp,
input.staticProps != null
? input.staticProps.rotate(m_rImagePipe.getParams().rotation)
? input.staticProps.rotate(m_rImagePipe.getParams().rotation())
: input.staticProps);
}

View File

@@ -121,13 +121,13 @@ public class LibcameraGpuFrameProvider extends FrameProvider {
public void requestHsvSettings(HSVParams params) {
LibCameraJNI.setThresholds(
settables.r_ptr,
params.getHsvLower().val[0] / 180.0,
params.getHsvLower().val[1] / 255.0,
params.getHsvLower().val[2] / 255.0,
params.getHsvUpper().val[0] / 180.0,
params.getHsvUpper().val[1] / 255.0,
params.getHsvUpper().val[2] / 255.0,
params.getHueInverted());
params.hsvLower().val[0] / 180.0,
params.hsvLower().val[1] / 255.0,
params.hsvLower().val[2] / 255.0,
params.hsvUpper().val[0] / 180.0,
params.hsvUpper().val[1] / 255.0,
params.hsvUpper().val[2] / 255.0,
params.hueInverted());
}
@Override

View File

@@ -90,14 +90,15 @@ public class Letterbox {
for (var t : unscaled) {
var scale = 1.0 / this.scale;
var boundingBox = t.bbox;
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;
ret.add(
new NeuralNetworkPipeResult(new Rect2d(x, y, width, height), t.classIdx, t.confidence));
new NeuralNetworkPipeResult(
new Rect2d(x, y, width, height), t.classIdx(), t.confidence()));
}
return ret;

View File

@@ -20,12 +20,14 @@ package org.photonvision.vision.pipe.impl;
import edu.wpi.first.apriltag.AprilTagDetection;
import edu.wpi.first.apriltag.AprilTagDetector;
import java.util.List;
import org.photonvision.vision.apriltag.AprilTagFamily;
import org.photonvision.vision.opencv.CVMat;
import org.photonvision.vision.opencv.Releasable;
import org.photonvision.vision.pipe.CVPipe;
public class AprilTagDetectionPipe
extends CVPipe<CVMat, List<AprilTagDetection>, AprilTagDetectionPipeParams>
extends CVPipe<
CVMat, List<AprilTagDetection>, AprilTagDetectionPipe.AprilTagDetectionPipeParams>
implements Releasable {
private AprilTagDetector m_detector = new AprilTagDetector();
@@ -58,11 +60,11 @@ public class AprilTagDetectionPipe
@Override
public void setParams(AprilTagDetectionPipeParams newParams) {
if (this.params == null || !this.params.equals(newParams)) {
m_detector.setConfig(newParams.detectorParams);
m_detector.setQuadThresholdParameters(newParams.quadParams);
m_detector.setConfig(newParams.detectorParams());
m_detector.setQuadThresholdParameters(newParams.quadParams());
m_detector.clearFamilies();
m_detector.addFamily(newParams.family.getNativeName());
m_detector.addFamily(newParams.family().getNativeName());
}
super.setParams(newParams);
@@ -73,4 +75,9 @@ public class AprilTagDetectionPipe
m_detector.close();
m_detector = null;
}
public static record AprilTagDetectionPipeParams(
AprilTagFamily family,
AprilTagDetector.Config detectorParams,
AprilTagDetector.QuadThresholdParameters quadParams) {}
}

View File

@@ -1,62 +0,0 @@
/*
* 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.pipe.impl;
import edu.wpi.first.apriltag.AprilTagDetector;
import org.photonvision.vision.apriltag.AprilTagFamily;
public class AprilTagDetectionPipeParams {
public final AprilTagFamily family;
public final AprilTagDetector.Config detectorParams;
public final AprilTagDetector.QuadThresholdParameters quadParams;
public AprilTagDetectionPipeParams(
AprilTagFamily tagFamily,
AprilTagDetector.Config config,
AprilTagDetector.QuadThresholdParameters quadParams) {
this.family = tagFamily;
this.detectorParams = config;
this.quadParams = quadParams;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((family == null) ? 0 : family.hashCode());
result = prime * result + ((detectorParams == null) ? 0 : detectorParams.hashCode());
result = prime * result + ((quadParams == null) ? 0 : quadParams.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
AprilTagDetectionPipeParams other = (AprilTagDetectionPipeParams) obj;
if (family != other.family) return false;
if (detectorParams == null) {
if (other.detectorParams != null) return false;
} else if (!detectorParams.equals(other.detectorParams)) return false;
if (quadParams == null) {
if (other.quadParams != null) return false;
} else if (!quadParams.equals(other.quadParams)) return false;
return true;
}
}

View File

@@ -57,8 +57,8 @@ public class AprilTagPoseEstimatorPipe
Calib3d.undistortImagePoints(
temp,
temp,
params.calibration.getCameraIntrinsicsMat(),
params.calibration.getDistCoeffsMat());
params.calibration().getCameraIntrinsicsMat(),
params.calibration().getDistCoeffsMat());
// Save out undistorted corners
corners = temp.toArray();
@@ -82,13 +82,13 @@ public class AprilTagPoseEstimatorPipe
in.getCenterY(),
fixedCorners);
return m_poseEstimator.estimateOrthogonalIteration(corrected, params.nIters);
return m_poseEstimator.estimateOrthogonalIteration(corrected, params.nIters());
}
@Override
public void setParams(AprilTagPoseEstimatorPipe.AprilTagPoseEstimatorPipeParams newParams) {
if (this.params == null || !this.params.equals(newParams)) {
m_poseEstimator.setConfig(newParams.config);
if (this.params == null || !this.params.config().equals(newParams.config())) {
m_poseEstimator.setConfig(newParams.config());
}
super.setParams(newParams);
@@ -99,37 +99,6 @@ public class AprilTagPoseEstimatorPipe
temp.release();
}
public static class AprilTagPoseEstimatorPipeParams {
final AprilTagPoseEstimator.Config config;
final CameraCalibrationCoefficients calibration;
final int nIters;
public AprilTagPoseEstimatorPipeParams(
Config config, CameraCalibrationCoefficients cal, int nIters) {
this.config = config;
this.nIters = nIters;
this.calibration = cal;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((config == null) ? 0 : config.hashCode());
result = prime * result + nIters;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
AprilTagPoseEstimatorPipeParams other = (AprilTagPoseEstimatorPipeParams) obj;
if (config == null) {
if (other.config != null) return false;
} else if (!config.equals(other.config)) return false;
return nIters == other.nIters;
}
}
public static record AprilTagPoseEstimatorPipeParams(
Config config, CameraCalibrationCoefficients calibration, int nIters) {}
}

View File

@@ -86,8 +86,8 @@ public class ArucoPoseEstimatorPipe
Calib3d.solvePnPGeneric(
objectPoints,
imagePoints,
params.calibration.getCameraIntrinsicsMat(),
params.calibration.getDistCoeffsMat(),
params.calibration().getCameraIntrinsicsMat(),
params.calibration().getDistCoeffsMat(),
rvecs,
tvecs,
false,
@@ -120,8 +120,8 @@ public class ArucoPoseEstimatorPipe
@Override
public void setParams(ArucoPoseEstimatorPipe.ArucoPoseEstimatorPipeParams newParams) {
// exact equality check OK here, the number shouldn't change
if (this.params == null || this.params.tagSize != newParams.tagSize) {
var tagSize = newParams.tagSize;
if (this.params == null || this.params.tagSize() != newParams.tagSize()) {
var tagSize = newParams.tagSize();
// This order is relevant for SOLVEPNP_IPPE_SQUARE
// The expected 2d correspondences with a tag facing the camera would be (BR, BL, TL, TR)
@@ -147,15 +147,7 @@ public class ArucoPoseEstimatorPipe
reprojectionErrors.release();
}
public static class ArucoPoseEstimatorPipeParams {
final CameraCalibrationCoefficients calibration;
final double tagSize;
// object vertices defined by tag size
public ArucoPoseEstimatorPipeParams(CameraCalibrationCoefficients cal, double tagSize) {
this.calibration = cal;
this.tagSize = tagSize;
}
}
// object vertices defined by tag size
public static record ArucoPoseEstimatorPipeParams(
CameraCalibrationCoefficients calibration, double tagSize) {}
}

View File

@@ -41,12 +41,12 @@ public class Collect2dTargetsPipe
var calculationParams =
new TrackedTarget.TargetCalculationParameters(
params.targetOrientation == TargetOrientation.Landscape,
params.targetOffsetPointEdge,
params.robotOffsetPointMode,
params.robotOffsetSinglePoint,
params.dualOffsetValues,
params.frameStaticProperties);
params.targetOrientation() == TargetOrientation.Landscape,
params.targetOffsetPointEdge(),
params.robotOffsetPointMode(),
params.robotOffsetSinglePoint(),
params.dualOffsetValues(),
params.frameStaticProperties());
for (PotentialTarget target : in) {
targets.add(new TrackedTarget(target, calculationParams, target.shape));
@@ -55,27 +55,11 @@ public class Collect2dTargetsPipe
return targets;
}
public static class Collect2dTargetsParams {
private final RobotOffsetPointMode robotOffsetPointMode;
private final Point robotOffsetSinglePoint;
private final DualOffsetValues dualOffsetValues;
private final TargetOffsetPointEdge targetOffsetPointEdge;
private final TargetOrientation targetOrientation;
private final FrameStaticProperties frameStaticProperties;
public Collect2dTargetsParams(
RobotOffsetPointMode robotOffsetPointMode,
Point robotOffsetSinglePoint,
DualOffsetValues dualOffsetValues,
TargetOffsetPointEdge targetOffsetPointEdge,
TargetOrientation orientation,
FrameStaticProperties frameStaticProperties) {
this.frameStaticProperties = frameStaticProperties;
this.robotOffsetPointMode = robotOffsetPointMode;
this.robotOffsetSinglePoint = robotOffsetSinglePoint;
this.dualOffsetValues = dualOffsetValues;
this.targetOffsetPointEdge = targetOffsetPointEdge;
targetOrientation = orientation;
}
}
public static record Collect2dTargetsParams(
RobotOffsetPointMode robotOffsetPointMode,
Point robotOffsetSinglePoint,
DualOffsetValues dualOffsetValues,
TargetOffsetPointEdge targetOffsetPointEdge,
TargetOrientation targetOrientation,
FrameStaticProperties frameStaticProperties) {}
}

View File

@@ -121,12 +121,12 @@ public class CornerDetectionPipe
var isOpen = !convexHull && target.hasSubContours();
var peri = Imgproc.arcLength(targetContour, true);
Imgproc.approxPolyDP(
targetContour, polyOutput, params.accuracyPercentage / 600.0 * peri, !isOpen);
targetContour, polyOutput, params.accuracyPercentage() / 600.0 * peri, !isOpen);
// we must have at least 4 corners for this strategy to work.
// If we are looking for an exact side count that is handled here too.
var pointList = new ArrayList<>(polyOutput.toList());
if (pointList.size() < 4 || (params.exactSideCount && params.sideCount != pointList.size()))
if (pointList.size() < 4 || (params.exactSideCount() && params.sideCount() != pointList.size()))
return null;
target.setApproximateBoundingPolygon(polyOutput);
@@ -177,29 +177,15 @@ public class CornerDetectionPipe
return List.of(bl, br, tr, tl);
}
public static class CornerDetectionPipeParameters {
private final DetectionStrategy cornerDetectionStrategy;
private final boolean calculateConvexHulls;
private final boolean exactSideCount;
private final int sideCount;
/** This number can be changed to change how "accurate" our approximate polygon must be. */
private final double accuracyPercentage;
public CornerDetectionPipeParameters(
DetectionStrategy cornerDetectionStrategy,
boolean calculateConvexHulls,
boolean exactSideCount,
int sideCount,
double accuracyPercentage) {
this.cornerDetectionStrategy = cornerDetectionStrategy;
this.calculateConvexHulls = calculateConvexHulls;
this.exactSideCount = exactSideCount;
this.sideCount = sideCount;
this.accuracyPercentage = accuracyPercentage;
}
}
/**
* @param accuracyPercentage Represents how "accurate" our approximate polygon must be.
*/
public static record CornerDetectionPipeParameters(
DetectionStrategy cornerDetectionStrategy,
boolean calculateConvexHulls,
boolean exactSideCount,
int sideCount,
double accuracyPercentage) {}
public enum DetectionStrategy {
APPROX_POLY_DP_AND_EXTREME_CORNERS

View File

@@ -38,21 +38,22 @@ public class Draw2dCrosshairPipe
Pair<Mat, List<TrackedTarget>>, Draw2dCrosshairPipe.Draw2dCrosshairParams> {
@Override
protected Void process(Pair<Mat, List<TrackedTarget>> in) {
if (!params.shouldDraw) return null;
if (!params.shouldDraw()) return null;
var image = in.getLeft();
if (params.showCrosshair) {
double x = params.frameStaticProperties.centerX;
double y = params.frameStaticProperties.centerY;
double scale = params.frameStaticProperties.imageWidth / (double) params.divisor.value / 32.0;
if (params.showCrosshair()) {
double x = params.frameStaticProperties().centerX;
double y = params.frameStaticProperties().centerY;
double scale =
params.frameStaticProperties().imageWidth / (double) params.divisor().value / 32.0;
switch (params.robotOffsetPointMode) {
switch (params.robotOffsetPointMode()) {
case None -> {}
case Single -> {
if (params.singleOffsetPoint.x != 0 && params.singleOffsetPoint.y != 0) {
x = params.singleOffsetPoint.x;
y = params.singleOffsetPoint.y;
if (params.singleOffsetPoint().x != 0 && params.singleOffsetPoint().y != 0) {
x = params.singleOffsetPoint().x;
y = params.singleOffsetPoint().y;
}
}
case Dual -> {
@@ -61,7 +62,7 @@ public class Draw2dCrosshairPipe
if (target != null) {
var area = target.getArea();
var offsetCrosshair =
TargetCalculations.calculateDualOffsetCrosshair(params.dualOffsetValues, area);
TargetCalculations.calculateDualOffsetCrosshair(params.dualOffsetValues(), area);
x = offsetCrosshair.x;
y = offsetCrosshair.y;
}
@@ -69,45 +70,30 @@ public class Draw2dCrosshairPipe
}
}
x /= (double) params.divisor.value;
y /= (double) params.divisor.value;
x /= (double) params.divisor().value;
y /= (double) params.divisor().value;
Point xMax = new Point(x + scale, y);
Point xMin = new Point(x - scale, y);
Point yMax = new Point(x, y + scale);
Point yMin = new Point(x, y - scale);
Imgproc.line(image, xMax, xMin, ColorHelper.colorToScalar(params.crosshairColor));
Imgproc.line(image, yMax, yMin, ColorHelper.colorToScalar(params.crosshairColor));
Imgproc.line(image, xMax, xMin, ColorHelper.colorToScalar(params.crosshairColor()));
Imgproc.line(image, yMax, yMin, ColorHelper.colorToScalar(params.crosshairColor()));
}
return null;
}
public static class Draw2dCrosshairParams {
public boolean showCrosshair = true;
public Color crosshairColor = Color.GREEN;
public final boolean shouldDraw;
public final FrameStaticProperties frameStaticProperties;
public final ImageRotationMode rotMode;
public final RobotOffsetPointMode robotOffsetPointMode;
public final Point singleOffsetPoint;
public final DualOffsetValues dualOffsetValues;
private final FrameDivisor divisor;
public Draw2dCrosshairParams(
FrameStaticProperties frameStaticProperties,
FrameDivisor divisor,
ImageRotationMode rotMode) {
shouldDraw = true;
this.frameStaticProperties = frameStaticProperties;
this.rotMode = rotMode;
robotOffsetPointMode = RobotOffsetPointMode.None;
singleOffsetPoint = new Point();
dualOffsetValues = new DualOffsetValues();
this.divisor = divisor;
}
public static record Draw2dCrosshairParams(
boolean shouldDraw,
RobotOffsetPointMode robotOffsetPointMode,
Point singleOffsetPoint,
DualOffsetValues dualOffsetValues,
FrameStaticProperties frameStaticProperties,
FrameDivisor divisor,
ImageRotationMode rotMode,
boolean showCrosshair,
Color crosshairColor) {
public Draw2dCrosshairParams(
boolean shouldDraw,
RobotOffsetPointMode robotOffsetPointMode,
@@ -116,13 +102,30 @@ public class Draw2dCrosshairPipe
FrameStaticProperties frameStaticProperties,
FrameDivisor divisor,
ImageRotationMode rotMode) {
this.shouldDraw = shouldDraw;
this.frameStaticProperties = frameStaticProperties;
this.robotOffsetPointMode = robotOffsetPointMode;
this.singleOffsetPoint = singleOffsetPoint;
this.dualOffsetValues = dualOffsetValues;
this.divisor = divisor;
this.rotMode = rotMode;
this(
shouldDraw,
robotOffsetPointMode,
singleOffsetPoint,
dualOffsetValues,
frameStaticProperties,
divisor,
rotMode,
true,
Color.GREEN);
}
public Draw2dCrosshairParams(
FrameStaticProperties frameStaticProperties,
FrameDivisor divisor,
ImageRotationMode rotMode) {
this(
true,
RobotOffsetPointMode.None,
new Point(),
new DualOffsetValues(),
frameStaticProperties,
divisor,
rotMode);
}
}
}

View File

@@ -43,7 +43,7 @@ public class DrawCalibrationPipe
@Override
protected Void process(Pair<Mat, List<TrackedTarget>> in) {
if (!params.drawAllSnapshots) return null;
if (!params.drawAllSnapshots()) return null;
var image = in.getLeft();
@@ -65,7 +65,8 @@ public class DrawCalibrationPipe
c =
new Point(
c.x / params.divisor.value.doubleValue(), c.y / params.divisor.value.doubleValue());
c.x / params.divisor().value.doubleValue(),
c.y / params.divisor().value.doubleValue());
var r2 = r / Math.sqrt(2);
var color = chessboardColors[i % chessboardColors.length];
@@ -82,13 +83,5 @@ public class DrawCalibrationPipe
return null;
}
public static class DrawCalibrationPipeParams {
private final FrameDivisor divisor;
public boolean drawAllSnapshots;
public DrawCalibrationPipeParams(FrameDivisor divisor, boolean drawSnapshots) {
this.divisor = divisor;
this.drawAllSnapshots = drawSnapshots;
}
}
public static record DrawCalibrationPipeParams(FrameDivisor divisor, boolean drawAllSnapshots) {}
}

View File

@@ -38,7 +38,7 @@ public class FilterContoursPipe
}
// we need the whole list for outlier rejection
rejectOutliers(m_filteredContours, params.xTol, params.yTol);
rejectOutliers(m_filteredContours, params.xTol(), params.yTol());
return m_filteredContours;
}
@@ -81,66 +81,31 @@ public class FilterContoursPipe
// Area Filtering.
double areaPercentage =
minAreaRect.size.area() / params.getFrameStaticProperties().imageArea * 100.0;
double minAreaPercentage = params.getArea().getFirst();
double maxAreaPercentage = params.getArea().getSecond();
minAreaRect.size.area() / params.frameStaticProperties().imageArea * 100.0;
double minAreaPercentage = params.area().getFirst();
double maxAreaPercentage = params.area().getSecond();
if (areaPercentage < minAreaPercentage || areaPercentage > maxAreaPercentage) return;
// Fullness Filtering.
double contourArea = contour.getArea();
double minFullness = params.getFullness().getFirst() * minAreaRect.size.area() / 100.0;
double maxFullness = params.getFullness().getSecond() * minAreaRect.size.area() / 100.0;
double minFullness = params.fullness().getFirst() * minAreaRect.size.area() / 100.0;
double maxFullness = params.fullness().getSecond() * minAreaRect.size.area() / 100.0;
if (contourArea <= minFullness || contourArea >= maxFullness) return;
// Aspect Ratio Filtering.
double aspectRatio =
TargetCalculations.getAspectRatio(contour.getMinAreaRect(), params.isLandscape);
if (aspectRatio < params.getRatio().getFirst() || aspectRatio > params.getRatio().getSecond())
return;
TargetCalculations.getAspectRatio(contour.getMinAreaRect(), params.isLandscape());
if (aspectRatio < params.ratio().getFirst() || aspectRatio > params.ratio().getSecond()) return;
m_filteredContours.add(contour);
}
public static class FilterContoursParams {
private final DoubleCouple m_area;
private final DoubleCouple m_ratio;
private final DoubleCouple m_fullness;
private final FrameStaticProperties m_frameStaticProperties;
private final double xTol; // IQR tolerance for x
private final double yTol; // IQR tolerance for x
public final boolean isLandscape;
public FilterContoursParams(
DoubleCouple area,
DoubleCouple ratio,
DoubleCouple extent,
FrameStaticProperties camProperties,
double xTol,
double yTol,
boolean isLandscape) {
this.m_area = area;
this.m_ratio = ratio;
this.m_fullness = extent;
this.m_frameStaticProperties = camProperties;
this.xTol = xTol;
this.yTol = yTol;
this.isLandscape = isLandscape;
}
public DoubleCouple getArea() {
return m_area;
}
public DoubleCouple getRatio() {
return m_ratio;
}
public DoubleCouple getFullness() {
return m_fullness;
}
public FrameStaticProperties getFrameStaticProperties() {
return m_frameStaticProperties;
}
}
public static record FilterContoursParams(
DoubleCouple area,
DoubleCouple ratio,
DoubleCouple fullness,
FrameStaticProperties frameStaticProperties,
double xTol, // IQR tolerance for x
double yTol, // IQR tolerance for y
boolean isLandscape) {}
}

View File

@@ -41,49 +41,24 @@ public class FilterObjectDetectionsPipe
}
private void filterContour(NeuralNetworkPipeResult contour) {
var boc = contour.bbox;
var boc = contour.bbox();
// Area filtering
double areaPercentage = boc.area() / params.getFrameStaticProperties().imageArea * 100.0;
double minAreaPercentage = params.getArea().getFirst();
double maxAreaPercentage = params.getArea().getSecond();
double areaPercentage = boc.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;
if (aspectRatio < params.getRatio().getFirst() || aspectRatio > params.getRatio().getSecond())
return;
if (aspectRatio < params.ratio().getFirst() || aspectRatio > params.ratio().getSecond()) return;
m_filteredContours.add(contour);
}
public static class FilterContoursParams {
private final DoubleCouple m_area;
private final DoubleCouple m_ratio;
private final FrameStaticProperties m_frameStaticProperties;
public final boolean isLandscape;
public FilterContoursParams(
DoubleCouple area,
DoubleCouple ratio,
FrameStaticProperties camProperties,
boolean isLandscape) {
this.m_area = area;
this.m_ratio = ratio;
this.m_frameStaticProperties = camProperties;
this.isLandscape = isLandscape;
}
public DoubleCouple getArea() {
return m_area;
}
public DoubleCouple getRatio() {
return m_ratio;
}
public FrameStaticProperties getFrameStaticProperties() {
return m_frameStaticProperties;
}
}
public static record FilterContoursParams(
DoubleCouple area,
DoubleCouple ratio,
FrameStaticProperties frameStaticProperties,
boolean isLandscape) {}
}

View File

@@ -48,40 +48,20 @@ public class FilterShapesPipe
}
private boolean shouldRemove(CVShape shape) {
return shape.shape != params.desiredShape
|| shape.contour.getArea() / params.getFrameStaticProperties().imageArea * 100.0
> params.maxArea
|| shape.contour.getArea() / params.getFrameStaticProperties().imageArea * 100.0
< params.minArea
|| shape.contour.getPerimeter() > params.maxPeri
|| shape.contour.getPerimeter() < params.minPeri;
return shape.shape != params.desiredShape()
|| shape.contour.getArea() / params.frameStaticProperties().imageArea * 100.0
> params.maxArea()
|| shape.contour.getArea() / params.frameStaticProperties().imageArea * 100.0
< params.minArea()
|| shape.contour.getPerimeter() > params.maxPeri()
|| shape.contour.getPerimeter() < params.minPeri();
}
public static class FilterShapesPipeParams {
private final ContourShape desiredShape;
private final FrameStaticProperties frameStaticProperties;
private final double minArea;
private final double maxArea;
private final double minPeri;
private final double maxPeri;
public FilterShapesPipeParams(
ContourShape desiredShape,
double minArea,
double maxArea,
double minPeri,
double maxPeri,
FrameStaticProperties frameStaticProperties) {
this.desiredShape = desiredShape;
this.minArea = minArea;
this.maxArea = maxArea;
this.minPeri = minPeri;
this.maxPeri = maxPeri;
this.frameStaticProperties = frameStaticProperties;
}
public FrameStaticProperties getFrameStaticProperties() {
return frameStaticProperties;
}
}
public static record FilterShapesPipeParams(
ContourShape desiredShape,
double minArea,
double maxArea,
double minPeri,
double maxPeri,
FrameStaticProperties frameStaticProperties) {}
}

View File

@@ -91,30 +91,30 @@ public class FindBoardCornersPipe
* number of corners.
*/
this.patternSize =
params.type == UICalibrationData.BoardType.CHESSBOARD
? new Size(params.boardWidth - 1, params.boardHeight - 1)
: new Size(params.boardWidth, params.boardHeight);
params.type() == UICalibrationData.BoardType.CHESSBOARD
? new Size(params.boardWidth() - 1, params.boardHeight() - 1)
: new Size(params.boardWidth(), params.boardHeight());
// Chessboard and dot board have different 3D points to project as a dot board
// has alternating
// dots per column
if (params.type == UICalibrationData.BoardType.CHESSBOARD) {
if (params.type() == UICalibrationData.BoardType.CHESSBOARD) {
// Here we can create an NxN grid since a chessboard is rectangular
for (int heightIdx = 0; heightIdx < patternSize.height; heightIdx++) {
for (int widthIdx = 0; widthIdx < patternSize.width; widthIdx++) {
double boardYCoord = heightIdx * params.gridSize;
double boardXCoord = widthIdx * params.gridSize;
double boardYCoord = heightIdx * params.gridSize();
double boardXCoord = widthIdx * params.gridSize();
objectPoints.push_back(new MatOfPoint3f(new Point3(boardXCoord, boardYCoord, 0.0)));
}
}
} else if (params.type == UICalibrationData.BoardType.CHARUCOBOARD) {
} else if (params.type() == UICalibrationData.BoardType.CHARUCOBOARD) {
board =
new CharucoBoard(
new Size(params.boardWidth, params.boardHeight),
(float) params.gridSize,
(float) params.markerSize,
Objdetect.getPredefinedDictionary(params.tagFamily.getValue()));
board.setLegacyPattern(params.useOldPattern);
new Size(params.boardWidth(), params.boardHeight()),
(float) params.gridSize(),
(float) params.markerSize(),
Objdetect.getPredefinedDictionary(params.tagFamily().getValue()));
board.setLegacyPattern(params.useOldPattern());
detector = new CharucoDetector(board);
detector.getDetectorParameters().set_adaptiveThreshConstant(10);
detector.getDetectorParameters().set_adaptiveThreshWinSizeMin(11);
@@ -122,7 +122,7 @@ public class FindBoardCornersPipe
detector.getDetectorParameters().set_adaptiveThreshWinSizeMax(91);
} else {
logger.error("Can't create pattern for unknown board type " + params.type);
logger.error("Can't create pattern for unknown board type " + params.type());
}
}
@@ -145,7 +145,7 @@ public class FindBoardCornersPipe
* @return
*/
private double getFindCornersScaleFactor(Mat inFrame) {
return 1.0 / params.divisor.value;
return 1.0 / params.divisor().value;
}
/**
@@ -185,8 +185,8 @@ public class FindBoardCornersPipe
* @return the size to scale the input mat to
*/
private Size getFindCornersImgSize(Mat in) {
int width = in.cols() / params.divisor.value;
int height = in.rows() / params.divisor.value;
int width = in.cols() / params.divisor().value;
int height = in.rows() / params.divisor().value;
return new Size(width, height);
}
@@ -222,7 +222,7 @@ public class FindBoardCornersPipe
*/
private Size getWindowSize(MatOfPoint2f inPoints) {
double windowHalfWidth = 11; // Dot board uses fixed-size window half-width
if (params.type == UICalibrationData.BoardType.CHESSBOARD) {
if (params.type() == UICalibrationData.BoardType.CHESSBOARD) {
// Chessboard uses a dynamic sized window based on how far apart the corners are
windowHalfWidth = Math.floor(getApproxMinSpacing(inPoints) * 0.50);
windowHalfWidth = Math.max(1, windowHalfWidth);
@@ -254,7 +254,7 @@ public class FindBoardCornersPipe
// Get the size of the inFrame
this.imageSize = new Size(inFrame.width(), inFrame.height());
if (params.type == UICalibrationData.BoardType.CHARUCOBOARD) {
if (params.type() == UICalibrationData.BoardType.CHARUCOBOARD) {
Mat objPoints =
new Mat(); // 3 dimensional currentObjectPoints, the physical target ChArUco Board
Mat imgPoints =
@@ -295,10 +295,10 @@ public class FindBoardCornersPipe
// for each backend
{
Point[] boardCorners =
new Point[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];
new Point[(this.params.boardHeight() - 1) * (this.params.boardWidth() - 1)];
Point3[] objectPoints =
new Point3[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];
levels = new float[(this.params.boardHeight - 1) * (this.params.boardWidth - 1)];
new Point3[(this.params.boardHeight() - 1) * (this.params.boardWidth() - 1)];
levels = new float[(this.params.boardHeight() - 1) * (this.params.boardWidth() - 1)];
for (int i = 0; i < detectedIds.total(); i++) {
int id = (int) detectedIds.get(i, 0)[0];
@@ -329,7 +329,7 @@ public class FindBoardCornersPipe
// this since we
// don't need that copy. See:
// https://github.com/opencv/opencv/blob/a8ec6586118c3f8e8f48549a85f2da7a5b78bcc9/modules/imgproc/src/resize.cpp#L4185
if (params.divisor != FrameDivisor.NONE) {
if (params.divisor() != FrameDivisor.NONE) {
Imgproc.resize(inFrame, smallerInFrame, getFindCornersImgSize(inFrame));
} else {
smallerInFrame = inFrame;
@@ -371,65 +371,15 @@ public class FindBoardCornersPipe
return new FindBoardCornersPipeResult(inFrame.size(), objPts, outBoardCorners, outLevels);
}
public static class FindCornersPipeParams {
final int boardHeight;
final int boardWidth;
final UICalibrationData.BoardType type;
final double gridSize;
final double markerSize;
final FrameDivisor divisor;
final UICalibrationData.TagFamily tagFamily;
final boolean useOldPattern;
public FindCornersPipeParams(
int boardHeight,
int boardWidth,
UICalibrationData.BoardType type,
UICalibrationData.TagFamily tagFamily,
double gridSize,
double markerSize,
FrameDivisor divisor,
boolean useOldPattern) {
this.boardHeight = boardHeight;
this.boardWidth = boardWidth;
this.tagFamily = tagFamily;
this.type = type;
this.gridSize = gridSize; // meter
this.markerSize = markerSize; // meter
this.divisor = divisor;
this.useOldPattern = useOldPattern;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + boardHeight;
result = prime * result + boardWidth;
result = prime * result + ((type == null) ? 0 : type.hashCode());
long temp;
temp = Double.doubleToLongBits(gridSize);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((divisor == null) ? 0 : divisor.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
FindCornersPipeParams other = (FindCornersPipeParams) obj;
if (boardHeight != other.boardHeight) return false;
if (boardWidth != other.boardWidth) return false;
if (tagFamily != other.tagFamily) return false;
if (useOldPattern != other.useOldPattern) return false;
if (type != other.type) return false;
if (Double.doubleToLongBits(gridSize) != Double.doubleToLongBits(other.gridSize))
return false;
return divisor == other.divisor;
}
}
public static record FindCornersPipeParams(
int boardHeight,
int boardWidth,
UICalibrationData.BoardType type,
UICalibrationData.TagFamily tagFamily,
double gridSize, // meters
double markerSize, // meters
FrameDivisor divisor,
boolean useOldPattern) {}
public static class FindBoardCornersPipeResult implements Releasable {
public Size size;

View File

@@ -49,9 +49,9 @@ public class FindCirclesPipe
circles.release();
List<CVShape> output = new ArrayList<>();
var diag = params.diagonalLengthPx;
var minRadius = (int) (params.minRadius * diag / 100.0);
var maxRadius = (int) (params.maxRadius * diag / 100.0);
var diag = params.diagonalLengthPx();
var minRadius = (int) (params.minRadius() * diag / 100.0);
var maxRadius = (int) (params.maxRadius() * diag / 100.0);
Imgproc.HoughCircles(
in.getLeft(),
@@ -65,9 +65,9 @@ public class FindCirclesPipe
unless some small very circles need to be detected.
*/
1.0,
params.minDist,
params.maxCannyThresh,
Math.max(1.0, params.accuracy),
params.minDist(),
params.maxCannyThresh(),
Math.max(1.0, params.accuracy()),
minRadius,
maxRadius);
// Great, we now found the center point of the circle, and it's radius, but we have no idea what
@@ -91,8 +91,8 @@ public class FindCirclesPipe
// NOTE: This means that the centroid of the contour must be within the "allowable
// threshold"
// of the center of the circle
if (Math.abs(x_center - (mu.m10 / mu.m00)) <= params.allowableThreshold
&& Math.abs(y_center - (mu.m01 / mu.m00)) <= params.allowableThreshold) {
if (Math.abs(x_center - (mu.m10 / mu.m00)) <= params.allowableThreshold()
&& Math.abs(y_center - (mu.m01 / mu.m00)) <= params.allowableThreshold()) {
// If it is, then add it to the output array
output.add(new CVShape(contour, new Point(c[0], c[1]), c[2]));
unmatchedContours.remove(contour);
@@ -107,41 +107,29 @@ public class FindCirclesPipe
return output;
}
public static class FindCirclePipeParams {
private final int allowableThreshold;
private final int minRadius;
private final int maxRadius;
private final int minDist;
private final int maxCannyThresh;
private final int accuracy;
private final double diagonalLengthPx;
/*
* @params minDist - Minimum distance between the centers of the detected circles.
* If the parameter is too small, multiple neighbor circles may be falsely detected in addition to a true one. If it is too large, some circles may be missed.
*
* @param maxCannyThresh -First method-specific parameter. In case of #HOUGH_GRADIENT and #HOUGH_GRADIENT_ALT, it is the higher threshold of the two passed to the Canny edge detector (the lower one is twice smaller).
* Note that #HOUGH_GRADIENT_ALT uses #Scharr algorithm to compute image derivatives, so the threshold value should normally be higher, such as 300 or normally exposed and contrasty images.
*
*
* @param allowableThreshold - When finding the corresponding contour, this is used to see how close a center should be to a contour for it to be considered THAT contour.
* Should be increased with lower resolutions and decreased with higher resolution
* */
public FindCirclePipeParams(
int allowableThreshold,
int minRadius,
int minDist,
int maxRadius,
int maxCannyThresh,
int accuracy,
double diagonalLengthPx) {
this.allowableThreshold = allowableThreshold;
this.minRadius = minRadius;
this.maxRadius = maxRadius;
this.minDist = minDist;
this.maxCannyThresh = maxCannyThresh;
this.accuracy = accuracy;
this.diagonalLengthPx = diagonalLengthPx;
}
}
/**
* @param allowableThreshold When finding the corresponding contour, this is used to see how close
* a center should be to a contour for it to be considered THAT contour. Should be increased
* with lower resolutions and decreased with higher resolution
* @param minRadius Minimum circle radius, [0, 100]
* @param minDist Minimum distance between the centers of the detected circles. If the parameter
* is too small, multiple neighbor circles may be falsely detected in addition to a true one.
* If it is too large, some circles may be missed.
* @param maxRadius Maximum circle radius, [0, 100]
* @param maxCannyThresh First method-specific parameter. In case of #HOUGH_GRADIENT and
* #HOUGH_GRADIENT_ALT, it is the higher threshold of the two passed to the Canny edge
* detector (the lower one is twice smaller). Note that #HOUGH_GRADIENT_ALT uses #Scharr
* algorithm to compute image derivatives, so the threshold value should normally be higher,
* such as 300 or normally exposed and contrasty images.
* @param accuracy Circle accuracy, [1, 100]
* @param diagonalLengthPx The diagonal length of the image in pixels
*/
public static record FindCirclePipeParams(
int allowableThreshold,
int minRadius,
int minDist,
int maxRadius,
int maxCannyThresh,
int accuracy,
double diagonalLengthPx) {}
}

View File

@@ -56,19 +56,17 @@ public class FindPolygonPipe
private int getCorners(Contour contour) {
var approx =
contour.getApproxPolyDp(
(100 - params.accuracyPercentage) / 100.0 * Imgproc.arcLength(contour.getMat2f(), true),
(100 - params.accuracyPercentage())
/ 100.0
* Imgproc.arcLength(contour.getMat2f(), true),
true);
// The height of the resultant approximation is the number of vertices
return (int) approx.size().height;
}
public static class FindPolygonPipeParams {
private final double accuracyPercentage;
// Should be a value between 0-100
public FindPolygonPipeParams(double accuracyPercentage) {
this.accuracyPercentage = accuracyPercentage;
}
}
/**
* @param accuracyPercentage Accuracy percentage, 0-100
*/
public static record FindPolygonPipeParams(double accuracyPercentage) {}
}

View File

@@ -38,14 +38,14 @@ public class GroupContoursPipe
m_targets.clear();
if (params.getGroup() == ContourGroupingMode.Single) {
if (params.group() == ContourGroupingMode.Single) {
for (var contour : input) {
m_targets.add(new PotentialTarget(contour));
}
}
// Check if we have at least 2 targets for 2 or more
// This will only ever return 1 contour!
else if (params.getGroup() == ContourGroupingMode.TwoOrMore
else if (params.group() == ContourGroupingMode.TwoOrMore
&& input.size() >= ContourGroupingMode.TwoOrMore.count) {
// Just blob everything together
Contour groupedContour = Contour.combineContourList(input);
@@ -53,7 +53,7 @@ public class GroupContoursPipe
m_targets.add(new PotentialTarget(groupedContour, input));
}
} else {
int groupingCount = params.getGroup().count;
int groupingCount = params.group().count;
if (input.size() >= groupingCount) {
input.sort(Contour.SortByMomentsX);
@@ -73,7 +73,7 @@ public class GroupContoursPipe
// FYI: This method only takes 2 contours!
Contour groupedContour =
Contour.groupContoursByIntersection(
groupingSet.get(0), groupingSet.get(1), params.getIntersection());
groupingSet.get(0), groupingSet.get(1), params.intersection());
if (groupedContour != null) {
m_targets.add(new PotentialTarget(groupedContour, groupingSet));
@@ -88,22 +88,6 @@ public class GroupContoursPipe
return m_targets;
}
public static class GroupContoursParams {
private final ContourGroupingMode m_group;
private final ContourIntersectionDirection m_intersection;
public GroupContoursParams(
ContourGroupingMode group, ContourIntersectionDirection intersectionDirection) {
m_group = group;
m_intersection = intersectionDirection;
}
public ContourGroupingMode getGroup() {
return m_group;
}
public ContourIntersectionDirection getIntersection() {
return m_intersection;
}
}
public static record GroupContoursParams(
ContourGroupingMode group, ContourIntersectionDirection intersection) {}
}

View File

@@ -32,24 +32,24 @@ public class HSVPipe extends CVPipe<Mat, Mat, HSVPipe.HSVParams> {
// rather than copying. Free performance!
Imgproc.cvtColor(in, outputMat, Imgproc.COLOR_BGR2HSV, 3);
if (params.getHueInverted()) {
if (params.hueInverted()) {
// In Java code we do this by taking an image thresholded
// from [0, minHue] and ORing it with [maxHue, 180]
// we want hue from the end of the slider to max hue
Scalar firstLower = params.getHsvLower().clone();
Scalar firstUpper = params.getHsvUpper().clone();
firstLower.val[0] = params.getHsvUpper().val[0];
Scalar firstLower = params.hsvLower().clone();
Scalar firstUpper = params.hsvUpper().clone();
firstLower.val[0] = params.hsvUpper().val[0];
firstUpper.val[0] = 180;
var lowerThresholdMat = new Mat();
Core.inRange(outputMat, firstLower, firstUpper, lowerThresholdMat);
// We want hue from 0 to the start of the slider
var secondLower = params.getHsvLower().clone();
var secondUpper = params.getHsvUpper().clone();
var secondLower = params.hsvLower().clone();
var secondUpper = params.hsvUpper().clone();
secondLower.val[0] = 0;
secondUpper.val[0] = params.getHsvLower().val[0];
secondUpper.val[0] = params.hsvLower().val[0];
// Now that the output mat's been used by the first inRange, it's fine to mutate it
Core.inRange(outputMat, secondLower, secondUpper, outputMat);
@@ -60,35 +60,19 @@ public class HSVPipe extends CVPipe<Mat, Mat, HSVPipe.HSVParams> {
lowerThresholdMat.release();
} else {
Core.inRange(outputMat, params.getHsvLower(), params.getHsvUpper(), outputMat);
Core.inRange(outputMat, params.hsvLower(), params.hsvUpper(), outputMat);
}
return outputMat;
}
public static class HSVParams {
private final Scalar m_hsvLower;
private final Scalar m_hsvUpper;
private final boolean m_hueInverted;
public static record HSVParams(Scalar hsvLower, Scalar hsvUpper, boolean hueInverted) {
public HSVParams(
IntegerCouple hue, IntegerCouple saturation, IntegerCouple value, boolean hueInverted) {
m_hsvLower = new Scalar(hue.getFirst(), saturation.getFirst(), value.getFirst());
m_hsvUpper = new Scalar(hue.getSecond(), saturation.getSecond(), value.getSecond());
this.m_hueInverted = hueInverted;
}
public Scalar getHsvLower() {
return m_hsvLower;
}
public Scalar getHsvUpper() {
return m_hsvUpper;
}
public boolean getHueInverted() {
return m_hueInverted;
this(
new Scalar(hue.getFirst(), saturation.getFirst(), value.getFirst()),
new Scalar(hue.getSecond(), saturation.getSecond(), value.getSecond()),
hueInverted);
}
}
}

View File

@@ -43,9 +43,9 @@ public class MultiTargetPNPPipe
@Override
protected Optional<MultiTargetPNPResult> process(List<TrackedTarget> targetList) {
if (params == null
|| params.cameraCoefficients == null
|| params.cameraCoefficients.getCameraIntrinsicsMat() == null
|| params.cameraCoefficients.getDistCoeffsMat() == null) {
|| params.cameraCoefficients() == null
|| params.cameraCoefficients().getCameraIntrinsicsMat() == null
|| params.cameraCoefficients().getDistCoeffsMat() == null) {
if (!hasWarned) {
logger.warn(
"Cannot perform solvePNP an uncalibrated camera! Please calibrate this resolution...");
@@ -62,7 +62,7 @@ public class MultiTargetPNPPipe
var tagIDsUsed = new ArrayList<Short>();
for (var target : targetList) {
int id = target.getFiducialId();
if (params.atfl.getTagPose(id).isPresent()) tagIDsUsed.add((short) id);
if (params.atfl().getTagPose(id).isPresent()) tagIDsUsed.add((short) id);
}
// Only run with multiple targets
@@ -72,11 +72,11 @@ public class MultiTargetPNPPipe
var estimatedPose =
VisionEstimation.estimateCamPosePNP(
params.cameraCoefficients.cameraIntrinsics.getAsWpilibMat(),
params.cameraCoefficients.distCoeffs.getAsWpilibMat(),
params.cameraCoefficients().cameraIntrinsics.getAsWpilibMat(),
params.cameraCoefficients().distCoeffs.getAsWpilibMat(),
TrackedTarget.simpleFromTrackedTargets(targetList),
params.atfl,
params.targetModel);
params.atfl(),
params.targetModel());
if (estimatedPose.isPresent()) {
return Optional.of(new MultiTargetPNPResult(estimatedPose.get(), tagIDsUsed));
@@ -85,18 +85,8 @@ public class MultiTargetPNPPipe
}
}
public static class MultiTargetPNPPipeParams {
private final CameraCalibrationCoefficients cameraCoefficients;
private final AprilTagFieldLayout atfl;
private final TargetModel targetModel;
public MultiTargetPNPPipeParams(
CameraCalibrationCoefficients cameraCoefficients,
AprilTagFieldLayout atfl,
TargetModel targetModel) {
this.cameraCoefficients = cameraCoefficients;
this.atfl = atfl;
this.targetModel = targetModel;
}
}
public static record MultiTargetPNPPipeParams(
CameraCalibrationCoefficients cameraCoefficients,
AprilTagFieldLayout atfl,
TargetModel targetModel) {}
}

View File

@@ -19,14 +19,4 @@ package org.photonvision.vision.pipe.impl;
import org.opencv.core.Rect2d;
public class NeuralNetworkPipeResult {
public NeuralNetworkPipeResult(Rect2d boundingBox, int classIdx, double confidence) {
bbox = boundingBox;
this.classIdx = classIdx;
this.confidence = confidence;
}
public final int classIdx;
public final Rect2d bbox;
public final double confidence;
}
public record NeuralNetworkPipeResult(Rect2d bbox, int classIdx, double confidence) {}

View File

@@ -42,9 +42,9 @@ public class ObjectDetectionPipe
@Override
protected List<NeuralNetworkPipeResult> process(CVMat in) {
// Check if the model has changed
if (detector.getModel() != params.model) {
if (detector.getModel() != params.model()) {
detector.release();
detector = params.model.load();
detector = params.model().load();
}
Mat frame = in.getMat();
@@ -52,17 +52,10 @@ public class ObjectDetectionPipe
return List.of();
}
return detector.detect(in.getMat(), params.nms, params.confidence);
return detector.detect(in.getMat(), params.nms(), params.confidence());
}
public static class ObjectDetectionPipeParams {
public double confidence;
public double nms;
public int max_detections;
public Model model;
public ObjectDetectionPipeParams() {}
}
public static record ObjectDetectionPipeParams(double confidence, double nms, Model model) {}
public List<String> getClassNames() {
return detector.getClasses();

View File

@@ -32,22 +32,12 @@ public class ResizeImagePipe extends MutatingPipe<Mat, ResizeImagePipe.ResizeIma
*/
@Override
protected Void process(Mat in) {
int width = in.cols() / params.getDivisor().value;
int height = in.rows() / params.getDivisor().value;
int width = in.cols() / params.divisor().value;
int height = in.rows() / params.divisor().value;
Imgproc.resize(in, in, new Size(width, height));
return null;
}
public static class ResizeImageParams {
private final FrameDivisor divisor;
public ResizeImageParams(FrameDivisor divisor) {
this.divisor = divisor;
}
public FrameDivisor getDivisor() {
return divisor;
}
}
public static record ResizeImageParams(FrameDivisor divisor) {}
}

View File

@@ -40,21 +40,15 @@ public class RotateImagePipe extends MutatingPipe<Mat, RotateImagePipe.RotateIma
*/
@Override
protected Void process(Mat in) {
Core.rotate(in, in, params.rotation.value);
Core.rotate(in, in, params.rotation().value);
return null;
}
public static class RotateImageParams {
public static record RotateImageParams(ImageRotationMode rotation) {
public static RotateImageParams DEFAULT = new RotateImageParams(ImageRotationMode.DEG_0);
public ImageRotationMode rotation;
public RotateImageParams() {
rotation = DEFAULT.rotation;
}
public RotateImageParams(ImageRotationMode rotation) {
this.rotation = rotation;
this(DEFAULT.rotation);
}
}
}

View File

@@ -44,9 +44,9 @@ public class SolvePNPPipe
@Override
protected List<TrackedTarget> process(List<TrackedTarget> targetList) {
if (params.cameraCoefficients == null
|| params.cameraCoefficients.getCameraIntrinsicsMat() == null
|| params.cameraCoefficients.getDistCoeffsMat() == null) {
if (params.cameraCoefficients() == null
|| params.cameraCoefficients().getCameraIntrinsicsMat() == null
|| params.cameraCoefficients().getDistCoeffsMat() == null) {
if (!hasWarned) {
logger.warn(
"Cannot perform solvePNP an uncalibrated camera! Please calibrate this resolution...");
@@ -65,9 +65,9 @@ public class SolvePNPPipe
var corners = target.getTargetCorners();
if (corners == null
|| corners.isEmpty()
|| params.cameraCoefficients == null
|| params.cameraCoefficients.getCameraIntrinsicsMat() == null
|| params.cameraCoefficients.getDistCoeffsMat() == null) {
|| params.cameraCoefficients() == null
|| params.cameraCoefficients().getCameraIntrinsicsMat() == null
|| params.cameraCoefficients().getDistCoeffsMat() == null) {
return;
}
this.imagePoints.fromList(corners);
@@ -76,10 +76,10 @@ public class SolvePNPPipe
var tVec = new Mat();
try {
Calib3d.solvePnP(
params.targetModel.getRealWorldTargetCoordinates(),
params.targetModel().getRealWorldTargetCoordinates(),
imagePoints,
params.cameraCoefficients.getCameraIntrinsicsMat(),
params.cameraCoefficients.getDistCoeffsMat(),
params.cameraCoefficients().getCameraIntrinsicsMat(),
params.cameraCoefficients().getDistCoeffsMat(),
rVec,
tVec);
} catch (Exception e) {
@@ -103,14 +103,6 @@ public class SolvePNPPipe
target.setAltCameraToTarget3d(new Transform3d());
}
public static class SolvePNPPipeParams {
private final CameraCalibrationCoefficients cameraCoefficients;
private final TargetModel targetModel;
public SolvePNPPipeParams(
CameraCalibrationCoefficients cameraCoefficients, TargetModel targetModel) {
this.cameraCoefficients = cameraCoefficients;
this.targetModel = targetModel;
}
}
public static record SolvePNPPipeParams(
CameraCalibrationCoefficients cameraCoefficients, TargetModel targetModel) {}
}

View File

@@ -39,46 +39,23 @@ public class SortContoursPipe
if (!in.isEmpty()) {
m_sortedContours.addAll(in);
if (params.getSortMode() != ContourSortMode.Centermost) {
m_sortedContours.sort(params.getSortMode().getComparator());
if (params.sortMode() != ContourSortMode.Centermost) {
m_sortedContours.sort(params.sortMode().getComparator());
} else {
// we need knowledge of camera properties to calculate this distance -- do it ourselves
m_sortedContours.sort(Comparator.comparingDouble(this::calcSquareCenterDistance));
}
}
return new ArrayList<>(
m_sortedContours.subList(0, Math.min(in.size(), params.getMaxTargets())));
return new ArrayList<>(m_sortedContours.subList(0, Math.min(in.size(), params.maxTargets())));
}
private double calcSquareCenterDistance(PotentialTarget tgt) {
return Math.sqrt(
Math.pow(params.getCamProperties().centerX - tgt.getMinAreaRect().center.x, 2)
+ Math.pow(params.getCamProperties().centerY - tgt.getMinAreaRect().center.y, 2));
Math.pow(params.frameStaticProperties().centerX - tgt.getMinAreaRect().center.x, 2)
+ Math.pow(params.frameStaticProperties().centerY - tgt.getMinAreaRect().center.y, 2));
}
public static class SortContoursParams {
private final ContourSortMode m_sortMode;
private final int m_maxTargets;
private final FrameStaticProperties m_frameStaticProperties;
public SortContoursParams(
ContourSortMode sortMode, int maxTargets, FrameStaticProperties camProperties) {
m_sortMode = sortMode;
m_maxTargets = maxTargets;
m_frameStaticProperties = camProperties;
}
public ContourSortMode getSortMode() {
return m_sortMode;
}
public FrameStaticProperties getCamProperties() {
return m_frameStaticProperties;
}
public int getMaxTargets() {
return m_maxTargets;
}
}
public static record SortContoursParams(
ContourSortMode sortMode, int maxTargets, FrameStaticProperties frameStaticProperties) {}
}

View File

@@ -40,7 +40,7 @@ public class SpeckleRejectPipe
}
averageArea /= in.size();
double minAllowedArea = params.getMinPercentOfAvg() / 100.0 * averageArea;
double minAllowedArea = params.minPercentOfAvg() / 100.0 * averageArea;
for (Contour c : in) {
if (c.getArea() >= minAllowedArea) {
m_despeckledContours.add(c);
@@ -53,15 +53,5 @@ public class SpeckleRejectPipe
return m_despeckledContours;
}
public static class SpeckleRejectParams {
private final double m_minPercentOfAvg;
public SpeckleRejectParams(double minPercentOfAvg) {
m_minPercentOfAvg = minPercentOfAvg;
}
public double getMinPercentOfAvg() {
return m_minPercentOfAvg;
}
}
public static record SpeckleRejectParams(double minPercentOfAvg) {}
}

View File

@@ -38,7 +38,7 @@ import org.photonvision.vision.frame.Frame;
import org.photonvision.vision.frame.FrameThresholdType;
import org.photonvision.vision.pipe.CVPipe.CVPipeResult;
import org.photonvision.vision.pipe.impl.AprilTagDetectionPipe;
import org.photonvision.vision.pipe.impl.AprilTagDetectionPipeParams;
import org.photonvision.vision.pipe.impl.AprilTagDetectionPipe.AprilTagDetectionPipeParams;
import org.photonvision.vision.pipe.impl.AprilTagPoseEstimatorPipe;
import org.photonvision.vision.pipe.impl.AprilTagPoseEstimatorPipe.AprilTagPoseEstimatorPipeParams;
import org.photonvision.vision.pipe.impl.CalculateFPSPipe;

View File

@@ -56,9 +56,6 @@ public class ObjectDetectionPipeline
@Override
protected void setPipeParamsImpl() {
var params = new ObjectDetectionPipeParams();
params.confidence = settings.confidence;
params.nms = settings.nms;
Optional<Model> selectedModel =
NeuralNetworkModelManager.getInstance().getModel(settings.model);
@@ -71,8 +68,8 @@ public class ObjectDetectionPipeline
if (selectedModel.isEmpty()) {
selectedModel = Optional.of(NullModel.getInstance());
}
params.model = selectedModel.get();
var params =
new ObjectDetectionPipeParams(settings.confidence, settings.nms, selectedModel.get());
objectDetectorPipe.setParams(params);

View File

@@ -56,11 +56,11 @@ public class PotentialTarget implements Releasable {
}
public PotentialTarget(NeuralNetworkPipeResult det) {
this.shape = new CVShape(new Contour(det.bbox), ContourShape.Quadrilateral);
this.shape = new CVShape(new Contour(det.bbox()), ContourShape.Quadrilateral);
this.m_mainContour = this.shape.getContour();
m_subContours = List.of();
this.clsId = det.classIdx;
this.confidence = det.confidence;
this.clsId = det.classIdx();
this.confidence = det.confidence();
}
public PotentialTarget(CVShape cvShape) {