mirror of
https://github.com/PhotonVision/photonvision
synced 2026-06-28 02:11:40 +00:00
Libcamera JNI Maven Support (#1105)
Download our libcamera JNI from Maven <3
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user