Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable multi-CSI-camera with libcamera #1068

Merged
merged 10 commits into from
Dec 25, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public Map<String, Object> toHashMap() {
generalSubmap.put(
"gpuAcceleration",
LibCameraJNI.isSupported()
? "Zerocopy Libcamera on " + LibCameraJNI.getSensorModel().getFriendlyName()
? "Zerocopy Libcamera Working"
: ""); // TODO add support for other types of GPU accel
generalSubmap.put("hardwareModel", hardwareConfig.deviceName);
generalSubmap.put("hardwarePlatform", Platform.getPlatformName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
public class LinuxCmds extends CmdBase {
public void initCmds(HardwareConfig config) {
// CPU
cpuMemoryCommand = "awk '/MemTotal:/ {print int($2 / 1000);}' /proc/meminfo";
cpuMemoryCommand = "free -m | awk 'FNR == 2 {print $3}'";

// TODO: boards have lots of thermal devices. Hard to pick the CPU

Expand All @@ -32,7 +32,7 @@ public void initCmds(HardwareConfig config) {
cpuUptimeCommand = "uptime -p | cut -c 4-";

// RAM
ramUsageCommand = "awk '/MemAvailable:/ {print int($2 / 1000);}' /proc/meminfo";
ramUsageCommand = "free -m | awk 'FNR == 2 {print $3}'";

// Disk
diskUsageCommand = "df ./ --output=pcent | tail -n +2";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void initCmds(HardwareConfig config) {
super.initCmds(config);

// CPU
cpuMemoryCommand = "vcgencmd get_mem arm | grep -Eo '[0-9]+'";
cpuMemoryCommand = "free -m | awk 'FNR == 2 {print $2}'";
cpuTemperatureCommand = "sed 's/.\\{3\\}$/.&/' /sys/class/thermal/thermal_zone0/temp";
cpuThrottleReasonCmd =
"if (( $(( $(vcgencmd get_throttled | grep -Eo 0x[0-9a-fA-F]*) & 0x01 )) != 0x00 )); then echo \"LOW VOLTAGE\"; "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ public class LibCameraJNI {
private static boolean libraryLoaded = false;
private static final Logger logger = new Logger(LibCameraJNI.class, LogGroup.Camera);

public static final Object CAMERA_LOCK = new Object();

public static synchronized void forceLoad() throws IOException {
if (libraryLoaded) return;

Expand Down Expand Up @@ -93,8 +91,13 @@ public String getFriendlyName() {
}
}

public static SensorModel getSensorModel() {
int model = getSensorModelRaw();
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];
}

Expand All @@ -107,88 +110,101 @@ public static boolean isSupported() {

private static native boolean isLibraryWorking();

public static native int getSensorModelRaw();
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 success of creating a camera object
* @return the runner pointer for the camera.
*/
public static native boolean createCamera(int width, int height, int rotation);
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();
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();
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();
public static native boolean destroyCamera(long r_ptr);

// ======================================================== //

// Set thresholds on [0..1]
public static native boolean setThresholds(
double hl, double sl, double vl, double hu, double su, double vu, boolean hueInverted);
long r_ptr,
double hl,
double sl,
double vl,
double hu,
double su,
double vu,
boolean hueInverted);

public static native boolean setAutoExposure(boolean doAutoExposure);
public static native boolean setAutoExposure(long r_ptr, boolean doAutoExposure);

// Exposure time, in microseconds
public static native boolean setExposure(int exposureUs);
public static native boolean setExposure(long r_ptr, int exposureUs);

// Set brightness on [-1, 1]
public static native boolean setBrightness(double brightness);
public static native boolean setBrightness(long r_ptr, double brightness);

// Unknown ranges for red and blue AWB gain
public static native boolean setAwbGain(double red, double blue);
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();
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(boolean copyIn, boolean copyOut);
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(double analog);
public static native boolean setAnalogGain(long r_ptr, double analog);

/** Block until a new frame is available from native code. */
public static native boolean awaitNewFrame();
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();
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();
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(int type);
public static native boolean setGpuProcessType(long r_ptr, int type);

public static native int getGpuProcessType(long p_ptr);

public static native int getGpuProcessType();
/** Release a pair pointer back to the libcamera driver code to be filled again */
public static native boolean releasePair(long p_ptr);

// Release a frame pointer back to the libcamera driver code to be filled again */
// public static native long returnFrame(long frame);
/** Get an array containing the names/ids/paths of all connected CSI cameras from libcamera. */
public static native String[] getCameraNames();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.vision.camera;

import edu.wpi.first.cscore.UsbCameraInfo;
import java.util.Arrays;

public class CameraInfo extends UsbCameraInfo {
public final CameraType cameraType;

public CameraInfo(
int dev, String path, String name, String[] otherPaths, int vendorId, int productId) {
super(dev, path, name, otherPaths, vendorId, productId);
cameraType = CameraType.UsbCamera;
}

public CameraInfo(
int dev,
String path,
String name,
String[] otherPaths,
int vendorId,
int productId,
CameraType cameraType) {
super(dev, path, name, otherPaths, vendorId, productId);
this.cameraType = cameraType;
}

public CameraInfo(UsbCameraInfo info) {
super(info.dev, info.path, info.name, info.otherPaths, info.vendorId, info.productId);
cameraType = CameraType.UsbCamera;
}

/**
* @return True, if this camera is reported from V4L and is a CSI camera.
*/
public boolean getIsV4lCsiCamera() {
return (Arrays.stream(otherPaths).anyMatch(it -> it.contains("csi-video"))
|| getBaseName().equals("unicam"));
}

/**
* @return The base name of the camera aka the name as just ascii.
*/
public String getBaseName() {
return name.replaceAll("[^\\x00-\\x7F]", "");
}

/**
* @param baseName
* @return Returns a human readable name
*/
public String getHumanReadableName() {
return getBaseName().replaceAll(" ", "_");
}

@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof UsbCameraInfo || o instanceof CameraInfo)) return false;
UsbCameraInfo other = (UsbCameraInfo) o;
return path.equals(other.path)
// && a.dev == b.dev (dev is not constant in Windows)
&& name.equals(other.name)
&& productId == other.productId
&& vendorId == other.vendorId;
}
}
Loading