From 0ce49bd8f2acd023fdc4a28cb57b8cc21ad5cc1f Mon Sep 17 00:00:00 2001 From: Chris Gerth Date: Tue, 27 Oct 2020 16:57:11 -0500 Subject: [PATCH] Improve Pi Non-GPU-accel exposure (#148) Changes exposure setters to accept a floating point input (rather than integer) Updates the UI to change exposure in increments of 0.1 (rather than 1.0) Updates quirky PI camera logic to use the raw_ interface with scaling/offset logic matching the GPU-accelerated pi3 camera logic from Declan. Adds logic to disable auto-white-balance in the PI camera, which should yield more consistent vision processing results. --- .../src/views/PipelineViews/InputTab.vue | 5 +- .../vision/camera/FileVisionSource.java | 2 +- .../vision/camera/USBCameraSource.java | 48 ++++++++++++++++--- .../vision/pipeline/CVPipelineSettings.java | 2 +- .../processes/VisionSourceSettables.java | 2 +- .../processes/VisionModuleManagerTest.java | 2 +- 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/photon-client/src/views/PipelineViews/InputTab.vue b/photon-client/src/views/PipelineViews/InputTab.vue index 19bac375e..c789e4571 100644 --- a/photon-client/src/views/PipelineViews/InputTab.vue +++ b/photon-client/src/views/PipelineViews/InputTab.vue @@ -5,6 +5,7 @@ name="Exposure" min="0" max="100" + step="0.1" tooltip="Directly controls how much light is allowed to fall onto the sensor, which affects brightness" :slider-cols="largeBox" @input="handlePipelineData('cameraExposure')" @@ -87,10 +88,10 @@ }, cameraExposure: { get() { - return parseInt(this.$store.getters.currentPipelineSettings.cameraExposure); + return parseFloat(this.$store.getters.currentPipelineSettings.cameraExposure); }, set(val) { - this.$store.commit("mutatePipeline", {"cameraExposure": parseInt(val)}); + this.$store.commit("mutatePipeline", {"cameraExposure": parseFloat(val)}); } }, cameraBrightness: { diff --git a/photon-server/src/main/java/org/photonvision/vision/camera/FileVisionSource.java b/photon-server/src/main/java/org/photonvision/vision/camera/FileVisionSource.java index 538cd6b9b..f1042a0aa 100644 --- a/photon-server/src/main/java/org/photonvision/vision/camera/FileVisionSource.java +++ b/photon-server/src/main/java/org/photonvision/vision/camera/FileVisionSource.java @@ -89,7 +89,7 @@ public class FileVisionSource implements VisionSource { } @Override - public void setExposure(int exposure) {} + public void setExposure(double exposure) {} @Override public void setBrightness(int brightness) {} diff --git a/photon-server/src/main/java/org/photonvision/vision/camera/USBCameraSource.java b/photon-server/src/main/java/org/photonvision/vision/camera/USBCameraSource.java index 4ce79fe53..caf2d22fd 100644 --- a/photon-server/src/main/java/org/photonvision/vision/camera/USBCameraSource.java +++ b/photon-server/src/main/java/org/photonvision/vision/camera/USBCameraSource.java @@ -28,7 +28,6 @@ import org.photonvision.common.configuration.CameraConfiguration; import org.photonvision.common.configuration.ConfigManager; import org.photonvision.common.logging.LogGroup; import org.photonvision.common.logging.Logger; -import org.photonvision.common.util.math.MathUtils; import org.photonvision.vision.frame.FrameProvider; import org.photonvision.vision.frame.provider.USBFrameProvider; import org.photonvision.vision.processes.VisionSource; @@ -60,6 +59,18 @@ public class USBCameraSource implements VisionSource { usbCameraSettables = new USBCameraSettables(config); usbFrameProvider = new USBFrameProvider(cvSink, usbCameraSettables); + + if (cameraQuirks.hasQuirk(CameraQuirk.PiCam)) { + // Pick a bunch of reasonable setting defaults for vision processing. + camera.getProperty("exposure_dynamic_framerate").set(0); + camera.getProperty("auto_exposure_bias").set(0); + camera.getProperty("image_stabilization").set(0); + camera.getProperty("iso_sensitivity").set(0); + camera.getProperty("iso_sensitivity_auto").set(0); + camera.getProperty("exposure_metering_mode").set(0); + camera.getProperty("scene_mode").set(0); + camera.getProperty("power_line_frequency").set(2); + } } @Override @@ -80,17 +91,40 @@ public class USBCameraSource implements VisionSource { calculateFrameStaticProps(); } + private int timeToPiCamV2RawExposure(double time_us) { + int retVal = + (int) Math.round(time_us / 100.0); // PiCamV2 needs exposure time in units of 100us/bit + return Math.min(Math.max(retVal, 1), 10000); // Cap to allowable range for parameter + } + + private double pctToExposureTimeUs(double pct_in) { + // Mirror the photonvision raspicam driver's algorithm for picking an exposure time + // from a 0-100% input + final double PADDING_LOW_US = 100; + final double PADDING_HIGH_US = 200; + return PADDING_LOW_US + + (pct_in / 100.0) * ((1e6 / (double) camera.getVideoMode().fps) - PADDING_HIGH_US); + } + @Override - public void setExposure(int exposure) { + public void setExposure(double exposure) { try { + int scaledExposure = 1; if (cameraQuirks.hasQuirk(CameraQuirk.PiCam)) { + camera.getProperty("white_balance_auto_preset").set(2); // Auto white-balance off camera.getProperty("auto_exposure").set(1); // auto exposure off - camera - .getProperty("exposure_time_absolute") - .set(MathUtils.safeDivide(10000, exposure)); // exposure time is a range, 0-10000 + + scaledExposure = + (int) Math.round(timeToPiCamV2RawExposure(pctToExposureTimeUs(exposure))); + logger.debug("Setting camera raw exposure to " + Integer.toString(scaledExposure)); + camera.getProperty("raw_exposure_time_absolute").set(scaledExposure); + camera.getProperty("raw_exposure_time_absolute").set(scaledExposure); + } else { - camera.setExposureManual(exposure); - camera.setExposureManual(exposure); + scaledExposure = (int) Math.round(exposure); + logger.debug("Setting camera exposure to " + Integer.toString(scaledExposure)); + camera.setExposureManual(scaledExposure); + camera.setExposureManual(scaledExposure); } } catch (VideoException e) { logger.error("Failed to set camera exposure!", e); diff --git a/photon-server/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java b/photon-server/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java index e59407023..ac17f3d96 100644 --- a/photon-server/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java +++ b/photon-server/src/main/java/org/photonvision/vision/pipeline/CVPipelineSettings.java @@ -39,7 +39,7 @@ public class CVPipelineSettings implements Cloneable { public ImageFlipMode inputImageFlipMode = ImageFlipMode.NONE; public ImageRotationMode inputImageRotationMode = ImageRotationMode.DEG_0; public String pipelineNickname = "New Pipeline"; - public int cameraExposure = 50; + public double cameraExposure = 50; public int cameraBrightness = 50; public int cameraGain = 50; public int cameraVideoModeIndex = 0; diff --git a/photon-server/src/main/java/org/photonvision/vision/processes/VisionSourceSettables.java b/photon-server/src/main/java/org/photonvision/vision/processes/VisionSourceSettables.java index 7181a11b8..de722efa8 100644 --- a/photon-server/src/main/java/org/photonvision/vision/processes/VisionSourceSettables.java +++ b/photon-server/src/main/java/org/photonvision/vision/processes/VisionSourceSettables.java @@ -42,7 +42,7 @@ public abstract class VisionSourceSettables { return configuration; } - public abstract void setExposure(int exposure); + public abstract void setExposure(double exposure); public abstract void setBrightness(int brightness); diff --git a/photon-server/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java b/photon-server/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java index 7879cb0df..136dbd303 100644 --- a/photon-server/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java +++ b/photon-server/src/test/java/org/photonvision/vision/processes/VisionModuleManagerTest.java @@ -69,7 +69,7 @@ public class VisionModuleManagerTest { } @Override - public void setExposure(int exposure) {} + public void setExposure(double exposure) {} @Override public void setBrightness(int brightness) {}