From 9991f8670cf664ecbccec732967416c9ca55359e Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 15 Oct 2023 12:17:40 -0400 Subject: [PATCH 1/5] Bump wpilib versions to 2024 beta 1 (#947) --- .github/workflows/main.yml | 7 ++++--- build.gradle | 18 ++++++++++------ gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 ++- photon-core/build.gradle | 2 +- .../vision/aruco/ArucoDetectorParams.java | 15 ++++++------- .../vision/aruco/PhotonArucoDetector.java | 2 +- .../vision/pipe/impl/ArucoDetectionPipe.java | 4 ++-- .../pipe/impl/ArucoDetectionPipeParams.java | 2 +- photon-lib/build.gradle | 12 ++++++----- photon-lib/publish.gradle | 21 ++++++++++++------- photon-lib/src/generate/photonlib.json.in | 3 ++- .../cpp/photonlib/PhotonPoseEstimator.cpp | 11 ++++------ photon-server/build.gradle | 2 +- photon-targeting/build.gradle | 2 +- .../aimandrange/build.gradle | 2 +- photonlib-cpp-examples/aimandrange/gradlew | 3 ++- .../aimandrange/settings.gradle | 2 +- .../aimandrange/src/main/include/Robot.h | 4 ++-- .../aimattarget/build.gradle | 2 +- photonlib-cpp-examples/aimattarget/gradlew | 3 ++- .../aimattarget/settings.gradle | 2 +- .../aimattarget/src/main/include/Robot.h | 2 +- .../apriltagExample/build.gradle | 2 +- .../apriltagExample/gradlew | 3 ++- .../apriltagExample/settings.gradle | 2 +- .../src/main/include/Drivetrain.h | 4 ++-- photonlib-cpp-examples/build.gradle | 7 ------- .../getinrange/build.gradle | 2 +- photonlib-cpp-examples/getinrange/gradlew | 3 ++- .../getinrange/settings.gradle | 2 +- .../getinrange/src/main/include/Robot.h | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- photonlib-cpp-examples/gradlew | 3 ++- .../aimandrange/build.gradle | 2 +- .../aimandrange/settings.gradle | 2 +- .../aimattarget/build.gradle | 2 +- .../aimattarget/settings.gradle | 2 +- photonlib-java-examples/build.gradle | 7 ------- .../getinrange/build.gradle | 2 +- .../getinrange/settings.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- photonlib-java-examples/gradlew | 3 ++- .../simaimandrange/build.gradle | 2 +- .../simaimandrange/settings.gradle | 2 +- .../swervedriveposeestsim/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../swervedriveposeestsim/gradlew | 3 ++- .../swervedriveposeestsim/settings.gradle | 2 +- .../src/main/java/frc/robot/Constants.java | 13 ++---------- shared/common.gradle | 19 ++++++++++++----- shared/config.gradle | 13 +++++++----- 52 files changed, 125 insertions(+), 115 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e2c3d773cc..94a5e5f075 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -106,14 +106,14 @@ jobs: run: | chmod +x gradlew ./gradlew copyPhotonlib -x check - ./gradlew buildAllExamples -x check --max-workers 2 + ./gradlew build -x check --max-workers 2 - name: Build C++ examples working-directory: photonlib-cpp-examples run: | chmod +x gradlew ./gradlew copyPhotonlib -x check - ./gradlew buildAllExamples -x check --max-workers 2 + ./gradlew build -x check --max-workers 2 photon-build-all: # The type of runner that the job will run on. runs-on: ubuntu-22.04 @@ -225,6 +225,7 @@ jobs: include: - os: windows-2022 artifact-name: Win64 + architecture: x64 # Mac builds are broken due to opencv class loading issues -- disable for now # - os: macos-11 # artifact-name: macOS @@ -255,7 +256,7 @@ jobs: fail-fast: false matrix: include: - - container: wpilib/roborio-cross-ubuntu:2023-22.04 + - container: wpilib/roborio-cross-ubuntu:2024-22.04 artifact-name: Athena - container: wpilib/raspbian-cross-ubuntu:bullseye-22.04 artifact-name: Raspbian diff --git a/build.gradle b/build.gradle index cbe84c5549..199729e75a 100644 --- a/build.gradle +++ b/build.gradle @@ -2,12 +2,12 @@ plugins { id "com.diffplug.spotless" version "6.19.0" id "com.github.johnrengelman.shadow" version "7.1.2" id "com.github.node-gradle.node" version "3.1.1" apply false - id "edu.wpi.first.GradleJni" version "1.0.0" - id "edu.wpi.first.GradleVsCode" version "1.1.0" - id "edu.wpi.first.NativeUtils" version "2023.11.1" apply false + id "edu.wpi.first.GradleJni" version "1.1.0" + id "edu.wpi.first.GradleVsCode" version "1.3.0" + id "edu.wpi.first.NativeUtils" version "2024.2.0" apply false id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2" id "org.hidetake.ssh" version "2.10.1" - id 'edu.wpi.first.WpilibTools' version '1.0.0' + id 'edu.wpi.first.WpilibTools' version '1.1.0' } import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency; @@ -26,10 +26,12 @@ allprojects { apply from: "versioningHelper.gradle" ext { - wpilibVersion = "2023.4.2" - opencvVersion = "4.6.0-4" + wpilibVersion = "2024.1.1-beta-1" + openCVversion = "4.8.0-1" joglVersion = "2.4.0-rc-20200307" javalinVersion = "5.6.2" + frcYear = "2024" + pubVersion = versionString isDev = pubVersion.startsWith("dev") @@ -61,3 +63,7 @@ spotless { targetExclude("photon-lib/src/main/java/org/photonvision/PhotonVersion.java") } } + +wrapper { + gradleVersion '8.3' +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661ee7..7f959b087a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services\.gradle\.org/distributions/gradle-8\.3-bin\.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 8bedea1ac8..89a57c4dfe 100755 --- a/gradlew +++ b/gradlew @@ -36,7 +36,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photon-core/build.gradle b/photon-core/build.gradle index dc805fff55..5ebef599a4 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'edu.wpi.first.WpilibTools' version '1.0.0' + id 'edu.wpi.first.WpilibTools' version '1.1.0' } import java.nio.file.Path diff --git a/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java b/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java index a4486a1041..f6af5d3047 100644 --- a/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java +++ b/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java @@ -17,10 +17,9 @@ package org.photonvision.vision.aruco; -import org.opencv.aruco.Aruco; -import org.opencv.aruco.ArucoDetector; -import org.opencv.aruco.DetectorParameters; -import org.opencv.aruco.Dictionary; +import org.opencv.objdetect.ArucoDetector; +import org.opencv.objdetect.DetectorParameters; +import org.opencv.objdetect.Objdetect; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; @@ -31,7 +30,7 @@ public class ArucoDetectorParams { private int m_iterations = -1; private double m_accuracy = -1; - DetectorParameters parameters = DetectorParameters.create(); + DetectorParameters parameters = new DetectorParameters(); ArucoDetector detector; public ArucoDetectorParams() { @@ -39,7 +38,9 @@ public ArucoDetectorParams() { setCornerAccuracy(25); setCornerRefinementMaxIterations(100); - detector = new ArucoDetector(Dictionary.get(Aruco.DICT_APRILTAG_16h5), parameters); + detector = + new ArucoDetector( + Objdetect.getPredefinedDictionary(Objdetect.DICT_APRILTAG_16h5), parameters); } public void setDecimation(float decimate) { @@ -56,7 +57,7 @@ public void setDecimation(float decimate) { public void setCornerRefinementMaxIterations(int iters) { if (iters == m_iterations || iters <= 0) return; - parameters.set_cornerRefinementMethod(Aruco.CORNER_REFINE_SUBPIX); + parameters.set_cornerRefinementMethod(Objdetect.CORNER_REFINE_SUBPIX); parameters.set_cornerRefinementMaxIterations(iters); // 200 m_iterations = iters; diff --git a/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java b/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java index b04418059a..0963a2812a 100644 --- a/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java @@ -24,8 +24,8 @@ import edu.wpi.first.math.util.Units; import java.util.ArrayList; import org.opencv.aruco.Aruco; -import org.opencv.aruco.ArucoDetector; import org.opencv.core.Mat; +import org.opencv.objdetect.ArucoDetector; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipe.java index d5bc76bf55..b18978c912 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipe.java @@ -19,8 +19,8 @@ import edu.wpi.first.math.util.Units; import java.util.List; -import org.opencv.aruco.DetectorParameters; import org.opencv.core.Mat; +import org.opencv.objdetect.DetectorParameters; import org.photonvision.vision.aruco.ArucoDetectionResult; import org.photonvision.vision.aruco.PhotonArucoDetector; import org.photonvision.vision.pipe.CVPipe; @@ -45,6 +45,6 @@ public void setParams(ArucoDetectionPipeParams params) { } public DetectorParameters getParameters() { - return params == null ? null : params.detectorParams.get_params(); + return params == null ? null : params.detectorParams.getDetectorParameters(); } } diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipeParams.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipeParams.java index 98e9daf411..62d3214030 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipeParams.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/ArucoDetectionPipeParams.java @@ -18,7 +18,7 @@ package org.photonvision.vision.pipe.impl; import java.util.Objects; -import org.opencv.aruco.ArucoDetector; +import org.opencv.objdetect.ArucoDetector; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; public class ArucoDetectionPipeParams { diff --git a/photon-lib/build.gradle b/photon-lib/build.gradle index 1536e9b0d6..d7952394df 100644 --- a/photon-lib/build.gradle +++ b/photon-lib/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'edu.wpi.first.WpilibTools' version '1.0.0' + id 'edu.wpi.first.WpilibTools' version '1.1.0' } import java.nio.file.Path @@ -50,10 +50,10 @@ dependencies { implementation "com.fasterxml.jackson.core:jackson-core:2.12.4" implementation "com.fasterxml.jackson.core:jackson-databind:2.12.4" - implementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-java:$opencvVersion" - implementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-jni:$opencvVersion:$jniPlatform" + implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:$openCVversion" + implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-jni:$openCVversion:$jniPlatform" - implementation "org.ejml:ejml-simple:0.41" + implementation "org.ejml:ejml-simple:0.42" // Junit testImplementation wpilibTools.deps.wpilibJava("cscore") @@ -126,7 +126,9 @@ task generateVendorJson() { if (photonlibFileOutput.exists()) { photonlibFileOutput.delete() } - def read = photonlibFileInput.text.replace('${photon_version}', pubVersion) + def read = photonlibFileInput.text + .replace('${photon_version}', pubVersion) + .replace('${frc_year}', frcYear) photonlibFileOutput.write(read) } diff --git a/photon-lib/publish.gradle b/photon-lib/publish.gradle index 725d47c0e5..9967b24ee2 100644 --- a/photon-lib/publish.gradle +++ b/photon-lib/publish.gradle @@ -37,8 +37,8 @@ copyAllOutputs.dependsOn outputVersions ext.addTaskToCopyAllOutputs = { task -> copyAllOutputs.dependsOn task - copyAllOutputs.inputs.file task.archivePath - copyAllOutputs.from task.archivePath + copyAllOutputs.inputs.file task.archiveFile + copyAllOutputs.from task.archiveFile } def artifactGroupId = 'org.photonvision' @@ -49,7 +49,7 @@ def javaBaseName = "_GROUP_org_photonvision_photonlib_ID_${baseArtifactId}-java_ task cppHeadersZip(type: Zip) { destinationDirectory = outputsFolder archiveBaseName = zipBaseName - classifier = "headers" + archiveClassifier = "headers" from(licenseFile) { into '/' @@ -67,7 +67,7 @@ task cppHeadersZip(type: Zip) { task cppSourcesZip(type: Zip) { destinationDirectory = outputsFolder archiveBaseName = zipBaseName - classifier = "sources" + archiveClassifier = "sources" from(licenseFile) { into '/' @@ -84,12 +84,12 @@ build.dependsOn cppSourcesZip addTaskToCopyAllOutputs(cppSourcesZip) task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' + archiveClassifier = 'sources' from sourceSets.main.allSource } task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' + archiveClassifier = 'javadoc' from javadoc.destinationDir } @@ -102,14 +102,14 @@ task outputJar(type: Jar, dependsOn: classes) { task outputSourcesJar(type: Jar, dependsOn: classes) { archiveBaseName = javaBaseName destinationDirectory = outputsFolder - classifier = 'sources' + archiveClassifier = 'sources' from sourceSets.main.allSource } task outputJavadocJar(type: Jar, dependsOn: javadoc) { archiveBaseName = javaBaseName destinationDirectory = outputsFolder - classifier = 'javadoc' + archiveClassifier = 'javadoc' from javadoc.destinationDir } @@ -198,5 +198,10 @@ model { } } +// So I don't actually know the _right_ way to tell gradle that the vendordep json publish requires generation first, so we're doing this +getTasksByName("publishVendorjsonPublicationToMavenLocal", false).each { + it.mustRunAfter generateVendorJson +} + publishToMavenLocal.dependsOn libraryBuild publish.dependsOn libraryBuild diff --git a/photon-lib/src/generate/photonlib.json.in b/photon-lib/src/generate/photonlib.json.in index dddc1a0e6b..9b8f63aaa4 100644 --- a/photon-lib/src/generate/photonlib.json.in +++ b/photon-lib/src/generate/photonlib.json.in @@ -2,7 +2,8 @@ "fileName": "photonlib.json", "name": "photonlib", "version": "${photon_version}", - "uuid": "515fe07e-bfc6-11fa-b3de-0242ac130004 ", + "uuid": "515fe07e-bfc6-11fa-b3de-0242ac130004", + "frcYear": "${frc_year}", "mavenUrls": [ "https://maven.photonvision.org/repository/internal", "https://maven.photonvision.org/repository/snapshots" diff --git a/photon-lib/src/main/native/cpp/photonlib/PhotonPoseEstimator.cpp b/photon-lib/src/main/native/cpp/photonlib/PhotonPoseEstimator.cpp index 9282e3bdd5..f25e01eba5 100644 --- a/photon-lib/src/main/native/cpp/photonlib/PhotonPoseEstimator.cpp +++ b/photon-lib/src/main/native/cpp/photonlib/PhotonPoseEstimator.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -337,21 +338,17 @@ frc::Pose3d detail::ToPose3d(const cv::Mat& tvec, const cv::Mat& rvec) { R = R.t(); // rotation of inverse cv::Mat tvecI = -R * tvec; // translation of inverse - Vectord<3> tv; + Eigen::Matrix tv; tv[0] = +tvecI.at(2, 0); tv[1] = -tvecI.at(0, 0); tv[2] = -tvecI.at(1, 0); - Vectord<3> rv; + Eigen::Matrix rv; rv[0] = +rvec.at(2, 0); rv[1] = -rvec.at(0, 0); rv[2] = +rvec.at(1, 0); return Pose3d(Translation3d(meter_t{tv[0]}, meter_t{tv[1]}, meter_t{tv[2]}), - Rotation3d( - // radian_t{rv[0]}, - // radian_t{rv[1]}, - // radian_t{rv[2]} - rv, radian_t{rv.norm()})); + Rotation3d(rv)); } std::optional PhotonPoseEstimator::MultiTagPnpStrategy( diff --git a/photon-server/build.gradle b/photon-server/build.gradle index a37e4f3ea0..92b50a81b8 100644 --- a/photon-server/build.gradle +++ b/photon-server/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'edu.wpi.first.WpilibTools' version '1.0.0' + id 'edu.wpi.first.WpilibTools' version '1.1.0' } apply plugin: "application" diff --git a/photon-targeting/build.gradle b/photon-targeting/build.gradle index 08a8dca19b..168ecc981e 100644 --- a/photon-targeting/build.gradle +++ b/photon-targeting/build.gradle @@ -4,7 +4,7 @@ apply from: "${rootDir}/shared/common.gradle" dependencies { implementation wpilibTools.deps.wpilibJava("wpimath") - implementation "org.ejml:ejml-simple:0.41" + implementation "org.ejml:ejml-simple:0.42" // Junit testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") diff --git a/photonlib-cpp-examples/aimandrange/build.gradle b/photonlib-cpp-examples/aimandrange/build.gradle index 67e4323b4c..ecedfafb40 100644 --- a/photonlib-cpp-examples/aimandrange/build.gradle +++ b/photonlib-cpp-examples/aimandrange/build.gradle @@ -1,7 +1,7 @@ plugins { id "cpp" id "google-test-test-suite" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" id "com.dorongold.task-tree" version "2.1.0" } diff --git a/photonlib-cpp-examples/aimandrange/gradlew b/photonlib-cpp-examples/aimandrange/gradlew index a69d9cb6c2..0ef4c1e860 100755 --- a/photonlib-cpp-examples/aimandrange/gradlew +++ b/photonlib-cpp-examples/aimandrange/gradlew @@ -80,7 +80,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-cpp-examples/aimandrange/settings.gradle b/photonlib-cpp-examples/aimandrange/settings.gradle index e387260182..b9552a3f3c 100644 --- a/photonlib-cpp-examples/aimandrange/settings.gradle +++ b/photonlib-cpp-examples/aimandrange/settings.gradle @@ -7,7 +7,7 @@ pluginManagement { mavenLocal() jcenter() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-cpp-examples/aimandrange/src/main/include/Robot.h b/photonlib-cpp-examples/aimandrange/src/main/include/Robot.h index c8a205c295..26518470c9 100644 --- a/photonlib-cpp-examples/aimandrange/src/main/include/Robot.h +++ b/photonlib-cpp-examples/aimandrange/src/main/include/Robot.h @@ -53,11 +53,11 @@ class Robot : public frc::TimedRobot { // PID constants should be tuned per robot const double LINEAR_P = 0.1; const double LINEAR_D = 0.0; - frc2::PIDController forwardController{LINEAR_P, 0.0, LINEAR_D}; + frc::PIDController forwardController{LINEAR_P, 0.0, LINEAR_D}; const double ANGULAR_P = 0.1; const double ANGULAR_D = 0.0; - frc2::PIDController turnController{ANGULAR_P, 0.0, ANGULAR_D}; + frc::PIDController turnController{ANGULAR_P, 0.0, ANGULAR_D}; // Change this to match the name of your camera photonlib::PhotonCamera camera{"photonvision"}; diff --git a/photonlib-cpp-examples/aimattarget/build.gradle b/photonlib-cpp-examples/aimattarget/build.gradle index 67e4323b4c..ecedfafb40 100644 --- a/photonlib-cpp-examples/aimattarget/build.gradle +++ b/photonlib-cpp-examples/aimattarget/build.gradle @@ -1,7 +1,7 @@ plugins { id "cpp" id "google-test-test-suite" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" id "com.dorongold.task-tree" version "2.1.0" } diff --git a/photonlib-cpp-examples/aimattarget/gradlew b/photonlib-cpp-examples/aimattarget/gradlew index a69d9cb6c2..0ef4c1e860 100755 --- a/photonlib-cpp-examples/aimattarget/gradlew +++ b/photonlib-cpp-examples/aimattarget/gradlew @@ -80,7 +80,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-cpp-examples/aimattarget/settings.gradle b/photonlib-cpp-examples/aimattarget/settings.gradle index 2f39cd03ad..44fbca7512 100644 --- a/photonlib-cpp-examples/aimattarget/settings.gradle +++ b/photonlib-cpp-examples/aimattarget/settings.gradle @@ -7,7 +7,7 @@ pluginManagement { mavenLocal() jcenter() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-cpp-examples/aimattarget/src/main/include/Robot.h b/photonlib-cpp-examples/aimattarget/src/main/include/Robot.h index 8b5344a26a..a639c34008 100644 --- a/photonlib-cpp-examples/aimattarget/src/main/include/Robot.h +++ b/photonlib-cpp-examples/aimattarget/src/main/include/Robot.h @@ -42,7 +42,7 @@ class Robot : public frc::TimedRobot { // Change this to match the name of your camera photonlib::PhotonCamera camera{"photonvision"}; // PID constants should be tuned per robot - frc2::PIDController controller{.1, 0, 0}; + frc::PIDController controller{.1, 0, 0}; frc::XboxController xboxController{0}; diff --git a/photonlib-cpp-examples/apriltagExample/build.gradle b/photonlib-cpp-examples/apriltagExample/build.gradle index 67e4323b4c..ecedfafb40 100644 --- a/photonlib-cpp-examples/apriltagExample/build.gradle +++ b/photonlib-cpp-examples/apriltagExample/build.gradle @@ -1,7 +1,7 @@ plugins { id "cpp" id "google-test-test-suite" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" id "com.dorongold.task-tree" version "2.1.0" } diff --git a/photonlib-cpp-examples/apriltagExample/gradlew b/photonlib-cpp-examples/apriltagExample/gradlew index a69d9cb6c2..0ef4c1e860 100644 --- a/photonlib-cpp-examples/apriltagExample/gradlew +++ b/photonlib-cpp-examples/apriltagExample/gradlew @@ -80,7 +80,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-cpp-examples/apriltagExample/settings.gradle b/photonlib-cpp-examples/apriltagExample/settings.gradle index 2f39cd03ad..44fbca7512 100644 --- a/photonlib-cpp-examples/apriltagExample/settings.gradle +++ b/photonlib-cpp-examples/apriltagExample/settings.gradle @@ -7,7 +7,7 @@ pluginManagement { mavenLocal() jcenter() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h b/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h index 2799c703d0..903256cbed 100644 --- a/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h +++ b/photonlib-cpp-examples/apriltagExample/src/main/include/Drivetrain.h @@ -109,8 +109,8 @@ class Drivetrain { frc::Encoder m_leftEncoder{0, 1}; frc::Encoder m_rightEncoder{2, 3}; - frc2::PIDController m_leftPIDController{8.5, 0.0, 0.0}; - frc2::PIDController m_rightPIDController{8.5, 0.0, 0.0}; + frc::PIDController m_leftPIDController{8.5, 0.0, 0.0}; + frc::PIDController m_rightPIDController{8.5, 0.0, 0.0}; frc::AnalogGyro m_gyro{0}; diff --git a/photonlib-cpp-examples/build.gradle b/photonlib-cpp-examples/build.gradle index cb4661a3a9..0d262a1504 100644 --- a/photonlib-cpp-examples/build.gradle +++ b/photonlib-cpp-examples/build.gradle @@ -28,10 +28,3 @@ spotless { } apply from: "examples.gradle" - -// Task that depends on the build task for every example -task buildAllExamples { task -> - exampleFolderNames.each { line -> - task.dependsOn(line + ":build") - } -} diff --git a/photonlib-cpp-examples/getinrange/build.gradle b/photonlib-cpp-examples/getinrange/build.gradle index 67e4323b4c..ecedfafb40 100644 --- a/photonlib-cpp-examples/getinrange/build.gradle +++ b/photonlib-cpp-examples/getinrange/build.gradle @@ -1,7 +1,7 @@ plugins { id "cpp" id "google-test-test-suite" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" id "com.dorongold.task-tree" version "2.1.0" } diff --git a/photonlib-cpp-examples/getinrange/gradlew b/photonlib-cpp-examples/getinrange/gradlew index a69d9cb6c2..0ef4c1e860 100755 --- a/photonlib-cpp-examples/getinrange/gradlew +++ b/photonlib-cpp-examples/getinrange/gradlew @@ -80,7 +80,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-cpp-examples/getinrange/settings.gradle b/photonlib-cpp-examples/getinrange/settings.gradle index 8301f6020c..d8802bb464 100644 --- a/photonlib-cpp-examples/getinrange/settings.gradle +++ b/photonlib-cpp-examples/getinrange/settings.gradle @@ -7,7 +7,7 @@ pluginManagement { mavenLocal() jcenter() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-cpp-examples/getinrange/src/main/include/Robot.h b/photonlib-cpp-examples/getinrange/src/main/include/Robot.h index a67f2122ed..08dec61134 100644 --- a/photonlib-cpp-examples/getinrange/src/main/include/Robot.h +++ b/photonlib-cpp-examples/getinrange/src/main/include/Robot.h @@ -53,7 +53,7 @@ class Robot : public frc::TimedRobot { // PID constants should be tuned per robot const double P_GAIN = 0.1; const double D_GAIN = 0.0; - frc2::PIDController controller{P_GAIN, 0.0, D_GAIN}; + frc::PIDController controller{P_GAIN, 0.0, D_GAIN}; // Change this to match the name of your camera photonlib::PhotonCamera camera{"photonvision"}; diff --git a/photonlib-cpp-examples/gradle/wrapper/gradle-wrapper.properties b/photonlib-cpp-examples/gradle/wrapper/gradle-wrapper.properties index ae04661ee7..7f959b087a 100644 --- a/photonlib-cpp-examples/gradle/wrapper/gradle-wrapper.properties +++ b/photonlib-cpp-examples/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services\.gradle\.org/distributions/gradle-8\.3-bin\.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/photonlib-cpp-examples/gradlew b/photonlib-cpp-examples/gradlew index 8bedea1ac8..89a57c4dfe 100755 --- a/photonlib-cpp-examples/gradlew +++ b/photonlib-cpp-examples/gradlew @@ -36,7 +36,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-java-examples/aimandrange/build.gradle b/photonlib-java-examples/aimandrange/build.gradle index fa1a30798c..7d6e58895a 100644 --- a/photonlib-java-examples/aimandrange/build.gradle +++ b/photonlib-java-examples/aimandrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/aimandrange/settings.gradle b/photonlib-java-examples/aimandrange/settings.gradle index df32b747bf..c48c299f78 100644 --- a/photonlib-java-examples/aimandrange/settings.gradle +++ b/photonlib-java-examples/aimandrange/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-java-examples/aimattarget/build.gradle b/photonlib-java-examples/aimattarget/build.gradle index 9feeaeaa44..d98e56b952 100644 --- a/photonlib-java-examples/aimattarget/build.gradle +++ b/photonlib-java-examples/aimattarget/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/aimattarget/settings.gradle b/photonlib-java-examples/aimattarget/settings.gradle index 67499c9943..df10542440 100644 --- a/photonlib-java-examples/aimattarget/settings.gradle +++ b/photonlib-java-examples/aimattarget/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-java-examples/build.gradle b/photonlib-java-examples/build.gradle index 29a90b45f9..35f533e690 100644 --- a/photonlib-java-examples/build.gradle +++ b/photonlib-java-examples/build.gradle @@ -28,10 +28,3 @@ spotless { targetExclude("photon-lib/src/main/java/org/photonvision/PhotonVersion.java") } } - -// Task that depends on the build task for every example -task buildAllExamples { task -> - exampleFolderNames.each { line -> - task.dependsOn(line + ":build") - } -} diff --git a/photonlib-java-examples/getinrange/build.gradle b/photonlib-java-examples/getinrange/build.gradle index fa1a30798c..7d6e58895a 100644 --- a/photonlib-java-examples/getinrange/build.gradle +++ b/photonlib-java-examples/getinrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/getinrange/settings.gradle b/photonlib-java-examples/getinrange/settings.gradle index f95cb46e53..cfa00c4e15 100644 --- a/photonlib-java-examples/getinrange/settings.gradle +++ b/photonlib-java-examples/getinrange/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-java-examples/gradle/wrapper/gradle-wrapper.properties b/photonlib-java-examples/gradle/wrapper/gradle-wrapper.properties index ae04661ee7..7f959b087a 100644 --- a/photonlib-java-examples/gradle/wrapper/gradle-wrapper.properties +++ b/photonlib-java-examples/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services\.gradle\.org/distributions/gradle-8\.3-bin\.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/photonlib-java-examples/gradlew b/photonlib-java-examples/gradlew index 8bedea1ac8..89a57c4dfe 100755 --- a/photonlib-java-examples/gradlew +++ b/photonlib-java-examples/gradlew @@ -36,7 +36,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-java-examples/simaimandrange/build.gradle b/photonlib-java-examples/simaimandrange/build.gradle index fa1a30798c..7d6e58895a 100644 --- a/photonlib-java-examples/simaimandrange/build.gradle +++ b/photonlib-java-examples/simaimandrange/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/simaimandrange/settings.gradle b/photonlib-java-examples/simaimandrange/settings.gradle index bedc34e6ab..464d86e81f 100644 --- a/photonlib-java-examples/simaimandrange/settings.gradle +++ b/photonlib-java-examples/simaimandrange/settings.gradle @@ -6,7 +6,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-java-examples/swervedriveposeestsim/build.gradle b/photonlib-java-examples/swervedriveposeestsim/build.gradle index 7610d2a2ac..424e9fa54a 100644 --- a/photonlib-java-examples/swervedriveposeestsim/build.gradle +++ b/photonlib-java-examples/swervedriveposeestsim/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id "edu.wpi.first.GradleRIO" version "2023.4.2" + id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-1" } sourceCompatibility = JavaVersion.VERSION_11 diff --git a/photonlib-java-examples/swervedriveposeestsim/gradle/wrapper/gradle-wrapper.properties b/photonlib-java-examples/swervedriveposeestsim/gradle/wrapper/gradle-wrapper.properties index c23a1b3177..d3fb630184 100644 --- a/photonlib-java-examples/swervedriveposeestsim/gradle/wrapper/gradle-wrapper.properties +++ b/photonlib-java-examples/swervedriveposeestsim/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=permwrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services\.gradle\.org/distributions/gradle-8\.3-bin\.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=permwrapper/dists diff --git a/photonlib-java-examples/swervedriveposeestsim/gradlew b/photonlib-java-examples/swervedriveposeestsim/gradlew index a69d9cb6c2..0ef4c1e860 100644 --- a/photonlib-java-examples/swervedriveposeestsim/gradlew +++ b/photonlib-java-examples/swervedriveposeestsim/gradlew @@ -80,7 +80,8 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_NAME="Gradle" APP_BASE_NAME=${0##*/} diff --git a/photonlib-java-examples/swervedriveposeestsim/settings.gradle b/photonlib-java-examples/swervedriveposeestsim/settings.gradle index 48c039ed86..091a37a027 100644 --- a/photonlib-java-examples/swervedriveposeestsim/settings.gradle +++ b/photonlib-java-examples/swervedriveposeestsim/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2023' + String frcYear = '2024' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') diff --git a/photonlib-java-examples/swervedriveposeestsim/src/main/java/frc/robot/Constants.java b/photonlib-java-examples/swervedriveposeestsim/src/main/java/frc/robot/Constants.java index 708d87a5b0..1cea631e93 100644 --- a/photonlib-java-examples/swervedriveposeestsim/src/main/java/frc/robot/Constants.java +++ b/photonlib-java-examples/swervedriveposeestsim/src/main/java/frc/robot/Constants.java @@ -36,7 +36,6 @@ import edu.wpi.first.math.numbers.N1; import edu.wpi.first.math.numbers.N3; import edu.wpi.first.math.util.Units; -import java.io.IOException; public class Constants { public static class Vision { @@ -46,16 +45,8 @@ public static class Vision { new Transform3d(new Translation3d(0.5, 0.0, 0.5), new Rotation3d(0, 0, 0)); // The layout of the AprilTags on the field - public static final AprilTagFieldLayout kTagLayout; - - static { - try { - kTagLayout = AprilTagFields.kDefaultField.loadAprilTagLayoutField(); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } + public static final AprilTagFieldLayout kTagLayout = + AprilTagFields.kDefaultField.loadAprilTagLayoutField(); // The standard deviations of our vision estimated poses, which affect correction rate // (Fake values. Experiment and determine estimation noise on an actual robot.) diff --git a/shared/common.gradle b/shared/common.gradle index 684cf24bf9..e8181507e3 100644 --- a/shared/common.gradle +++ b/shared/common.gradle @@ -36,10 +36,10 @@ dependencies { implementation wpilibTools.deps.wpilibJava("hal") implementation wpilibTools.deps.wpilibJava("wpilibj") - implementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-java:$opencvVersion" - implementation "edu.wpi.first.thirdparty.frc2023.opencv:opencv-jni:$opencvVersion:$jniPlatform" + implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-java:$openCVversion" + implementation "edu.wpi.first.thirdparty.frc2024.opencv:opencv-jni:$openCVversion:$jniPlatform" - implementation "org.ejml:ejml-simple:0.41" + implementation "org.ejml:ejml-simple:0.42" // test stuff testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") @@ -51,6 +51,7 @@ test { events "passed", "skipped", "failed", "standardOut", "standardError" } workingDir = new File("${rootDir}") + finalizedBy jacocoTestReport } task testHeadless(type: Test) { @@ -67,11 +68,19 @@ task generateJavaDocs(type: Javadoc) { destinationDir = file("${projectDir}/build/docs") } + +jacoco { + toolVersion = "0.8.9" + reportsDirectory = layout.buildDirectory.dir('customJacocoReportDir') +} + jacocoTestReport { -// dependsOn testHeadless // Tests are required to run before generating the report + dependsOn testHeadless reports { - xml.enabled true + xml.required = true + csv.required = false + html.outputLocation = layout.buildDirectory.dir('jacocoHtml') } afterEvaluate { diff --git a/shared/config.gradle b/shared/config.gradle index 104faaec04..3d5f91b150 100644 --- a/shared/config.gradle +++ b/shared/config.gradle @@ -6,11 +6,14 @@ nativeUtils.withCrossLinuxArm64() // Configure WPI dependencies. nativeUtils.wpi.configureDependencies { + opencvYear = 'frc2024' + googleTestYear = "frc2023" + wpiVersion = wpilibVersion wpimathVersion = wpilibVersion - niLibVersion = "2023.3.0" - opencvVersion = "4.6.0-4" - googleTestVersion = "1.12.1-1" + niLibVersion = "2024.1.1" + opencvVersion = openCVversion + googleTestVersion = "1.12.1-2" imguiVersion = "1.86-1" } @@ -91,7 +94,7 @@ ext.createComponentZipTasks = { components, names, base, type, project, func -> def task = project.tasks.create(base + "-${key}", type) { description = 'Creates component archive for platform ' + key destinationDirectory = outputsFolder - classifier = key + archiveClassifier = key archiveBaseName = '_M_' + base duplicatesStrategy = 'exclude' @@ -125,7 +128,7 @@ ext.createAllCombined = { list, name, base, type, project -> list.each { if (it.name.endsWith('debug')) return - from project.zipTree(it.archivePath) + from project.zipTree(it.archiveFile) dependsOn it } } From 760de0ff86081a5a6e4a505ea485f32c185d5902 Mon Sep 17 00:00:00 2001 From: Sriman Achanta <68172138+srimanachanta@users.noreply.github.com> Date: Sun, 15 Oct 2023 12:31:23 -0400 Subject: [PATCH 2/5] [photon-core] [NFC] Code Cleanup, spelling, and grammar (#945) --- .../common/configuration/ConfigManager.java | 14 ++++---- .../configuration/LegacyConfigProvider.java | 5 ++- .../common/configuration/NetworkConfig.java | 4 +-- .../configuration/PhotonConfiguration.java | 8 ++--- .../configuration/SqlConfigProvider.java | 13 +++----- .../common/dataflow/DataChangeSubscriber.java | 1 - .../networktables/NTDataPublisher.java | 15 ++++----- .../networktables/NetworkTablesManager.java | 2 +- .../common/hardware/GPIO/pi/PigpioPin.java | 2 -- .../common/hardware/GPIO/pi/PigpioSocket.java | 3 +- .../common/hardware/HardwareManager.java | 4 +-- .../common/hardware/PiVersion.java | 2 +- .../common/hardware/Platform.java | 1 - .../common/hardware/VisionLED.java | 8 ++--- .../hardware/metrics/MetricsManager.java | 6 ++-- .../common/hardware/metrics/cmds/CmdBase.java | 2 +- .../photonvision/common/logging/Logger.java | 14 +++----- .../common/networking/NetworkManager.java | 8 ++--- .../common/networking/NetworkUtils.java | 12 +++---- .../common/networking/RoborioFinder.java | 2 +- .../photonvision/common/util/ShellExec.java | 11 ++++--- .../photonvision/common/util/TestUtils.java | 4 +-- .../common/util/TimedTaskManager.java | 4 +-- .../common/util/math/MathUtils.java | 10 ++---- .../common/util/numbers/NumberCouple.java | 6 +--- .../org/photonvision/raspi/LibCameraJNI.java | 18 +++++----- .../vision/apriltag/AprilTagFamily.java | 2 +- .../vision/aruco/ArucoDetectorParams.java | 4 +-- .../vision/aruco/PhotonArucoDetector.java | 2 +- .../vision/camera/FileVisionSource.java | 2 +- .../vision/camera/LibcameraGpuSettables.java | 2 +- .../vision/camera/QuirkyCamera.java | 2 +- .../vision/camera/USBCameraSource.java | 22 ++++++------- .../vision/frame/FrameProvider.java | 10 +++--- .../frame/consumer/FileSaveFrameConsumer.java | 6 ++-- .../frame/consumer/MJPGFrameConsumer.java | 1 - .../frame/provider/CpuImageProcessor.java | 3 +- .../frame/provider/FileFrameProvider.java | 2 +- .../photonvision/vision/opencv/CVShape.java | 2 +- .../photonvision/vision/opencv/Contour.java | 8 +---- .../vision/opencv/ContourSortMode.java | 2 +- .../impl/AprilTagDetectionPipeParams.java | 5 ++- .../pipe/impl/AprilTagPoseEstimatorPipe.java | 5 ++- .../vision/pipe/impl/CalculateFPSPipe.java | 2 +- .../vision/pipe/impl/Calibrate3dPipe.java | 24 +++++++------- .../vision/pipe/impl/CornerDetectionPipe.java | 25 ++++---------- .../vision/pipe/impl/Draw2dCrosshairPipe.java | 4 +-- .../vision/pipe/impl/Draw3dTargetsPipe.java | 16 ++++----- .../pipe/impl/FindBoardCornersPipe.java | 15 ++++----- .../vision/pipe/impl/FindCirclesPipe.java | 4 +-- .../vision/pipe/impl/FindPolygonPipe.java | 1 - .../pipe/impl/GPUAcceleratedHSVPipe.java | 33 +++++++++++++------ .../vision/pipe/impl/HSVPipe.java | 1 - .../vision/pipe/impl/SortContoursPipe.java | 2 +- .../vision/pipe/impl/SpeckleRejectPipe.java | 2 +- .../vision/pipeline/AprilTagPipeline.java | 6 ++-- .../vision/pipeline/ArucoPipeline.java | 1 - .../vision/pipeline/CVPipelineSettings.java | 2 +- .../vision/pipeline/Calibrate3dPipeline.java | 4 +-- .../vision/pipeline/ColoredShapePipeline.java | 6 ++-- .../vision/pipeline/OutputStreamPipeline.java | 2 +- .../vision/pipeline/ReflectivePipeline.java | 1 - .../vision/processes/PipelineManager.java | 27 ++++++++------- .../vision/processes/VisionModule.java | 7 ++-- .../vision/processes/VisionModuleManager.java | 15 ++++----- .../vision/processes/VisionSourceManager.java | 5 ++- .../vision/target/TargetCalculations.java | 1 - .../vision/target/TargetModel.java | 4 +-- .../vision/target/TrackedTarget.java | 29 +++++++--------- .../vision/videoStream/SocketVideoStream.java | 7 ++-- .../videoStream/SocketVideoStreamManager.java | 13 ++++---- .../common/configuration/ConfigTest.java | 6 ++-- .../common/configuration/SQLConfigTest.java | 3 +- .../common/util/LogFileManagementTest.java | 8 ++--- .../photonvision/hardware/HardwareTest.java | 3 +- .../vision/pipeline/AprilTagTest.java | 4 +-- .../vision/pipeline/ArucoPipelineTest.java | 2 +- .../vision/pipeline/Calibrate3dPipeTest.java | 5 +-- .../vision/pipeline/CirclePNPTest.java | 3 +- .../processes/VisionModuleManagerTest.java | 5 +-- .../vision/target/TargetCalculationsTest.java | 9 +++-- 81 files changed, 261 insertions(+), 305 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/ConfigManager.java b/photon-core/src/main/java/org/photonvision/common/configuration/ConfigManager.java index f3cb9c1eb1..48a6e011bf 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/ConfigManager.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/ConfigManager.java @@ -28,7 +28,8 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; -import java.util.*; +import java.util.Date; +import java.util.List; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.file.FileUtils; @@ -47,13 +48,13 @@ public class ConfigManager { private final ConfigProvider m_provider; - private Thread settingsSaveThread; + private final Thread settingsSaveThread; private long saveRequestTimestamp = -1; enum ConfigSaveStrategy { SQL, LEGACY, - ATOMIC_ZIP; + ATOMIC_ZIP } // This logic decides which kind of ConfigManager we load as the default. If we want @@ -115,9 +116,8 @@ private void translateLegacyIfPresent(Path folderPath) { e1.printStackTrace(); } - // So we can't save the old config, and we couldn't copy the folder - // But we've loaded the config. So just try to delete the directory so we don't try to load - // form it next time. That does mean we have no backup recourse, tho + // Delete the directory because we were successfully able to load the config but were unable + // to save or copy the folder. if (maybeCams.exists()) FileUtils.deleteDirectory(maybeCams.toPath()); } @@ -225,7 +225,7 @@ public String taToLogFname(TemporalAccessor date) { } public Date logFnameToDate(String fname) throws ParseException { - // Strip away known unneded portions of the log file name + // Strip away known unneeded portions of the log file name fname = fname.replace(LOG_PREFIX, "").replace(LOG_EXT, ""); DateFormat format = new SimpleDateFormat(LOG_DATE_TIME_FORMAT); return format.parse(fname); diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/LegacyConfigProvider.java b/photon-core/src/main/java/org/photonvision/common/configuration/LegacyConfigProvider.java index 7051ab7ef9..6dca0ddcb2 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/LegacyConfigProvider.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/LegacyConfigProvider.java @@ -55,7 +55,7 @@ class LegacyConfigProvider extends ConfigProvider { final File configDirectoryFile; private long saveRequestTimestamp = -1; - private Thread settingsSaveThread; + private final Thread settingsSaveThread; public static void saveUploadedSettingsZip(File uploadPath) { var folderPath = Path.of(System.getProperty("java.io.tmpdir"), "photonvision").toFile(); @@ -67,7 +67,6 @@ public static void saveUploadedSettingsZip(File uploadPath) { logger.info("Copied settings successfully!"); } catch (IOException e) { logger.error("Exception copying uploaded settings!", e); - return; } } @@ -371,7 +370,7 @@ public String taToLogFname(TemporalAccessor date) { } public Date logFnameToDate(String fname) throws ParseException { - // Strip away known unneded portions of the log file name + // Strip away known unneeded portions of the log file name fname = fname.replace(LOG_PREFIX, "").replace(LOG_EXT, ""); DateFormat format = new SimpleDateFormat(LOG_DATE_TIME_FORMAT); return format.parse(fname); diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/NetworkConfig.java b/photon-core/src/main/java/org/photonvision/common/configuration/NetworkConfig.java index a623120711..36e1efe8e3 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/NetworkConfig.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/NetworkConfig.java @@ -30,7 +30,7 @@ import org.photonvision.common.util.file.JacksonUtils; public class NetworkConfig { - // Can be a integer team number, or a IP address + // Can be an integer team number, or an IP address public String ntServerAddress = "0"; public NetworkMode connectionType = NetworkMode.DHCP; public String staticIp = ""; @@ -58,7 +58,7 @@ public NetworkConfig() { .orElse("Wired connection 1"); } - // We can (usually) manage networking on Linux devices, and if we can we should try to. Command + // We can (usually) manage networking on Linux devices, and if we can, we should try to. Command // line inhibitions happen at a level above this class setShouldManage(deviceCanManageNetwork()); } diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java index f0cc2bfa1a..262cdd7111 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java @@ -33,10 +33,10 @@ // TODO rename this class public class PhotonConfiguration { - private HardwareConfig hardwareConfig; - private HardwareSettings hardwareSettings; + private final HardwareConfig hardwareConfig; + private final HardwareSettings hardwareSettings; private NetworkConfig networkConfig; - private HashMap cameraConfigurations; + private final HashMap cameraConfigurations; public PhotonConfiguration( HardwareConfig hardwareConfig, @@ -113,7 +113,7 @@ public Map toHashMap() { var lightingConfig = new UILightingConfig(); lightingConfig.brightness = hardwareSettings.ledBrightnessPercentage; - lightingConfig.supported = (hardwareConfig.ledPins.size() != 0); + lightingConfig.supported = !hardwareConfig.ledPins.isEmpty(); settingsSubmap.put("lighting", SerializationUtils.objectToHashMap(lightingConfig)); var generalSubmap = new HashMap(); diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/SqlConfigProvider.java b/photon-core/src/main/java/org/photonvision/common/configuration/SqlConfigProvider.java index e9957eb161..df63b27588 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/SqlConfigProvider.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/SqlConfigProvider.java @@ -21,11 +21,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -255,8 +251,7 @@ private String getOneConfigFile(Connection conn, String filename) { var result = query.executeQuery(); while (result.next()) { - var contents = result.getString("contents"); - return contents; + return result.getString("contents"); } } catch (SQLException e) { logger.error("SQL Err getting file " + filename, e); @@ -286,7 +281,7 @@ private void saveCameras(Connection conn) { statement.setString(2, JacksonUtils.serializeToString(config)); statement.setString(3, JacksonUtils.serializeToString(config.driveModeSettings)); - // Serializing a list of abstract classes sucks. Instead, make it into a array + // Serializing a list of abstract classes sucks. Instead, make it into an array // of strings, which we can later unpack back into individual settings List settings = config.pipelineSettings.stream() @@ -424,7 +419,7 @@ public boolean saveUploadedNetworkConfig(Path uploadPath) { private HashMap loadCameraConfigs(Connection conn) { HashMap loadedConfigurations = new HashMap<>(); - // Querry every single row of the cameras db + // Query every single row of the cameras db PreparedStatement query = null; try { query = diff --git a/photon-core/src/main/java/org/photonvision/common/dataflow/DataChangeSubscriber.java b/photon-core/src/main/java/org/photonvision/common/dataflow/DataChangeSubscriber.java index 9ebbd61052..92a06fdb67 100644 --- a/photon-core/src/main/java/org/photonvision/common/dataflow/DataChangeSubscriber.java +++ b/photon-core/src/main/java/org/photonvision/common/dataflow/DataChangeSubscriber.java @@ -21,7 +21,6 @@ import java.util.Objects; import org.photonvision.common.dataflow.events.DataChangeEvent; -@SuppressWarnings("rawtypes") public abstract class DataChangeSubscriber { public final List wantedSources; public final List wantedDestinations; diff --git a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NTDataPublisher.java b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NTDataPublisher.java index 0f7c3caa64..56bae44bd7 100644 --- a/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NTDataPublisher.java +++ b/photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NTDataPublisher.java @@ -41,7 +41,7 @@ public class NTDataPublisher implements CVPipelineResultConsumer { private final NetworkTable rootTable = NetworkTablesManager.getInstance().kRootTable; - private NTTopicSet ts = new NTTopicSet(); + private final NTTopicSet ts = new NTTopicSet(); NTDataChangeListener pipelineIndexListener; private final Supplier pipelineIndexSupplier; @@ -181,15 +181,12 @@ public void accept(CVPipelineResult result) { } // Something in the result can sometimes be null -- so check probably too many things - if (result != null - && result.inputAndOutputFrame != null + if (result.inputAndOutputFrame != null && result.inputAndOutputFrame.frameStaticProperties != null && result.inputAndOutputFrame.frameStaticProperties.cameraCalibration != null) { var fsp = result.inputAndOutputFrame.frameStaticProperties; - if (fsp.cameraCalibration != null) { - ts.cameraIntrinsicsPublisher.accept(fsp.cameraCalibration.getIntrinsicsArr()); - ts.cameraDistortionPublisher.accept(fsp.cameraCalibration.getExtrinsicsArr()); - } + ts.cameraIntrinsicsPublisher.accept(fsp.cameraCalibration.getIntrinsicsArr()); + ts.cameraDistortionPublisher.accept(fsp.cameraCalibration.getExtrinsicsArr()); } else { ts.cameraIntrinsicsPublisher.accept(new double[] {}); ts.cameraDistortionPublisher.accept(new double[] {}); @@ -215,8 +212,8 @@ public static List simpleFromTrackedTargets(List pulses) throws PigpioException // ## extension ## // III on/off/delay * pulses - if (pulses == null || pulses.size() == 0) return 0; + if (pulses == null || pulses.isEmpty()) return 0; try { ByteBuffer bb = ByteBuffer.allocate(pulses.size() * 12); diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java index c640730a7d..008724e9b7 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/HardwareManager.java @@ -48,9 +48,9 @@ public class HardwareManager { private final StatusLED statusLED; @SuppressWarnings("FieldCanBeLocal") - private IntegerSubscriber ledModeRequest; + private final IntegerSubscriber ledModeRequest; - private IntegerPublisher ledModeState; + private final IntegerPublisher ledModeState; @SuppressWarnings({"FieldCanBeLocal", "unused"}) private final NTDataChangeListener ledModeListener; diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/PiVersion.java b/photon-core/src/main/java/org/photonvision/common/hardware/PiVersion.java index f0817f70ce..dbf23ac7e4 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/PiVersion.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/PiVersion.java @@ -34,7 +34,7 @@ public enum PiVersion { private static final ShellExec shell = new ShellExec(true, false); private static final PiVersion currentPiVersion = calcPiVersion(); - private PiVersion(String s) { + PiVersion(String s) { this.identifier = s.toLowerCase(); } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/Platform.java b/photon-core/src/main/java/org/photonvision/common/hardware/Platform.java index 5644b53e0c..1c500ab722 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/Platform.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/Platform.java @@ -17,7 +17,6 @@ package org.photonvision.common.hardware; -import com.jogamp.common.os.Platform.OSType; import edu.wpi.first.util.RuntimeDetector; import java.io.BufferedReader; import java.io.IOException; diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java b/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java index 96d9937504..7d1d5273b3 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/VisionLED.java @@ -46,7 +46,7 @@ public class VisionLED { private int mappedBrightnessPercentage; - private Consumer modeConsumer; + private final Consumer modeConsumer; public VisionLED( List ledPins, @@ -179,11 +179,7 @@ private void setInternal(VisionLEDMode newLedMode, boolean fromNT) { } currentLedMode = newLedMode; logger.info( - "Changing LED mode from \"" - + lastLedMode.toString() - + "\" to \"" - + newLedMode.toString() - + "\""); + "Changing LED mode from \"" + lastLedMode.toString() + "\" to \"" + newLedMode + "\""); } else { if (currentLedMode == VisionLEDMode.kDefault) { switch (newLedMode) { diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java index f0320910e2..d38b527187 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java @@ -37,7 +37,7 @@ public class MetricsManager { CmdBase cmds; - private ShellExec runCommand = new ShellExec(true, true); + private final ShellExec runCommand = new ShellExec(true, true); public void setConfig(HardwareConfig config) { if (config.hasCommandsConfigured()) { @@ -153,8 +153,8 @@ public synchronized String execute(String command) { + "\nExit code: " + runCommand.getExitCode() + "\n Exception: " - + e.toString() - + sw.toString()); + + e + + sw); return ""; } } diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java index 298ca7dd5d..69473ad9e1 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/CmdBase.java @@ -35,6 +35,6 @@ public class CmdBase { public String diskUsageCommand = ""; public void initCmds(HardwareConfig config) { - return; // default - do nothing + // default - do nothing } } diff --git a/photon-core/src/main/java/org/photonvision/common/logging/Logger.java b/photon-core/src/main/java/org/photonvision/common/logging/Logger.java index 5972e3c750..b06fea13fa 100644 --- a/photon-core/src/main/java/org/photonvision/common/logging/Logger.java +++ b/photon-core/src/main/java/org/photonvision/common/logging/Logger.java @@ -21,12 +21,7 @@ import java.nio.file.Path; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import java.util.function.Supplier; import org.apache.commons.lang3.tuple.Pair; import org.photonvision.common.configuration.ConfigManager; @@ -54,7 +49,7 @@ public class Logger { private static final List> uiBacklog = new ArrayList<>(); private static boolean connected = false; - private static UILogAppender uiLogAppender = new UILogAppender(); + private static final UILogAppender uiLogAppender = new UILogAppender(); private final String className; private final LogGroup group; @@ -133,7 +128,7 @@ public static void cleanLogs(Path folderToClean) { HashMap logFileStartDateMap = new HashMap<>(); // Remove any files from the list for which we can't parse a start date from their name. - // Simultaneously populate our HashMap with Date objects repeseting the file-name + // Simultaneously populate our HashMap with Date objects representing the file-name // indicated log start time. logFileList.removeIf( (File arg0) -> { @@ -160,7 +155,6 @@ public static void cleanLogs(Path folderToClean) { if (logCounter < MAX_LOGS_TO_KEEP) { // Skip over the first MAX_LOGS_TO_KEEP files logCounter++; - continue; } else { // Delete this file. file.delete(); @@ -332,7 +326,7 @@ public FileLogAppender(Path logFilePath) { 3000L); } catch (FileNotFoundException e) { out = null; - System.err.println("Unable to log to file " + logFilePath.toString()); + System.err.println("Unable to log to file " + logFilePath); } } diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java index 6b6a13b031..22543c4bb3 100644 --- a/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java +++ b/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java @@ -54,7 +54,7 @@ public void initialize(boolean shouldManage) { } // always set hostname - if (config.hostname.length() > 0) { + if (!config.hostname.isEmpty()) { try { var shell = new ShellExec(true, false); shell.executeBashCommand("cat /etc/hostname | tr -d \" \\t\\n\\r\""); @@ -107,7 +107,7 @@ public void initialize(boolean shouldManage) { } } else if (config.connectionType == NetworkMode.STATIC) { var shell = new ShellExec(); - if (config.staticIp.length() > 0) { + if (!config.staticIp.isEmpty()) { try { shell.executeBashCommand( config @@ -116,7 +116,7 @@ public void initialize(boolean shouldManage) { .replace(NetworkConfig.NM_IP_STRING, config.staticIp)); if (Platform.isRaspberryPi()) { - // Pi's need to manually have their interface adjusted?? and the 5 second sleep is + // Pi's need to manually have their interface adjusted?? and the 5-second sleep is // integral in my testing (Matt) shell.executeBashCommand( "sh -c 'nmcli con down " @@ -125,7 +125,7 @@ public void initialize(boolean shouldManage) { + config.getEscapedInterfaceName() + "'"); } else { - // for now just bring down /up -- more testing needed on beelink et al + // for now just bring down /up -- more testing needed on beelink et al. shell.executeBashCommand( "sh -c 'nmcli con down " + config.getEscapedInterfaceName() diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java index 2bcf0072a0..cf84309e57 100644 --- a/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java +++ b/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java @@ -31,12 +31,12 @@ public class NetworkUtils { private static final Logger logger = new Logger(NetworkUtils.class, LogGroup.General); - public static enum NMType { + public enum NMType { NMTYPE_ETHERNET("ethernet"), NMTYPE_WIFI("wifi"), NMTYPE_UNKNOWN(""); - private NMType(String id) { + NMType(String id) { identifier = id; } @@ -59,7 +59,7 @@ public NMDeviceInfo(String c, String d, String type) { nmType = NMType.typeForString(type); } - public final String connName; // Human readable name used by "nmcli con" + public final String connName; // Human-readable name used by "nmcli con" public final String devName; // underlying device, used by dhclient public final NMType nmType; @@ -108,15 +108,15 @@ public static List getAllInterfaces() { logger.error("Could not get active NM ifaces!", e); } - logger.debug("Found network interfaces:\n" + ret.toString()); + logger.debug("Found network interfaces:\n" + ret); allInterfaces = ret; return ret; } public static List getAllActiveInterfaces() { - // Seems like if a interface exists but isn't actually connected, the connection name will be an - // empty string. Check here and only return connections with non-empty names + // Seems like if an interface exists but isn't actually connected, the connection name will be + // an empty string. Check here and only return connections with non-empty names return getAllInterfaces().stream() .filter(it -> !it.connName.trim().isEmpty()) .collect(Collectors.toList()); diff --git a/photon-core/src/main/java/org/photonvision/common/networking/RoborioFinder.java b/photon-core/src/main/java/org/photonvision/common/networking/RoborioFinder.java index f94beb6123..e6b42112fc 100644 --- a/photon-core/src/main/java/org/photonvision/common/networking/RoborioFinder.java +++ b/photon-core/src/main/java/org/photonvision/common/networking/RoborioFinder.java @@ -42,7 +42,7 @@ public static RoborioFinder getInstance() { public void findRios() { HashMap map = new HashMap<>(); var subMap = new HashMap(); - // Seperate from the above so we don't hold stuff up + // Separate from the above so we don't hold stuff up System.setProperty("java.net.preferIPv4Stack", "true"); subMap.put( "deviceips", diff --git a/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java b/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java index 159be868ea..c8433ec158 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java +++ b/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java @@ -22,12 +22,13 @@ import org.photonvision.common.logging.Logger; /** Execute external process and optionally read output buffer. */ -@SuppressWarnings({"unused", "ConstantConditions"}) +@SuppressWarnings({"unused"}) public class ShellExec { private static final Logger logger = new Logger(ShellExec.class, LogGroup.General); private int exitCode; - private boolean readOutput, readError; + private final boolean readOutput; + private final boolean readError; private StreamGobbler errorGobbler, outputGobbler; public ShellExec() { @@ -55,7 +56,7 @@ public int executeBashCommand(String command, boolean wait) throws IOException { boolean success = false; Runtime r = Runtime.getRuntime(); - // Use bash -c so we can handle things like multi commands separated by ; and + // Use bash -c, so we can handle things like multi commands separated by ; and // things like quotes, $, |, and \. My tests show that command comes as // one argument to bash, so we do not need to quote it to make it one thing. // Also, exec may object if it does not have an executable file as the first thing, @@ -160,8 +161,8 @@ public String getError() { */ @SuppressWarnings("WeakerAccess") private static class StreamGobbler extends Thread { - private InputStream is; - private StringBuilder output; + private final InputStream is; + private final StringBuilder output; private volatile boolean completed; // mark volatile to guarantee a thread safety public StreamGobbler(InputStream is, boolean readStream) { diff --git a/photon-core/src/main/java/org/photonvision/common/util/TestUtils.java b/photon-core/src/main/java/org/photonvision/common/util/TestUtils.java index f547ba71ca..d109f97834 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/TestUtils.java +++ b/photon-core/src/main/java/org/photonvision/common/util/TestUtils.java @@ -245,7 +245,7 @@ Path getPath() { } public static Path getResourcesFolderPath(boolean testMode) { - System.out.println("CWD: " + Path.of("").toAbsolutePath().toString()); + System.out.println("CWD: " + Path.of("").toAbsolutePath()); // VSCode likes to make this path relative to the wrong root directory, so a fun hack to tell // if it's wrong @@ -362,7 +362,7 @@ public static CameraCalibrationCoefficients getLaptop() { return getCoeffs("laptop.json", true); } - private static int DefaultTimeoutMillis = 5000; + private static final int DefaultTimeoutMillis = 5000; public static void showImage(Mat frame, String title, int timeoutMs) { if (frame.empty()) return; diff --git a/photon-core/src/main/java/org/photonvision/common/util/TimedTaskManager.java b/photon-core/src/main/java/org/photonvision/common/util/TimedTaskManager.java index 4b613bdeb5..7107faae32 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/TimedTaskManager.java +++ b/photon-core/src/main/java/org/photonvision/common/util/TimedTaskManager.java @@ -40,9 +40,7 @@ private static class CaughtThreadFactory implements ThreadFactory { public Thread newThread(@NotNull Runnable r) { Thread thread = defaultThreadFactory.newThread(r); thread.setUncaughtExceptionHandler( - (t, e) -> { - logger.error("TimedTask threw uncaught exception!", e); - }); + (t, e) -> logger.error("TimedTask threw uncaught exception!", e)); return thread; } } diff --git a/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java b/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java index 01bff3237c..526311afc5 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java +++ b/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java @@ -20,11 +20,7 @@ import edu.wpi.first.math.MatBuilder; import edu.wpi.first.math.Nat; import edu.wpi.first.math.VecBuilder; -import edu.wpi.first.math.geometry.CoordinateSystem; -import edu.wpi.first.math.geometry.Pose3d; -import edu.wpi.first.math.geometry.Quaternion; -import edu.wpi.first.math.geometry.Rotation3d; -import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.geometry.*; import edu.wpi.first.math.util.Units; import edu.wpi.first.util.WPIUtilJNI; import java.util.Arrays; @@ -92,7 +88,7 @@ public static double getPercentile(List list, double p) { throw new IllegalArgumentException("invalid quantile value: " + p); } - if (list.size() == 0) { + if (list.isEmpty()) { return Double.NaN; } if (list.size() == 1) { @@ -206,7 +202,7 @@ public static Pose3d convertOpenCVtoPhotonPose(Transform3d cameraToTarget3d) { new Rotation3d(VecBuilder.fill(1, 0, 0), Units.degreesToRadians(180)); /** - * Apply a 180 degree rotation about X to the rotation component of a given Apriltag pose. This + * Apply a 180-degree rotation about X to the rotation component of a given Apriltag pose. This * aligns it with the OpenCV poses we use in other places. */ public static Transform3d convertApriltagtoOpenCV(Transform3d pose) { diff --git a/photon-core/src/main/java/org/photonvision/common/util/numbers/NumberCouple.java b/photon-core/src/main/java/org/photonvision/common/util/numbers/NumberCouple.java index e555e361f9..9a6047c797 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/numbers/NumberCouple.java +++ b/photon-core/src/main/java/org/photonvision/common/util/numbers/NumberCouple.java @@ -60,11 +60,7 @@ public boolean equals(Object obj) { return false; } - if (!couple.second.equals(second)) { - return false; - } - - return true; + return couple.second.equals(second); } @JsonIgnore diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java index 4ebc2c8735..a75ae2e579 100644 --- a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java +++ b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java @@ -28,7 +28,7 @@ public class LibCameraJNI { private static boolean libraryLoaded = false; - private static Logger logger = new Logger(LibCameraJNI.class, LogGroup.Camera); + private static final Logger logger = new Logger(LibCameraJNI.class, LogGroup.Camera); public static final Object CAMERA_LOCK = new Object(); @@ -120,7 +120,7 @@ public static boolean isSupported() { /** * Starts the camera thresholder and display threads running. Make sure that this function is - * called syncronously with stopCamera and returnFrame! + * called synchronously with stopCamera and returnFrame! */ public static native boolean startCamera(); @@ -141,7 +141,7 @@ public static native boolean setThresholds( // Exposure time, in microseconds public static native boolean setExposure(int exposureUs); - // Set brighness on [-1, 1] + // Set brightness on [-1, 1] public static native boolean setBrightness(double brightness); // Unknown ranges for red and blue AWB gain @@ -164,18 +164,18 @@ public static native boolean setThresholds( // Analog gain multiplier to apply to all color channels, on [1, Big Number] public static native boolean setAnalogGain(double analog); - /** Block until a new frame is avaliable from native code. */ + /** Block until a new frame is available from native code. */ public static native boolean awaitNewFrame(); /** - * Get a pointer to the most recent color mat generated. Call this immediatly after awaitNewFrame, - * and call onlly once per new frame! + * Get a pointer to the most recent color mat generated. Call this immediately after + * awaitNewFrame, and call only once per new frame! */ public static native long takeColorFrame(); /** - * Get a pointer to the most recent processed mat generated. Call this immediatly after - * awaitNewFrame, and call onlly once per new frame! + * Get a pointer to the most recent processed mat generated. Call this immediately after + * awaitNewFrame, and call only once per new frame! */ public static native long takeProcessedFrame(); @@ -186,6 +186,6 @@ public static native boolean setThresholds( public static native int getGpuProcessType(); - // /** Release a frame pointer back to the libcamera driver code to be filled again */ + // Release a frame pointer back to the libcamera driver code to be filled again */ // public static native long returnFrame(long frame); } diff --git a/photon-core/src/main/java/org/photonvision/vision/apriltag/AprilTagFamily.java b/photon-core/src/main/java/org/photonvision/vision/apriltag/AprilTagFamily.java index afbfc463a2..8fbda02683 100644 --- a/photon-core/src/main/java/org/photonvision/vision/apriltag/AprilTagFamily.java +++ b/photon-core/src/main/java/org/photonvision/vision/apriltag/AprilTagFamily.java @@ -28,7 +28,7 @@ public enum AprilTagFamily { kTagCustom48h11; public String getNativeName() { - // We wanna strip the leading kT and replace with "t" + // We want to strip the leading kT and replace with "t" return this.name().replaceFirst("kT", "t"); } } diff --git a/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java b/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java index f6af5d3047..24ea8dafab 100644 --- a/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java +++ b/photon-core/src/main/java/org/photonvision/vision/aruco/ArucoDetectorParams.java @@ -48,9 +48,9 @@ public void setDecimation(float decimate) { logger.info("Setting decimation from " + m_decimate + " to " + decimate); - // We only need to mutate the parameters -- the detector keeps a poitner to the parameters + // We only need to mutate the parameters -- the detector keeps a pointer to the parameters // object internally, so it should automatically update - parameters.set_aprilTagQuadDecimate((float) decimate); + parameters.set_aprilTagQuadDecimate(decimate); m_decimate = decimate; } diff --git a/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java b/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java index 0963a2812a..6bb10f7baa 100644 --- a/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java +++ b/photon-core/src/main/java/org/photonvision/vision/aruco/PhotonArucoDetector.java @@ -58,7 +58,7 @@ public PhotonArucoDetector() { ids = new Mat(); tvecs = new Mat(); rvecs = new Mat(); - corners = new ArrayList(); + corners = new ArrayList<>(); tagPose = new Pose3d(); translation = new Translation3d(); rotation = new Rotation3d(); diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/FileVisionSource.java b/photon-core/src/main/java/org/photonvision/vision/camera/FileVisionSource.java index e028dfe369..951135fcf4 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/FileVisionSource.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/FileVisionSource.java @@ -35,7 +35,7 @@ public class FileVisionSource extends VisionSource { public FileVisionSource(CameraConfiguration cameraConfiguration) { super(cameraConfiguration); var calibration = - cameraConfiguration.calibrations.size() > 0 + !cameraConfiguration.calibrations.isEmpty() ? cameraConfiguration.calibrations.get(0) : null; frameProvider = diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java index 22c76131aa..7e0bd5c5c7 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java @@ -132,7 +132,7 @@ public void setExposure(double exposure) { // If we set exposure too low, libcamera crashes or slows down // Very weird and smelly // For now, band-aid this by just not setting it lower than the "it breaks" limit - // Limit is different depending on camera. + // is different depending on camera. if (sensorModel == LibCameraJNI.SensorModel.OV9281) { if (exposure < 6.0) { exposure = 6.0; diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/QuirkyCamera.java b/photon-core/src/main/java/org/photonvision/vision/camera/QuirkyCamera.java index 780a12786e..da2dc835cb 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/QuirkyCamera.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/QuirkyCamera.java @@ -105,7 +105,7 @@ public static QuirkyCamera getQuirkyCamera(int usbVid, int usbPid) { public static QuirkyCamera getQuirkyCamera(int usbVid, int usbPid, String baseName) { for (var qc : quirkyCameras) { - boolean hasBaseName = !qc.baseName.equals(""); + boolean hasBaseName = !qc.baseName.isEmpty(); boolean matchesBaseName = qc.baseName.equals(baseName) || !hasBaseName; if (qc.usbVid == usbVid && qc.usbPid == usbPid && matchesBaseName) { return qc; diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java b/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java index cdad1ca24e..ad235da26a 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/USBCameraSource.java @@ -18,7 +18,10 @@ package org.photonvision.vision.camera; import edu.wpi.first.cameraserver.CameraServer; -import edu.wpi.first.cscore.*; +import edu.wpi.first.cscore.CvSink; +import edu.wpi.first.cscore.UsbCamera; +import edu.wpi.first.cscore.VideoException; +import edu.wpi.first.cscore.VideoMode; import java.util.*; import java.util.stream.Collectors; import org.photonvision.common.configuration.CameraConfiguration; @@ -120,8 +123,8 @@ public void setAutoExposure(boolean cameraAutoExposure) { if (!cameraAutoExposure) { // Pick a bunch of reasonable setting defaults for vision processing retroreflective camera.getProperty("auto_exposure_bias").set(0); - camera.getProperty("iso_sensitivity_auto").set(0); // Disable auto ISO adjustement - camera.getProperty("iso_sensitivity").set(0); // Manual ISO adjustement + camera.getProperty("iso_sensitivity_auto").set(0); // Disable auto ISO adjustment + camera.getProperty("iso_sensitivity").set(0); // Manual ISO adjustment camera.getProperty("white_balance_auto_preset").set(2); // Auto white-balance disabled camera.getProperty("auto_exposure").set(1); // auto exposure disabled } else { @@ -129,7 +132,7 @@ public void setAutoExposure(boolean cameraAutoExposure) { // nice-for-humans camera.getProperty("auto_exposure_bias").set(12); camera.getProperty("iso_sensitivity_auto").set(1); - camera.getProperty("iso_sensitivity").set(1); // Manual ISO adjustement by default + camera.getProperty("iso_sensitivity").set(1); // Manual ISO adjustment by default camera.getProperty("white_balance_auto_preset").set(1); // Auto white-balance enabled camera.getProperty("auto_exposure").set(0); // auto exposure enabled } @@ -179,15 +182,14 @@ public void setExposure(double exposure) { try { int scaledExposure = 1; if (cameraQuirks.hasQuirk(CameraQuirk.PiCam)) { - scaledExposure = - (int) Math.round(timeToPiCamRawExposure(pctToExposureTimeUs(exposure))); - logger.debug("Setting camera raw exposure to " + Integer.toString(scaledExposure)); + scaledExposure = Math.round(timeToPiCamRawExposure(pctToExposureTimeUs(exposure))); + logger.debug("Setting camera raw exposure to " + scaledExposure); camera.getProperty("raw_exposure_time_absolute").set(scaledExposure); camera.getProperty("raw_exposure_time_absolute").set(scaledExposure); } else { scaledExposure = (int) Math.round(exposure); - logger.debug("Setting camera exposure to " + Integer.toString(scaledExposure)); + logger.debug("Setting camera exposure to " + scaledExposure); camera.setExposureManual(scaledExposure); camera.setExposureManual(scaledExposure); } @@ -264,9 +266,7 @@ public HashMap getAllVideoModes() { } else { modes = camera.enumerateVideoModes(); } - for (int i = 0; i < modes.length; i++) { - var videoMode = modes[i]; - + for (VideoMode videoMode : modes) { // Filter grey modes if (videoMode.pixelFormat == VideoMode.PixelFormat.kGray || videoMode.pixelFormat == VideoMode.PixelFormat.kUnknown) { diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/FrameProvider.java b/photon-core/src/main/java/org/photonvision/vision/frame/FrameProvider.java index 3c405632de..70de9c5115 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/FrameProvider.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/FrameProvider.java @@ -24,15 +24,15 @@ public interface FrameProvider extends Supplier { String getName(); - /** Ask the camera to produce a certain kind of processed image (eg HSV or greyscale) */ - public void requestFrameThresholdType(FrameThresholdType type); + /** Ask the camera to produce a certain kind of processed image (e.g. HSV or greyscale) */ + void requestFrameThresholdType(FrameThresholdType type); /** Ask the camera to rotate frames it outputs */ - public void requestFrameRotation(ImageRotationMode rotationMode); + void requestFrameRotation(ImageRotationMode rotationMode); /** Ask the camera to provide either the input, output, or both frames. */ - public void requestFrameCopies(boolean copyInput, boolean copyOutput); + void requestFrameCopies(boolean copyInput, boolean copyOutput); /** Ask the camera to rotate frames it outputs */ - public void requestHsvSettings(HSVPipe.HSVParams params); + void requestHsvSettings(HSVPipe.HSVParams params); } diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java index e9773122a0..2552b5fbdb 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/FileSaveFrameConsumer.java @@ -33,8 +33,8 @@ public class FileSaveFrameConsumer implements Consumer { // Formatters to generate unique, timestamped file names - private static String FILE_PATH = ConfigManager.getInstance().getImageSavePath().toString(); - private static String FILE_EXTENSION = ".jpg"; + private static final String FILE_PATH = ConfigManager.getInstance().getImageSavePath().toString(); + private static final String FILE_EXTENSION = ".jpg"; DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); DateFormat tf = new SimpleDateFormat("hhmmssSS"); private final String NT_SUFFIX = "SaveImgCmd"; @@ -44,7 +44,7 @@ public class FileSaveFrameConsumer implements Consumer { private final Logger logger; private long imgSaveCountInternal = 0; private String camNickname; - private String fnamePrefix; + private final String fnamePrefix; private IntegerEntry entry; public FileSaveFrameConsumer(String camNickname, String streamPrefix) { diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/MJPGFrameConsumer.java b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/MJPGFrameConsumer.java index b070e372d5..06f9bf986b 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/consumer/MJPGFrameConsumer.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/consumer/MJPGFrameConsumer.java @@ -106,7 +106,6 @@ public class MJPGFrameConsumer { private CvSource cvSource; private MjpegServer mjpegServer; - @SuppressWarnings("FieldCanBeLocal") private VideoListener listener; private final NetworkTable table; diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/provider/CpuImageProcessor.java b/photon-core/src/main/java/org/photonvision/vision/frame/provider/CpuImageProcessor.java index f9f28c0a51..034ae6615e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/provider/CpuImageProcessor.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/provider/CpuImageProcessor.java @@ -30,7 +30,7 @@ import org.photonvision.vision.pipe.impl.RotateImagePipe; public abstract class CpuImageProcessor implements FrameProvider { - protected class CapturedFrame { + protected static class CapturedFrame { CVMat colorImage; FrameStaticProperties staticProps; long captureTimestamp; @@ -119,6 +119,5 @@ public void requestHsvSettings(HSVPipe.HSVParams params) { @Override public void requestFrameCopies(boolean copyInput, boolean copyOutput) { // We don't actually do zero-copy, so this method is a no-op - return; } } diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/provider/FileFrameProvider.java b/photon-core/src/main/java/org/photonvision/vision/frame/provider/FileFrameProvider.java index 8c7272ba21..315e4e3de3 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/provider/FileFrameProvider.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/provider/FileFrameProvider.java @@ -63,7 +63,7 @@ public FileFrameProvider(Path path, double fov, CameraCalibrationCoefficients ca public FileFrameProvider( Path path, double fov, int maxFPS, CameraCalibrationCoefficients calibration) { if (!Files.exists(path)) - throw new RuntimeException("Invalid path for image: " + path.toAbsolutePath().toString()); + throw new RuntimeException("Invalid path for image: " + path.toAbsolutePath()); this.path = path; this.millisDelay = 1000 / maxFPS; diff --git a/photon-core/src/main/java/org/photonvision/vision/opencv/CVShape.java b/photon-core/src/main/java/org/photonvision/vision/opencv/CVShape.java index 71f21cc1d4..99b8c2f107 100644 --- a/photon-core/src/main/java/org/photonvision/vision/opencv/CVShape.java +++ b/photon-core/src/main/java/org/photonvision/vision/opencv/CVShape.java @@ -32,7 +32,7 @@ public class CVShape implements Releasable { private MatOfPoint3f customTarget = null; - private MatOfPoint2f approxCurve = new MatOfPoint2f(); + private final MatOfPoint2f approxCurve = new MatOfPoint2f(); public CVShape(Contour contour, ContourShape shape) { this.contour = contour; diff --git a/photon-core/src/main/java/org/photonvision/vision/opencv/Contour.java b/photon-core/src/main/java/org/photonvision/vision/opencv/Contour.java index ea89c8b50a..181de0b3b3 100644 --- a/photon-core/src/main/java/org/photonvision/vision/opencv/Contour.java +++ b/photon-core/src/main/java/org/photonvision/vision/opencv/Contour.java @@ -21,13 +21,7 @@ import java.util.Collection; import java.util.Comparator; import org.jetbrains.annotations.Nullable; -import org.opencv.core.CvType; -import org.opencv.core.MatOfInt; -import org.opencv.core.MatOfPoint; -import org.opencv.core.MatOfPoint2f; -import org.opencv.core.Point; -import org.opencv.core.Rect; -import org.opencv.core.RotatedRect; +import org.opencv.core.*; import org.opencv.imgproc.Imgproc; import org.opencv.imgproc.Moments; import org.photonvision.common.util.math.MathUtils; diff --git a/photon-core/src/main/java/org/photonvision/vision/opencv/ContourSortMode.java b/photon-core/src/main/java/org/photonvision/vision/opencv/ContourSortMode.java index d4cf359e49..2ceff908ec 100644 --- a/photon-core/src/main/java/org/photonvision/vision/opencv/ContourSortMode.java +++ b/photon-core/src/main/java/org/photonvision/vision/opencv/ContourSortMode.java @@ -35,7 +35,7 @@ public enum ContourSortMode { (Math.pow(rect.getMinAreaRect().center.y, 2) + Math.pow(rect.getMinAreaRect().center.x, 2)))); - private Comparator m_comparator; + private final Comparator m_comparator; ContourSortMode(Comparator comparator) { m_comparator = comparator; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagDetectionPipeParams.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagDetectionPipeParams.java index f7c8ab2b20..ab52d7bcfb 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagDetectionPipeParams.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagDetectionPipeParams.java @@ -46,8 +46,7 @@ public boolean equals(Object obj) { 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; - return true; + return other.detectorParams == null; + } else return detectorParams.equals(other.detectorParams); } } diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagPoseEstimatorPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagPoseEstimatorPipe.java index 1dc118a3eb..9738202e3e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagPoseEstimatorPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/AprilTagPoseEstimatorPipe.java @@ -46,7 +46,7 @@ public AprilTagPoseEstimatorPipe() { @Override protected AprilTagPoseEstimate process(AprilTagDetection in) { // Save the corner points of our detection to an array - Point corners[] = new Point[4]; + Point[] corners = new Point[4]; for (int i = 0; i < 4; i++) { corners[i] = new Point(in.getCornerX(i), in.getCornerY(i)); } @@ -128,8 +128,7 @@ public boolean equals(Object obj) { if (config == null) { if (other.config != null) return false; } else if (!config.equals(other.config)) return false; - if (nIters != other.nIters) return false; - return true; + return nIters == other.nIters; } } } diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CalculateFPSPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CalculateFPSPipe.java index 7789c70b60..113da4d357 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CalculateFPSPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CalculateFPSPipe.java @@ -23,7 +23,7 @@ public class CalculateFPSPipe extends CVPipe { - private LinearFilter fpsFilter = LinearFilter.movingAverage(20); + private final LinearFilter fpsFilter = LinearFilter.movingAverage(20); StopWatch clock = new StopWatch(); @Override diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Calibrate3dPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Calibrate3dPipe.java index a454400bb9..bfca09d6fd 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Calibrate3dPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Calibrate3dPipe.java @@ -24,7 +24,9 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Triple; import org.opencv.calib3d.Calib3d; -import org.opencv.core.*; +import org.opencv.core.Mat; +import org.opencv.core.MatOfDouble; +import org.opencv.core.Size; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; @@ -38,24 +40,24 @@ public class Calibrate3dPipe Calibrate3dPipe.CalibratePipeParams> { // Camera matrix stores the center of the image and focal length across the x and y-axis in a 3x3 // matrix - private Mat cameraMatrix = new Mat(); + private final Mat cameraMatrix = new Mat(); // Stores the radical and tangential distortion in a 5x1 matrix - private MatOfDouble distortionCoefficients = new MatOfDouble(); + private final MatOfDouble distortionCoefficients = new MatOfDouble(); - // For loggging + // For logging private static final Logger logger = new Logger(Calibrate3dPipe.class, LogGroup.General); // Translational and rotational matrices - private List rvecs = new ArrayList<>(); - private List tvecs = new ArrayList<>(); + private final List rvecs = new ArrayList<>(); + private final List tvecs = new ArrayList<>(); // The Standard deviation of the estimated parameters - private Mat stdDeviationsIntrinsics = new Mat(); - private Mat stdDeviationsExtrinsics = new Mat(); + private final Mat stdDeviationsIntrinsics = new Mat(); + private final Mat stdDeviationsExtrinsics = new Mat(); // Contains the re projection error of each snapshot by re projecting the corners we found and - // finding the euclidean distance between the actual corners. - private Mat perViewErrors = new Mat(); + // finding the Euclidean distance between the actual corners. + private final Mat perViewErrors = new Mat(); // RMS of the calibration private double calibrationAccuracy; @@ -135,7 +137,7 @@ protected CameraCalibrationCoefficients process(List> in) } // Calculate standard deviation of the RMS error of the snapshots - private static double calculateSD(double numArray[]) { + private static double calculateSD(double[] numArray) { double sum = 0.0, standardDeviation = 0.0; int length = numArray.length; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java index ed3ae46e54..ff7f6fd3ea 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java @@ -18,10 +18,7 @@ package org.photonvision.vision.pipe.impl; import edu.wpi.first.math.geometry.Translation2d; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; +import java.util.*; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; import org.opencv.imgproc.Imgproc; @@ -42,18 +39,10 @@ protected List process(List targetList) { for (var target : targetList) { // detect corners. Might implement more algorithms later but // APPROX_POLY_DP_AND_EXTREME_CORNERS should be year agnostic - switch (params.cornerDetectionStrategy) { - case APPROX_POLY_DP_AND_EXTREME_CORNERS: - { - var targetCorners = - detectExtremeCornersByApproxPolyDp(target, params.calculateConvexHulls); - target.setTargetCorners(targetCorners); - break; - } - default: - { - break; - } + if (Objects.requireNonNull(params.cornerDetectionStrategy) + == DetectionStrategy.APPROX_POLY_DP_AND_EXTREME_CORNERS) { + var targetCorners = detectExtremeCornersByApproxPolyDp(target, params.calculateConvexHulls); + target.setTargetCorners(targetCorners); } } return targetList; @@ -133,7 +122,7 @@ private List detectExtremeCornersByApproxPolyDp(TrackedTarget target, boo we want a number between 0 and 0.16 out of a percentage from 0 to 100 so take accuracy and divide by 600 - Furthermore, we know that the contour is open if we haven't done convex hulls + Furthermore, we know that the contour is open if we haven't done convex hulls, and it has subcontours. */ var isOpen = !convexHull && target.hasSubContours(); @@ -158,7 +147,7 @@ private List detectExtremeCornersByApproxPolyDp(TrackedTarget target, boo var distanceToTrComparator = Comparator.comparingDouble((Point p) -> distanceBetween(p, boundingBoxCorners.get(3))); - // top left and top right are the poly corners closest to the bouding box tl and tr + // top left and top right are the poly corners closest to the bounding box tl and tr pointList.sort(distanceToTlComparator); var tl = pointList.get(0); pointList.remove(tl); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw2dCrosshairPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw2dCrosshairPipe.java index 913ad5dd22..53f5852e72 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw2dCrosshairPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw2dCrosshairPipe.java @@ -17,7 +17,7 @@ package org.photonvision.vision.pipe.impl; -import java.awt.Color; +import java.awt.*; import java.util.List; import org.apache.commons.lang3.tuple.Pair; import org.opencv.core.Mat; @@ -62,7 +62,7 @@ protected Void process(Pair> in) { } break; case Dual: - if (in.getRight().size() >= 1) { + if (!in.getRight().isEmpty()) { var target = in.getRight().get(0); if (target != null) { var area = target.getArea(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java index a3668f99de..b88bd0ba17 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java @@ -22,12 +22,8 @@ import java.util.List; import org.apache.commons.lang3.tuple.Pair; import org.opencv.calib3d.Calib3d; -import org.opencv.core.Mat; -import org.opencv.core.MatOfPoint; -import org.opencv.core.MatOfPoint2f; -import org.opencv.core.MatOfPoint3f; +import org.opencv.core.*; import org.opencv.core.Point; -import org.opencv.core.Point3; import org.opencv.imgproc.Imgproc; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; @@ -95,7 +91,7 @@ protected Void process(Pair> in) { jac); if (params.redistortPoints) { - // Distort the points so they match the image they're being overlaid on + // Distort the points, so they match the image they're being overlaid on distortPoints(tempMat, tempMat); } @@ -111,7 +107,7 @@ protected Void process(Pair> in) { jac); if (params.redistortPoints) { - // Distort the points so they match the image they're being overlaid on + // Distort the points, so they match the image they're being overlaid on distortPoints(tempMat, tempMat); } var topPoints = tempMat.toList(); @@ -119,7 +115,7 @@ protected Void process(Pair> in) { dividePointList(bottomPoints); dividePointList(topPoints); - // floor, then pillers, then top + // floor, then pillars, then top for (int i = 0; i < bottomPoints.size(); i++) { Imgproc.line( in.getLeft(), @@ -241,11 +237,11 @@ private void distortPoints(MatOfPoint2f src, MatOfPoint2f dst) { double r2 = x * x + y * y; // square of the radius from center - // Radial distorsion + // Radial distortion double xDistort = x * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); double yDistort = y * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2); - // Tangential distorsion + // Tangential distortion xDistort = xDistort + (2 * p1 * x * y + p2 * (r2 + 2 * x * x)); yDistort = yDistort + (p1 * (r2 + 2 * y * y) + 2 * p2 * x * y); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindBoardCornersPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindBoardCornersPipe.java index 9a5e38fac7..678566c707 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindBoardCornersPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindBoardCornersPipe.java @@ -39,8 +39,8 @@ public class FindBoardCornersPipe Size imageSize; Size patternSize; - // Configure the optimizations used while using openCV's find corners algorithm - // Since we return results in real-time, we want ensure it goes as fast as possible + // Configure the optimizations used while using OpenCV's find corners algorithm + // Since we return results in real-time, we want to ensure it goes as fast as possible // and fails as fast as possible. final int findChessboardFlags = Calib3d.CALIB_CB_NORMALIZE_IMAGE @@ -48,9 +48,9 @@ public class FindBoardCornersPipe | Calib3d.CALIB_CB_FILTER_QUADS | Calib3d.CALIB_CB_FAST_CHECK; - private MatOfPoint2f boardCorners = new MatOfPoint2f(); + private final MatOfPoint2f boardCorners = new MatOfPoint2f(); - // Intermedeate result mat's + // Intermediate result mat's Mat smallerInFrame = new Mat(); MatOfPoint2f smallerBoardCorners = new MatOfPoint2f(); @@ -213,7 +213,7 @@ private Size getWindowSize(MatOfPoint2f inPoints) { } /** - * Find chessboard corners given a input mat and output mat to draw on + * Find chessboard corners given an input mat and output mat to draw on * * @return Frame resolution, object points, board corners */ @@ -223,7 +223,7 @@ private Triple findBoardCorners(Pair in) { var inFrame = in.getLeft(); var outFrame = in.getRight(); - // Convert the inFrame to grayscale to increase contrast + // Convert the inFrame too grayscale to increase contrast Imgproc.cvtColor(inFrame, inFrame, Imgproc.COLOR_BGR2GRAY); boolean boardFound = false; @@ -328,8 +328,7 @@ public boolean equals(Object obj) { if (type != other.type) return false; if (Double.doubleToLongBits(gridSize) != Double.doubleToLongBits(other.gridSize)) return false; - if (divisor != other.divisor) return false; - return true; + return divisor == other.divisor; } } } diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindCirclesPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindCirclesPipe.java index 8113ce35dd..07ec9ecd82 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindCirclesPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindCirclesPipe.java @@ -70,7 +70,7 @@ protected List process(Pair> in) { 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 + // Great, we now found the center point of the circle, and it's radius, but we have no idea what // contour it corresponds to // Each contour can only match to one circle, so we keep a list of unmatched contours around and // only match against them @@ -121,7 +121,7 @@ public static class FindCirclePipeParams { * 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 shough normally be higher, such as 300 or normally exposed and contrasty images. + * 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. diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindPolygonPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindPolygonPipe.java index 01db9a3480..dfadfda53f 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindPolygonPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/FindPolygonPipe.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.List; -import org.opencv.core.*; import org.opencv.imgproc.Imgproc; import org.photonvision.vision.opencv.CVShape; import org.photonvision.vision.opencv.Contour; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/GPUAcceleratedHSVPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/GPUAcceleratedHSVPipe.java index 42e8a8567c..5b49faf641 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/GPUAcceleratedHSVPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/GPUAcceleratedHSVPipe.java @@ -76,7 +76,7 @@ public class GPUAcceleratedHSVPipe extends CVPipe { "", "void main() {", " vec2 uv = gl_FragCoord.xy/resolution;", - // Important! We do this .bgr swizzle because the image comes in as BGR but we pretend + // Important! We do this .bgr swizzle because the image comes in as BGR, but we pretend // it's RGB for convenience+speed " vec3 col = texture2D(texture0, uv).bgr;", // Only the first value in the vec4 gets used for GL_RED, and only the last value gets @@ -230,7 +230,7 @@ public GPUAcceleratedHSVPipe(PBOMode pboMode) { + "', version '" + gl.glGetString(GL.GL_VERSION) + "', and profile '" - + profile.toString() + + profile + "'"); var fmt = GLBuffers.newDirectIntBuffer(1); @@ -242,7 +242,7 @@ public GPUAcceleratedHSVPipe(PBOMode pboMode) { // index for the generic position input) gl.glBindAttribLocation(programId, 0, "position"); - // Compile and setup our two shaders with our program + // Compile and set up our two shaders with our program final int vertexId = createShader(gl, programId, k_vertexShader, GL_VERTEX_SHADER); final int fragmentId = createShader(gl, programId, k_fragmentShader, GL_FRAGMENT_SHADER); @@ -298,7 +298,10 @@ public GPUAcceleratedHSVPipe(PBOMode pboMode) { FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(k_vertexPositions); gl.glBindBuffer(GL_ARRAY_BUFFER, vertexVBOIds.get(0)); gl.glBufferData( - GL_ARRAY_BUFFER, vertexBuffer.capacity() * Float.BYTES, vertexBuffer, GL_STATIC_DRAW); + GL_ARRAY_BUFFER, + (long) vertexBuffer.capacity() * Float.BYTES, + vertexBuffer, + GL_STATIC_DRAW); // Set up pixel unpack buffer (a PBO to transfer image data to the GPU) if (pboMode != PBOMode.NONE) { @@ -389,12 +392,18 @@ protected Mat process(Mat in) { if (pboMode != PBOMode.NONE) { gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(0)); gl.glBufferData( - GLES3.GL_PIXEL_PACK_BUFFER, in.width() * in.height(), null, GLES3.GL_STREAM_READ); + GLES3.GL_PIXEL_PACK_BUFFER, + (long) in.width() * in.height(), + null, + GLES3.GL_STREAM_READ); if (pboMode == PBOMode.DOUBLE_BUFFERED) { gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(1)); gl.glBufferData( - GLES3.GL_PIXEL_PACK_BUFFER, in.width() * in.height(), null, GLES3.GL_STREAM_READ); + GLES3.GL_PIXEL_PACK_BUFFER, + (long) in.width() * in.height(), + null, + GLES3.GL_STREAM_READ); } } } @@ -459,14 +468,17 @@ protected Mat process(Mat in) { // GPU // This causes the previous data in the PBO to be discarded gl.glBufferData( - GLES3.GL_PIXEL_UNPACK_BUFFER, in.width() * in.height() * 3, null, GLES3.GL_STREAM_DRAW); + GLES3.GL_PIXEL_UNPACK_BUFFER, + (long) in.width() * in.height() * 3, + null, + GLES3.GL_STREAM_DRAW); - // Map the a buffer of GPU memory into a place that's accessible by us + // Map the buffer of GPU memory into a place that's accessible by us var buf = gl.glMapBufferRange( GLES3.GL_PIXEL_UNPACK_BUFFER, 0, - in.width() * in.height() * 3, + (long) in.width() * in.height() * 3, GLES3.GL_MAP_WRITE_BIT); buf.put(inputBytes); @@ -527,7 +539,8 @@ private Mat saveMatPBO(GLES3 gl, int width, int height, boolean doubleBuffered) // Map the PBO into the CPU's memory gl.glBindBuffer(GLES3.GL_PIXEL_PACK_BUFFER, packPBOIds.get(packNextIndex)); var buf = - gl.glMapBufferRange(GLES3.GL_PIXEL_PACK_BUFFER, 0, width * height, GLES3.GL_MAP_READ_BIT); + gl.glMapBufferRange( + GLES3.GL_PIXEL_PACK_BUFFER, 0, (long) width * height, GLES3.GL_MAP_READ_BIT); buf.get(outputBytes); outputMat.put(0, 0, outputBytes); gl.glUnmapBuffer(GLES3.GL_PIXEL_PACK_BUFFER); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/HSVPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/HSVPipe.java index 162c975d16..e0ba36ce0e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/HSVPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/HSVPipe.java @@ -40,7 +40,6 @@ protected Mat process(Mat in) { Scalar firstLower = params.getHsvLower().clone(); Scalar firstUpper = params.getHsvUpper().clone(); firstLower.val[0] = params.getHsvUpper().val[0]; - ; firstUpper.val[0] = 180; var lowerThresholdMat = new Mat(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SortContoursPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SortContoursPipe.java index c1f6f57126..7bdc9bf0d6 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SortContoursPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SortContoursPipe.java @@ -37,7 +37,7 @@ protected List process(List in) { } m_sortedContours.clear(); - if (in.size() > 0) { + if (!in.isEmpty()) { m_sortedContours.addAll(in); if (params.getSortMode() != ContourSortMode.Centermost) { m_sortedContours.sort(params.getSortMode().getComparator()); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SpeckleRejectPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SpeckleRejectPipe.java index 2349eef735..4f4c5ce547 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SpeckleRejectPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SpeckleRejectPipe.java @@ -33,7 +33,7 @@ protected List process(List in) { } m_despeckledContours.clear(); - if (in.size() > 0) { + if (!in.isEmpty()) { double averageArea = 0.0; for (Contour c : in) { averageArea += c.getArea(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java index 4e773605f4..af1c5145d4 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java @@ -29,13 +29,15 @@ 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.*; +import org.photonvision.vision.pipe.impl.AprilTagDetectionPipe; +import org.photonvision.vision.pipe.impl.AprilTagDetectionPipeParams; +import org.photonvision.vision.pipe.impl.AprilTagPoseEstimatorPipe; import org.photonvision.vision.pipe.impl.AprilTagPoseEstimatorPipe.AprilTagPoseEstimatorPipeParams; +import org.photonvision.vision.pipe.impl.CalculateFPSPipe; import org.photonvision.vision.pipeline.result.CVPipelineResult; import org.photonvision.vision.target.TrackedTarget; import org.photonvision.vision.target.TrackedTarget.TargetCalculationParameters; -@SuppressWarnings("DuplicatedCode") public class AprilTagPipeline extends CVPipeline { private final AprilTagDetectionPipe aprilTagDetectionPipe = new AprilTagDetectionPipe(); private final AprilTagPoseEstimatorPipe poseEstimatorPipe = new AprilTagPoseEstimatorPipe(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ArucoPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ArucoPipeline.java index 69ca5f9341..26a5c60151 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ArucoPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ArucoPipeline.java @@ -48,7 +48,6 @@ import org.photonvision.vision.target.TrackedTarget; import org.photonvision.vision.target.TrackedTarget.TargetCalculationParameters; -@SuppressWarnings("DuplicatedCode") public class ArucoPipeline extends CVPipeline { private final RotateImagePipe rotateImagePipe = new RotateImagePipe(); private final GrayscalePipe grayscalePipe = new GrayscalePipe(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java index 077b4254a1..f836e30999 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java @@ -40,7 +40,7 @@ public class CVPipelineSettings implements Cloneable { public ImageRotationMode inputImageRotationMode = ImageRotationMode.DEG_0; public String pipelineNickname = "New Pipeline"; public boolean cameraAutoExposure = false; - // manual exposure only used if cameraAutoExposure if false + // manual exposure only used if cameraAutoExposure is false public double cameraExposure = 20; public int cameraBrightness = 50; // Currently only used by a few cameras (notably the zero-copy Pi Camera driver) with the Gain diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/Calibrate3dPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/Calibrate3dPipeline.java index fecb36d071..261b8fd522 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/Calibrate3dPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/Calibrate3dPipeline.java @@ -46,7 +46,7 @@ public class Calibrate3dPipeline extends CVPipeline { - // For loggging + // For logging private static final Logger logger = new Logger(Calibrate3dPipeline.class, LogGroup.General); // Only 2 pipes needed, one for finding the board corners and one for actually calibrating @@ -63,7 +63,7 @@ public class Calibrate3dPipeline /// Output of the calibration, getter method is set for this. private CVPipeResult calibrationOutput; - private int minSnapshots; + private final int minSnapshots; private boolean calibrating = false; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ColoredShapePipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ColoredShapePipeline.java index df108d7b2f..190de9e68b 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ColoredShapePipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ColoredShapePipeline.java @@ -24,14 +24,16 @@ import org.opencv.core.Point; import org.photonvision.vision.frame.Frame; import org.photonvision.vision.frame.FrameThresholdType; -import org.photonvision.vision.opencv.*; +import org.photonvision.vision.opencv.CVShape; +import org.photonvision.vision.opencv.Contour; +import org.photonvision.vision.opencv.ContourShape; +import org.photonvision.vision.opencv.DualOffsetValues; import org.photonvision.vision.pipe.CVPipe.CVPipeResult; import org.photonvision.vision.pipe.impl.*; import org.photonvision.vision.pipeline.result.CVPipelineResult; import org.photonvision.vision.target.PotentialTarget; import org.photonvision.vision.target.TrackedTarget; -@SuppressWarnings({"DuplicatedCode"}) public class ColoredShapePipeline extends CVPipeline { private final SpeckleRejectPipe speckleRejectPipe = new SpeckleRejectPipe(); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/OutputStreamPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/OutputStreamPipeline.java index 666ecd8ce2..0474b6b562 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/OutputStreamPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/OutputStreamPipeline.java @@ -150,7 +150,7 @@ public CVPipelineResult process( if (!(settings instanceof AprilTagPipelineSettings) && !(settings instanceof ArucoPipelineSettings)) { - // If we're processing anything other than Apriltags.. + // If we're processing anything other than Apriltags... var draw2dCrosshairResultOnOutput = draw2dCrosshairPipe.run(Pair.of(outMat, targetsToDraw)); sumPipeNanosElapsed += pipeProfileNanos[4] = draw2dCrosshairResultOnOutput.nanosElapsed; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/ReflectivePipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/ReflectivePipeline.java index cea2e31422..e961488b78 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/ReflectivePipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/ReflectivePipeline.java @@ -30,7 +30,6 @@ import org.photonvision.vision.target.TrackedTarget; /** Represents a pipeline for tracking retro-reflective targets. */ -@SuppressWarnings({"DuplicatedCode"}) public class ReflectivePipeline extends CVPipeline { private final FindContoursPipe findContoursPipe = new FindContoursPipe(); private final SpeckleRejectPipe speckleRejectPipe = new SpeckleRejectPipe(); diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/PipelineManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/PipelineManager.java index f898aad573..e4f6df3a97 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/PipelineManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/PipelineManager.java @@ -21,7 +21,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; -import org.opencv.aruco.Aruco; import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.common.dataflow.DataChangeService; @@ -67,7 +66,7 @@ public PipelineManager( this.driverModePipeline.setSettings(driverSettings); - if (userPipelines.size() < 1) addPipeline(PipelineType.Reflective); + if (userPipelines.isEmpty()) addPipeline(PipelineType.Reflective); } public PipelineManager(CameraConfiguration config) { @@ -247,9 +246,9 @@ public void setDriverMode(boolean state) { } /** - * Returns whether or not driver mode is active. + * Returns whether driver mode is active. * - * @return Whether or not driver mode is active. + * @return Whether driver mode is active. */ public boolean getDriverMode() { return currentPipelineIndex == DRIVERMODE_INDEX; @@ -261,7 +260,7 @@ public boolean getDriverMode() { /** * Sorts the pipeline list by index, and reassigns their indexes to match the new order.
*
- * I don't like this but I have no other ideas, and it works so + * I don't like this, but I have no other ideas, and it works so */ private void reassignIndexes() { userPipelineSettings.sort(PipelineSettingsIndexComparator); @@ -314,7 +313,7 @@ private CVPipelineSettings createSettingsForType(PipelineType type, String nickn } default: { - logger.error("Got invalid pipeline type: " + type.toString()); + logger.error("Got invalid pipeline type: " + type); return null; } } @@ -376,31 +375,31 @@ public int duplicatePipeline(int index) { private static String createUniqueName( String nickname, List existingSettings) { - String uniqueName = nickname; + StringBuilder uniqueName = new StringBuilder(nickname); while (true) { - String finalUniqueName = uniqueName; // To get around lambda capture + String finalUniqueName = uniqueName.toString(); // To get around lambda capture var conflictingName = existingSettings.stream().anyMatch(it -> it.pipelineNickname.equals(finalUniqueName)); if (!conflictingName) { // If no conflict, we're done - return uniqueName; + return uniqueName.toString(); } else { // Otherwise, we need to add a suffix to the name // If the string doesn't already end in "([0-9]*)", we'll add it // If it does, we'll increment the number in the suffix - if (uniqueName.matches(".*\\([0-9]*\\)")) { + if (uniqueName.toString().matches(".*\\([0-9]*\\)")) { // Because java strings are immutable, we have to do this curstedness // This is like doing "New pipeline (" + 2 + ")" - var parenStart = uniqueName.lastIndexOf('('); + var parenStart = uniqueName.toString().lastIndexOf('('); var parenEnd = uniqueName.length() - 1; var number = Integer.parseInt(uniqueName.substring(parenStart + 1, parenEnd)) + 1; - uniqueName = uniqueName.substring(0, parenStart + 1) + number + ")"; + uniqueName = new StringBuilder(uniqueName.substring(0, parenStart + 1) + number + ")"); } else { - uniqueName += " (1)"; + uniqueName.append(" (1)"); } } } @@ -442,7 +441,7 @@ public void changePipelineType(int newType) { return; } - logger.info("Adding new pipe of type " + type.toString() + " at idx " + idx); + logger.info("Adding new pipe of type " + type + " at idx " + idx); newSettings.pipelineIndex = idx; userPipelineSettings.set(idx, newSettings); setPipelineInternal(idx); diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java index bf0ac94427..d246a45b52 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModule.java @@ -20,7 +20,10 @@ import edu.wpi.first.cscore.VideoException; import edu.wpi.first.math.util.Units; import io.javalin.websocket.WsContext; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.function.BiConsumer; import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; @@ -294,7 +297,7 @@ public void setFov(double fov) { var settables = visionSource.getSettables(); logger.trace(() -> "Setting " + settables.getConfiguration().nickname + ") FOV (" + fov + ")"); - // Only set FOV if we have no vendor JSON and we aren't using a PiCAM + // Only set FOV if we have no vendor JSON, and we aren't using a PiCAM if (isVendorCamera()) { logger.info("Cannot set FOV on a vendor device! Ignoring..."); } else { diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleManager.java index 4ddcd74d5c..d75e62cb7e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionModuleManager.java @@ -65,18 +65,15 @@ public List addSources(List visionSources) { addedModules.put(visionSource.getCameraConfiguration().streamIndex, module); } - var sortedModulesList = - addedModules.entrySet().stream() - .sorted(Comparator.comparingInt(Map.Entry::getKey)) // sort by stream index - .map(Map.Entry::getValue) // map to Stream of VisionModule - .collect(Collectors.toList()); // collect in a List - - return sortedModulesList; + return addedModules.entrySet().stream() + .sorted(Comparator.comparingInt(Map.Entry::getKey)) // sort by stream index + .map(Map.Entry::getValue) // map to Stream of VisionModule + .collect(Collectors.toList()); // collect in a List } private void assignCameraIndex(List config) { - // We won't necessarily have already added all of the cameras we need to at this point - // But by operating on the list, we have a fairly good idea of which we need to change + // We won't necessarily have already added all the cameras we need to at this point + // But by operating on the list, we have a fairly good idea of which we need to change, // but it's not guaranteed that we change the correct one // The best we can do is try to avoid a case where the stream index runs away to infinity // since we can only stream 5 cameras at once diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index bcb75e7f32..4b31ba0691 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -19,7 +19,10 @@ import edu.wpi.first.cscore.UsbCamera; import edu.wpi.first.cscore.UsbCameraInfo; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; import java.util.stream.Collectors; diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java index 2079abc560..60b278aae4 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java @@ -33,7 +33,6 @@ public static double calculatePitch( return -Math.toDegrees(Math.atan((offsetCenterY - targetCenterY) / verticalFocalLength)); } - @SuppressWarnings("DuplicatedCode") public static double calculateSkew(boolean isLandscape, RotatedRect minAreaRect) { // https://namkeenman.wordpress.com/2015/12/18/open-cv-determine-angle-of-rotatedrect-minarearect/ var angle = minAreaRect.angle; 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 4ead2f7784..ea06638af8 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 @@ -127,8 +127,8 @@ public enum TargetModel implements Releasable { Units.inchesToMeters(3 * 2)); @JsonIgnore private MatOfPoint3f realWorldTargetCoordinates; - @JsonIgnore private MatOfPoint3f visualizationBoxBottom = new MatOfPoint3f(); - @JsonIgnore private MatOfPoint3f visualizationBoxTop = new MatOfPoint3f(); + @JsonIgnore private final MatOfPoint3f visualizationBoxBottom = new MatOfPoint3f(); + @JsonIgnore private final MatOfPoint3f visualizationBoxTop = new MatOfPoint3f(); @JsonProperty("realWorldCoordinatesArray") private List realWorldCoordinatesArray; diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java b/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java index 4bb7997047..72c5cdf600 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java @@ -24,16 +24,14 @@ import edu.wpi.first.math.geometry.Translation3d; import java.util.HashMap; import java.util.List; -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.MatOfPoint; -import org.opencv.core.MatOfPoint2f; -import org.opencv.core.Point; -import org.opencv.core.RotatedRect; +import org.opencv.core.*; import org.photonvision.common.util.math.MathUtils; import org.photonvision.vision.aruco.ArucoDetectionResult; import org.photonvision.vision.frame.FrameStaticProperties; -import org.photonvision.vision.opencv.*; +import org.photonvision.vision.opencv.CVShape; +import org.photonvision.vision.opencv.Contour; +import org.photonvision.vision.opencv.DualOffsetValues; +import org.photonvision.vision.opencv.Releasable; public class TrackedTarget implements Releasable { public final Contour m_mainContour; @@ -126,11 +124,9 @@ public TrackedTarget( tvec.put( 0, 0, - new double[] { - bestPose.getTranslation().getX(), - bestPose.getTranslation().getY(), - bestPose.getTranslation().getZ() - }); + bestPose.getTranslation().getX(), + bestPose.getTranslation().getY(), + bestPose.getTranslation().getZ()); setCameraRelativeTvec(tvec); // Opencv expects a 3d vector with norm = angle and direction = axis @@ -185,10 +181,9 @@ public TrackedTarget(ArucoDetectionResult result, TargetCalculationParameters pa var axisangle = VecBuilder.fill(result.getRvec()[0], result.getRvec()[1], result.getRvec()[2]); Rotation3d rotation = new Rotation3d(axisangle, axisangle.normF()); - Transform3d targetPose = - MathUtils.convertOpenCVtoPhotonTransform(new Transform3d(translation, rotation)); - m_bestCameraToTarget3d = targetPose; + m_bestCameraToTarget3d = + MathUtils.convertOpenCVtoPhotonTransform(new Transform3d(translation, rotation)); } } @@ -209,7 +204,7 @@ public double getPoseAmbiguity() { } /** - * Set the approximate bouding polygon. + * Set the approximate bounding polygon. * * @param boundingPolygon List of points to copy. Not modified. */ @@ -262,7 +257,7 @@ public void calculateValues(TargetCalculationParameters params) { params.dualOffsetValues, params.robotOffsetPointMode); - // order of this stuff doesnt matter though + // order of this stuff doesn't matter though m_pitch = TargetCalculations.calculatePitch( m_targetOffsetPoint.y, m_robotOffsetPoint.y, params.verticalFocalLength); diff --git a/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStream.java b/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStream.java index 3a9eed25f1..dae270e9f6 100644 --- a/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStream.java +++ b/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStream.java @@ -35,7 +35,7 @@ public class SocketVideoStream implements Consumer { // Gets set to true when another class reads out valid jpeg bytes at least once // Set back to false when another frame is freshly converted - // Should eliminate synchronization issues of differeing rates of putting frames in + // Should eliminate synchronization issues of differing rates of putting frames in // and taking them back out boolean frameWasConsumed = false; @@ -53,8 +53,7 @@ public SocketVideoStream(int portID) { this.portID = portID; oldSchoolServer = new MJPGFrameConsumer( - CameraServerJNI.getHostname() + "_Port_" + Integer.toString(portID) + "_MJPEG_Server", - portID); + CameraServerJNI.getHostname() + "_Port_" + portID + "_MJPEG_Server", portID); } @Override @@ -64,7 +63,7 @@ public void accept(CVMat image) { .tryLock()) { // we assume frames are coming in frequently. Just skip this frame if we're // locked doing something else. try { - // Does a single-shot frame recieve and convert to JPEG for efficency + // Does a single-shot frame receive and convert to JPEG for efficiency // Will not capture/convert again until convertNextFrame() is called if (image != null && !image.getMat().empty() && jpegBytes == null) { frameWasConsumed = false; diff --git a/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStreamManager.java b/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStreamManager.java index 258c8805ff..ceb84feab6 100644 --- a/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStreamManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/videoStream/SocketVideoStreamManager.java @@ -29,8 +29,8 @@ public class SocketVideoStreamManager { private final Logger logger = new Logger(SocketVideoStreamManager.class, LogGroup.Camera); - private Map streams = new Hashtable(); - private Map userSubscriptions = new Hashtable(); + private final Map streams = new Hashtable<>(); + private final Map userSubscriptions = new Hashtable<>(); private static class ThreadSafeSingleton { private static final SocketVideoStreamManager INSTANCE = new SocketVideoStreamManager(); @@ -45,13 +45,13 @@ private SocketVideoStreamManager() {} // Register a new available camera stream public void addStream(SocketVideoStream newStream) { streams.put(newStream.portID, newStream); - logger.debug("Added new stream for port " + Integer.toString(newStream.portID)); + logger.debug("Added new stream for port " + newStream.portID); } // Remove a previously-added camera stream, and unsubscribe all users public void removeStream(SocketVideoStream oldStream) { streams.remove(oldStream.portID); - logger.debug("Removed stream for port " + Integer.toString(oldStream.portID)); + logger.debug("Removed stream for port " + oldStream.portID); } // Indicate a user would like to subscribe to a camera stream and get frames from it periodically @@ -61,8 +61,7 @@ public void addSubscription(WsContext user, int streamPortID) { userSubscriptions.put(user, streamPortID); stream.addUser(); } else { - logger.error( - "User attempted to subscribe to non-existent port " + Integer.toString(streamPortID)); + logger.error("User attempted to subscribe to non-existent port " + streamPortID); } } @@ -92,7 +91,7 @@ public ByteBuffer getSendFrame(WsContext user) { } } - // Causes all streams to "re-trigger" and recieve and convert their next mjpeg frame + // Causes all streams to "re-trigger" and receive and convert their next mjpeg frame // Only invoke this after all returned jpeg Strings have been used. public void allStreamConvertNextFrame() { for (SocketVideoStream stream : streams.values()) { diff --git a/photon-core/src/test/java/org/photonvision/common/configuration/ConfigTest.java b/photon-core/src/test/java/org/photonvision/common/configuration/ConfigTest.java index 8aa1456002..beaae0e4d9 100644 --- a/photon-core/src/test/java/org/photonvision/common/configuration/ConfigTest.java +++ b/photon-core/src/test/java/org/photonvision/common/configuration/ConfigTest.java @@ -107,13 +107,13 @@ public void deserializeConfig() { Assertions.assertTrue( reflectivePipelineSettings instanceof ReflectivePipelineSettings, - "Conig loaded pipeline settings for index 0 not of expected type ReflectivePipelineSettings!"); + "Config loaded pipeline settings for index 0 not of expected type ReflectivePipelineSettings!"); Assertions.assertTrue( coloredShapePipelineSettings instanceof ColoredShapePipelineSettings, - "Conig loaded pipeline settings for index 1 not of expected type ColoredShapePipelineSettings!"); + "Config loaded pipeline settings for index 1 not of expected type ColoredShapePipelineSettings!"); Assertions.assertTrue( apriltagPipelineSettings instanceof AprilTagPipelineSettings, - "Conig loaded pipeline settings for index 2 not of expected type AprilTagPipelineSettings!"); + "Config loaded pipeline settings for index 2 not of expected type AprilTagPipelineSettings!"); } @AfterAll diff --git a/photon-core/src/test/java/org/photonvision/common/configuration/SQLConfigTest.java b/photon-core/src/test/java/org/photonvision/common/configuration/SQLConfigTest.java index ad2567bc3f..18d4ae5b6a 100644 --- a/photon-core/src/test/java/org/photonvision/common/configuration/SQLConfigTest.java +++ b/photon-core/src/test/java/org/photonvision/common/configuration/SQLConfigTest.java @@ -21,7 +21,8 @@ import java.nio.file.Path; import java.util.List; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.photonvision.common.util.TestUtils; import org.photonvision.vision.camera.CameraType; import org.photonvision.vision.pipeline.AprilTagPipelineSettings; diff --git a/photon-core/src/test/java/org/photonvision/common/util/LogFileManagementTest.java b/photon-core/src/test/java/org/photonvision/common/util/LogFileManagementTest.java index bfd36a6aea..e834874641 100644 --- a/photon-core/src/test/java/org/photonvision/common/util/LogFileManagementTest.java +++ b/photon-core/src/test/java/org/photonvision/common/util/LogFileManagementTest.java @@ -57,17 +57,15 @@ public void fileCleanupTest() { } // Confirm new log files were created - Assertions.assertEquals( - true, - Logger.MAX_LOGS_TO_KEEP + 5 <= countLogFiles(testDir), - "Not enough log files discovered"); + Assertions.assertTrue( + Logger.MAX_LOGS_TO_KEEP + 5 <= countLogFiles(testDir), "Not enough log files discovered"); // Run the log cleanup routine Logger.cleanLogs(Path.of(testDir)); // Confirm we deleted log files Assertions.assertEquals( - true, Logger.MAX_LOGS_TO_KEEP == countLogFiles(testDir), "Not enough log files deleted"); + Logger.MAX_LOGS_TO_KEEP, countLogFiles(testDir), "Not enough log files deleted"); // Clean uptest directory org.photonvision.common.util.file.FileUtils.deleteDirectory(Path.of(testDir)); diff --git a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java index 8f6924d755..9d371d25f8 100644 --- a/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java +++ b/photon-core/src/test/java/org/photonvision/hardware/HardwareTest.java @@ -17,7 +17,8 @@ package org.photonvision.hardware; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.photonvision.common.hardware.GPIO.CustomGPIO; diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java index 23f494428d..1ece69fc55 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java @@ -60,7 +60,7 @@ public void testApriltagFacingCamera() { pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera); printTestResults(pipelineResult); } catch (RuntimeException e) { - // For now, will throw coz rotation3d ctor + // For now, will throw because of the Rotation3d ctor return; } @@ -116,7 +116,7 @@ public void testApriltagDistorted() { pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera); printTestResults(pipelineResult); } catch (RuntimeException e) { - // For now, will throw coz rotation3d ctor + // For now, will throw because of the Rotation3d ctor return; } diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/ArucoPipelineTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/ArucoPipelineTest.java index 06d2fb4f5a..168b7b92e9 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/ArucoPipelineTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/ArucoPipelineTest.java @@ -59,7 +59,7 @@ public void testApriltagFacingCameraAruco() { pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera); printTestResults(pipelineResult); } catch (RuntimeException e) { - // For now, will throw coz rotation3d ctor + // For now, will throw because of the Rotation3d ctor return; } diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/Calibrate3dPipeTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/Calibrate3dPipeTest.java index bec81b2c49..72547829f1 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/Calibrate3dPipeTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/Calibrate3dPipeTest.java @@ -17,7 +17,8 @@ package org.photonvision.vision.pipeline; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import edu.wpi.first.math.util.Units; import java.io.File; @@ -310,7 +311,7 @@ public void calibrateSquaresCommon( assertTrue(centerYErrPct < 10.0); System.out.println("Per View Errors: " + Arrays.toString(cal.perViewErrors)); - System.out.println("Camera Intrinsics: " + cal.cameraIntrinsics.toString()); + System.out.println("Camera Intrinsics: " + cal.cameraIntrinsics); System.out.println("Dist Coeffs: " + cal.distCoeffs.toString()); System.out.println("Standard Deviation: " + cal.standardDeviation); System.out.println( diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/CirclePNPTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/CirclePNPTest.java index 14a51b230e..5a4bc4fcd5 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/CirclePNPTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/CirclePNPTest.java @@ -17,7 +17,8 @@ package org.photonvision.vision.pipeline; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeEach; diff --git a/photon-core/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java b/photon-core/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java index 9e16b6c75d..e0a8376159 100644 --- a/photon-core/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java @@ -24,7 +24,9 @@ import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.common.dataflow.CVPipelineResultConsumer; @@ -169,7 +171,6 @@ public void testMultipleStreamIndex() { Arrays.toString( modules.stream() .map(it -> it.visionSource.getCameraConfiguration().streamIndex) - .collect(Collectors.toList()) .toArray())); var idxs = modules.stream() diff --git a/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java b/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java index 29ecd7654d..0dd36d90e9 100644 --- a/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java @@ -21,8 +21,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.opencv.core.*; +import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; +import org.opencv.core.RotatedRect; +import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; import org.photonvision.common.util.TestUtils; import org.photonvision.common.util.numbers.DoubleCouple; @@ -31,8 +33,9 @@ public class TargetCalculationsTest { - private static Size imageSize = new Size(800, 600); - private static Point imageCenterPoint = new Point(imageSize.width / 2, imageSize.height / 2); + private static final Size imageSize = new Size(800, 600); + private static final Point imageCenterPoint = + new Point(imageSize.width / 2, imageSize.height / 2); private static final double diagFOV = Math.toRadians(70.0); private static final FrameStaticProperties props = From c8c9e779ab9d8af5f42688c4d7f3284914822477 Mon Sep 17 00:00:00 2001 From: amquake Date: Sun, 15 Oct 2023 10:44:47 -0700 Subject: [PATCH 3/5] [photon-core] 2D Detection data accuracy (#896) Use calibration data for 2d target info when available (principal point, FOV) Correct perspective distortion in 2d yaw/pitch info --- .../vision/frame/FrameStaticProperties.java | 55 ++++--- .../vision/target/TargetCalculations.java | 31 +++- .../vision/target/TrackedTarget.java | 55 ++++--- .../vision/target/TargetCalculationsTest.java | 147 +++++++++++++----- .../org/photonvision/VisionSystemSimTest.java | 8 +- 5 files changed, 200 insertions(+), 96 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/FrameStaticProperties.java b/photon-core/src/main/java/org/photonvision/vision/frame/FrameStaticProperties.java index f03db3a60e..cedda04d22 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/FrameStaticProperties.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/FrameStaticProperties.java @@ -39,7 +39,7 @@ public class FrameStaticProperties { * Instantiates a new Frame static properties. * * @param mode The Video Mode of the camera. - * @param fov The fov of the image. + * @param fov The FOV (Field Of Vision) of the image in degrees. */ public FrameStaticProperties(VideoMode mode, double fov, CameraCalibrationCoefficients cal) { this(mode != null ? mode.width : 1, mode != null ? mode.height : 1, fov, cal); @@ -48,9 +48,9 @@ public FrameStaticProperties(VideoMode mode, double fov, CameraCalibrationCoeffi /** * Instantiates a new Frame static properties. * - * @param imageWidth The width of the image. - * @param imageHeight The width of the image. - * @param fov The fov of the image. + * @param imageWidth The width of the image in pixels. + * @param imageHeight The width of the image in pixels. + * @param fov The FOV (Field Of Vision) of the image in degrees. */ public FrameStaticProperties( int imageWidth, int imageHeight, double fov, CameraCalibrationCoefficients cal) { @@ -61,30 +61,47 @@ public FrameStaticProperties( imageArea = this.imageWidth * this.imageHeight; - // Todo -- if we have calibration, use it's center point? - centerX = ((double) this.imageWidth / 2) - 0.5; - centerY = ((double) this.imageHeight / 2) - 0.5; - centerPoint = new Point(centerX, centerY); - - // TODO if we have calibration use it here instead // pinhole model calculations - DoubleCouple horizVertViews = - calculateHorizontalVerticalFoV(this.fov, this.imageWidth, this.imageHeight); + if (cameraCalibration != null && cameraCalibration.getCameraIntrinsicsMat() != null) { + // Use calibration data + var camIntrinsics = cameraCalibration.getCameraIntrinsicsMat(); + centerX = camIntrinsics.get(0, 2)[0]; + centerY = camIntrinsics.get(1, 2)[0]; + centerPoint = new Point(centerX, centerY); + horizontalFocalLength = camIntrinsics.get(0, 0)[0]; + verticalFocalLength = camIntrinsics.get(1, 1)[0]; + } else { + // No calibration data. Calculate from user provided diagonal FOV + centerX = (this.imageWidth / 2.0) - 0.5; + centerY = (this.imageHeight / 2.0) - 0.5; + centerPoint = new Point(centerX, centerY); - horizontalFocalLength = this.imageWidth / (2 * Math.tan(horizVertViews.getFirst() / 2)); - verticalFocalLength = this.imageHeight / (2 * Math.tan(horizVertViews.getSecond() / 2)); + DoubleCouple horizVertViews = + calculateHorizontalVerticalFoV(this.fov, this.imageWidth, this.imageHeight); + double horizFOV = Math.toRadians(horizVertViews.getFirst()); + double vertFOV = Math.toRadians(horizVertViews.getSecond()); + horizontalFocalLength = (this.imageWidth / 2.0) / Math.tan(horizFOV / 2.0); + verticalFocalLength = (this.imageHeight / 2.0) / Math.tan(vertFOV / 2.0); + } } + /** + * Calculates the horizontal and vertical FOV components from a given diagonal FOV and image size. + * + * @param diagonalFoV Diagonal FOV in degrees + * @param imageWidth Image width in pixels + * @param imageHeight Image height in pixels + * @return Horizontal and vertical FOV in degrees + */ public static DoubleCouple calculateHorizontalVerticalFoV( double diagonalFoV, int imageWidth, int imageHeight) { - double diagonalView = Math.toRadians(diagonalFoV); + diagonalFoV = Math.toRadians(diagonalFoV); double diagonalAspect = Math.hypot(imageWidth, imageHeight); double horizontalView = - Math.atan(Math.tan(diagonalView / 2) * (imageWidth / diagonalAspect)) * 2; - double verticalView = - Math.atan(Math.tan(diagonalView / 2) * (imageHeight / diagonalAspect)) * 2; + Math.atan(Math.tan(diagonalFoV / 2) * (imageWidth / diagonalAspect)) * 2; + double verticalView = Math.atan(Math.tan(diagonalFoV / 2) * (imageHeight / diagonalAspect)) * 2; - return new DoubleCouple(horizontalView, verticalView); + return new DoubleCouple(Math.toDegrees(horizontalView), Math.toDegrees(verticalView)); } } diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java b/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java index 60b278aae4..5e64237443 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TargetCalculations.java @@ -23,14 +23,29 @@ import org.photonvision.vision.opencv.DualOffsetValues; public class TargetCalculations { - public static double calculateYaw( - double offsetCenterX, double targetCenterX, double horizontalFocalLength) { - return Math.toDegrees(Math.atan((offsetCenterX - targetCenterX) / horizontalFocalLength)); - } - - public static double calculatePitch( - double offsetCenterY, double targetCenterY, double verticalFocalLength) { - return -Math.toDegrees(Math.atan((offsetCenterY - targetCenterY) / verticalFocalLength)); + /** + * Calculates the yaw and pitch of a point in the image. Yaw and pitch must be calculated together + * to account for perspective distortion. Yaw is positive right, and pitch is positive up. + * + * @param offsetCenterX The X value of the offset principal point (cx) in pixels + * @param targetCenterX The X value of the target's center point in pixels + * @param horizontalFocalLength The horizontal focal length (fx) in pixels + * @param offsetCenterY The Y value of the offset principal point (cy) in pixels + * @param targetCenterY The Y value of the target's center point in pixels + * @param verticalFocalLength The vertical focal length (fy) in pixels + * @return The yaw and pitch from the principal axis to the target center, in degrees. + */ + public static DoubleCouple calculateYawPitch( + double offsetCenterX, + double targetCenterX, + double horizontalFocalLength, + double offsetCenterY, + double targetCenterY, + double verticalFocalLength) { + double yaw = Math.atan((targetCenterX - offsetCenterX) / horizontalFocalLength); + double pitch = + Math.atan((offsetCenterY - targetCenterY) / (verticalFocalLength / Math.cos(yaw))); + return new DoubleCouple(Math.toDegrees(yaw), Math.toDegrees(pitch)); } public static double calculateSkew(boolean isLandscape, RotatedRect minAreaRect) { diff --git a/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java b/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java index 72c5cdf600..922ec5e6ec 100644 --- a/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java +++ b/photon-core/src/main/java/org/photonvision/vision/target/TrackedTarget.java @@ -73,13 +73,16 @@ public TrackedTarget( TargetCalculationParameters params) { m_targetOffsetPoint = new Point(tagDetection.getCenterX(), tagDetection.getCenterY()); m_robotOffsetPoint = new Point(); - - m_pitch = - TargetCalculations.calculatePitch( - tagDetection.getCenterY(), params.cameraCenterPoint.y, params.verticalFocalLength); - m_yaw = - TargetCalculations.calculateYaw( - tagDetection.getCenterX(), params.cameraCenterPoint.x, params.horizontalFocalLength); + var yawPitch = + TargetCalculations.calculateYawPitch( + params.cameraCenterPoint.x, + tagDetection.getCenterX(), + params.horizontalFocalLength, + params.cameraCenterPoint.y, + tagDetection.getCenterY(), + params.verticalFocalLength); + m_yaw = yawPitch.getFirst(); + m_pitch = yawPitch.getSecond(); var bestPose = new Transform3d(); var altPose = new Transform3d(); @@ -138,13 +141,16 @@ public TrackedTarget( public TrackedTarget(ArucoDetectionResult result, TargetCalculationParameters params) { m_targetOffsetPoint = new Point(result.getCenterX(), result.getCenterY()); m_robotOffsetPoint = new Point(); - - m_pitch = - TargetCalculations.calculatePitch( - result.getCenterY(), params.cameraCenterPoint.y, params.verticalFocalLength); - m_yaw = - TargetCalculations.calculateYaw( - result.getCenterX(), params.cameraCenterPoint.x, params.horizontalFocalLength); + var yawPitch = + TargetCalculations.calculateYawPitch( + params.cameraCenterPoint.x, + result.getCenterX(), + params.horizontalFocalLength, + params.cameraCenterPoint.y, + result.getCenterY(), + params.verticalFocalLength); + m_yaw = yawPitch.getFirst(); + m_pitch = yawPitch.getSecond(); double[] xCorners = result.getxCorners(); double[] yCorners = result.getyCorners(); @@ -246,7 +252,7 @@ public MatOfPoint2f getApproximateBoundingPolygon() { } public void calculateValues(TargetCalculationParameters params) { - // this MUST happen in this exact order! + // this MUST happen in this exact order! (TODO: document why) m_targetOffsetPoint = TargetCalculations.calculateTargetOffsetPoint( params.isLandscape, params.targetOffsetPointEdge, getMinAreaRect()); @@ -257,13 +263,18 @@ public void calculateValues(TargetCalculationParameters params) { params.dualOffsetValues, params.robotOffsetPointMode); - // order of this stuff doesn't matter though - m_pitch = - TargetCalculations.calculatePitch( - m_targetOffsetPoint.y, m_robotOffsetPoint.y, params.verticalFocalLength); - m_yaw = - TargetCalculations.calculateYaw( - m_targetOffsetPoint.x, m_robotOffsetPoint.x, params.horizontalFocalLength); + // order of this stuff doesnt matter though + var yawPitch = + TargetCalculations.calculateYawPitch( + m_robotOffsetPoint.x, + m_targetOffsetPoint.x, + params.horizontalFocalLength, + m_robotOffsetPoint.y, + m_targetOffsetPoint.y, + params.verticalFocalLength); + m_yaw = yawPitch.getFirst(); + m_pitch = yawPitch.getSecond(); + m_area = m_mainContour.getMinAreaRect().size.area() / params.imageArea * 100; m_skew = TargetCalculations.calculateSkew(params.isLandscape, getMinAreaRect()); diff --git a/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java b/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java index 0dd36d90e9..7b23d3a600 100644 --- a/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/target/TargetCalculationsTest.java @@ -17,14 +17,19 @@ package org.photonvision.vision.target; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Translation3d; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.opencv.core.MatOfPoint2f; -import org.opencv.core.Point; -import org.opencv.core.RotatedRect; -import org.opencv.core.Size; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.opencv.calib3d.Calib3d; +import org.opencv.core.*; import org.opencv.imgproc.Imgproc; import org.photonvision.common.util.TestUtils; import org.photonvision.common.util.numbers.DoubleCouple; @@ -33,9 +38,9 @@ public class TargetCalculationsTest { - private static final Size imageSize = new Size(800, 600); - private static final Point imageCenterPoint = - new Point(imageSize.width / 2, imageSize.height / 2); + private static Size imageSize = new Size(800, 600); + private static Point imageCenterPoint = + new Point(imageSize.width / 2.0 - 0.5, imageSize.height / 2.0 - 0.5); private static final double diagFOV = Math.toRadians(70.0); private static final FrameStaticProperties props = @@ -52,43 +57,104 @@ public class TargetCalculationsTest { props.verticalFocalLength, imageSize.width * imageSize.height); - @BeforeEach - public void Init() { + @BeforeAll + public static void setup() { TestUtils.loadLibraries(); } @Test - public void yawTest() { - var targetPixelOffsetX = 100; - var targetCenterPoint = new Point(imageCenterPoint.x + targetPixelOffsetX, imageCenterPoint.y); - - var trueYaw = - Math.atan((imageCenterPoint.x - targetCenterPoint.x) / params.horizontalFocalLength); - - var yaw = - TargetCalculations.calculateYaw( - imageCenterPoint.x, targetCenterPoint.x, params.horizontalFocalLength); - - assertEquals(Math.toDegrees(trueYaw), yaw, 0.025, "Yaw not as expected"); + public void testYawPitchBehavior() { + double targetPixelOffsetX = 100; + double targetPixelOffsetY = 100; + var targetCenterPoint = + new Point(imageCenterPoint.x + targetPixelOffsetX, imageCenterPoint.y + targetPixelOffsetY); + + var targetYawPitch = + TargetCalculations.calculateYawPitch( + imageCenterPoint.x, + targetCenterPoint.x, + params.horizontalFocalLength, + imageCenterPoint.y, + targetCenterPoint.y, + params.verticalFocalLength); + + assertTrue(targetYawPitch.getFirst() > 0, "Yaw is not positive right"); + assertTrue(targetYawPitch.getSecond() < 0, "Pitch is not positive up"); + + var fovs = + FrameStaticProperties.calculateHorizontalVerticalFoV( + diagFOV, (int) imageSize.width, (int) imageSize.height); + var maxYaw = + TargetCalculations.calculateYawPitch( + imageCenterPoint.x, + 2 * imageCenterPoint.x, + params.horizontalFocalLength, + imageCenterPoint.y, + imageCenterPoint.y, + params.verticalFocalLength); + assertEquals(fovs.getFirst() / 2.0, maxYaw.getFirst(), 0.025, "Horizontal FOV check failed"); + var maxPitch = + TargetCalculations.calculateYawPitch( + imageCenterPoint.x, + imageCenterPoint.x, + params.horizontalFocalLength, + imageCenterPoint.y, + 0, + params.verticalFocalLength); + assertEquals(fovs.getSecond() / 2.0, maxPitch.getSecond(), 0.025, "Vertical FOV check failed"); } - @Test - public void pitchTest() { - var targetPixelOffsetY = 100; - var targetCenterPoint = new Point(imageCenterPoint.x, imageCenterPoint.y + targetPixelOffsetY); - - var truePitch = - Math.atan((imageCenterPoint.y - targetCenterPoint.y) / params.verticalFocalLength); - - var pitch = - TargetCalculations.calculatePitch( - imageCenterPoint.y, targetCenterPoint.y, params.verticalFocalLength); + private static Stream testYawPitchCalcArgs() { + return Stream.of( + // (yaw, pitch) in degrees + Arguments.of(0, 0), + Arguments.of(10, 0), + Arguments.of(0, 10), + Arguments.of(10, 10), + Arguments.of(-10, -10), + Arguments.of(30, 45), + Arguments.of(-45, -20)); + } - assertEquals(Math.toDegrees(truePitch) * -1, pitch, 0.025, "Pitch not as expected"); + private static double[] testCameraMatrix = {240, 0, 320, 0, 240, 320, 0, 0, 1}; + + @ParameterizedTest + @MethodSource("testYawPitchCalcArgs") + public void testYawPitchCalc(double yawDeg, double pitchDeg) { + Mat testCameraMat = new Mat(3, 3, CvType.CV_64F); + testCameraMat.put(0, 0, testCameraMatrix); + // Since we create this translation using the given yaw/pitch, we should see the same angles + // calculated + var targetTrl = + new Translation3d(1, new Rotation3d(0, Math.toRadians(pitchDeg), Math.toRadians(yawDeg))); + // NWU to EDN + var objectPoints = + new MatOfPoint3f(new Point3(-targetTrl.getY(), -targetTrl.getZ(), targetTrl.getX())); + var imagePoints = new MatOfPoint2f(); + // Project translation into camera image + Calib3d.projectPoints( + objectPoints, + new MatOfDouble(0, 0, 0), + new MatOfDouble(0, 0, 0), + testCameraMat, + new MatOfDouble(0, 0, 0, 0, 0), + imagePoints); + var point = imagePoints.toArray()[0]; + // Test if the target yaw/pitch calculation matches what the target was created with + var yawPitch = + TargetCalculations.calculateYawPitch( + point.x, + testCameraMatrix[2], + testCameraMatrix[0], + point.y, + testCameraMatrix[5], + testCameraMatrix[4]); + assertEquals(yawDeg, yawPitch.getFirst(), 1e-3, "Yaw calculation incorrect"); + assertEquals(pitchDeg, yawPitch.getSecond(), 1e-3, "Pitch calculation incorrect"); } @Test - public void targetOffsetTest() { + public void testTargetOffset() { Point center = new Point(0, 0); Size rectSize = new Size(10, 5); double angle = 30; @@ -105,11 +171,6 @@ public void targetOffsetTest() { assertEquals(2.17, result.y, 0.1, "Target offset Y not as expected"); } - public static void main(String[] args) { - TestUtils.loadLibraries(); - new TargetCalculationsTest().targetOffsetTest(); - } - @Test public void testSkewCalculation() { // Setup @@ -191,14 +252,14 @@ public void testSkewCalculation() { public void testCameraFOVCalculation() { final DoubleCouple glowormHorizVert = FrameStaticProperties.calculateHorizontalVerticalFoV(74.8, 640, 480); - var gwHorizDeg = Math.toDegrees(glowormHorizVert.getFirst()); - var gwVertDeg = Math.toDegrees(glowormHorizVert.getSecond()); + var gwHorizDeg = glowormHorizVert.getFirst(); + var gwVertDeg = glowormHorizVert.getSecond(); assertEquals(62.7, gwHorizDeg, .3); assertEquals(49, gwVertDeg, .3); } @Test - public void robotOffsetDualTest() { + public void testDualOffsetCrosshair() { final DualOffsetValues dualOffsetValues = new DualOffsetValues( new Point(400, 150), 10, diff --git a/photon-lib/src/test/java/org/photonvision/VisionSystemSimTest.java b/photon-lib/src/test/java/org/photonvision/VisionSystemSimTest.java index 5ab7d00ce2..c53e006717 100644 --- a/photon-lib/src/test/java/org/photonvision/VisionSystemSimTest.java +++ b/photon-lib/src/test/java/org/photonvision/VisionSystemSimTest.java @@ -297,12 +297,12 @@ public void testYawAngles(double testYaw) { var res = camera.getLatestResult(); assertTrue(res.hasTargets()); var tgt = res.getBestTarget(); - assertEquals(tgt.getYaw(), testYaw, kRotDeltaDeg); + assertEquals(testYaw, tgt.getYaw(), kRotDeltaDeg); } @ParameterizedTest @ValueSource(doubles = {-10, -5, -0, -1, -2, 5, 7, 10.23, 20.21, -19.999}) - public void testCameraPitch(double testPitch) { + public void testPitchAngles(double testPitch) { final var targetPose = new Pose3d(new Translation3d(15.98, 0, 0), new Rotation3d(0, 0, 3 * Math.PI / 4)); final var robotPose = new Pose2d(new Translation2d(10, 0), new Rotation2d(0)); @@ -331,7 +331,7 @@ public void testCameraPitch(double testPitch) { assertEquals(testPitch, tgt.getPitch(), kRotDeltaDeg); } - private static Stream distCalCParamProvider() { + private static Stream testDistanceCalcArgs() { // Arbitrary and fairly random assortment of distances, camera pitches, and heights return Stream.of( Arguments.of(5, -15.98, 0), @@ -354,7 +354,7 @@ private static Stream distCalCParamProvider() { } @ParameterizedTest - @MethodSource("distCalCParamProvider") + @MethodSource("testDistanceCalcArgs") public void testDistanceCalc(double testDist, double testPitch, double testHeight) { // Assume dist along ground and tgt height the same. Iterate over other parameters. From 7f949627918bf6c348797d40971d034fe5757d63 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 15 Oct 2023 13:45:30 -0400 Subject: [PATCH 4/5] Build photonlib json first (#952) --- photon-lib/build.gradle | 13 ++++++------- photon-lib/publish.gradle | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/photon-lib/build.gradle b/photon-lib/build.gradle index d7952394df..baae8fcc7d 100644 --- a/photon-lib/build.gradle +++ b/photon-lib/build.gradle @@ -120,23 +120,22 @@ task generateVendorJson() { outputs.file photonlibFileOutput inputs.file photonlibFileInput - doLast { - println "Writing version ${pubVersion} to $photonlibFileOutput" + println "Writing vendor JSON ${pubVersion} to $photonlibFileOutput" if (photonlibFileOutput.exists()) { photonlibFileOutput.delete() } + photonlibFileOutput.parentFile.mkdirs() + def read = photonlibFileInput.text .replace('${photon_version}', pubVersion) .replace('${frc_year}', frcYear) - photonlibFileOutput.write(read) - } + photonlibFileOutput.text = read outputs.upToDateWhen { false } } -build.dependsOn generateVendorJson - +build.mustRunAfter generateVendorJson task writeCurrentVersion { def versionFileIn = file("${rootDir}/shared/PhotonVersion.java.in") @@ -147,7 +146,7 @@ task writeCurrentVersion { versionString) } -build.dependsOn writeCurrentVersion +build.mustRunAfter writeCurrentVersion tasks.withType(Javadoc) { options.encoding = 'UTF-8' diff --git a/photon-lib/publish.gradle b/photon-lib/publish.gradle index 9967b24ee2..ca67c43b0e 100644 --- a/photon-lib/publish.gradle +++ b/photon-lib/publish.gradle @@ -198,8 +198,7 @@ model { } } -// So I don't actually know the _right_ way to tell gradle that the vendordep json publish requires generation first, so we're doing this -getTasksByName("publishVendorjsonPublicationToMavenLocal", false).each { +tasks.withType(PublishToMavenRepository) { it.mustRunAfter generateVendorJson } From ad4f462fd62fabfe5268ea58f7d5bd69c5d94875 Mon Sep 17 00:00:00 2001 From: amquake Date: Sun, 15 Oct 2023 10:46:55 -0700 Subject: [PATCH 5/5] [photon-core] [2024] Cleanup and document coordinate system conversion (#894) * cleanup and document coordinate conversion * spotless * bump wpilib version * coordinate conversion tests * fix/document SolvePNPPipe models and corners * format * run lint --------- Co-authored-by: Matthew Morley --- .../common/util/math/MathUtils.java | 93 +++++++------------ .../vision/pipe/impl/CornerDetectionPipe.java | 61 ++++++------ .../vision/pipe/impl/Draw3dTargetsPipe.java | 31 +++---- .../vision/pipe/impl/SolvePNPPipe.java | 29 +----- .../vision/pipeline/AprilTagPipeline.java | 6 +- .../vision/target/TargetModel.java | 93 ++++++++++--------- .../common/util/CoordinateConversionTest.java | 79 ++++++++++++++++ .../vision/pipeline/AprilTagTest.java | 13 ++- .../vision/pipeline/SolvePNPTest.java | 70 ++++++++------ 9 files changed, 258 insertions(+), 217 deletions(-) create mode 100644 photon-core/src/test/java/org/photonvision/common/util/CoordinateConversionTest.java diff --git a/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java b/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java index 526311afc5..2b6bb31ae3 100644 --- a/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java +++ b/photon-core/src/main/java/org/photonvision/common/util/math/MathUtils.java @@ -17,10 +17,12 @@ package org.photonvision.common.util.math; -import edu.wpi.first.math.MatBuilder; -import edu.wpi.first.math.Nat; +import edu.wpi.first.apriltag.AprilTagPoseEstimate; import edu.wpi.first.math.VecBuilder; -import edu.wpi.first.math.geometry.*; +import edu.wpi.first.math.geometry.CoordinateSystem; +import edu.wpi.first.math.geometry.Pose3d; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.math.util.Units; import edu.wpi.first.util.WPIUtilJNI; import java.util.Arrays; @@ -137,73 +139,46 @@ public static double lerp(double startValue, double endValue, double t) { return startValue + (endValue - startValue) * t; } - public static Pose3d EDNtoNWU(final Pose3d pose) { - // Change of basis matrix from EDN to NWU. Each column vector is one of the - // old basis vectors mapped to its representation in the new basis. - // - // E (+X) -> N (-Y), D (+Y) -> W (-Z), N (+Z) -> U (+X) - var R = new MatBuilder<>(Nat.N3(), Nat.N3()).fill(0, 0, 1, -1, 0, 0, 0, -1, 0); - - // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ - double w = Math.sqrt(1.0 + R.get(0, 0) + R.get(1, 1) + R.get(2, 2)) / 2.0; - double x = (R.get(2, 1) - R.get(1, 2)) / (4.0 * w); - double y = (R.get(0, 2) - R.get(2, 0)) / (4.0 * w); - double z = (R.get(1, 0) - R.get(0, 1)) / (4.0 * w); - var rotationQuat = new Rotation3d(new Quaternion(w, x, y, z)); - - return new Pose3d( - pose.getTranslation().rotateBy(rotationQuat), pose.getRotation().rotateBy(rotationQuat)); - } - /** - * All our solvepnp code returns a tag with X left, Y up, and Z out of the tag To better match - * wpilib, we want to apply another rotation so that we get Z up, X out of the tag, and Y to the - * right. We apply the following change of basis: X -> Y Y -> Z Z -> X + * OpenCV uses the EDN coordinate system, but WPILib uses NWU. Converts a camera-to-target + * transformation from EDN to NWU. + * + *

Note: The detected target's rvec and tvec perform a rotation-translation transformation + * which converts points in the target's coordinate system to the camera's. This means applying + * the transformation to the target point (0,0,0) for example would give the target's center + * relative to the camera. Conveniently, if we make a translation-rotation transformation out of + * these components instead, we get the transformation from the camera to the target. + * + * @param cameraToTarget3d A camera-to-target Transform3d in EDN. + * @return A camera-to-target Transform3d in NWU. */ - private static final Rotation3d WPILIB_BASE_ROTATION = - new Rotation3d(new MatBuilder<>(Nat.N3(), Nat.N3()).fill(0, 1, 0, 0, 0, 1, 1, 0, 0)); - public static Transform3d convertOpenCVtoPhotonTransform(Transform3d cameraToTarget3d) { // TODO: Refactor into new pipe? - // CameraToTarget _should_ be in opencv-land EDN - var nwu = - CoordinateSystem.convert( - new Pose3d().transformBy(cameraToTarget3d), - CoordinateSystem.EDN(), - CoordinateSystem.NWU()); - return new Transform3d(nwu.getTranslation(), WPILIB_BASE_ROTATION.rotateBy(nwu.getRotation())); - } - - public static Pose3d convertOpenCVtoPhotonPose(Transform3d cameraToTarget3d) { - // TODO: Refactor into new pipe? - // CameraToTarget _should_ be in opencv-land EDN - var nwu = - CoordinateSystem.convert( - new Pose3d().transformBy(cameraToTarget3d), - CoordinateSystem.EDN(), - CoordinateSystem.NWU()); - return new Pose3d(nwu.getTranslation(), WPILIB_BASE_ROTATION.rotateBy(nwu.getRotation())); + return CoordinateSystem.convert( + cameraToTarget3d, CoordinateSystem.EDN(), CoordinateSystem.NWU()); } /* - * The AprilTag pose rotation outputs are X left, Y down, Z away from the tag - * with the tag facing - * the camera upright and the camera facing the target parallel to the floor. - * But our OpenCV - * solvePNP code would have X left, Y up, Z towards the camera with the target - * facing the camera - * and both parallel to the floor. So we apply a base rotation to the rotation - * component of the - * apriltag pose to make it consistent with the EDN system that OpenCV uses, - * internally a 180 - * rotation about the X axis + * From the AprilTag repo: + * "The coordinate system has the origin at the camera center. The z-axis points from the camera + * center out the camera lens. The x-axis is to the right in the image taken by the camera, and + * y is down. The tag's coordinate frame is centered at the center of the tag, with x-axis to the + * right, y-axis down, and z-axis into the tag." + * + * This means our detected transformation will be in EDN. Our subsequent uses of the transformation, + * however, assume the tag's z-axis point away from the tag instead of into it. This means we + * have to correct the transformation's rotation. */ private static final Rotation3d APRILTAG_BASE_ROTATION = - new Rotation3d(VecBuilder.fill(1, 0, 0), Units.degreesToRadians(180)); + new Rotation3d(VecBuilder.fill(0, 1, 0), Units.degreesToRadians(180)); /** - * Apply a 180-degree rotation about X to the rotation component of a given Apriltag pose. This - * aligns it with the OpenCV poses we use in other places. + * AprilTag returns a camera-to-tag transform in EDN, but the tag's z-axis points into the tag + * instead of away from it and towards the camera. This means we have to correct the + * transformation's rotation. + * + * @param pose The Transform3d with translation and rotation directly from the {@link + * AprilTagPoseEstimate}. */ public static Transform3d convertApriltagtoOpenCV(Transform3d pose) { var ocvRotation = APRILTAG_BASE_ROTATION.rotateBy(pose.getRotation()); diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java index ff7f6fd3ea..bed3c6b5ef 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/CornerDetectionPipe.java @@ -17,7 +17,6 @@ package org.photonvision.vision.pipe.impl; -import edu.wpi.first.math.geometry.Translation2d; import java.util.*; import org.opencv.core.MatOfPoint2f; import org.opencv.core.Point; @@ -25,6 +24,10 @@ import org.photonvision.vision.pipe.CVPipe; import org.photonvision.vision.target.TrackedTarget; +/** + * Determines the target corners of the {@link TrackedTarget}. The {@link + * CornerDetectionPipeParameters} affect how these corners are calculated. + */ public class CornerDetectionPipe extends CVPipe< List, @@ -49,15 +52,15 @@ protected List process(List targetList) { } /** - * @param target the target to find the corners of. - * @return the corners. left top, left bottom, right bottom, right top + * @param target The target to find the corners of. + * @return Corners: (bottom-left, bottom-right, top-right, top-left) */ private List findBoundingBoxCorners(TrackedTarget target) { // extract the corners var points = new Point[4]; target.m_mainContour.getMinAreaRect().points(points); - // find the tl/tr/bl/br corners + // find the bl/br/tr/tl corners // first, min by left/right var list_ = Arrays.asList(points); list_.sort(leftRightComparator); @@ -68,13 +71,12 @@ private List findBoundingBoxCorners(TrackedTarget target) { var right = new ArrayList<>(List.of(list_.get(2), list_.get(3))); right.sort(verticalComparator); - // tl tr bl br var tl = left.get(0); var bl = left.get(1); var tr = right.get(0); var br = right.get(1); - return List.of(tl, bl, br, tr); + return List.of(bl, br, tr, tl); } /** @@ -83,31 +85,22 @@ private List findBoundingBoxCorners(TrackedTarget target) { * @return The straight line distance between them. */ private static double distanceBetween(Point a, Point b) { - return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); - } - - /** - * @param a First point. - * @param b Second point. - * @return The straight line distance between them. - */ - private static double distanceBetween(Translation2d a, Translation2d b) { - return Math.sqrt(Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2)); + double xDelta = a.x - b.x; + double yDelta = a.y - b.y; + return Math.sqrt(xDelta * xDelta + yDelta * yDelta); } /** - * Find the 4 most extreme corners, + * Find the 4 most extreme corners of the target's contour. * - * @param target the target to track. - * @param convexHull weather to use the convex hull of the target. - * @return the 4 extreme corners of the contour. + * @param target The target to track. + * @param convexHull Whether to use the convex hull of the contour instead. + * @return The 4 extreme corners of the contour: (bottom-left, bottom-right, top-right, top-left) */ private List detectExtremeCornersByApproxPolyDp(TrackedTarget target, boolean convexHull) { var centroid = target.getMinAreaRect().center; - Comparator distanceProvider = - Comparator.comparingDouble( - (Point point) -> - Math.sqrt(Math.pow(centroid.x - point.x, 2) + Math.pow(centroid.y - point.y, 2))); + Comparator compareCenterDist = + Comparator.comparingDouble((Point point) -> distanceBetween(centroid, point)); MatOfPoint2f targetContour; if (convexHull) { @@ -141,17 +134,17 @@ private List detectExtremeCornersByApproxPolyDp(TrackedTarget target, boo // left top, left bottom, right bottom, right top var boundingBoxCorners = findBoundingBoxCorners(target); - var distanceToTlComparator = - Comparator.comparingDouble((Point p) -> distanceBetween(p, boundingBoxCorners.get(0))); - - var distanceToTrComparator = + var compareDistToTl = Comparator.comparingDouble((Point p) -> distanceBetween(p, boundingBoxCorners.get(3))); - // top left and top right are the poly corners closest to the bounding box tl and tr - pointList.sort(distanceToTlComparator); + var compareDistToTr = + Comparator.comparingDouble((Point p) -> distanceBetween(p, boundingBoxCorners.get(2))); + + // top left and top right are the poly corners closest to the bouding box tl and tr + pointList.sort(compareDistToTl); var tl = pointList.get(0); pointList.remove(tl); - pointList.sort(distanceToTrComparator); + pointList.sort(compareDistToTr); var tr = pointList.get(0); pointList.remove(tr); @@ -177,11 +170,11 @@ private List detectExtremeCornersByApproxPolyDp(TrackedTarget target, boo } } if (leftList.isEmpty() || rightList.isEmpty()) return null; - leftList.sort(distanceProvider); - rightList.sort(distanceProvider); + leftList.sort(compareCenterDist); + rightList.sort(compareCenterDist); var bl = leftList.get(leftList.size() - 1); var br = rightList.get(rightList.size() - 1); - return List.of(tl, bl, br, tr); + return List.of(bl, br, tr, tl); } public static class CornerDetectionPipeParameters { diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java index b88bd0ba17..99927ea0f1 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/Draw3dTargetsPipe.java @@ -127,7 +127,7 @@ protected Void process(Pair> in) { // Draw X, Y and Z axis MatOfPoint3f pointMat = new MatOfPoint3f(); - // Those points are in opencv-land, but we are in NWU + // OpenCV expects coordinates in EDN, but we want to visualize in NWU // NWU | EDN // X: Z // Y: -X @@ -136,11 +136,15 @@ protected Void process(Pair> in) { var list = List.of( new Point3(0, 0, 0), - new Point3(0, 0, AXIS_LEN), - new Point3(AXIS_LEN, 0, 0), - new Point3(0, AXIS_LEN, 0)); + new Point3(0, 0, AXIS_LEN), // x-axis + new Point3(-AXIS_LEN, 0, 0), // y-axis + new Point3(0, -AXIS_LEN, 0)); // z-axis pointMat.fromList(list); + // The detected target's rvec and tvec perform a rotation-translation transformation which + // converts points in the target's coordinate system to the camera's. This means applying + // the transformation to the target point (0,0,0) for example would give the target's center + // relative to the camera. Calib3d.projectPoints( pointMat, target.getCameraRelativeRvec(), @@ -152,19 +156,22 @@ protected Void process(Pair> in) { var axisPoints = tempMat.toList(); dividePointList(axisPoints); - // Red = x, green y, blue z + // XYZ is RGB + // y-axis = green Imgproc.line( in.getLeft(), axisPoints.get(0), axisPoints.get(2), ColorHelper.colorToScalar(Color.GREEN), 3); + // z-axis = blue Imgproc.line( in.getLeft(), axisPoints.get(0), axisPoints.get(3), ColorHelper.colorToScalar(Color.BLUE), 3); + // x-axis = red Imgproc.line( in.getLeft(), axisPoints.get(0), @@ -172,6 +179,7 @@ protected Void process(Pair> in) { ColorHelper.colorToScalar(Color.RED), 3); + // box edges perpendicular to tag for (int i = 0; i < bottomPoints.size(); i++) { Imgproc.line( in.getLeft(), @@ -180,6 +188,7 @@ protected Void process(Pair> in) { ColorHelper.colorToScalar(Color.blue), 3); } + // box edges parallel to tag for (int i = 0; i < topPoints.size(); i++) { Imgproc.line( in.getLeft(), @@ -265,18 +274,6 @@ private void divideMat2f(MatOfPoint2f src, MatOfPoint dst) { dst.fromArray(pointArray); } - private void divideMat2f(MatOfPoint2f src, MatOfPoint2f dst) { - var hull = src.toArray(); - var pointArray = new Point[hull.length]; - for (int i = 0; i < hull.length; i++) { - var hullAtI = hull[i]; - pointArray[i] = - new Point( - hullAtI.x / (double) params.divisor.value, hullAtI.y / (double) params.divisor.value); - } - dst.fromArray(pointArray); - } - /** Scale a given point list by the current frame divisor. the point list is mutated! */ private void dividePointList(List points) { for (var p : points) { diff --git a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SolvePNPPipe.java b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SolvePNPPipe.java index db773f6375..6a887d3a18 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SolvePNPPipe.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipe/impl/SolvePNPPipe.java @@ -18,7 +18,6 @@ package org.photonvision.vision.pipe.impl; import edu.wpi.first.math.VecBuilder; -import edu.wpi.first.math.geometry.Pose3d; import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Transform3d; import edu.wpi.first.math.geometry.Translation3d; @@ -27,7 +26,6 @@ import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint2f; -import org.opencv.core.Scalar; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.math.MathUtils; @@ -99,33 +97,12 @@ private void calculateTargetPose(TrackedTarget target) { VecBuilder.fill(rVec.get(0, 0)[0], rVec.get(1, 0)[0], rVec.get(2, 0)[0]), Core.norm(rVec)); - Pose3d targetPose = MathUtils.convertOpenCVtoPhotonPose(new Transform3d(translation, rotation)); - target.setBestCameraToTarget3d( - new Transform3d(targetPose.getTranslation(), targetPose.getRotation())); + Transform3d camToTarget = + MathUtils.convertOpenCVtoPhotonTransform(new Transform3d(translation, rotation)); + target.setBestCameraToTarget3d(camToTarget); target.setAltCameraToTarget3d(new Transform3d()); } - Mat rotationMatrix = new Mat(); - Mat inverseRotationMatrix = new Mat(); - Mat pzeroWorld = new Mat(); - Mat kMat = new Mat(); - Mat scaledTvec; - - /** - * Element-wise scale a matrix by a given factor - * - * @param src the source matrix - * @param factor by how much to scale each element - * @return the scaled matrix - */ - @SuppressWarnings("SameParameterValue") - private static Mat matScale(Mat src, double factor) { - Mat dst = new Mat(src.rows(), src.cols(), src.type()); - Scalar s = new Scalar(factor); - Core.multiply(src, s, dst); - return dst; - } - public static class SolvePNPPipeParams { private final CameraCalibrationCoefficients cameraCoefficients; private final TargetModel targetModel; diff --git a/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java b/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java index af1c5145d4..c7ffe1a83c 100644 --- a/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java +++ b/photon-core/src/main/java/org/photonvision/vision/pipeline/AprilTagPipeline.java @@ -146,8 +146,10 @@ protected CVPipelineResult process(Frame frame, AprilTagPipelineSettings setting new TargetCalculationParameters( false, null, null, null, null, frameStaticProperties)); - var correctedBestPose = MathUtils.convertOpenCVtoPhotonPose(target.getBestCameraToTarget3d()); - var correctedAltPose = MathUtils.convertOpenCVtoPhotonPose(target.getAltCameraToTarget3d()); + var correctedBestPose = + MathUtils.convertOpenCVtoPhotonTransform(target.getBestCameraToTarget3d()); + var correctedAltPose = + MathUtils.convertOpenCVtoPhotonTransform(target.getAltCameraToTarget3d()); target.setBestCameraToTarget3d( new Transform3d(correctedBestPose.getTranslation(), correctedBestPose.getRotation())); 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 ea06638af8..198cbd5139 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 @@ -25,37 +25,50 @@ import org.opencv.core.MatOfPoint3f; import org.opencv.core.Point3; import org.photonvision.vision.opencv.Releasable; - +import org.photonvision.vision.pipe.impl.CornerDetectionPipe; +import org.photonvision.vision.pipe.impl.SolvePNPPipe; + +/** + * A model representing the vertices of targets with known shapes. The vertices are in the EDN + * coordinate system. When creating a TargetModel, the vertices must be supplied in a certain order + * to ensure correct correspondence with corners detected in 2D for use with SolvePNP. For planar + * targets, we expect the target's Z-axis to point towards the camera. + * + *

{@link SolvePNPPipe} expects 3d object points to correspond to the {@link CornerDetectionPipe} + * implementation. The 2d corner detection finds the 4 extreme corners (bottom-left, bottom-right, + * top-right, top-left). To match our expectations, this means the model vertices would look like: + * + *

    + *
  • (+x, +y, 0) + *
  • (-x, +y, 0) + *
  • (-x, -y, 0) + *
  • (+x, -y, 0) + *
+ * + *

AprilTag models are currently only used for drawing on the output stream. + */ public enum TargetModel implements Releasable { - k2020HighGoalOuter( - List.of( - new Point3(Units.inchesToMeters(-19.625), 0, 0), - new Point3(Units.inchesToMeters(-9.819867), Units.inchesToMeters(-17), 0), - new Point3(Units.inchesToMeters(9.819867), Units.inchesToMeters(-17), 0), - new Point3(Units.inchesToMeters(19.625), 0, 0)), - Units.inchesToMeters(12)), - k2020HighGoalInner( + k2016HighGoal( List.of( - new Point3(Units.inchesToMeters(-19.625), 0, Units.inchesToMeters(2d * 12d + 5.25)), - new Point3( - Units.inchesToMeters(-9.819867), - Units.inchesToMeters(-17), - Units.inchesToMeters(2d * 12d + 5.25)), - new Point3( - Units.inchesToMeters(9.819867), - Units.inchesToMeters(-17), - Units.inchesToMeters(2d * 12d + 5.25)), - new Point3(Units.inchesToMeters(19.625), 0, Units.inchesToMeters(2d * 12d + 5.25))), - Units.inchesToMeters(12)), - + new Point3(Units.inchesToMeters(10), Units.inchesToMeters(6), 0), + new Point3(Units.inchesToMeters(-10), Units.inchesToMeters(6), 0), + new Point3(Units.inchesToMeters(-10), Units.inchesToMeters(-6), 0), + new Point3(Units.inchesToMeters(10), Units.inchesToMeters(-6), 0)), + Units.inchesToMeters(6)), k2019DualTarget( List.of( - new Point3(Units.inchesToMeters(-5.936), Units.inchesToMeters(2.662), 0), - new Point3(Units.inchesToMeters(-7.313), Units.inchesToMeters(-2.662), 0), - new Point3(Units.inchesToMeters(7.313), Units.inchesToMeters(-2.662), 0), - new Point3(Units.inchesToMeters(5.936), Units.inchesToMeters(2.662), 0)), + new Point3(Units.inchesToMeters(7.313), Units.inchesToMeters(2.662), 0), + new Point3(Units.inchesToMeters(-7.313), Units.inchesToMeters(2.662), 0), + new Point3(Units.inchesToMeters(-5.936), Units.inchesToMeters(-2.662), 0), + new Point3(Units.inchesToMeters(5.936), Units.inchesToMeters(-2.662), 0)), 0.1), - + k2020HighGoalOuter( + List.of( + new Point3(Units.inchesToMeters(9.819867), Units.inchesToMeters(8.5), 0), + new Point3(Units.inchesToMeters(-9.819867), Units.inchesToMeters(8.5), 0), + new Point3(Units.inchesToMeters(-19.625), Units.inchesToMeters(-8.5), 0), + new Point3(Units.inchesToMeters(19.625), Units.inchesToMeters(-8.5), 0)), + Units.inchesToMeters(12)), kCircularPowerCell7in( List.of( new Point3( @@ -94,36 +107,26 @@ public enum TargetModel implements Releasable { -Units.inchesToMeters(9.5) / 2, -Units.inchesToMeters(9.5) / 2)), 0), - k2016HighGoal( - List.of( - new Point3(Units.inchesToMeters(-10), Units.inchesToMeters(12), 0), - new Point3(Units.inchesToMeters(-10), Units.inchesToMeters(0), 0), - new Point3(Units.inchesToMeters(10), Units.inchesToMeters(0), 0), - new Point3(Units.inchesToMeters(10), Units.inchesToMeters(12), 0)), - Units.inchesToMeters(6)), - k200mmAprilTag( // Nominal edge length of 200 mm includes the white border, but solvePNP corners - // do not + k200mmAprilTag( // Corners of the tag's inner black square (excluding white border) List.of( - new Point3(-Units.inchesToMeters(3.25), Units.inchesToMeters(3.25), 0), new Point3(Units.inchesToMeters(3.25), Units.inchesToMeters(3.25), 0), - new Point3(Units.inchesToMeters(3.25), -Units.inchesToMeters(3.25), 0), - new Point3(-Units.inchesToMeters(3.25), -Units.inchesToMeters(3.25), 0)), + new Point3(-Units.inchesToMeters(3.25), Units.inchesToMeters(3.25), 0), + new Point3(-Units.inchesToMeters(3.25), -Units.inchesToMeters(3.25), 0), + new Point3(Units.inchesToMeters(3.25), -Units.inchesToMeters(3.25), 0)), Units.inchesToMeters(3.25 * 2)), - kAruco6in_16h5( // Nominal edge length of 200 mm includes the white border, but solvePNP corners - // do not + kAruco6in_16h5( // Corners of the tag's inner black square (excluding white border) List.of( new Point3(Units.inchesToMeters(3), Units.inchesToMeters(3), 0), new Point3(Units.inchesToMeters(3), -Units.inchesToMeters(3), 0), new Point3(-Units.inchesToMeters(3), -Units.inchesToMeters(3), 0), new Point3(Units.inchesToMeters(3), -Units.inchesToMeters(3), 0)), Units.inchesToMeters(3 * 2)), - k6in_16h5( // Nominal edge length of 200 mm includes the white border, but solvePNP corners - // do not + k6in_16h5( // Corners of the tag's inner black square (excluding white border) List.of( - new Point3(-Units.inchesToMeters(3), Units.inchesToMeters(3), 0), new Point3(Units.inchesToMeters(3), Units.inchesToMeters(3), 0), - new Point3(Units.inchesToMeters(3), -Units.inchesToMeters(3), 0), - new Point3(-Units.inchesToMeters(3), -Units.inchesToMeters(3), 0)), + new Point3(-Units.inchesToMeters(3), Units.inchesToMeters(3), 0), + new Point3(-Units.inchesToMeters(3), -Units.inchesToMeters(3), 0), + new Point3(Units.inchesToMeters(3), -Units.inchesToMeters(3), 0)), Units.inchesToMeters(3 * 2)); @JsonIgnore private MatOfPoint3f realWorldTargetCoordinates; diff --git a/photon-core/src/test/java/org/photonvision/common/util/CoordinateConversionTest.java b/photon-core/src/test/java/org/photonvision/common/util/CoordinateConversionTest.java new file mode 100644 index 0000000000..a452f517f5 --- /dev/null +++ b/photon-core/src/test/java/org/photonvision/common/util/CoordinateConversionTest.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +package org.photonvision.common.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wpi.first.math.VecBuilder; +import edu.wpi.first.math.geometry.Rotation3d; +import edu.wpi.first.math.geometry.Transform3d; +import edu.wpi.first.math.geometry.Translation3d; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.photonvision.common.util.math.MathUtils; + +public class CoordinateConversionTest { + @BeforeAll + public static void Init() { + TestUtils.loadLibraries(); + } + + @Test + public void testAprilTagToOpenCV() { + // AprilTag and OpenCV both use the EDN coordinate system. AprilTag, however, assumes the tag's + // z-axis points away from the camera while we expect it to point towards the camera. + var apriltag = + new Transform3d( + new Translation3d(1, 2, 3), + new Rotation3d(Math.toRadians(5), Math.toRadians(10), Math.toRadians(15))); + var opencv = MathUtils.convertApriltagtoOpenCV(apriltag); + final var expectedTrl = new Translation3d(1, 2, 3); + assertEquals( + expectedTrl, opencv.getTranslation(), "AprilTag to OpenCV translation conversion failed"); + var apriltagXaxis = new Translation3d(1, 0, 0).rotateBy(apriltag.getRotation()); + var apriltagYaxis = new Translation3d(0, 1, 0).rotateBy(apriltag.getRotation()); + var apriltagZaxis = new Translation3d(0, 0, 1).rotateBy(apriltag.getRotation()); + var opencvXaxis = new Translation3d(1, 0, 0).rotateBy(opencv.getRotation()); + var opencvYaxis = new Translation3d(0, 1, 0).rotateBy(opencv.getRotation()); + var opencvZaxis = new Translation3d(0, 0, 1).rotateBy(opencv.getRotation()); + assertEquals( + apriltagXaxis.unaryMinus(), + opencvXaxis, + "AprilTag to OpenCV rotation conversion failed(X-axis)"); + assertEquals( + apriltagYaxis, opencvYaxis, "AprilTag to OpenCV rotation conversion failed(Y-axis)"); + assertEquals( + apriltagZaxis.unaryMinus(), + opencvZaxis, + "AprilTag to OpenCV rotation conversion failed(Z-axis)"); + } + + @Test + public void testOpenCVToPhoton() { + // OpenCV uses the EDN coordinate system while wpilib is in NWU. + var opencv = + new Transform3d( + new Translation3d(1, 2, 3), new Rotation3d(VecBuilder.fill(1, 2, 3), Math.PI / 8)); + var wpilib = MathUtils.convertOpenCVtoPhotonTransform(opencv); + final var expectedTrl = new Translation3d(3, -1, -2); + assertEquals( + expectedTrl, wpilib.getTranslation(), "OpenCV to WPILib translation conversion failed"); + var expectedRot = new Rotation3d(VecBuilder.fill(3, -1, -2), Math.PI / 8); + assertEquals(expectedRot, wpilib.getRotation(), "OpenCV to WPILib rotation conversion failed"); + } +} diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java index 1ece69fc55..fe1b7b9d9c 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/AprilTagTest.java @@ -75,20 +75,19 @@ public void testApriltagFacingCamera() { // these numbers are not *accurate*, but they are known and expected var pose = pipelineResult.targets.get(0).getBestCameraToTarget3d(); Assertions.assertEquals(2, pose.getTranslation().getX(), 0.2); - Assertions.assertEquals(0.0, pose.getTranslation().getY(), 0.2); + Assertions.assertEquals(0.1, pose.getTranslation().getY(), 0.2); Assertions.assertEquals(0.0, pose.getTranslation().getZ(), 0.2); - var objX = new Translation3d(1, 0, 0).rotateBy(pose.getRotation()).getY(); - var objY = new Translation3d(0, 1, 0).rotateBy(pose.getRotation()).getZ(); - var objZ = new Translation3d(0, 0, 1).rotateBy(pose.getRotation()).getX(); + // We expect the object axes to be in NWU, with the x-axis coming out of the tag + // This visible tag is facing the camera almost parallel, so in world space: - // We expect the object X to be forward, or -X in world space + // The object's X axis should be (-1, 0, 0) Assertions.assertEquals( -1, new Translation3d(1, 0, 0).rotateBy(pose.getRotation()).getX(), 0.1); - // We expect the object Y axis to be right, or negative-Y in world space + // The object's Y axis should be (0, -1, 0) Assertions.assertEquals( -1, new Translation3d(0, 1, 0).rotateBy(pose.getRotation()).getY(), 0.1); - // We expect the object Z axis to be up, or +Z in world space + // The object's Z axis should be (0, 0, 1) Assertions.assertEquals(1, new Translation3d(0, 0, 1).rotateBy(pose.getRotation()).getZ(), 0.1); } diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/SolvePNPTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/SolvePNPTest.java index 02852308b9..03bcc2535d 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/SolvePNPTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/SolvePNPTest.java @@ -19,11 +19,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import edu.wpi.first.math.geometry.Rotation3d; import edu.wpi.first.math.geometry.Translation3d; import edu.wpi.first.math.util.Units; import java.util.stream.Collectors; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.photonvision.common.util.TestUtils; @@ -115,28 +116,31 @@ public void test2019() { pipeline.getSettings().hueInverted); frameProvider.requestHsvSettings(hsvParams); - CVPipelineResult pipelineResult; - - pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera); + CVPipelineResult pipelineResult = pipeline.run(frameProvider.get(), QuirkyCamera.DefaultCamera); printTestResults(pipelineResult); - // these numbers are not *accurate*, but they are known and expected - var pose = pipelineResult.targets.get(0).getBestCameraToTarget3d(); - Assertions.assertEquals(1.1, pose.getTranslation().getX(), 0.05); - Assertions.assertEquals(0.0, pose.getTranslation().getY(), 0.05); - - // We expect the object X to be forward, or -X in world space - Assertions.assertEquals( - -1, new Translation3d(1, 0, 0).rotateBy(pose.getRotation()).getX(), 0.05); - // We expect the object Y axis to be right, or negative-Y in world space - Assertions.assertEquals( - -1, new Translation3d(0, 1, 0).rotateBy(pose.getRotation()).getY(), 0.05); - // We expect the object Z axis to be up, or +Z in world space - Assertions.assertEquals( - 1, new Translation3d(0, 0, 1).rotateBy(pose.getRotation()).getZ(), 0.05); + // Draw on input + var outputPipe = new OutputStreamPipeline(); + outputPipe.process( + pipelineResult.inputAndOutputFrame, pipeline.getSettings(), pipelineResult.targets); TestUtils.showImage( - pipelineResult.inputAndOutputFrame.colorImage.getMat(), "Pipeline output", 999999); + pipelineResult.inputAndOutputFrame.processedImage.getMat(), "Pipeline output", 999999); + + var pose = pipelineResult.targets.get(0).getBestCameraToTarget3d(); + // these numbers are not *accurate*, but they are known and expected + var expectedTrl = new Translation3d(1.1, -0.05, -0.05); + assertTrue( + expectedTrl.getDistance(pose.getTranslation()) < 0.05, + "SolvePNP translation estimation failed"); + // We expect the object axes to be in NWU, with the x-axis coming out of the tag + // This target is facing the camera almost parallel, so in world space: + // The object's X axis should be (-1, 0, 0) + assertEquals(-1, new Translation3d(1, 0, 0).rotateBy(pose.getRotation()).getX(), 0.05); + // The object's Y axis should be (0, -1, 0) + assertEquals(-1, new Translation3d(0, 1, 0).rotateBy(pose.getRotation()).getY(), 0.05); + // The object's Z axis should be (0, 0, 1) + assertEquals(1, new Translation3d(0, 0, 1).rotateBy(pose.getRotation()).getZ(), 0.05); } @Test @@ -175,15 +179,27 @@ public void test2020() { outputPipe.process( pipelineResult.inputAndOutputFrame, pipeline.getSettings(), pipelineResult.targets); - // these numbers are not *accurate*, but they are known and expected - var pose = pipelineResult.targets.get(0).getBestCameraToTarget3d(); - Assertions.assertEquals(Units.inchesToMeters(240.26), pose.getTranslation().getX(), 0.05); - Assertions.assertEquals(Units.inchesToMeters(35), pose.getTranslation().getY(), 0.05); - // Z rotation should be mostly facing us - Assertions.assertEquals(Units.degreesToRadians(-140), pose.getRotation().getZ(), 1); - TestUtils.showImage( - pipelineResult.inputAndOutputFrame.colorImage.getMat(), "Pipeline output", 999999); + pipelineResult.inputAndOutputFrame.processedImage.getMat(), "Pipeline output", 999999); + + var pose = pipelineResult.targets.get(0).getBestCameraToTarget3d(); + // these numbers are not *accurate*, but they are known and expected + var expectedTrl = + new Translation3d( + Units.inchesToMeters(236), Units.inchesToMeters(36), Units.inchesToMeters(-53)); + assertTrue( + expectedTrl.getDistance(pose.getTranslation()) < 0.05, + "SolvePNP translation estimation failed"); + // We expect the object axes to be in NWU, with the x-axis coming out of the tag + // Rotation around Z axis (yaw) should be mostly facing us + var xAxis = new Translation3d(1, 0, 0); + var yAxis = new Translation3d(0, 1, 0); + var zAxis = new Translation3d(0, 0, 1); + var expectedRot = + new Rotation3d(Math.toRadians(-20), Math.toRadians(-20), Math.toRadians(-120)); + assertTrue(xAxis.rotateBy(expectedRot).getDistance(xAxis.rotateBy(pose.getRotation())) < 0.1); + assertTrue(yAxis.rotateBy(expectedRot).getDistance(yAxis.rotateBy(pose.getRotation())) < 0.1); + assertTrue(zAxis.rotateBy(expectedRot).getDistance(zAxis.rotateBy(pose.getRotation())) < 0.1); } private static void continuouslyRunPipeline(Frame frame, ReflectivePipelineSettings settings) {