diff --git a/build.gradle b/build.gradle index fdf2fa70cb..2b8ef8ded2 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ allprojects { mavenCentral() mavenLocal() maven { url = "https://maven.photonvision.org/repository/internal/" } + maven { url = "https://maven.photonvision.org/repository/snapshots/" } } wpilibRepositories.addAllReleaseRepositories(it) wpilibRepositories.addAllDevelopmentRepositories(it) @@ -30,13 +31,14 @@ ext { isDev = pubVersion.startsWith("dev") // A list, for legacy reasons, with only the current platform contained - String nativeName = wpilibTools.platformMapper.currentPlatform.platformName; - if (nativeName == "linuxx64") nativeName = "linuxx86-64"; - if (nativeName == "winx64") nativeName = "windowsx86-64"; - if (nativeName == "macx64") nativeName = "osxx86-64"; - if (nativeName == "macarm64") nativeName = "osxarm64"; + wpilibNativeName = wpilibTools.platformMapper.currentPlatform.platformName; + def nativeName = wpilibNativeName + if (wpilibNativeName == "linuxx64") nativeName = "linuxx86-64"; + if (wpilibNativeName == "winx64") nativeName = "windowsx86-64"; + if (wpilibNativeName == "macx64") nativeName = "osxx86-64"; + if (wpilibNativeName == "macarm64") nativeName = "osxarm64"; jniPlatform = nativeName - println("Building for platform " + jniPlatform) + println("Building for platform " + jniPlatform + " wpilib: " + wpilibNativeName) } wpilibTools.deps.wpilibVersion = wpilibVersion diff --git a/photon-core/build.gradle b/photon-core/build.gradle index 6fbf407797..b402a84734 100644 --- a/photon-core/build.gradle +++ b/photon-core/build.gradle @@ -27,6 +27,9 @@ dependencies { implementation wpilibTools.deps.wpilibJava("apriltag") implementation "org.xerial:sqlite-jdbc:3.41.0.0" + + implementation "org.photonvision:photon-mrcal-java:dev-Unknown" + implementation "org.photonvision:photon-mrcal-jni:dev-Unknown:" + wpilibNativeName; } task writeCurrentVersionJava { 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 b625258582..00b55af584 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 @@ -28,9 +28,9 @@ @SuppressWarnings("unused") public enum Platform { // WPILib Supported (JNI) - WINDOWS_64("Windows x64", "winx86-64", false, OSType.WINDOWS, true), - LINUX_32("Linux x86", "linuxx86-64", false, OSType.LINUX, true), - LINUX_64("Linux x64", "linuxx86-64", false, OSType.LINUX, true), + WINDOWS_64("Windows x64", "winx64", false, OSType.WINDOWS, true), + LINUX_32("Linux x86", "linuxx64", false, OSType.LINUX, true), + LINUX_64("Linux x64", "linuxx64", false, OSType.LINUX, true), LINUX_RASPBIAN32( "Linux Raspbian 32-bit", "linuxarm32", @@ -51,7 +51,7 @@ public enum Platform { LINUX_ARM64("Linux ARM64", "linuxarm64", false, OSType.LINUX, true), // ODROID C2, N2 // Completely unsupported - WINDOWS_32("Windows x86", "windowsx86-64", false, OSType.WINDOWS, false), + WINDOWS_32("Windows x86", "windowsx64", false, OSType.WINDOWS, false), MACOS("Mac OS", "osxuniversal", false, OSType.MACOS, false), UNKNOWN("Unsupported Platform", "", false, OSType.UNKNOWN, false); @@ -233,4 +233,9 @@ private static boolean fileHasText(String filename, String text) { return false; } } + + public static boolean isWindows() { + var p = getCurrentPlatform(); + return (p == WINDOWS_32 || p == WINDOWS_64); + } } diff --git a/photon-core/src/main/java/org/photonvision/jni/PhotonJniCommon.java b/photon-core/src/main/java/org/photonvision/jni/PhotonJniCommon.java index 3e7138b8b0..80d867f8fd 100644 --- a/photon-core/src/main/java/org/photonvision/jni/PhotonJniCommon.java +++ b/photon-core/src/main/java/org/photonvision/jni/PhotonJniCommon.java @@ -24,6 +24,8 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; + import org.photonvision.common.hardware.Platform; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; @@ -32,44 +34,52 @@ public abstract class PhotonJniCommon { static boolean libraryLoaded = false; protected static Logger logger = null; - protected static synchronized void forceLoad(Class clazz, String libraryName) throws IOException { - if (libraryLoaded) return; - if (logger == null) logger = new Logger(clazz, LogGroup.Camera); - - try { + protected static synchronized void forceLoad(Class clazz, List libraries) throws IOException { + if (libraryLoaded) + return; + if (logger == null) + logger = new Logger(clazz, LogGroup.Camera); - // We always extract the shared object (we could hash each so, but that's a lot of work) - var arch_name = Platform.getNativeLibraryFolderName(); - var nativeLibName = System.mapLibraryName(libraryName); - var in = clazz.getResourceAsStream("/nativelibraries/" + arch_name + "/" + nativeLibName); - - if (in == null) { - libraryLoaded = false; - return; - } + for (var libraryName : libraries) { + try { + // We always extract the shared object (we could hash each so, but that's a lot of work) + var arch_name = Platform.getNativeLibraryFolderName(); + var nativeLibName = System.mapLibraryName(libraryName); + var in = clazz.getResourceAsStream("/nativelibraries/" + arch_name + "/" + nativeLibName); - File temp = File.createTempFile(nativeLibName, ""); - FileOutputStream fos = new FileOutputStream(temp); + if (in == null) { + libraryLoaded = false; + return; + } - int read = -1; - byte[] buffer = new byte[1024]; - while((read = in.read(buffer)) != -1) { - fos.write(buffer, 0, read); - } - fos.close(); - in.close(); + // It's important that we don't mangle the names of these files on Windows at least + File temp = new File(System.getProperty("java.io.tmpdir"), nativeLibName); + FileOutputStream fos = new FileOutputStream(temp); - System.load(temp.getAbsolutePath()); + int read = -1; + byte[] buffer = new byte[1024]; + while ((read = in.read(buffer)) != -1) { + fos.write(buffer, 0, read); + } + fos.close(); + in.close(); - libraryLoaded = true; - logger.info("Successfully loaded shared object"); + System.load(temp.getAbsolutePath()); + logger.info("Successfully loaded shared object " + temp.getName()); - } catch (UnsatisfiedLinkError e) { - logger.error("Couldn't load shared object", e); - e.printStackTrace(); - logger.error(System.getProperty("java.library.path")); + } catch (UnsatisfiedLinkError e) { + logger.error("Couldn't load shared object " + libraryName, e); + e.printStackTrace(); + // logger.error(System.getProperty("java.library.path")); + break; + } } + libraryLoaded = true; + } + + protected static synchronized void forceLoad(Class clazz, String libraryName) throws IOException { + forceLoad(clazz, List.of(libraryName)); } public static boolean isWorking() { diff --git a/photon-core/src/main/java/org/photonvision/mrcal/MrCal.java b/photon-core/src/main/java/org/photonvision/mrcal/MrCal.java new file mode 100644 index 0000000000..ce48d77714 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/mrcal/MrCal.java @@ -0,0 +1,43 @@ +/* + * 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 MrCal extends PhotonJniCommon { + + 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(MrCal.class, List.of("libamd", "libcamd", "libcolamd", "libccolamd", "openblas", "libgcc_s_seh-1", "libgfortran-5", "liblapack", "libcholmod", "mrcal_jni")); + } + } + + public static void main(String[] args) throws IOException, InterruptedException { + MrCal.forceLoad(); + } +} diff --git a/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNI.java b/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNI.java deleted file mode 100644 index e2b569eac4..0000000000 --- a/photon-core/src/main/java/org/photonvision/mrcal/MrCalJNI.java +++ /dev/null @@ -1,102 +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.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.opencv.core.MatOfPoint2f; -import org.photonvision.jni.PhotonJniCommon; - -public class MrCalJNI extends PhotonJniCommon { - - - public static synchronized void forceLoad() throws IOException { - forceLoad(MrCalJNI.class, "mrcal_jni"); - } - - - public static class MrCalResult { - public boolean success; - public double[] intrinsics; - public double rms_error; - public double[] residuals; - public double warp_x; - public double warp_y; - public int Noutliers; - - public MrCalResult(boolean success) { - this.success = success; - } - public MrCalResult( - boolean success, double[] intrinsics, double rms_error, double[] residuals, double warp_x, - double warp_y, int Noutliers) { - this.success = success; - this.intrinsics = intrinsics; - this.rms_error = rms_error; - this.residuals = residuals; - this.warp_x = warp_x; - this.warp_y = warp_y; - this.Noutliers = Noutliers; - } - - @Override - public String toString() { - return "MrCalResult [success=" + success + ", intrinsics=" + Arrays.toString(intrinsics) + ", rms_error=" - + rms_error + ", warp_x=" + warp_x + ", warp_y=" - + warp_y + ", Noutliers=" + Noutliers + "]"; - } - } - - public static native MrCalResult mrcal_calibrate_camera( - double[] observations_board, - int boardWidth, int boardHeight, double boardSpacing, - int imageWidth, int imageHeight, double focalLen); - - - public static MrCalResult calibrateCamera( - List board_corners, - int boardWidth, int boardHeight, double boardSpacing, - int imageWidth, int imageHeight, double focalLen) { - - double[] observations = new double[boardWidth * boardHeight * 3 * board_corners.size()]; - - int i = 0; - for (var board : board_corners) { - var corners = board.toArray(); - // Assume that we're correct in terms of row/column major-ness (lol) - for (var c : corners) { - float level = 1.0f; // if we have mrgingham, use level from that. Otherwise, hard-coded to 1 - - observations[i * 3 + 0] = c.x; - observations[i * 3 + 1] = c.y; - observations[i * 3 + 2] = level; - - i += 1; - } - } - - if (i * 3 != observations.length) { - return new MrCalResult(false); - } - - return mrcal_calibrate_camera(observations, boardWidth, boardHeight, boardSpacing, imageWidth, imageHeight, focalLen); - } -} diff --git a/photon-core/src/main/resources/nativelibraries/linuxx86-64/libmrcal_jni.so b/photon-core/src/main/resources/nativelibraries/linuxx86-64/libmrcal_jni.so deleted file mode 100755 index 28389aafc9..0000000000 Binary files a/photon-core/src/main/resources/nativelibraries/linuxx86-64/libmrcal_jni.so and /dev/null differ diff --git a/photon-core/src/test/java/org/photonvision/mrcal/MrCalTest.java b/photon-core/src/test/java/org/photonvision/mrcal/MrCalTest.java index 8a63e6a143..03f67405f9 100644 --- a/photon-core/src/test/java/org/photonvision/mrcal/MrCalTest.java +++ b/photon-core/src/test/java/org/photonvision/mrcal/MrCalTest.java @@ -30,10 +30,15 @@ public class MrCalTest { @BeforeAll static public void load() { - assertDoesNotThrow(MrCalJNI::forceLoad); + assertDoesNotThrow(MrCal::forceLoad); assertDoesNotThrow(TestUtils::loadLibraries); } + @Test + public void smokeTest() { + + } + @Test public void calibrateSquares640x480_pi() { // Pi3 and V1.3 camera