From bf22b5f1db5cc42e085997195dca3da7b1de70ed Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 17:47:58 -0500 Subject: [PATCH 1/8] Almost there. --- .../configuration/PhotonConfiguration.java | 2 +- .../hardware/metrics/cmds/LinuxCmds.java | 4 +- .../org/photonvision/raspi/LibCameraJNI.java | 57 ++- .../vision/camera/CameraInfo.java | 58 +++ .../vision/camera/LibcameraGpuSettables.java | 31 +- .../provider/LibcameraGpuFrameProvider.java | 17 +- .../vision/processes/VisionSourceManager.java | 477 ++++++++---------- .../processes/VisionSourceManagerTest.java | 136 +++-- 8 files changed, 418 insertions(+), 364 deletions(-) create mode 100644 photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java diff --git a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java index fac672636c..cfd3b677a8 100644 --- a/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java +++ b/photon-core/src/main/java/org/photonvision/common/configuration/PhotonConfiguration.java @@ -137,7 +137,7 @@ public Map 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()); diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java index 1a7d18f35e..9abff7ce9f 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/LinuxCmds.java @@ -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 @@ -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"; diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java index 8a12aeabfa..761e09b6c0 100644 --- a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java +++ b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java @@ -93,8 +93,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]; } @@ -107,54 +112,57 @@ 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 r_ptr); /** * Get the current time, in the same timebase as libcamera gives the frame capture time. Units are @@ -162,25 +170,25 @@ public static native boolean setThresholds( */ 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]. @@ -189,6 +197,13 @@ public static native boolean setThresholds( public static native int getGpuProcessType(); - // Release a frame pointer back to the libcamera driver code to be filled again */ - // public static native long returnFrame(long frame); + /** + * 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(); } diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java new file mode 100644 index 0000000000..a12d978791 --- /dev/null +++ b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java @@ -0,0 +1,58 @@ +package org.photonvision.vision.camera; + +import java.util.Arrays; + +import edu.wpi.first.cscore.UsbCameraInfo; + +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; + } + + /** + * + * @return True, if this camera is reported from V4L and is a CSI camera. + */ + public boolean getIsV4lCsiCamera() { + if(otherPaths == null) return false; + 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(" ", "_"); + } + + public boolean Equals(Object o) { + if (o == this) + return true; + if (!(o instanceof UsbCameraInfo)) + 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; + } + +} diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java index 2fdde391d2..4301d94a9d 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java @@ -34,7 +34,7 @@ public class LibcameraGpuSettables extends VisionSourceSettables { private boolean lastAutoExposureActive; private int lastGain = 50; private Pair lastAwbGains = new Pair<>(18, 18); - private boolean m_initialized = false; + public long r_ptr = 0; private final LibCameraJNI.SensorModel sensorModel; @@ -53,7 +53,7 @@ public LibcameraGpuSettables(CameraConfiguration configuration) { videoModes = new HashMap<>(); - sensorModel = LibCameraJNI.getSensorModel(); + sensorModel = LibCameraJNI.getSensorModel(configuration.path); if (sensorModel == LibCameraJNI.SensorModel.IMX219) { // Settings for the IMX219 sensor, which is used on the Pi Camera Module v2 @@ -121,7 +121,7 @@ public double getFOV() { @Override public void setAutoExposure(boolean cameraAutoExposure) { lastAutoExposureActive = cameraAutoExposure; - LibCameraJNI.setAutoExposure(cameraAutoExposure); + LibCameraJNI.setAutoExposure(r_ptr, cameraAutoExposure); } @Override @@ -147,7 +147,7 @@ public void setExposure(double exposure) { } lastManualExposure = exposure; - var success = LibCameraJNI.setExposure((int) Math.round(exposure) * 800); + var success = LibCameraJNI.setExposure(r_ptr,(int) Math.round(exposure) * 800); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera exposure"); } @@ -155,7 +155,7 @@ public void setExposure(double exposure) { public void setBrightness(int brightness) { lastBrightness = brightness; double realBrightness = MathUtils.map(brightness, 0.0, 100.0, -1.0, 1.0); - var success = LibCameraJNI.setBrightness(realBrightness); + var success = LibCameraJNI.setBrightness(r_ptr,realBrightness); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera brightness"); } @@ -163,7 +163,7 @@ public void setBrightness(int brightness) { public void setGain(int gain) { lastGain = gain; // TODO units here seem odd -- 5ish seems legit? So divide by 10 - var success = LibCameraJNI.setAnalogGain(gain / 10.0); + var success = LibCameraJNI.setAnalogGain(r_ptr, gain / 10.0); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera gain"); } @@ -185,7 +185,7 @@ public void setBlueGain(int blue) { public void setAwbGain(int red, int blue) { if (sensorModel != LibCameraJNI.SensorModel.OV9281) { - var success = LibCameraJNI.setAwbGain(red / 10.0, blue / 10.0); + var success = LibCameraJNI.setAwbGain(r_ptr, red / 10.0, blue / 10.0); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera AWB gains"); } } @@ -202,28 +202,27 @@ protected void setVideoModeInternal(VideoMode videoMode) { // We need to make sure that other threads don't try to do anything funny while we're recreating // the camera synchronized (LibCameraJNI.CAMERA_LOCK) { - if (m_initialized) { + if (r_ptr!=0) { logger.debug("Stopping libcamera"); - if (!LibCameraJNI.stopCamera()) { + if (!LibCameraJNI.stopCamera(r_ptr)) { logger.error("Couldn't stop a zero copy Pi Camera while switching video modes"); } logger.debug("Destroying libcamera"); - if (!LibCameraJNI.destroyCamera()) { + if (!LibCameraJNI.destroyCamera(r_ptr)) { logger.error("Couldn't destroy a zero copy Pi Camera while switching video modes"); } } logger.debug("Creating libcamera"); - if (!LibCameraJNI.createCamera( - mode.width, mode.height, (m_rotationMode == ImageRotationMode.DEG_180 ? 180 : 0))) { + r_ptr = LibCameraJNI.createCamera( + getConfiguration().path, mode.width, mode.height, (m_rotationMode == ImageRotationMode.DEG_180 ? 180 : 0)); + if (r_ptr==0) { logger.error("Couldn't create a zero copy Pi Camera while switching video modes"); } logger.debug("Starting libcamera"); - if (!LibCameraJNI.startCamera()) { + if (!LibCameraJNI.startCamera(r_ptr)) { logger.error("Couldn't start a zero copy Pi Camera while switching video modes"); } - - m_initialized = true; } // We don't store last settings on the native side, and when you change video mode these get @@ -234,7 +233,7 @@ protected void setVideoModeInternal(VideoMode videoMode) { setGain(lastGain); setAwbGain(lastAwbGains.getFirst(), lastAwbGains.getSecond()); - LibCameraJNI.setFramesToCopy(true, true); + LibCameraJNI.setFramesToCopy(r_ptr,true, true); currentVideoMode = mode; } diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java index 7b76f658ad..34b40283a0 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java @@ -47,19 +47,20 @@ public String getName() { @Override public Frame get() { - // We need to make sure that other threads don't try to change video modes while we're waiting + // We need to make sure that other threads don't try to change video modes while + // we're waiting // for a frame // System.out.println("GET!"); synchronized (LibCameraJNI.CAMERA_LOCK) { - var success = LibCameraJNI.awaitNewFrame(); + var p_ptr = LibCameraJNI.awaitNewFrame(settables.r_ptr); - if (!success) { + if (p_ptr == 0) { System.out.println("No new frame"); return new Frame(); } - var colorMat = new CVMat(new Mat(LibCameraJNI.takeColorFrame())); - var processedMat = new CVMat(new Mat(LibCameraJNI.takeProcessedFrame())); + var colorMat = new CVMat(new Mat(LibCameraJNI.takeColorFrame(p_ptr))); + var processedMat = new CVMat(new Mat(LibCameraJNI.takeProcessedFrame(p_ptr))); // System.out.println("Color mat: " + colorMat.getMat().size()); @@ -73,7 +74,7 @@ public Frame get() { } var now = LibCameraJNI.getLibcameraTimestamp(); - var capture = LibCameraJNI.getFrameCaptureTime(); + var capture = LibCameraJNI.getFrameCaptureTime(settables.r_ptr); var latency = (now - capture); return new Frame( @@ -97,7 +98,7 @@ public void requestFrameRotation(ImageRotationMode rotationMode) { @Override public void requestHsvSettings(HSVParams params) { - LibCameraJNI.setThresholds( + LibCameraJNI.setThresholds(settables.r_ptr, params.getHsvLower().val[0] / 180.0, params.getHsvLower().val[1] / 255.0, params.getHsvLower().val[2] / 255.0, @@ -109,6 +110,6 @@ public void requestHsvSettings(HSVParams params) { @Override public void requestFrameCopies(boolean copyInput, boolean copyOutput) { - LibCameraJNI.setFramesToCopy(copyInput, copyOutput); + LibCameraJNI.setFramesToCopy(settables.r_ptr, copyInput, copyOutput); } } diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index 970b1a6ae7..3b4b9bebd2 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -18,13 +18,11 @@ package org.photonvision.vision.processes; import edu.wpi.first.cscore.UsbCamera; -import edu.wpi.first.cscore.UsbCameraInfo; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; @@ -35,6 +33,7 @@ import org.photonvision.common.logging.Logger; import org.photonvision.common.util.TimedTaskManager; import org.photonvision.raspi.LibCameraJNI; +import org.photonvision.vision.camera.CameraInfo; import org.photonvision.vision.camera.CameraQuirk; import org.photonvision.vision.camera.CameraType; import org.photonvision.vision.camera.LibcameraGpuSource; @@ -44,7 +43,8 @@ public class VisionSourceManager { private static final Logger logger = new Logger(VisionSourceManager.class, LogGroup.Camera); private static final List deviceBlacklist = List.of("bcm2835-isp"); - final List knownUsbCameras = new CopyOnWriteArrayList<>(); + final List knownCameras = new CopyOnWriteArrayList<>(); + final List unmatchedLoadedConfigs = new CopyOnWriteArrayList<>(); private boolean hasWarned; private String ignoredCamerasRegex = ""; @@ -57,10 +57,11 @@ public static VisionSourceManager getInstance() { return SingletonHolder.INSTANCE; } - VisionSourceManager() {} + VisionSourceManager() { + } public void registerTimedTask() { - TimedTaskManager.getInstance().addTask("VisionSourceManager", this::tryMatchUSBCams, 3000); + TimedTaskManager.getInstance().addTask("VisionSourceManager", this::tryMatchCams, 3000); } public void registerLoadedConfigs(CameraConfiguration... configs) { @@ -68,7 +69,8 @@ public void registerLoadedConfigs(CameraConfiguration... configs) { } /** - * Register new camera configs loaded from disk. This will add them to the list of configs to try + * Register new camera configs loaded from disk. This will add them to the list + * of configs to try * to match, and also automatically spawn new vision processes as necessary. * * @param configs The loaded camera configs. @@ -77,12 +79,36 @@ public void registerLoadedConfigs(Collection configs) { unmatchedLoadedConfigs.addAll(configs); } - protected Supplier> cameraInfoSupplier = - () -> List.of(UsbCamera.enumerateUsbCameras()); + /** + * Pre filter out any csi cameras to return just USB Cameras. Allow defining the + * camerainfo. + * + * @return a list containing usbcamerainfo. + */ + protected List getConnectedUSBCameras() { + List cameraInfos = List.of(UsbCamera.enumerateUsbCameras()).stream().map(c -> (CameraInfo) c) + .collect(Collectors.toList()); + return cameraInfos; + } + + /** + * Retrieve the list of csi cameras from libcamera. + * + * @return a list containing csicamerainfo. + */ + protected List getConnectedCSICameras() { + List cameraInfos = new ArrayList(); + for (String path : LibCameraJNI.getCameraNames()) { + String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); + cameraInfos.add(new CameraInfo(-1, path, name, null, -1, -1, CameraType.ZeroCopyPicam)); + } + return cameraInfos; + } - protected void tryMatchUSBCams() { - var visionSourceList = tryMatchUSBCamImpl(); - if (visionSourceList == null) return; + protected void tryMatchCams() { + var visionSourceList = tryMatchCamImpl(); + if (visionSourceList == null) + return; logger.info("Adding " + visionSourceList.size() + " configs to VMM."); ConfigManager.getInstance().addCameraConfigurations(visionSourceList); @@ -94,70 +120,60 @@ protected void tryMatchUSBCams() { "fullsettings", ConfigManager.getInstance().getConfig().toHashMap())); } - protected List tryMatchUSBCamImpl() { - return tryMatchUSBCamImpl(true); + protected List tryMatchCamImpl() { + return tryMatchCamImpl(null); } - protected List tryMatchUSBCamImpl(boolean createSources) { - // Detect cameras using CSCore - List connectedCameras = - new ArrayList<>(filterAllowedDevices(cameraInfoSupplier.get())); - - // Remove all known devices - var notYetLoadedCams = new ArrayList(); - for (var connectedCam : connectedCameras) { - boolean cameraIsUnknown = true; - for (UsbCameraInfo knownCam : this.knownUsbCameras) { - if (usbCamEquals(knownCam, connectedCam)) { - cameraIsUnknown = false; - break; - } - } - if (cameraIsUnknown) { - notYetLoadedCams.add(connectedCam); - } + /** + * @param cameraInfos Used to feed camera info for unit tests. + * @return New VisionSources. + */ + protected List tryMatchCamImpl(ArrayList cameraInfos) { + boolean createSources = true; + List connectedCameras; + if (cameraInfos == null) { + // Detect USB cameras using CSCore + connectedCameras = new ArrayList<>(filterAllowedDevices(getConnectedUSBCameras())); + // Detect CSI cameras using libcamera + connectedCameras.addAll(new ArrayList<>(filterAllowedDevices(getConnectedCSICameras()))); + } + else{ + connectedCameras = new ArrayList<>(filterAllowedDevices(cameraInfos)); + createSources = false; } - if (notYetLoadedCams.isEmpty()) return null; - - if (connectedCameras.isEmpty()) { + // Return no new sources because there are no new sources + if (connectedCameras.isEmpty() && !cameraInfos.isEmpty()) { logger.warn( - "No USB cameras were detected! Check that all cameras are connected, and that the path is correct."); + "No cameras were detected! Check that all cameras are connected, and that the path is correct."); return null; } - logger.debug("Matching " + notYetLoadedCams.size() + " new cameras!"); - // Sort out just the USB cams - var usbCamConfigs = new ArrayList<>(); - for (var config : unmatchedLoadedConfigs) { - if (config.cameraType == CameraType.UsbCamera) usbCamConfigs.add(config); - } + connectedCameras.removeIf(c -> knownCameras.contains(c)); + + // All cameras are already loaded return no new sources. + if (connectedCameras.isEmpty()) + return null; + + logger.debug("Matching " + connectedCameras.size() + " new cameras!"); // Debug prints - for (var info : notYetLoadedCams) { + for (var info : connectedCameras) { logger.info("Adding local video device - \"" + info.name + "\" at \"" + info.path + "\""); } - if (!usbCamConfigs.isEmpty()) - logger.debug("Trying to match " + usbCamConfigs.size() + " unmatched configs..."); + if (!unmatchedLoadedConfigs.isEmpty()) + logger.debug("Trying to match " + unmatchedLoadedConfigs.size() + " unmatched configs..."); // Match camera configs to physical cameras - - List matchedCameras; - - if (!createSources) { - matchedCameras = matchUSBCameras(notYetLoadedCams, unmatchedLoadedConfigs, false); - } else { - matchedCameras = matchUSBCameras(notYetLoadedCams, unmatchedLoadedConfigs); - } + List matchedCameras = matchCameras(connectedCameras, unmatchedLoadedConfigs); unmatchedLoadedConfigs.removeAll(matchedCameras); if (!unmatchedLoadedConfigs.isEmpty() && !hasWarned) { logger.warn( - () -> - "After matching, " - + unmatchedLoadedConfigs.size() - + " configs remained unmatched. Is your camera disconnected?"); + () -> "After matching, " + + unmatchedLoadedConfigs.size() + + " configs remained unmatched. Is your camera disconnected?"); logger.warn( "Unloaded configs: " + unmatchedLoadedConfigs.stream() @@ -167,12 +183,10 @@ protected List tryMatchUSBCamImpl(boolean createSources) { } // We add the matched cameras to the known camera list - for (var cam : notYetLoadedCams) { - if (this.knownUsbCameras.stream().noneMatch(it -> usbCamEquals(it, cam))) { - this.knownUsbCameras.add(cam); - } - } - if (matchedCameras.isEmpty()) return null; + this.knownCameras.addAll(connectedCameras); + + if (matchedCameras.isEmpty()) + return null; // for unit tests only! if (!createSources) { @@ -185,192 +199,179 @@ protected List tryMatchUSBCamImpl(boolean createSources) { // Print info about each vision source for (var src : sources) { logger.debug( - () -> - "Matched config for camera \"" - + src.getFrameProvider().getName() - + "\" and loaded " - + src.getCameraConfiguration().pipelineSettings.size() - + " pipelines"); + () -> "Matched config for camera \"" + + src.getFrameProvider().getName() + + "\" and loaded " + + src.getCameraConfiguration().pipelineSettings.size() + + " pipelines"); } return sources; } /** - * Create {@link CameraConfiguration}s based on a list of detected USB cameras and the configs on + * Create {@link CameraConfiguration}s based on a list of detected USB cameras + * and the configs on * disk. * - * @param detectedCamInfos Information about currently connected USB cameras. - * @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from disk. + * @param detectedCamInfos Information about currently connected USB cameras. + * @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from + * disk. * @return the matched configurations. */ - protected List matchUSBCameras( - List detectedCamInfos, List loadedUsbCamConfigs) { - return matchUSBCameras(detectedCamInfos, loadedUsbCamConfigs, true); - } + public List matchCameras( + List detectedCamInfos, + List loadedUsbCamConfigs) { - /** - * Create {@link CameraConfiguration}s based on a list of detected USB cameras and the configs on - * disk. - * - * @param detectedCamInfos Information about currently connected USB cameras. - * @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from disk. - * @param useJNI If false, this is a unit test and the JNI should not be used for CSI devices. - * @return the matched configurations. - */ - private List matchUSBCameras( - List detectedCamInfos, - List loadedUsbCamConfigs, - boolean useJNI) { var detectedCameraList = new ArrayList<>(detectedCamInfos); ArrayList cameraConfigurations = new ArrayList<>(); - List unmatchedAfterByID = new ArrayList<>(loadedUsbCamConfigs); - - // loop over all the configs loaded from disk, attempting to match each camera - // to a config by path-by-id on linux - for (CameraConfiguration config : loadedUsbCamConfigs) { - UsbCameraInfo cameraInfo; + cameraConfigurations.addAll(matchByPathByID(detectedCameraList, loadedUsbCamConfigs)); + cameraConfigurations.addAll(matchByPath(detectedCameraList, loadedUsbCamConfigs)); + cameraConfigurations.addAll(matchByName(detectedCameraList, loadedUsbCamConfigs)); + cameraConfigurations.addAll( + createConfigsForCameras(detectedCameraList, loadedUsbCamConfigs, cameraConfigurations)); - if (config.otherPaths.length == 0) { - logger.debug("No valid path-by-id found for config with name " + config.baseName); - } else { - // attempt matching by path and basename - logger.debug( - "Trying to find a match for loaded camera " - + config.baseName - + " with path-by-id " - + config.otherPaths[0]); - cameraInfo = - detectedCameraList.stream() - .filter( - usbCameraInfo -> - usbCameraInfo.otherPaths.length != 0 - && usbCameraInfo.otherPaths[0].equals(config.otherPaths[0]) - && cameraNameToBaseName(usbCameraInfo.name).equals(config.baseName)) - .findFirst() - .orElse(null); - - // If we actually matched a camera to a config, remove that camera from the list - // and add it to the output - if (cameraInfo != null) { - logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); - detectedCameraList.remove(cameraInfo); - unmatchedAfterByID.remove(config); - cameraConfigurations.add(mergeInfoIntoConfig(config, cameraInfo)); - } - } - } - - if (!unmatchedAfterByID.isEmpty() && !detectedCameraList.isEmpty()) { - logger.debug("Match by path-by-id failed, falling back to path-only matching"); - - List unmatchedAfterByPath = new ArrayList<>(loadedUsbCamConfigs); - - // now attempt to match the cameras and configs remaining by normal path - for (CameraConfiguration config : unmatchedAfterByID) { - UsbCameraInfo cameraInfo; - - // attempt matching by path and basename - logger.debug( - "Trying to find a match for loaded camera " - + config.baseName - + " with path " - + config.path); - cameraInfo = - detectedCameraList.stream() - .filter( - usbCameraInfo -> - usbCameraInfo.path.equals(config.path) - && cameraNameToBaseName(usbCameraInfo.name).equals(config.baseName)) - .findFirst() - .orElse(null); - - // If we actually matched a camera to a config, remove that camera from the list - // and add it to the output - if (cameraInfo != null) { - logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); - detectedCameraList.remove(cameraInfo); - unmatchedAfterByPath.remove(config); - cameraConfigurations.add(mergeInfoIntoConfig(config, cameraInfo)); - } - } - - if (!unmatchedAfterByPath.isEmpty() && !detectedCameraList.isEmpty()) { - logger.debug("Match by ID and path failed, falling back to name-only matching"); - - // if both path and ID based matching fails, attempt basename only match - for (CameraConfiguration config : unmatchedAfterByPath) { - UsbCameraInfo cameraInfo; + logger.debug("Matched or created " + cameraConfigurations.size() + " camera configs!"); + return cameraConfigurations; - logger.debug("Trying to find a match for loaded camera with name " + config.baseName); + } - cameraInfo = - detectedCameraList.stream() - .filter( - usbCameraInfo -> - cameraNameToBaseName(usbCameraInfo.name).equals(config.baseName)) - .findFirst() - .orElse(null); + // loop over all the configs loaded from disk, attempting to match each camera + // to a config by path-by-id on linux + private List matchByPathByID(List detectedCamInfos, + List loadedCamConfigs) { + List ret = new ArrayList(); + for (CameraConfiguration config : loadedCamConfigs) { + //Only run match path by id if the camera is not a CSI camera. + if (config.cameraType != CameraType.ZeroCopyPicam) { + CameraInfo cameraInfo; + if (config.otherPaths.length == 0) { + logger.debug("No valid path-by-id found for config with name " + config.baseName); + } else { + // attempt matching by path and basename + logger.debug( + "Trying to find a match for loaded camera " + + config.baseName + + " with path-by-id " + + config.otherPaths[0]); + cameraInfo = detectedCamInfos.stream() + .filter( + usbCameraInfo -> usbCameraInfo.otherPaths.length != 0 + && usbCameraInfo.otherPaths[0].equals(config.otherPaths[0]) + && usbCameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); // If we actually matched a camera to a config, remove that camera from the list // and add it to the output if (cameraInfo != null) { logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); - detectedCameraList.remove(cameraInfo); - cameraConfigurations.add(mergeInfoIntoConfig(config, cameraInfo)); + ret.add(mergeInfoIntoConfig(config, cameraInfo)); } } } } + return ret; + } + + private List matchByPath(List detectedCamInfos, + List loadedUsbCamConfigs) { + List ret = new ArrayList(); + // now attempt to match the cameras and configs remaining by normal path + for (CameraConfiguration config : loadedUsbCamConfigs) { + CameraInfo cameraInfo; - // If we have any unmatched cameras left, create a new CameraConfiguration for - // them here. + // attempt matching by path and basename + logger.debug( + "Trying to find a match for loaded camera " + + config.baseName + + " with path " + + config.path); + cameraInfo = detectedCamInfos.stream() + .filter( + usbCameraInfo -> usbCameraInfo.path.equals(config.path) + && usbCameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); + + // If we actually matched a camera to a config, remove that camera from the list + // and add it to the output + if (cameraInfo != null) { + logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); + detectedCamInfos.remove(cameraInfo); + ret.add(mergeInfoIntoConfig(config, cameraInfo)); + } + } + return ret; + + } + + //Try matching cameras to configs by name. + private List matchByName(List detectedCamInfos, + List loadedCamConfigs) { + List ret = new ArrayList(); + // if both path and ID based matching fails, attempt basename only match + for (CameraConfiguration config : loadedCamConfigs) { + CameraInfo cameraInfo; + + logger.debug("Trying to find a match for loaded camera with name " + config.baseName); + + cameraInfo = detectedCamInfos.stream() + .filter( + CameraInfo -> CameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); + + // If we actually matched a camera to a config, remove that camera from the list + // and add it to the output + if (cameraInfo != null) { + logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); + ret.add(mergeInfoIntoConfig(config, cameraInfo)); + } + } + return ret; + } + + // If we have any unmatched cameras left, create a new CameraConfiguration for + // them here. + private List createConfigsForCameras(List detectedCameraList, + List loadedCamConfigs, List loadedConfigs) { + List ret = new ArrayList(); logger.debug( "After matching loaded configs " + detectedCameraList.size() + " cameras were unmatched."); - for (UsbCameraInfo info : detectedCameraList) { + for (CameraInfo info : detectedCameraList) { // create new camera config for all new cameras - String baseName = cameraNameToBaseName(info.name); - String uniqueName = baseNameToUniqueName(baseName); + String baseName = info.getBaseName(); + String uniqueName = info.getHumanReadableName(); int suffix = 0; - while (containsName(cameraConfigurations, uniqueName) || containsName(uniqueName)) { + while (containsName(loadedConfigs, uniqueName) || containsName(uniqueName)) { suffix++; uniqueName = String.format("%s (%d)", uniqueName, suffix); } logger.info("Creating a new camera config for camera " + uniqueName); - // HACK -- for picams, we want to use the camera model String nickname = uniqueName; - if (isCsiCamera(info)) { - if (useJNI) { - nickname = LibCameraJNI.getSensorModel().toString(); - } else { - nickname = "CSICAM-DEV"; - } - } - CameraConfiguration configuration = - new CameraConfiguration(baseName, uniqueName, nickname, info.path, info.otherPaths); - cameraConfigurations.add(configuration); - } + CameraConfiguration configuration = new CameraConfiguration(baseName, uniqueName, nickname, info.path, + info.otherPaths); - logger.debug("Matched or created " + cameraConfigurations.size() + " camera configs!"); - return cameraConfigurations; - } + configuration.cameraType = info.cameraType; - private boolean isCsiCamera(UsbCameraInfo configuration) { - return (Arrays.stream(configuration.otherPaths).anyMatch(it -> it.contains("csi-video")) - || cameraNameToBaseName(configuration.name).equals("unicam")); + ret.add(configuration); + } + return ret; } - private CameraConfiguration mergeInfoIntoConfig(CameraConfiguration cfg, UsbCameraInfo info) { + private CameraConfiguration mergeInfoIntoConfig(CameraConfiguration cfg, CameraInfo info) { if (!cfg.path.equals(info.path)) { logger.debug("Updating path config from " + cfg.path + " to " + info.path); cfg.path = info.path; } cfg.otherPaths = info.otherPaths; + cfg.cameraType = info.cameraType; if (cfg.otherPaths.length != info.otherPaths.length) { logger.debug( @@ -400,94 +401,40 @@ public void setIgnoredCamerasRegex(String ignoredCamerasRegex) { this.ignoredCamerasRegex = ignoredCamerasRegex; } - private List filterAllowedDevices(List allDevices) { - List filteredDevices = new ArrayList<>(); - List badDevices = new ArrayList<>(); + /** + * Filter out any blacklisted or ignored devices. + * + * @param allDevices + * @return list of devices with blacklisted or ingore devices removed. + */ + private List filterAllowedDevices(List allDevices) { + List filteredDevices = new ArrayList<>(); for (var device : allDevices) { - // Filter devices that are physically the same device but may show up as multiple devices that - // really cant be accessed. First noticed with raspi 5 and ov5647. - - List paths = new ArrayList<>(); - - boolean skip = false; - if (device.otherPaths.length != 0) { - // Use the other paths to filter out devices that share the same path other than the index - // select only the lowest index. - // A ov5647 on a raspi 5 would show another path as - // platform-1000880000.pisp_be-video-index0, - // platform-1000880000.pisp_be-video-index4, and platform-1000880000.pisp_be-video-index5. - // This code will remove "indexX" from all the other paths from all the devices and make - // sure - // that we only take one camera stream from each device the stream with the lowest index. - for (String p : device.otherPaths) { - paths.add(p.split("index")[0]); - } - for (var otherDevice : filteredDevices) { - if (otherDevice.otherPaths.length == 0) continue; - List otherPaths = new ArrayList<>(); - for (String p : otherDevice.otherPaths) { - otherPaths.add(p.split("index")[0]); - } - if (paths.containsAll(otherPaths)) { - if (otherDevice.dev >= device.dev) { - badDevices.add(otherDevice); - } else { - skip = true; - break; - } - } - } - } - - filteredDevices.removeAll(badDevices); if (deviceBlacklist.contains(device.name)) { logger.trace( "Skipping blacklisted device: \"" + device.name + "\" at \"" + device.path + "\""); } else if (device.name.matches(ignoredCamerasRegex)) { logger.trace("Skipping ignored device: \"" + device.name + "\" at \"" + device.path); - } else if (!skip) { + } else if (device.getIsV4lCsiCamera()) { + } else { filteredDevices.add(device); logger.trace( "Adding local video device - \"" + device.name + "\" at \"" + device.path + "\""); - } else { - logger.trace("Skipping duplicate device: \"" + device.name + "\" at \"" + device.path); } } return filteredDevices; } - private boolean usbCamEquals(UsbCameraInfo a, UsbCameraInfo b) { - return a.path.equals(b.path) - // && a.dev == b.dev (dev is not constant in Windows) - && a.name.equals(b.name) - && a.productId == b.productId - && a.vendorId == b.vendorId; - } - - // Remove all non-ASCII characters - private static String cameraNameToBaseName(String cameraName) { - return cameraName.replaceAll("[^\\x00-\\x7F]", ""); - } - - // Replace spaces with underscores - private static String baseNameToUniqueName(String baseName) { - return baseName.replaceAll(" ", "_"); - } - private static List loadVisionSourcesFromCamConfigs( List camConfigs) { var cameraSources = new ArrayList(); for (var configuration : camConfigs) { logger.debug("Creating VisionSource for " + configuration); - // Picams should have csi-video in the path - boolean is_picam = - (Arrays.stream(configuration.otherPaths).anyMatch(it -> it.contains("csi-video")) - || configuration.baseName.equals("unicam")); boolean is_pi = Platform.isRaspberryPi(); - if (is_picam && is_pi) { - configuration.cameraType = CameraType.ZeroCopyPicam; + + if (configuration.cameraType==CameraType.ZeroCopyPicam && is_pi) { var piCamSrc = new LibcameraGpuSource(configuration); cameraSources.add(piCamSrc); } else { diff --git a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java index 4a995d26fd..9a4d4b4d95 100644 --- a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java @@ -20,21 +20,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import edu.wpi.first.cscore.UsbCameraInfo; import java.util.ArrayList; import org.junit.jupiter.api.Test; import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; +import org.photonvision.vision.camera.CameraInfo; public class VisionSourceManagerTest { @Test public void visionSourceTest() { var inst = new VisionSourceManager(); - var infoList = new ArrayList(); - inst.cameraInfoSupplier = () -> infoList; + var cameraInfos = new ArrayList(); ConfigManager.getInstance().load(); - inst.tryMatchUSBCamImpl(); + inst.tryMatchCamImpl(cameraInfos); var config3 = new CameraConfiguration( @@ -51,40 +50,40 @@ public void visionSourceTest() { "dev/video2", new String[] {"by-id/321"}); - UsbCameraInfo info1 = new UsbCameraInfo(0, "dev/video0", "testVideo", new String[0], 1, 2); + CameraInfo info1 = new CameraInfo(0, "dev/video0", "testVideo", new String[0], 1, 2); - infoList.add(info1); + cameraInfos.add(info1); inst.registerLoadedConfigs(config3, config4); - inst.tryMatchUSBCamImpl(false); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info1)); + assertTrue(inst.knownCameras.contains(info1)); assertEquals(2, inst.unmatchedLoadedConfigs.size()); - UsbCameraInfo info2 = - new UsbCameraInfo(0, "dev/video1", "secondTestVideo", new String[0], 2, 3); + CameraInfo info2 = + new CameraInfo(0, "dev/video1", "secondTestVideo", new String[0], 2, 3); - infoList.add(info2); + cameraInfos.add(info2); - var cams = inst.matchUSBCameras(infoList, inst.unmatchedLoadedConfigs); + var cams = inst.matchCameras(cameraInfos, inst.unmatchedLoadedConfigs); // assertEquals("testVideo (1)", cams.get(0).uniqueName); // Proper suffixing - inst.tryMatchUSBCamImpl(false); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info2)); + assertTrue(inst.knownCameras.contains(info2)); assertEquals(2, inst.unmatchedLoadedConfigs.size()); - UsbCameraInfo info3 = - new UsbCameraInfo(0, "dev/video2", "thirdTestVideo", new String[] {"by-id/123"}, 3, 4); + CameraInfo info3 = + new CameraInfo(0, "dev/video2", "thirdTestVideo", new String[] {"by-id/123"}, 3, 4); - UsbCameraInfo info4 = - new UsbCameraInfo(0, "dev/video3", "fourthTestVideo", new String[] {"by-id/321"}, 5, 6); + CameraInfo info4 = + new CameraInfo(0, "dev/video3", "fourthTestVideo", new String[] {"by-id/321"}, 5, 6); - infoList.add(info4); + cameraInfos.add(info4); - cams = inst.matchUSBCameras(infoList, inst.unmatchedLoadedConfigs); + cams = inst.matchCameras(cameraInfos, inst.unmatchedLoadedConfigs); var cam4 = cams.stream() @@ -96,14 +95,14 @@ public void visionSourceTest() { assertEquals(cam4.nickname, config4.nickname); - infoList.add(info3); + cameraInfos.add(info3); - cams = inst.matchUSBCameras(infoList, inst.unmatchedLoadedConfigs); + cams = inst.matchCameras(cameraInfos, inst.unmatchedLoadedConfigs); - inst.tryMatchUSBCamImpl(false); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info2)); - assertTrue(inst.knownUsbCameras.contains(info3)); + assertTrue(inst.knownCameras.contains(info2)); + assertTrue(inst.knownCameras.contains(info3)); var cam3 = cams.stream() @@ -121,8 +120,8 @@ public void visionSourceTest() { assertEquals(cam3.nickname, config3.nickname); assertEquals(cam4.nickname, config4.nickname); - UsbCameraInfo info5 = - new UsbCameraInfo( + CameraInfo info5 = + new CameraInfo( 2, "/dev/video2", "Left Camera", @@ -132,13 +131,13 @@ public void visionSourceTest() { }, 7, 8); - infoList.add(info5); - inst.tryMatchUSBCamImpl(false); + cameraInfos.add(info5); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info5)); + assertTrue(inst.knownCameras.contains(info5)); - UsbCameraInfo info6 = - new UsbCameraInfo( + CameraInfo info6 = + new CameraInfo( 3, "dev/video3", "Right Camera", @@ -148,52 +147,87 @@ public void visionSourceTest() { }, 9, 10); - infoList.add(info6); - inst.tryMatchUSBCamImpl(false); + cameraInfos.add(info6); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info6)); + assertTrue(inst.knownCameras.contains(info6)); // RPI 5 CSI Tests - UsbCameraInfo info7 = - new UsbCameraInfo( + + //CSI CAMERAS SHOULD NOT BE LOADED LIKE THIS THEY SHOULD GO THROUGH LIBCAM. + CameraInfo info7 = + new CameraInfo( 4, "dev/video4", "CSICAM-DEV", // Typically rp1-cfe for unit test changed to CSICAM-DEV new String[] {"/dev/v4l/by-path/platform-1f00110000.csi-video-index0"}, 11, 12); - infoList.add(info7); - inst.tryMatchUSBCamImpl(false); + cameraInfos.add(info7); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(inst.knownUsbCameras.contains(info7)); + assertTrue(!inst.knownCameras.contains(info7)); // This camera should not be recognized/used. - UsbCameraInfo info8 = - new UsbCameraInfo( + CameraInfo info8 = + new CameraInfo( 5, "dev/video8", "CSICAM-DEV", // Typically rp1-cfe for unit test changed to CSICAM-DEV new String[] {"/dev/v4l/by-path/platform-1f00110000.csi-video-index4"}, 13, 14); - infoList.add(info8); - inst.tryMatchUSBCamImpl(false); + cameraInfos.add(info8); + inst.tryMatchCamImpl(cameraInfos); - assertTrue(!inst.knownUsbCameras.contains(info8)); // This camera should not be recognized/used. + assertTrue(!inst.knownCameras.contains(info8)); // This camera should not be recognized/used. - UsbCameraInfo info9 = - new UsbCameraInfo( + CameraInfo info9 = + new CameraInfo( 6, "dev/video9", "CSICAM-DEV", // Typically rp1-cfe for unit test changed to CSICAM-DEV new String[] {"/dev/v4l/by-path/platform-1f00110000.csi-video-index5"}, 15, 16); - infoList.add(info9); - inst.tryMatchUSBCamImpl(false); + cameraInfos.add(info9); + inst.tryMatchCamImpl(cameraInfos); + + assertTrue(!inst.knownCameras.contains(info9)); // This camera should not be recognized/used. + assertEquals(6, inst.knownCameras.size()); + assertEquals(0, inst.unmatchedLoadedConfigs.size()); - assertTrue(!inst.knownUsbCameras.contains(info9)); // This camera should not be recognized/used. - assertEquals(7, inst.knownUsbCameras.size()); + //RPI LIBCAMERA CSI CAMERA TESTS + CameraInfo info10 = + new CameraInfo( + -1, + "/base/soc/i2c0mux/i2c@0/ov9281@60", + "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV + null, + -1, + -1); + cameraInfos.add(info10); + inst.tryMatchCamImpl(cameraInfos); + + assertTrue(inst.knownCameras.contains(info10)); + assertEquals(7, inst.knownCameras.size()); assertEquals(0, inst.unmatchedLoadedConfigs.size()); + + CameraInfo info11 = + new CameraInfo( + -1, + "/base/soc/i2c0mux/i2c@1/ov9281@60", + "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV + null, + -1, + -1); + cameraInfos.add(info11); + inst.tryMatchCamImpl(cameraInfos); + + assertTrue(inst.knownCameras.contains(info11)); + assertEquals(8, inst.knownCameras.size()); + assertEquals(0, inst.unmatchedLoadedConfigs.size()); + + } } From 46c060fc716b6222817f2c9c04a5bd4fbaceeb5c Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 17:48:38 -0500 Subject: [PATCH 2/8] Add JNI Check --- .../vision/processes/VisionSourceManager.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index 3b4b9bebd2..30f872e57a 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -98,10 +98,11 @@ protected List getConnectedUSBCameras() { */ protected List getConnectedCSICameras() { List cameraInfos = new ArrayList(); - for (String path : LibCameraJNI.getCameraNames()) { - String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); - cameraInfos.add(new CameraInfo(-1, path, name, null, -1, -1, CameraType.ZeroCopyPicam)); - } + if(LibCameraJNI.isSupported()) + for (String path : LibCameraJNI.getCameraNames()) { + String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); + cameraInfos.add(new CameraInfo(-1, path, name, null, -1, -1, CameraType.ZeroCopyPicam)); + } return cameraInfos; } From dc2359c58fba52fe9ff84e8462a26449e91c51c0 Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 20:47:36 -0500 Subject: [PATCH 3/8] Fix JNI issues, Fix equals issue. --- .../org/photonvision/raspi/LibCameraJNI.java | 8 +++----- .../vision/camera/CameraInfo.java | 11 ++++++++--- .../vision/camera/LibcameraGpuSettables.java | 14 ++++++++++++-- .../provider/LibcameraGpuFrameProvider.java | 16 ++++++++++++---- .../vision/processes/VisionSourceManager.java | 8 +++++--- .../processes/VisionSourceManagerTest.java | 4 ++-- .../nativelibraries/libphotonlibcamera.so | Bin 167016 -> 166696 bytes 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java index 761e09b6c0..d1dcf57fcf 100644 --- a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java +++ b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java @@ -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; @@ -162,7 +160,7 @@ public static native boolean setThresholds( * 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 r_ptr); + 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 @@ -193,9 +191,9 @@ public static native boolean setThresholds( /** * 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(); + public static native int getGpuProcessType(long p_ptr); /** * Release a pair pointer back to the libcamera driver code to be filled again diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java index a12d978791..71f7ae1a1e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java @@ -14,13 +14,17 @@ public CameraInfo(int dev, String path, String name, String[] otherPaths, int ve 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() { - if(otherPaths == null) return false; return (Arrays.stream(otherPaths).anyMatch(it -> it.contains("csi-video")) || getBaseName().equals("unicam")); } @@ -42,10 +46,11 @@ public String getHumanReadableName() { return getBaseName().replaceAll(" ", "_"); } - public boolean Equals(Object o) { + @Override + public boolean equals(Object o) { if (o == this) return true; - if (!(o instanceof UsbCameraInfo)) + if (!(o instanceof UsbCameraInfo || o instanceof CameraInfo)) return false; UsbCameraInfo other = (UsbCameraInfo) o; return path.equals(other.path) diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java index 4301d94a9d..38b5479dfa 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java @@ -38,7 +38,9 @@ public class LibcameraGpuSettables extends VisionSourceSettables { private final LibCameraJNI.SensorModel sensorModel; - private ImageRotationMode m_rotationMode; + private ImageRotationMode m_rotationMode = ImageRotationMode.DEG_0; + + public final Object CAMERA_LOCK = new Object(); public void setRotation(ImageRotationMode rotationMode) { if (rotationMode != m_rotationMode) { @@ -201,7 +203,7 @@ protected void setVideoModeInternal(VideoMode videoMode) { // We need to make sure that other threads don't try to do anything funny while we're recreating // the camera - synchronized (LibCameraJNI.CAMERA_LOCK) { + synchronized (CAMERA_LOCK) { if (r_ptr!=0) { logger.debug("Stopping libcamera"); if (!LibCameraJNI.stopCamera(r_ptr)) { @@ -218,6 +220,9 @@ protected void setVideoModeInternal(VideoMode videoMode) { getConfiguration().path, mode.width, mode.height, (m_rotationMode == ImageRotationMode.DEG_180 ? 180 : 0)); if (r_ptr==0) { logger.error("Couldn't create a zero copy Pi Camera while switching video modes"); + if (!LibCameraJNI.destroyCamera(r_ptr)) { + logger.error("Couldn't destroy a zero copy Pi Camera while switching video modes"); + } } logger.debug("Starting libcamera"); if (!LibCameraJNI.startCamera(r_ptr)) { @@ -242,4 +247,9 @@ protected void setVideoModeInternal(VideoMode videoMode) { public HashMap getAllVideoModes() { return videoModes; } + + public LibCameraJNI.SensorModel getModel() + { + return sensorModel; + } } diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java index 34b40283a0..1c9dcea978 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java @@ -20,6 +20,7 @@ import org.opencv.core.Mat; import org.photonvision.common.util.math.MathUtils; import org.photonvision.raspi.LibCameraJNI; +import org.photonvision.raspi.LibCameraJNI.SensorModel; import org.photonvision.vision.camera.LibcameraGpuSettables; import org.photonvision.vision.frame.Frame; import org.photonvision.vision.frame.FrameProvider; @@ -51,7 +52,7 @@ public Frame get() { // we're waiting // for a frame // System.out.println("GET!"); - synchronized (LibCameraJNI.CAMERA_LOCK) { + synchronized (settables.CAMERA_LOCK) { var p_ptr = LibCameraJNI.awaitNewFrame(settables.r_ptr); if (p_ptr == 0) { @@ -61,22 +62,26 @@ public Frame get() { var colorMat = new CVMat(new Mat(LibCameraJNI.takeColorFrame(p_ptr))); var processedMat = new CVMat(new Mat(LibCameraJNI.takeProcessedFrame(p_ptr))); + // System.out.println("Color mat: " + colorMat.getMat().size()); // Imgcodecs.imwrite("color" + i + ".jpg", colorMat.getMat()); // Imgcodecs.imwrite("processed" + (i) + ".jpg", processedMat.getMat()); - int itype = LibCameraJNI.getGpuProcessType(); + int itype = LibCameraJNI.getGpuProcessType(p_ptr); FrameThresholdType type = FrameThresholdType.NONE; if (itype < FrameThresholdType.values().length && itype >= 0) { type = FrameThresholdType.values()[itype]; } var now = LibCameraJNI.getLibcameraTimestamp(); - var capture = LibCameraJNI.getFrameCaptureTime(settables.r_ptr); + var capture = LibCameraJNI.getFrameCaptureTime(p_ptr); var latency = (now - capture); + LibCameraJNI.releasePair(p_ptr); + + return new Frame( colorMat, processedMat, @@ -88,7 +93,10 @@ public Frame get() { @Override public void requestFrameThresholdType(FrameThresholdType type) { - LibCameraJNI.setGpuProcessType(type.ordinal()); + if(settables.getModel() == SensorModel.OV9281 && type.equals(FrameThresholdType.GREYSCALE)) + LibCameraJNI.setGpuProcessType(settables.r_ptr,4); // 4 = Grayscale pass through. + else + LibCameraJNI.setGpuProcessType(settables.r_ptr,type.ordinal()); } @Override diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index 30f872e57a..be52e1bb4f 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -86,7 +86,7 @@ public void registerLoadedConfigs(Collection configs) { * @return a list containing usbcamerainfo. */ protected List getConnectedUSBCameras() { - List cameraInfos = List.of(UsbCamera.enumerateUsbCameras()).stream().map(c -> (CameraInfo) c) + List cameraInfos = List.of(UsbCamera.enumerateUsbCameras()).stream().map(c -> new CameraInfo(c)) .collect(Collectors.toList()); return cameraInfos; } @@ -101,7 +101,7 @@ protected List getConnectedCSICameras() { if(LibCameraJNI.isSupported()) for (String path : LibCameraJNI.getCameraNames()) { String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); - cameraInfos.add(new CameraInfo(-1, path, name, null, -1, -1, CameraType.ZeroCopyPicam)); + cameraInfos.add(new CameraInfo(-1, path, name, new String[]{}, -1, -1, CameraType.ZeroCopyPicam)); } return cameraInfos; } @@ -269,6 +269,7 @@ private List matchByPathByID(List detectedCamIn if (cameraInfo != null) { logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); ret.add(mergeInfoIntoConfig(config, cameraInfo)); + detectedCamInfos.remove(cameraInfo); } } } @@ -300,8 +301,8 @@ private List matchByPath(List detectedCamInfos, // and add it to the output if (cameraInfo != null) { logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); - detectedCamInfos.remove(cameraInfo); ret.add(mergeInfoIntoConfig(config, cameraInfo)); + detectedCamInfos.remove(cameraInfo); } } return ret; @@ -329,6 +330,7 @@ private List matchByName(List detectedCamInfos, if (cameraInfo != null) { logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); ret.add(mergeInfoIntoConfig(config, cameraInfo)); + detectedCamInfos.remove(cameraInfo); } } return ret; diff --git a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java index 9a4d4b4d95..8db31ab6b8 100644 --- a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java @@ -203,7 +203,7 @@ public void visionSourceTest() { -1, "/base/soc/i2c0mux/i2c@0/ov9281@60", "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV - null, + new String[]{}, -1, -1); cameraInfos.add(info10); @@ -218,7 +218,7 @@ public void visionSourceTest() { -1, "/base/soc/i2c0mux/i2c@1/ov9281@60", "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV - null, + new String[]{}, -1, -1); cameraInfos.add(info11); diff --git a/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so b/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so index c2d0d9b496acfcd3b9b2ba2eff0652f0c8ceef2b..cc927e646f16cf0b0b5ce1491ec0de53faea806b 100644 GIT binary patch literal 166696 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

eQYr&??yTIrl<#A!aQ$; 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 Date: Sat, 23 Dec 2023 22:37:55 -0500 Subject: [PATCH 4/8] More config checks and spotlessapply --- .../org/photonvision/raspi/LibCameraJNI.java | 19 +- .../vision/camera/CameraInfo.java | 48 +++-- .../provider/LibcameraGpuFrameProvider.java | 12 +- .../vision/processes/VisionSourceManager.java | 184 ++++++++++-------- .../processes/VisionSourceManagerTest.java | 51 ++++- 5 files changed, 193 insertions(+), 121 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java index d1dcf57fcf..d97d102681 100644 --- a/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java +++ b/photon-core/src/main/java/org/photonvision/raspi/LibCameraJNI.java @@ -143,7 +143,14 @@ public static boolean isSupported() { // 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); + 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); @@ -195,13 +202,9 @@ public static native boolean setThresholds( public static native int getGpuProcessType(long p_ptr); - /** - * Release a pair pointer back to the libcamera driver code to be filled again - */ + /** 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(); + /** Get an array containing the names/ids/paths of all connected CSI cameras from libcamera. */ + public static native String[] getCameraNames(); } diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java index 71f7ae1a1e..568453bdfe 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/CameraInfo.java @@ -1,27 +1,52 @@ -package org.photonvision.vision.camera; +/* + * 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 . + */ -import java.util.Arrays; +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) { + + 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) { + + 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) - { + + 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() { @@ -30,7 +55,6 @@ public boolean getIsV4lCsiCamera() { } /** - * * @return The base name of the camera aka the name as just ascii. */ public String getBaseName() { @@ -38,7 +62,6 @@ public String getBaseName() { } /** - * * @param baseName * @return Returns a human readable name */ @@ -48,10 +71,8 @@ public String getHumanReadableName() { @Override public boolean equals(Object o) { - if (o == this) - return true; - if (!(o instanceof UsbCameraInfo || o instanceof CameraInfo)) - return false; + 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) @@ -59,5 +80,4 @@ public boolean equals(Object o) { && productId == other.productId && vendorId == other.vendorId; } - } diff --git a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java index 1c9dcea978..36ebe47cfb 100644 --- a/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java +++ b/photon-core/src/main/java/org/photonvision/vision/frame/provider/LibcameraGpuFrameProvider.java @@ -62,7 +62,6 @@ public Frame get() { var colorMat = new CVMat(new Mat(LibCameraJNI.takeColorFrame(p_ptr))); var processedMat = new CVMat(new Mat(LibCameraJNI.takeProcessedFrame(p_ptr))); - // System.out.println("Color mat: " + colorMat.getMat().size()); @@ -81,7 +80,6 @@ public Frame get() { LibCameraJNI.releasePair(p_ptr); - return new Frame( colorMat, processedMat, @@ -93,10 +91,9 @@ public Frame get() { @Override public void requestFrameThresholdType(FrameThresholdType type) { - if(settables.getModel() == SensorModel.OV9281 && type.equals(FrameThresholdType.GREYSCALE)) - LibCameraJNI.setGpuProcessType(settables.r_ptr,4); // 4 = Grayscale pass through. - else - LibCameraJNI.setGpuProcessType(settables.r_ptr,type.ordinal()); + if (settables.getModel() == SensorModel.OV9281 && type.equals(FrameThresholdType.GREYSCALE)) + LibCameraJNI.setGpuProcessType(settables.r_ptr, 4); // 4 = Grayscale pass through. + else LibCameraJNI.setGpuProcessType(settables.r_ptr, type.ordinal()); } @Override @@ -106,7 +103,8 @@ public void requestFrameRotation(ImageRotationMode rotationMode) { @Override public void requestHsvSettings(HSVParams params) { - LibCameraJNI.setThresholds(settables.r_ptr, + LibCameraJNI.setThresholds( + settables.r_ptr, params.getHsvLower().val[0] / 180.0, params.getHsvLower().val[1] / 255.0, params.getHsvLower().val[2] / 255.0, diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index be52e1bb4f..853d19c017 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -57,8 +57,7 @@ public static VisionSourceManager getInstance() { return SingletonHolder.INSTANCE; } - VisionSourceManager() { - } + VisionSourceManager() {} public void registerTimedTask() { TimedTaskManager.getInstance().addTask("VisionSourceManager", this::tryMatchCams, 3000); @@ -69,8 +68,7 @@ public void registerLoadedConfigs(CameraConfiguration... configs) { } /** - * Register new camera configs loaded from disk. This will add them to the list - * of configs to try + * Register new camera configs loaded from disk. This will add them to the list of configs to try * to match, and also automatically spawn new vision processes as necessary. * * @param configs The loaded camera configs. @@ -80,36 +78,37 @@ public void registerLoadedConfigs(Collection configs) { } /** - * Pre filter out any csi cameras to return just USB Cameras. Allow defining the - * camerainfo. - * + * Pre filter out any csi cameras to return just USB Cameras. Allow defining the camerainfo. + * * @return a list containing usbcamerainfo. */ protected List getConnectedUSBCameras() { - List cameraInfos = List.of(UsbCamera.enumerateUsbCameras()).stream().map(c -> new CameraInfo(c)) - .collect(Collectors.toList()); + List cameraInfos = + List.of(UsbCamera.enumerateUsbCameras()).stream() + .map(c -> new CameraInfo(c)) + .collect(Collectors.toList()); return cameraInfos; } /** * Retrieve the list of csi cameras from libcamera. - * + * * @return a list containing csicamerainfo. */ protected List getConnectedCSICameras() { List cameraInfos = new ArrayList(); - if(LibCameraJNI.isSupported()) + if (LibCameraJNI.isSupported()) for (String path : LibCameraJNI.getCameraNames()) { String name = LibCameraJNI.getSensorModel(path).getFriendlyName(); - cameraInfos.add(new CameraInfo(-1, path, name, new String[]{}, -1, -1, CameraType.ZeroCopyPicam)); + cameraInfos.add( + new CameraInfo(-1, path, name, new String[] {}, -1, -1, CameraType.ZeroCopyPicam)); } return cameraInfos; } protected void tryMatchCams() { var visionSourceList = tryMatchCamImpl(); - if (visionSourceList == null) - return; + if (visionSourceList == null) return; logger.info("Adding " + visionSourceList.size() + " configs to VMM."); ConfigManager.getInstance().addCameraConfigurations(visionSourceList); @@ -126,7 +125,7 @@ protected List tryMatchCamImpl() { } /** - * @param cameraInfos Used to feed camera info for unit tests. + * @param cameraInfos Used to feed camera info for unit tests. * @return New VisionSources. */ protected List tryMatchCamImpl(ArrayList cameraInfos) { @@ -137,8 +136,7 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) connectedCameras = new ArrayList<>(filterAllowedDevices(getConnectedUSBCameras())); // Detect CSI cameras using libcamera connectedCameras.addAll(new ArrayList<>(filterAllowedDevices(getConnectedCSICameras()))); - } - else{ + } else { connectedCameras = new ArrayList<>(filterAllowedDevices(cameraInfos)); createSources = false; } @@ -153,8 +151,7 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) connectedCameras.removeIf(c -> knownCameras.contains(c)); // All cameras are already loaded return no new sources. - if (connectedCameras.isEmpty()) - return null; + if (connectedCameras.isEmpty()) return null; logger.debug("Matching " + connectedCameras.size() + " new cameras!"); @@ -167,14 +164,16 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) logger.debug("Trying to match " + unmatchedLoadedConfigs.size() + " unmatched configs..."); // Match camera configs to physical cameras - List matchedCameras = matchCameras(connectedCameras, unmatchedLoadedConfigs); + List matchedCameras = + matchCameras(connectedCameras, unmatchedLoadedConfigs); unmatchedLoadedConfigs.removeAll(matchedCameras); if (!unmatchedLoadedConfigs.isEmpty() && !hasWarned) { logger.warn( - () -> "After matching, " - + unmatchedLoadedConfigs.size() - + " configs remained unmatched. Is your camera disconnected?"); + () -> + "After matching, " + + unmatchedLoadedConfigs.size() + + " configs remained unmatched. Is your camera disconnected?"); logger.warn( "Unloaded configs: " + unmatchedLoadedConfigs.stream() @@ -186,8 +185,7 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) // We add the matched cameras to the known camera list this.knownCameras.addAll(connectedCameras); - if (matchedCameras.isEmpty()) - return null; + if (matchedCameras.isEmpty()) return null; // for unit tests only! if (!createSources) { @@ -200,51 +198,62 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) // Print info about each vision source for (var src : sources) { logger.debug( - () -> "Matched config for camera \"" - + src.getFrameProvider().getName() - + "\" and loaded " - + src.getCameraConfiguration().pipelineSettings.size() - + " pipelines"); + () -> + "Matched config for camera \"" + + src.getFrameProvider().getName() + + "\" and loaded " + + src.getCameraConfiguration().pipelineSettings.size() + + " pipelines"); } return sources; } /** - * Create {@link CameraConfiguration}s based on a list of detected USB cameras - * and the configs on + * Create {@link CameraConfiguration}s based on a list of detected USB cameras and the configs on * disk. * - * @param detectedCamInfos Information about currently connected USB cameras. - * @param loadedUsbCamConfigs The USB {@link CameraConfiguration}s loaded from - * disk. + * @param detectedCamInfos Information about currently connected USB cameras. + * @param loadedCamConfigs The USB {@link CameraConfiguration}s loaded from disk. * @return the matched configurations. */ public List matchCameras( - List detectedCamInfos, - List loadedUsbCamConfigs) { - + List detectedCamInfos, List loadedCamConfigs) { var detectedCameraList = new ArrayList<>(detectedCamInfos); - ArrayList cameraConfigurations = new ArrayList<>(); + ArrayList cameraConfigurations = new ArrayList(); + ArrayList unloadedConfigs = + new ArrayList(loadedCamConfigs); + + if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) + cameraConfigurations.addAll(matchByPathByID(detectedCameraList, unloadedConfigs)); + else logger.debug("Skipping matchByPath no configs or cameras left to match"); - cameraConfigurations.addAll(matchByPathByID(detectedCameraList, loadedUsbCamConfigs)); - cameraConfigurations.addAll(matchByPath(detectedCameraList, loadedUsbCamConfigs)); - cameraConfigurations.addAll(matchByName(detectedCameraList, loadedUsbCamConfigs)); - cameraConfigurations.addAll( - createConfigsForCameras(detectedCameraList, loadedUsbCamConfigs, cameraConfigurations)); + if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) + cameraConfigurations.addAll(matchByPath(detectedCameraList, unloadedConfigs)); + else logger.debug("Skipping matchByPath no configs or cameras left to match"); + + if (detectedCameraList.size() > 0 || unloadedConfigs.size() > 0) + cameraConfigurations.addAll(matchByName(detectedCameraList, unloadedConfigs)); + else logger.debug("Skipping matchByName no configs or cameras left to match"); + + if (detectedCameraList.size() > 0) + cameraConfigurations.addAll( + createConfigsForCameras(detectedCameraList, unloadedConfigs, cameraConfigurations)); logger.debug("Matched or created " + cameraConfigurations.size() + " camera configs!"); return cameraConfigurations; - } // loop over all the configs loaded from disk, attempting to match each camera // to a config by path-by-id on linux - private List matchByPathByID(List detectedCamInfos, - List loadedCamConfigs) { + private List matchByPathByID( + List detectedCamInfos, List unloadedConfigs) { List ret = new ArrayList(); - for (CameraConfiguration config : loadedCamConfigs) { - //Only run match path by id if the camera is not a CSI camera. + List unloadedConfigsCopy = + new ArrayList(unloadedConfigs); + + for (CameraConfiguration config : unloadedConfigsCopy) { + // Only run match path by id if the camera is not a CSI camera. if (config.cameraType != CameraType.ZeroCopyPicam) { CameraInfo cameraInfo; if (config.otherPaths.length == 0) { @@ -256,13 +265,15 @@ private List matchByPathByID(List detectedCamIn + config.baseName + " with path-by-id " + config.otherPaths[0]); - cameraInfo = detectedCamInfos.stream() - .filter( - usbCameraInfo -> usbCameraInfo.otherPaths.length != 0 - && usbCameraInfo.otherPaths[0].equals(config.otherPaths[0]) - && usbCameraInfo.getBaseName().equals(config.baseName)) - .findFirst() - .orElse(null); + cameraInfo = + detectedCamInfos.stream() + .filter( + usbCameraInfo -> + usbCameraInfo.otherPaths.length != 0 + && usbCameraInfo.otherPaths[0].equals(config.otherPaths[0]) + && usbCameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); // If we actually matched a camera to a config, remove that camera from the list // and add it to the output @@ -270,6 +281,7 @@ private List matchByPathByID(List detectedCamIn logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); ret.add(mergeInfoIntoConfig(config, cameraInfo)); detectedCamInfos.remove(cameraInfo); + unloadedConfigs.remove(config); } } } @@ -277,11 +289,13 @@ private List matchByPathByID(List detectedCamIn return ret; } - private List matchByPath(List detectedCamInfos, - List loadedUsbCamConfigs) { + private List matchByPath( + List detectedCamInfos, List unloadedConfigs) { List ret = new ArrayList(); + List unloadedConfigsCopy = + new ArrayList(unloadedConfigs); // now attempt to match the cameras and configs remaining by normal path - for (CameraConfiguration config : loadedUsbCamConfigs) { + for (CameraConfiguration config : unloadedConfigsCopy) { CameraInfo cameraInfo; // attempt matching by path and basename @@ -290,12 +304,14 @@ private List matchByPath(List detectedCamInfos, + config.baseName + " with path " + config.path); - cameraInfo = detectedCamInfos.stream() - .filter( - usbCameraInfo -> usbCameraInfo.path.equals(config.path) - && usbCameraInfo.getBaseName().equals(config.baseName)) - .findFirst() - .orElse(null); + cameraInfo = + detectedCamInfos.stream() + .filter( + usbCameraInfo -> + usbCameraInfo.path.equals(config.path) + && usbCameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); // If we actually matched a camera to a config, remove that camera from the list // and add it to the output @@ -303,27 +319,29 @@ private List matchByPath(List detectedCamInfos, logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); ret.add(mergeInfoIntoConfig(config, cameraInfo)); detectedCamInfos.remove(cameraInfo); + unloadedConfigs.remove(config); } } return ret; - } - //Try matching cameras to configs by name. - private List matchByName(List detectedCamInfos, - List loadedCamConfigs) { + // Try matching cameras to configs by name. + private List matchByName( + List detectedCamInfos, List unloadedConfigs) { List ret = new ArrayList(); + List unloadedConfigsCopy = + new ArrayList(unloadedConfigs); // if both path and ID based matching fails, attempt basename only match - for (CameraConfiguration config : loadedCamConfigs) { + for (CameraConfiguration config : unloadedConfigsCopy) { CameraInfo cameraInfo; logger.debug("Trying to find a match for loaded camera with name " + config.baseName); - cameraInfo = detectedCamInfos.stream() - .filter( - CameraInfo -> CameraInfo.getBaseName().equals(config.baseName)) - .findFirst() - .orElse(null); + cameraInfo = + detectedCamInfos.stream() + .filter(CameraInfo -> CameraInfo.getBaseName().equals(config.baseName)) + .findFirst() + .orElse(null); // If we actually matched a camera to a config, remove that camera from the list // and add it to the output @@ -331,6 +349,7 @@ private List matchByName(List detectedCamInfos, logger.debug("Matched the config for " + config.baseName + " to a physical camera!"); ret.add(mergeInfoIntoConfig(config, cameraInfo)); detectedCamInfos.remove(cameraInfo); + unloadedConfigs.remove(config); } } return ret; @@ -338,8 +357,10 @@ private List matchByName(List detectedCamInfos, // If we have any unmatched cameras left, create a new CameraConfiguration for // them here. - private List createConfigsForCameras(List detectedCameraList, - List loadedCamConfigs, List loadedConfigs) { + private List createConfigsForCameras( + List detectedCameraList, + List loadedCamConfigs, + List loadedConfigs) { List ret = new ArrayList(); logger.debug( "After matching loaded configs " + detectedCameraList.size() + " cameras were unmatched."); @@ -358,8 +379,8 @@ private List createConfigsForCameras(List detec String nickname = uniqueName; - CameraConfiguration configuration = new CameraConfiguration(baseName, uniqueName, nickname, info.path, - info.otherPaths); + CameraConfiguration configuration = + new CameraConfiguration(baseName, uniqueName, nickname, info.path, info.otherPaths); configuration.cameraType = info.cameraType; @@ -406,12 +427,11 @@ public void setIgnoredCamerasRegex(String ignoredCamerasRegex) { /** * Filter out any blacklisted or ignored devices. - * + * * @param allDevices * @return list of devices with blacklisted or ingore devices removed. */ private List filterAllowedDevices(List allDevices) { - List filteredDevices = new ArrayList<>(); for (var device : allDevices) { if (deviceBlacklist.contains(device.name)) { @@ -437,7 +457,7 @@ private static List loadVisionSourcesFromCamConfigs( boolean is_pi = Platform.isRaspberryPi(); - if (configuration.cameraType==CameraType.ZeroCopyPicam && is_pi) { + if (configuration.cameraType == CameraType.ZeroCopyPicam && is_pi) { var piCamSrc = new LibcameraGpuSource(configuration); cameraSources.add(piCamSrc); } else { diff --git a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java index 8db31ab6b8..487a9e5ae9 100644 --- a/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java +++ b/photon-core/src/test/java/org/photonvision/vision/processes/VisionSourceManagerTest.java @@ -25,6 +25,7 @@ import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.vision.camera.CameraInfo; +import org.photonvision.vision.camera.CameraType; public class VisionSourceManagerTest { @Test @@ -61,8 +62,7 @@ public void visionSourceTest() { assertTrue(inst.knownCameras.contains(info1)); assertEquals(2, inst.unmatchedLoadedConfigs.size()); - CameraInfo info2 = - new CameraInfo(0, "dev/video1", "secondTestVideo", new String[0], 2, 3); + CameraInfo info2 = new CameraInfo(0, "dev/video1", "secondTestVideo", new String[0], 2, 3); cameraInfos.add(info2); @@ -154,8 +154,7 @@ public void visionSourceTest() { // RPI 5 CSI Tests - - //CSI CAMERAS SHOULD NOT BE LOADED LIKE THIS THEY SHOULD GO THROUGH LIBCAM. + // CSI CAMERAS SHOULD NOT BE LOADED LIKE THIS THEY SHOULD GO THROUGH LIBCAM. CameraInfo info7 = new CameraInfo( 4, @@ -197,15 +196,16 @@ public void visionSourceTest() { assertEquals(6, inst.knownCameras.size()); assertEquals(0, inst.unmatchedLoadedConfigs.size()); - //RPI LIBCAMERA CSI CAMERA TESTS + // RPI LIBCAMERA CSI CAMERA TESTS CameraInfo info10 = new CameraInfo( -1, "/base/soc/i2c0mux/i2c@0/ov9281@60", "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV - new String[]{}, + new String[] {}, + -1, -1, - -1); + CameraType.ZeroCopyPicam); cameraInfos.add(info10); inst.tryMatchCamImpl(cameraInfos); @@ -218,9 +218,10 @@ public void visionSourceTest() { -1, "/base/soc/i2c0mux/i2c@1/ov9281@60", "OV9281", // Typically rp1-cfe for unit test changed to CSICAM-DEV - new String[]{}, + new String[] {}, + -1, -1, - -1); + CameraType.ZeroCopyPicam); cameraInfos.add(info11); inst.tryMatchCamImpl(cameraInfos); @@ -228,6 +229,36 @@ public void visionSourceTest() { assertEquals(8, inst.knownCameras.size()); assertEquals(0, inst.unmatchedLoadedConfigs.size()); - + CameraInfo info12 = + new CameraInfo( + -1, + " /base/axi/pcie@120000/rp1/i2c@80000/ov5647@36", + "Camera Module v1", + new String[] {}, + -1, + -1, + CameraType.ZeroCopyPicam); + cameraInfos.add(info12); + inst.tryMatchCamImpl(cameraInfos); + + assertTrue(inst.knownCameras.contains(info12)); + assertEquals(9, inst.knownCameras.size()); + assertEquals(0, inst.unmatchedLoadedConfigs.size()); + + CameraInfo info13 = + new CameraInfo( + -1, + "/base/axi/pcie@120000/rp1/i2c@88000/imx708@1a", + "Camera Module v3", + new String[] {}, + -1, + -1, + CameraType.ZeroCopyPicam); + cameraInfos.add(info13); + inst.tryMatchCamImpl(cameraInfos); + + assertTrue(inst.knownCameras.contains(info13)); + assertEquals(10, inst.knownCameras.size()); + assertEquals(0, inst.unmatchedLoadedConfigs.size()); } } From f07bb5fa143f8ec87e4fdf8f09955a5742ffb0ac Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 22:38:09 -0500 Subject: [PATCH 5/8] Update LibcameraGpuSettables.java --- .../vision/camera/LibcameraGpuSettables.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java index 38b5479dfa..45aefb5fc0 100644 --- a/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java +++ b/photon-core/src/main/java/org/photonvision/vision/camera/LibcameraGpuSettables.java @@ -149,7 +149,7 @@ public void setExposure(double exposure) { } lastManualExposure = exposure; - var success = LibCameraJNI.setExposure(r_ptr,(int) Math.round(exposure) * 800); + var success = LibCameraJNI.setExposure(r_ptr, (int) Math.round(exposure) * 800); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera exposure"); } @@ -157,7 +157,7 @@ public void setExposure(double exposure) { public void setBrightness(int brightness) { lastBrightness = brightness; double realBrightness = MathUtils.map(brightness, 0.0, 100.0, -1.0, 1.0); - var success = LibCameraJNI.setBrightness(r_ptr,realBrightness); + var success = LibCameraJNI.setBrightness(r_ptr, realBrightness); if (!success) LibcameraGpuSource.logger.warn("Couldn't set Pi Camera brightness"); } @@ -204,7 +204,7 @@ protected void setVideoModeInternal(VideoMode videoMode) { // We need to make sure that other threads don't try to do anything funny while we're recreating // the camera synchronized (CAMERA_LOCK) { - if (r_ptr!=0) { + if (r_ptr != 0) { logger.debug("Stopping libcamera"); if (!LibCameraJNI.stopCamera(r_ptr)) { logger.error("Couldn't stop a zero copy Pi Camera while switching video modes"); @@ -216,9 +216,13 @@ protected void setVideoModeInternal(VideoMode videoMode) { } logger.debug("Creating libcamera"); - r_ptr = LibCameraJNI.createCamera( - getConfiguration().path, mode.width, mode.height, (m_rotationMode == ImageRotationMode.DEG_180 ? 180 : 0)); - if (r_ptr==0) { + r_ptr = + LibCameraJNI.createCamera( + getConfiguration().path, + mode.width, + mode.height, + (m_rotationMode == ImageRotationMode.DEG_180 ? 180 : 0)); + if (r_ptr == 0) { logger.error("Couldn't create a zero copy Pi Camera while switching video modes"); if (!LibCameraJNI.destroyCamera(r_ptr)) { logger.error("Couldn't destroy a zero copy Pi Camera while switching video modes"); @@ -238,7 +242,7 @@ protected void setVideoModeInternal(VideoMode videoMode) { setGain(lastGain); setAwbGain(lastAwbGains.getFirst(), lastAwbGains.getSecond()); - LibCameraJNI.setFramesToCopy(r_ptr,true, true); + LibCameraJNI.setFramesToCopy(r_ptr, true, true); currentVideoMode = mode; } @@ -248,8 +252,7 @@ public HashMap getAllVideoModes() { return videoModes; } - public LibCameraJNI.SensorModel getModel() - { + public LibCameraJNI.SensorModel getModel() { return sensorModel; } } From cadda0a3c1c2ee5bfdf573929d19285300080c9a Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 22:50:07 -0500 Subject: [PATCH 6/8] Update VisionSourceManager.java --- .../vision/processes/VisionSourceManager.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java index 853d19c017..a84e6aba4e 100644 --- a/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java +++ b/photon-core/src/main/java/org/photonvision/vision/processes/VisionSourceManager.java @@ -47,6 +47,7 @@ public class VisionSourceManager { final List unmatchedLoadedConfigs = new CopyOnWriteArrayList<>(); private boolean hasWarned; + private boolean hasWarnedNoCameras = false; private String ignoredCamerasRegex = ""; private static class SingletonHolder { @@ -138,16 +139,21 @@ protected List tryMatchCamImpl(ArrayList cameraInfos) connectedCameras.addAll(new ArrayList<>(filterAllowedDevices(getConnectedCSICameras()))); } else { connectedCameras = new ArrayList<>(filterAllowedDevices(cameraInfos)); - createSources = false; + createSources = + false; // Dont create sources if we are using supplied camerainfo for unit tests. } // Return no new sources because there are no new sources if (connectedCameras.isEmpty() && !cameraInfos.isEmpty()) { - logger.warn( - "No cameras were detected! Check that all cameras are connected, and that the path is correct."); + if (hasWarnedNoCameras) { + logger.warn( + "No cameras were detected! Check that all cameras are connected, and that the path is correct."); + hasWarnedNoCameras = true; + } return null; - } + } else hasWarnedNoCameras = false; + // Remove any known cameras. connectedCameras.removeIf(c -> knownCameras.contains(c)); // All cameras are already loaded return no new sources. @@ -458,6 +464,7 @@ private static List loadVisionSourcesFromCamConfigs( boolean is_pi = Platform.isRaspberryPi(); if (configuration.cameraType == CameraType.ZeroCopyPicam && is_pi) { + // If the camera was loaded from libcamera then create its source using libcamera. var piCamSrc = new LibcameraGpuSource(configuration); cameraSources.add(piCamSrc); } else { From 9ecbc72d8ddcb7e6ee6849302f514598209d9343 Mon Sep 17 00:00:00 2001 From: BytingBulldogs3539 Date: Sat, 23 Dec 2023 23:02:24 -0500 Subject: [PATCH 7/8] Update PiCmds.java --- .../org/photonvision/common/hardware/metrics/cmds/PiCmds.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java index 6eb005acc5..ba72dc5531 100644 --- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java +++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/cmds/PiCmds.java @@ -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\"; " From d8fb114ab3cb4b8bd523af89f9d2b5b47968df24 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 24 Dec 2023 17:00:46 -0800 Subject: [PATCH 8/8] Remove print spam from libcam-driver --- .../nativelibraries/libphotonlibcamera.so | Bin 166696 -> 166624 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so b/photon-server/src/main/resources/nativelibraries/libphotonlibcamera.so index cc927e646f16cf0b0b5ce1491ec0de53faea806b..fe65d5813227d2226722443d22780b8093bafcb8 100644 GIT binary patch delta 47751 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
63Dsf7iD+Ryg*Lhi9*NWTnAnmr`MGf6& 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