Libcamera JNI Maven Support (#1105)

Download our libcamera JNI from Maven <3
This commit is contained in:
Programmers3539
2024-01-02 10:04:58 -08:00
committed by GitHub
parent e4f475a253
commit 341954c1eb
9 changed files with 86 additions and 215 deletions

View File

@@ -12,6 +12,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)
@@ -26,6 +27,7 @@ ext {
openCVversion = "4.8.0-2"
joglVersion = "2.4.0-rc-20200307"
javalinVersion = "5.6.2"
photonGlDriverLibVersion = "dev-v2023.1.0-6-g5e6f7fa"
frcYear = "2024"
pubVersion = versionString

View File

@@ -13,6 +13,9 @@ dependencies {
implementation 'org.zeroturnaround:zt-zip:1.14'
implementation "org.xerial:sqlite-jdbc:3.41.0.0"
implementation "org.photonvision:photon-libcamera-gl-driver-jni:$photonGlDriverLibVersion:linuxarm64"
implementation "org.photonvision:photon-libcamera-gl-driver-java:$photonGlDriverLibVersion"
}
task writeCurrentVersion {

View File

@@ -27,7 +27,7 @@ import org.photonvision.PhotonVersion;
import org.photonvision.common.hardware.Platform;
import org.photonvision.common.networking.NetworkUtils;
import org.photonvision.common.util.SerializationUtils;
import org.photonvision.raspi.LibCameraJNI;
import org.photonvision.raspi.LibCameraJNILoader;
import org.photonvision.vision.processes.VisionModule;
import org.photonvision.vision.processes.VisionModuleManager;
import org.photonvision.vision.processes.VisionSource;
@@ -136,7 +136,7 @@ public class PhotonConfiguration {
generalSubmap.put("version", PhotonVersion.versionString);
generalSubmap.put(
"gpuAcceleration",
LibCameraJNI.isSupported()
LibCameraJNILoader.isSupported()
? "Zerocopy Libcamera Working"
: ""); // TODO add support for other types of GPU accel
generalSubmap.put("hardwareModel", hardwareConfig.deviceName);

View File

@@ -1,210 +0,0 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.raspi;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
public class LibCameraJNI {
private static boolean libraryLoaded = false;
private static final Logger logger = new Logger(LibCameraJNI.class, LogGroup.Camera);
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) return;
try {
File libDirectory = Path.of("lib/").toFile();
if (!libDirectory.exists()) {
Files.createDirectory(libDirectory.toPath()).toFile();
}
// We always extract the shared object (we could hash each so, but that's a lot of work)
URL resourceURL = LibCameraJNI.class.getResource("/nativelibraries/libphotonlibcamera.so");
File libFile = Path.of("lib/libphotonlibcamera.so").toFile();
try (InputStream in = resourceURL.openStream()) {
if (libFile.exists()) Files.delete(libFile.toPath());
Files.copy(in, libFile.toPath());
} catch (Exception e) {
logger.error("Could not extract the native library!");
}
System.load(libFile.getAbsolutePath());
libraryLoaded = true;
logger.info("Successfully loaded libpicam shared object");
} catch (UnsatisfiedLinkError e) {
logger.error("Couldn't load libpicam shared object");
e.printStackTrace();
}
}
public enum SensorModel {
Disconnected,
OV5647, // Picam v1
IMX219, // Picam v2
IMX708, // Picam v3
IMX477, // Picam HQ
OV9281,
OV7251,
Unknown;
public String getFriendlyName() {
switch (this) {
case Disconnected:
return "Disconnected Camera";
case OV5647:
return "Camera Module v1";
case IMX219:
return "Camera Module v2";
case IMX708:
return "Camera Module v3";
case IMX477:
return "HQ Camera";
case OV9281:
return "OV9281";
case OV7251:
return "OV7251";
case Unknown:
default:
return "Unknown Camera";
}
}
}
public static SensorModel getSensorModel(long r_ptr) {
int model = getSensorModelRaw(r_ptr);
return SensorModel.values()[model];
}
public static SensorModel getSensorModel(String name) {
int model = getSensorModelRaw(name);
return SensorModel.values()[model];
}
public static boolean isSupported() {
return libraryLoaded
// && getSensorModel() != PicamJNI.SensorModel.Disconnected
// && Platform.isRaspberryPi()
&& isLibraryWorking();
}
private static native boolean isLibraryWorking();
public static native int getSensorModelRaw(long r_ptr);
public static native int getSensorModelRaw(String name);
// ======================================================== //
/**
* Creates a new runner with a given width/height/fps
*
* @param the path / name of the camera as given from libcamera.
* @param width Camera video mode width in pixels
* @param height Camera video mode height in pixels
* @param fps Camera video mode FPS
* @return the runner pointer for the camera.
*/
public static native long createCamera(String name, int width, int height, int rotation);
/**
* Starts the camera thresholder and display threads running. Make sure that this function is
* called synchronously with stopCamera and returnFrame!
*/
public static native boolean startCamera(long r_ptr);
/** Stops the camera runner. Make sure to call prior to destroying the camera! */
public static native boolean stopCamera(long r_ptr);
// Destroy all native resources associated with a camera. Ensure stop is called prior!
public static native boolean destroyCamera(long r_ptr);
// ======================================================== //
// Set thresholds on [0..1]
public static native boolean setThresholds(
long r_ptr,
double hl,
double sl,
double vl,
double hu,
double su,
double vu,
boolean hueInverted);
public static native boolean setAutoExposure(long r_ptr, boolean doAutoExposure);
// Exposure time, in microseconds
public static native boolean setExposure(long r_ptr, int exposureUs);
// Set brightness on [-1, 1]
public static native boolean setBrightness(long r_ptr, double brightness);
// Unknown ranges for red and blue AWB gain
public static native boolean setAwbGain(long r_ptr, double red, double blue);
/**
* Get the time when the first pixel exposure was started, in the same timebase as libcamera gives
* the frame capture time. Units are nanoseconds.
*/
public static native long getFrameCaptureTime(long p_ptr);
/**
* Get the current time, in the same timebase as libcamera gives the frame capture time. Units are
* nanoseconds.
*/
public static native long getLibcameraTimestamp();
public static native long setFramesToCopy(long r_ptr, boolean copyIn, boolean copyOut);
// Analog gain multiplier to apply to all color channels, on [1, Big Number]
public static native boolean setAnalogGain(long r_ptr, double analog);
/** Block until a new frame is available from native code. */
public static native long awaitNewFrame(long r_ptr);
/**
* 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(long pair_ptr);
/**
* 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(long pair_ptr);
/**
* Set the GPU processing type we should do. Enum of [none, HSV, greyscale, adaptive threshold].
*/
public static native boolean setGpuProcessType(long r_ptr, int type);
public static native int getGpuProcessType(long p_ptr);
/** Release a pair pointer back to the libcamera driver code to be filled again */
public static native boolean releasePair(long p_ptr);
/** Get an array containing the names/ids/paths of all connected CSI cameras from libcamera. */
public static native String[] getCameraNames();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) Photon Vision.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.photonvision.raspi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
public class LibCameraJNILoader {
private static boolean libraryLoaded = false;
private static final Logger logger = new Logger(LibCameraJNILoader.class, LogGroup.Camera);
public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) return;
var libraryName = "photonlibcamera";
try {
// We always extract the shared object (we could hash each so, but that's a lot of work)
var arch_name = "linuxarm64";
var nativeLibName = System.mapLibraryName(libraryName);
var resourcePath = "/nativelibraries/" + arch_name + "/" + nativeLibName;
var in = LibCameraJNILoader.class.getResourceAsStream(resourcePath);
if (in == null) {
logger.error("Failed to find internal native library at path " + resourcePath);
libraryLoaded = false;
return;
}
// 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);
int read = -1;
byte[] buffer = new byte[1024];
while ((read = in.read(buffer)) != -1) {
fos.write(buffer, 0, read);
}
fos.close();
in.close();
System.load(temp.getAbsolutePath());
logger.info("Successfully loaded shared object " + temp.getName());
} catch (UnsatisfiedLinkError e) {
logger.error("Couldn't load shared object " + libraryName, e);
e.printStackTrace();
// logger.error(System.getProperty("java.library.path"));
}
libraryLoaded = true;
}
public static boolean isSupported() {
return libraryLoaded && LibCameraJNI.isSupported();
}
}

View File

@@ -33,6 +33,7 @@ import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
import org.photonvision.common.util.TimedTaskManager;
import org.photonvision.raspi.LibCameraJNI;
import org.photonvision.raspi.LibCameraJNILoader;
import org.photonvision.vision.camera.CameraInfo;
import org.photonvision.vision.camera.CameraQuirk;
import org.photonvision.vision.camera.CameraType;
@@ -98,7 +99,7 @@ public class VisionSourceManager {
*/
protected List<CameraInfo> getConnectedCSICameras() {
List<CameraInfo> cameraInfos = new ArrayList<CameraInfo>();
if (LibCameraJNI.isSupported())
if (LibCameraJNILoader.isSupported())
for (String path : LibCameraJNI.getCameraNames()) {
String name = LibCameraJNI.getSensorModel(path).getFriendlyName();
cameraInfos.add(

View File

@@ -37,7 +37,7 @@ import org.photonvision.common.logging.Logger;
import org.photonvision.common.networking.NetworkManager;
import org.photonvision.common.util.TestUtils;
import org.photonvision.common.util.numbers.IntegerCouple;
import org.photonvision.raspi.LibCameraJNI;
import org.photonvision.raspi.LibCameraJNILoader;
import org.photonvision.server.Server;
import org.photonvision.vision.camera.FileVisionSource;
import org.photonvision.vision.opencv.CVMat;
@@ -311,7 +311,7 @@ public class Main {
try {
if (Platform.isRaspberryPi()) {
LibCameraJNI.forceLoad();
LibCameraJNILoader.forceLoad();
}
} catch (IOException e) {
logger.error("Failed to load libcamera-JNI!", e);