From 5e830fae57e1e80d7e2c7660194ab313f661fb05 Mon Sep 17 00:00:00 2001 From: Gold856 <117957790+Gold856@users.noreply.github.com> Date: Sat, 6 Dec 2025 13:10:47 -0500 Subject: [PATCH] Update mrcal-java (#2214) ## Description Updating to take advantage of the now independent mrcal-java (no longer need to install SuiteSparse and friends!) ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2025.3.2 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added --------- Co-authored-by: Matt Morley --- .github/workflows/build.yml | 23 +++--- README.md | 8 -- build.gradle | 2 +- photon-core/build.gradle | 2 +- .../websocket/UIPhotonConfiguration.java | 4 +- .../photonvision/common/util/TestUtils.java | 19 +++++ .../photonvision/mrcal/MrCalJNILoader.java | 82 ------------------- .../vision/pipe/impl/Calibrate3dPipe.java | 4 +- .../vision/pipeline/Calibrate3dPipeTest.java | 3 +- .../pipeline/CalibrationRotationPipeTest.java | 3 +- .../src/main/java/org/photonvision/Main.java | 5 +- 11 files changed, 40 insertions(+), 115 deletions(-) delete mode 100644 photon-core/src/main/java/org/photonvision/mrcal/MrCalJNILoader.java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 868599527..a24b48bd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,15 +50,24 @@ jobs: distribution: temurin - name: Install RoboRIO Toolchain run: ./gradlew installRoboRioToolchain + - name: Delete duplicate toolchains + run: | + find ~/.gradle/cache/ -name *roborio-academic* -exec rm -rf {} + + du -h . | sort -h + if: matrix.os == 'ubuntu-22.04' # Need to publish to maven local first, so that C++ sim can pick it up - name: Publish photonlib to maven local run: ./gradlew photon-targeting:publishtomavenlocal photon-lib:publishtomavenlocal -x check - name: Build Java examples working-directory: photonlib-java-examples - run: ./gradlew build + run: | + ./gradlew build + ./gradlew clean - name: Build C++ examples working-directory: photonlib-cpp-examples - run: ./gradlew build + run: | + ./gradlew build + ./gradlew clean playwright-tests: name: "Playwright E2E tests" runs-on: ubuntu-22.04 @@ -84,8 +93,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 - - name: Install mrcal deps - run: sudo apt-get update && sudo apt-get install -y libcholmod3 liblapack3 libsuitesparseconfig5 - name: Setup tests working-directory: photon-client run: | @@ -127,8 +134,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: 22 - - name: Install mrcal deps - run: sudo apt-get update && sudo apt-get install -y libcholmod3 liblapack3 libsuitesparseconfig5 - name: Gradle Build run: ./gradlew photon-targeting:build photon-core:build photon-server:build -x check - name: Gradle Tests and Coverage @@ -399,12 +404,6 @@ jobs: - uses: actions/download-artifact@v4 with: name: ${{ matrix.artifact-name }} - # On linux, install mrcal packages - - run: | - sudo apt-get update - sudo apt-get install --yes libcholmod3 liblapack3 libsuitesparseconfig5 - if: ${{ (matrix.os) == 'ubuntu-24.04' }} - # and actually run the jar - run: java -jar ${{ matrix.extraOpts }} *.jar --smoketest if: ${{ (matrix.os) != 'windows-latest' }} - run: ls *.jar | %{ Write-Host "Running $($_.Name)"; Start-Process "java" -ArgumentList "-jar `"$($_.FullName)`" --smoketest" -NoNewWindow -Wait; break } diff --git a/README.md b/README.md index 8b2a62c87..937b9c722 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,6 @@ PhotonVision uses the following additional out-of-source repositories for buildi - Custom build of OpenCV with GStreamer/Protobuf/other custom flags: https://github.com/PhotonVision/thirdparty-opencv - JNI code for aruco-nano: https://github.com/PhotonVision/aruconano-jni -## Additional packages - -For now, using mrcal requires installing these additional packages on Linux systems: - -``` -sudo apt install libcholmod3 liblapack3 libsuitesparseconfig5 -``` - ## Acknowledgments PhotonVision was forked from [Chameleon Vision](https://github.com/Chameleon-Vision/chameleon-vision/). Thank you to everyone who worked on the original project. diff --git a/build.gradle b/build.gradle index fcbbe71c3..e1b5cbaf9 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ ext { rknnVersion = "dev-v2025.0.0-5-g666c0c6" rubikVersion = "dev-v2025.1.0-6-g4a5e508" frcYear = "2025" - mrcalVersion = "v2025.0.0"; + mrcalVersion = "dev-v2025.0.0-2-g2adb187"; pubVersion = versionString diff --git a/photon-core/build.gradle b/photon-core/build.gradle index 6e9c99c1f..0f013eb93 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -56,7 +56,7 @@ dependencies { "osxx86-64", "osxarm64" ])) { - implementation("org.photonvision:photon-mrcal-jni:$mrcalVersion:$wpilibNativeName") { + wpilibNatives("org.photonvision:photon-mrcal-jni:$mrcalVersion:$wpilibNativeName") { transitive = false } } diff --git a/photon-core/src/main/java/org/photonvision/common/dataflow/websocket/UIPhotonConfiguration.java b/photon-core/src/main/java/org/photonvision/common/dataflow/websocket/UIPhotonConfiguration.java index 14dc59033..3373337e6 100644 --- a/photon-core/src/main/java/org/photonvision/common/dataflow/websocket/UIPhotonConfiguration.java +++ b/photon-core/src/main/java/org/photonvision/common/dataflow/websocket/UIPhotonConfiguration.java @@ -25,7 +25,7 @@ import org.photonvision.common.dataflow.networktables.NetworkTablesManager; import org.photonvision.common.hardware.Platform; import org.photonvision.common.networking.NetworkManager; import org.photonvision.common.networking.NetworkUtils; -import org.photonvision.mrcal.MrCalJNILoader; +import org.photonvision.common.util.TestUtils; import org.photonvision.raspi.LibCameraJNILoader; import org.photonvision.vision.processes.VisionModule; import org.photonvision.vision.processes.VisionSourceManager; @@ -54,7 +54,7 @@ public class UIPhotonConfiguration { PhotonVersion.versionString, // TODO add support for other types of GPU accel LibCameraJNILoader.getInstance().isSupported() ? "Zerocopy Libcamera Working" : "", - MrCalJNILoader.getInstance().isLoaded(), + TestUtils.isMrcalLoaded(), c.neuralNetworkPropertyManager().getModels(), NeuralNetworkModelManager.getInstance().getSupportedBackends(), c.getHardwareConfig().deviceName.isEmpty() 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 eeef84d49..28e1c1a79 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 @@ -20,6 +20,7 @@ package org.photonvision.common.util; import com.fasterxml.jackson.databind.ObjectMapper; import edu.wpi.first.math.geometry.Translation2d; import edu.wpi.first.math.util.Units; +import edu.wpi.first.util.CombinedRuntimeLoader; import java.awt.HeadlessException; import java.io.File; import java.io.IOException; @@ -32,10 +33,28 @@ import org.photonvision.vision.pipeline.result.CVPipelineResult; import org.photonvision.vision.target.TrackedTarget; public class TestUtils { + private static boolean hasMrcalLoaded = false; + public static boolean loadLibraries() { return LibraryLoader.loadWpiLibraries() && LibraryLoader.loadTargeting(); } + public static boolean loadMrcal() { + if (hasMrcalLoaded) return true; + try { + CombinedRuntimeLoader.loadLibraries(TestUtils.class, "mrcal_jni"); + hasMrcalLoaded = true; + } catch (IOException e) { + e.printStackTrace(); + hasMrcalLoaded = false; + } + return hasMrcalLoaded; + } + + public static boolean isMrcalLoaded() { + return hasMrcalLoaded; + } + @SuppressWarnings("unused") public enum WPI2019Image { kCargoAngledDark48in(1.2192), diff --git a/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNILoader.java b/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNILoader.java deleted file mode 100644 index 051c7978e..000000000 --- a/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNILoader.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) Photon Vision. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.photonvision.mrcal; - -import java.io.IOException; -import java.util.List; -import org.photonvision.common.hardware.Platform; -import org.photonvision.common.util.TestUtils; -import org.photonvision.jni.PhotonJNICommon; - -public class MrCalJNILoader extends PhotonJNICommon { - private boolean isLoaded; - private static MrCalJNILoader instance = null; - - private MrCalJNILoader() { - isLoaded = false; - } - - public static synchronized MrCalJNILoader getInstance() { - if (instance == null) instance = new MrCalJNILoader(); - - return instance; - } - - public static synchronized void forceLoad() throws IOException { - // Force load opencv - TestUtils.loadLibraries(); - - // Library naming is dumb and has "lib" appended for Windows when it ought not to - if (Platform.isWindows()) { - // Order is correct to match dependencies of libraries - forceLoad( - MrCalJNILoader.getInstance(), - MrCalJNILoader.class, - List.of( - "libamd", - "libcamd", - "libcolamd", - "libccolamd", - "openblas", - "libwinpthread-1", - "libgcc_s_seh-1", - "libquadmath-0", - "libgfortran-5", - "liblapack", - "libcholmod", - "mrcal_jni")); - } else { - // Nothing else to do on linux - forceLoad(MrCalJNILoader.getInstance(), MrCalJNILoader.class, List.of("mrcal_jni")); - } - - if (!MrCalJNILoader.getInstance().isLoaded()) { - throw new IOException("Unable to load mrcal JNI!"); - } - } - - @Override - public boolean isLoaded() { - return isLoaded; - } - - @Override - public void setLoaded(boolean state) { - isLoaded = state; - } -} 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 bfa96c5a3..aa7dbcadc 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 @@ -29,10 +29,10 @@ import org.opencv.core.*; import org.opencv.imgcodecs.Imgcodecs; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; +import org.photonvision.common.util.TestUtils; import org.photonvision.common.util.math.MathUtils; import org.photonvision.mrcal.MrCalJNI; import org.photonvision.mrcal.MrCalJNI.MrCalResult; -import org.photonvision.mrcal.MrCalJNILoader; import org.photonvision.vision.calibration.BoardObservation; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; import org.photonvision.vision.calibration.CameraLensModel; @@ -94,7 +94,7 @@ public class Calibrate3dPipe CameraCalibrationCoefficients ret; var start = System.nanoTime(); - if (MrCalJNILoader.getInstance().isLoaded() && params.useMrCal) { + if (TestUtils.isMrcalLoaded() && params.useMrCal) { logger.debug("Calibrating with mrcal!"); ret = calibrateMrcal( 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 139aa9a90..3442d68e0 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 @@ -37,7 +37,6 @@ import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.LogLevel; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.TestUtils; -import org.photonvision.mrcal.MrCalJNILoader; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; import org.photonvision.vision.camera.QuirkyCamera; import org.photonvision.vision.frame.Frame; @@ -52,7 +51,7 @@ public class Calibrate3dPipeTest { @BeforeAll public static void init() throws IOException { TestUtils.loadLibraries(); - MrCalJNILoader.forceLoad(); + TestUtils.loadMrcal(); var logLevel = LogLevel.DEBUG; Logger.setLevel(LogGroup.Camera, logLevel); diff --git a/photon-core/src/test/java/org/photonvision/vision/pipeline/CalibrationRotationPipeTest.java b/photon-core/src/test/java/org/photonvision/vision/pipeline/CalibrationRotationPipeTest.java index 53283228b..16b0e6655 100644 --- a/photon-core/src/test/java/org/photonvision/vision/pipeline/CalibrationRotationPipeTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/pipeline/CalibrationRotationPipeTest.java @@ -35,7 +35,6 @@ import org.photonvision.common.logging.LogLevel; import org.photonvision.common.logging.Logger; import org.photonvision.common.util.TestUtils; import org.photonvision.estimation.OpenCVHelp; -import org.photonvision.mrcal.MrCalJNILoader; import org.photonvision.vision.calibration.CameraCalibrationCoefficients; import org.photonvision.vision.calibration.CameraLensModel; import org.photonvision.vision.calibration.JsonMatOfDouble; @@ -50,7 +49,7 @@ public class CalibrationRotationPipeTest { @BeforeAll public static void init() throws IOException { TestUtils.loadLibraries(); - MrCalJNILoader.forceLoad(); + TestUtils.loadMrcal(); var logLevel = LogLevel.DEBUG; Logger.setLevel(LogGroup.Camera, logLevel); diff --git a/photon-server/src/main/java/org/photonvision/Main.java b/photon-server/src/main/java/org/photonvision/Main.java index 4e03758e2..5fdd535e0 100644 --- a/photon-server/src/main/java/org/photonvision/Main.java +++ b/photon-server/src/main/java/org/photonvision/Main.java @@ -41,7 +41,6 @@ import org.photonvision.common.util.TestUtils; import org.photonvision.jni.LibraryLoader; import org.photonvision.jni.RknnDetectorJNI; import org.photonvision.jni.RubikDetectorJNI; -import org.photonvision.mrcal.MrCalJNILoader; import org.photonvision.raspi.LibCameraJNILoader; import org.photonvision.server.Server; import org.photonvision.vision.apriltag.AprilTagFamily; @@ -262,8 +261,8 @@ public class Main { logger.error("Failed to load rubik-JNI!", e); } try { - MrCalJNILoader.forceLoad(); - } catch (IOException e) { + TestUtils.loadMrcal(); + } catch (Exception e) { logger.warn( "Failed to load mrcal-JNI! Camera calibration will fall back to opencv\n" + e.getMessage());