[Wip] Add auto exposure switch (#488)

* Add auto exposure switch

* Run wpiformat

* Update ZeroCopyPicamSource.java
This commit is contained in:
Matt
2022-10-09 22:41:40 -04:00
committed by GitHub
parent 4d5904dd6d
commit 87e7c3ca74
10 changed files with 111 additions and 98 deletions

View File

@@ -57,6 +57,7 @@ export default new Vuex.Store({
// Settings that apply to all pipeline types
cameraExposure: 1,
cameraBrightness: 2,
cameraAutoExposure: false,
cameraRedGain: 3,
cameraBlueGain: 4,
inputImageRotationMode: 0,

View File

@@ -1,7 +1,7 @@
<template>
<div>
<CVslider
v-if="cameraExposure !== -1"
:disabled="cameraAutoExposure"
v-model="cameraExposure"
name="Exposure"
min="0"
@@ -22,10 +22,27 @@
@input="handlePipelineData('cameraBrightness')"
@rollback="e => rollback('cameraBrightness', e)"
/>
<CVswitch
v-model="cameraAutoExposure"
class="pt-2"
name="Auto exposure"
@input="handlePipelineData('cameraAutoExposure')"
/>
<CVslider
v-if="cameraGain >= 0"
v-model="cameraGain"
name="Camera gain"
min="0"
max="100"
tooltip="Controls camera gain, similar to brightness"
:slider-cols="largeBox"
@input="handlePipelineData('cameraRedGain')"
@rollback="e => rollback('cameraRedGain', e)"
/>
<CVslider
v-if="cameraRedGain !== -1"
v-model="cameraRedGain"
name="Red AWB Gain"
name="Red Balance"
min="0"
max="100"
tooltip="Controls red automatic white balance gain, which affects how the camera captures colors in different conditions"
@@ -36,7 +53,7 @@
<CVslider
v-if="cameraBlueGain !== -1"
v-model="cameraBlueGain"
name="Blue AWB Gain"
name="Blue Balance"
min="0"
max="100"
tooltip="Controls blue automatic white balance gain, which affects how the camera captures colors in different conditions"
@@ -76,6 +93,7 @@
<script>
import CVslider from '../../components/common/cv-slider'
import CVselect from '../../components/common/cv-select'
import CVswitch from '../../components/common/cv-switch'
const unfilteredStreamDivisors = [1, 2, 4, 6];
@@ -84,6 +102,7 @@
components: {
CVslider,
CVselect,
CVswitch,
},
// eslint-disable-next-line vue/require-prop-types
props: ['value'],
@@ -109,6 +128,14 @@
this.$store.commit("mutatePipeline", {"cameraExposure": parseFloat(val)});
}
},
cameraAutoExposure: {
get() {
return this.$store.getters.currentPipelineSettings.cameraAutoExposure;
},
set(val) {
this.$store.commit("mutatePipeline", {"cameraAutoExposure": val});
}
},
cameraBrightness: {
get() {
return parseInt(this.$store.getters.currentPipelineSettings.cameraBrightness)

View File

@@ -91,15 +91,14 @@ public class FileVisionSource extends VisionSource {
@Override
public void setExposure(double exposure) {}
public void setAutoExposure(boolean cameraAutoExposure) {}
@Override
public void setBrightness(int brightness) {}
@Override
public void setGain(int gain) {}
@Override
public void setLowExposureOptimization(boolean mode) {}
@Override
public VideoMode getCurrentVideoMode() {
return videoMode;

View File

@@ -61,8 +61,9 @@ public class USBCameraSource extends VisionSource {
usbFrameProvider = null;
} else {
// Normal init
setLowExposureOptimizationImpl(false);
// auto exposure/brightness/gain will be set by the visionmodule later
disableAutoFocus();
usbCameraSettables = new USBCameraSettables(config);
usbFrameProvider = new USBFrameProvider(cvSink, usbCameraSettables);
}
@@ -79,58 +80,6 @@ public class USBCameraSource extends VisionSource {
}
}
void setLowExposureOptimizationImpl(boolean lowExposureMode) {
if (cameraQuirks.hasQuirk(CameraQuirk.PiCam)) {
// Case, we know this is a picam. Go through v4l2-ctl interface directly
// Common settings
camera
.getProperty("image_stabilization")
.set(0); // No image stabilization, as this will throw off odometry
camera.getProperty("power_line_frequency").set(2); // Assume 60Hz USA
camera.getProperty("scene_mode").set(0); // no presets
camera.getProperty("exposure_metering_mode").set(0);
camera.getProperty("exposure_dynamic_framerate").set(0);
if (lowExposureMode) {
// Pick a bunch of reasonable setting defaults for vision processing retroreflective
camera.getProperty("auto_exposure_bias").set(0);
camera.getProperty("iso_sensitivity_auto").set(0); // Disable auto ISO adjustement
camera.getProperty("iso_sensitivity").set(0); // Manual ISO adjustement
camera.getProperty("white_balance_auto_preset").set(2); // Auto white-balance disabled
camera.getProperty("auto_exposure").set(1); // auto exposure disabled
} else {
// Pick a bunch of reasonable setting defaults for driver, fiducials, or otherwise
// nice-for-humans
camera.getProperty("auto_exposure_bias").set(12);
camera.getProperty("iso_sensitivity_auto").set(1);
camera.getProperty("iso_sensitivity").set(1); // Manual ISO adjustement by default
camera.getProperty("white_balance_auto_preset").set(1); // Auto white-balance enabled
camera.getProperty("auto_exposure").set(0); // auto exposure enabled
}
} else {
// Case - this is some other USB cam. Default to wpilib's implementation
var canSetWhiteBalance = !cameraQuirks.hasQuirk(CameraQuirk.Gain);
if (lowExposureMode) {
// Pick a bunch of reasonable setting defaults for vision processing retroreflective
if (canSetWhiteBalance) {
camera.setWhiteBalanceManual(4000); // Auto white-balance disabled, 4000K preset
}
this.getSettables().setExposure(50); // auto exposure disabled, put a sane default
} else {
// Pick a bunch of reasonable setting defaults for driver, fiducials, or otherwise
// nice-for-humans
if (canSetWhiteBalance) {
camera.setWhiteBalanceAuto(); // Auto white-balance enabled
}
camera.setExposureAuto(); // auto exposure enabled
}
}
}
@Override
public FrameProvider getFrameProvider() {
return usbFrameProvider;
@@ -148,6 +97,59 @@ public class USBCameraSource extends VisionSource {
setVideoMode(videoModes.get(0));
}
public void setAutoExposure(boolean cameraAutoExposure) {
logger.debug("Setting auto exposure to " + cameraAutoExposure);
if (cameraQuirks.hasQuirk(CameraQuirk.PiCam)) {
// Case, we know this is a picam. Go through v4l2-ctl interface directly
// Common settings
camera
.getProperty("image_stabilization")
.set(0); // No image stabilization, as this will throw off odometry
camera.getProperty("power_line_frequency").set(2); // Assume 60Hz USA
camera.getProperty("scene_mode").set(0); // no presets
camera.getProperty("exposure_metering_mode").set(0);
camera.getProperty("exposure_dynamic_framerate").set(0);
if (!cameraAutoExposure) {
// Pick a bunch of reasonable setting defaults for vision processing retroreflective
camera.getProperty("auto_exposure_bias").set(0);
camera.getProperty("iso_sensitivity_auto").set(0); // Disable auto ISO adjustement
camera.getProperty("iso_sensitivity").set(0); // Manual ISO adjustement
camera.getProperty("white_balance_auto_preset").set(2); // Auto white-balance disabled
camera.getProperty("auto_exposure").set(1); // auto exposure disabled
} else {
// Pick a bunch of reasonable setting defaults for driver, fiducials, or otherwise
// nice-for-humans
camera.getProperty("auto_exposure_bias").set(12);
camera.getProperty("iso_sensitivity_auto").set(1);
camera.getProperty("iso_sensitivity").set(1); // Manual ISO adjustement by default
camera.getProperty("white_balance_auto_preset").set(1); // Auto white-balance enabled
camera.getProperty("auto_exposure").set(0); // auto exposure enabled
}
} else {
// Case - this is some other USB cam. Default to wpilib's implementation
var canSetWhiteBalance = !cameraQuirks.hasQuirk(CameraQuirk.Gain);
if (!cameraAutoExposure) {
// Pick a bunch of reasonable setting defaults for vision processing retroreflective
if (canSetWhiteBalance) {
camera.setWhiteBalanceManual(4000); // Auto white-balance disabled, 4000K preset
}
} else {
// Pick a bunch of reasonable setting defaults for driver, fiducials, or otherwise
// nice-for-humans
if (canSetWhiteBalance) {
camera.setWhiteBalanceAuto(); // Auto white-balance enabled
}
camera.setExposureAuto(); // auto exposure enabled
}
}
}
private int timeToPiCamRawExposure(double time_us) {
int retVal =
(int)
@@ -166,11 +168,6 @@ public class USBCameraSource extends VisionSource {
+ (pct_in / 100.0) * ((1e6 / (double) camera.getVideoMode().fps) - PADDING_HIGH_US);
}
@Override
public void setLowExposureOptimization(boolean mode) {
setLowExposureOptimizationImpl(mode);
}
@Override
public void setExposure(double exposure) {
if (exposure >= 0.0) {

View File

@@ -45,13 +45,6 @@ public class ZeroCopyPicamSource extends VisionSource {
settables = new PicamSettables(configuration);
frameProvider = new AcceleratedPicamFrameProvider(settables);
setLowExposureOptimizationImpl(false);
}
static void setLowExposureOptimizationImpl(boolean mode) {
// TODO - ZeroCopy does not... yet? ... have the configuration params necessary to make this
// work well.
}
@Override
@@ -94,6 +87,7 @@ public class ZeroCopyPicamSource extends VisionSource {
private FPSRatedVideoMode currentVideoMode;
private double lastExposure = 50;
private int lastBrightness = 50;
private boolean lastExposureMode;
private int lastGain = 50;
private Pair<Integer, Integer> lastAwbGains = new Pair(18, 18);
@@ -150,9 +144,15 @@ public class ZeroCopyPicamSource extends VisionSource {
return getCurrentVideoMode().fovMultiplier * getConfiguration().FOV;
}
@Override
public void setAutoExposure(boolean cameraAutoExposure) {
lastExposureMode = cameraAutoExposure;
// TODO (Matt) -- call PicamJNI's auto exposure function, when that exists
}
@Override
public void setExposure(double exposure) {
// Todo - for now, handle auto exposure by using 100% exposure
// Todo (Chris) - for now, handle auto exposure by using 100% exposure
if (exposure < 0.0) {
exposure = 100.0;
}
@@ -162,11 +162,6 @@ public class ZeroCopyPicamSource extends VisionSource {
if (failure) logger.warn("Couldn't set Pi Camera exposure");
}
@Override
public void setLowExposureOptimization(boolean mode) {
setLowExposureOptimizationImpl(mode);
}
@Override
public void setBrightness(int brightness) {
lastBrightness = brightness;
@@ -218,6 +213,7 @@ public class ZeroCopyPicamSource extends VisionSource {
// We don't store last settings on the native side, and when you change video mode these get
// reset on MMAL's end
setExposure(lastExposure);
setAutoExposure(lastExposureMode);
setBrightness(lastBrightness);
setGain(lastGain);
setAwbGain(lastAwbGains.getFirst(), lastAwbGains.getSecond());

View File

@@ -40,6 +40,7 @@ public class AprilTagPipelineSettings extends AdvancedPipelineSettings {
outputShowMultipleTargets = true;
targetModel = TargetModel.k200mmAprilTag;
cameraExposure = -1;
cameraAutoExposure = true;
ledMode = false;
}

View File

@@ -40,7 +40,8 @@ public class CVPipelineSettings implements Cloneable {
public ImageFlipMode inputImageFlipMode = ImageFlipMode.NONE;
public ImageRotationMode inputImageRotationMode = ImageRotationMode.DEG_0;
public String pipelineNickname = "New Pipeline";
// Only used if the pipeline type does not enable auto-exposure
public boolean cameraAutoExposure = false;
// manual exposure only used if cameraAutoExposure if false
public double cameraExposure = 100;
public int cameraBrightness = 50;
// Currently only used by a few cameras (notably the zero-copy Pi Camera driver) with the Gain

View File

@@ -44,7 +44,6 @@ import org.photonvision.vision.frame.consumer.FileSaveFrameConsumer;
import org.photonvision.vision.frame.consumer.MJPGFrameConsumer;
import org.photonvision.vision.pipeline.AdvancedPipelineSettings;
import org.photonvision.vision.pipeline.OutputStreamPipeline;
import org.photonvision.vision.pipeline.PipelineType;
import org.photonvision.vision.pipeline.ReflectivePipelineSettings;
import org.photonvision.vision.pipeline.UICalibrationData;
import org.photonvision.vision.pipeline.result.CVPipelineResult;
@@ -361,7 +360,7 @@ public class VisionModule {
settings.cameraBlueGain = -1;
}
settings.cameraExposure = -1;
settings.cameraAutoExposure = true;
setPipeline(PipelineManager.CAL_3D_INDEX);
}
@@ -400,22 +399,14 @@ public class VisionModule {
visionSource.getSettables().setBrightness(pipelineSettings.cameraBrightness);
visionSource.getSettables().setGain(pipelineSettings.cameraGain);
// set to true to change camera gain/exposure settings for low exposure
// false will keep the camera running in a more "nice-for-humans" mode
// Each camera type is allowed to decide what settings that exactly means
boolean lowExposureOptimization =
(pipelineSettings.pipelineType == PipelineType.ColoredShape
|| pipelineSettings.pipelineType == PipelineType.Reflective);
visionSource.getSettables().setLowExposureOptimization(lowExposureOptimization);
if (lowExposureOptimization) {
if (pipelineSettings.cameraExposure == -1)
// If manual exposure, force exposure slider to be valid
if (!pipelineSettings.cameraAutoExposure) {
if (pipelineSettings.cameraExposure < 0)
pipelineSettings.cameraExposure = 10; // reasonable default
} else {
// in human-friendly mode, exposure is automatic
pipelineSettings.cameraExposure = -1;
}
visionSource.getSettables().setExposure(pipelineSettings.cameraExposure);
visionSource.getSettables().setAutoExposure(pipelineSettings.cameraAutoExposure);
if (cameraQuirks.hasQuirk(CameraQuirk.Gain)) {
// If the gain is disabled for some reason, re-enable it

View File

@@ -44,6 +44,8 @@ public abstract class VisionSourceSettables {
public abstract void setExposure(double exposure);
public abstract void setAutoExposure(boolean cameraAutoExposure);
public abstract void setBrightness(int brightness);
public abstract void setGain(int gain);
@@ -64,8 +66,6 @@ public abstract class VisionSourceSettables {
calculateFrameStaticProps();
}
public abstract void setLowExposureOptimization(boolean mode);
protected abstract void setVideoModeInternal(VideoMode videoMode);
@SuppressWarnings("unused")

View File

@@ -78,9 +78,6 @@ public class VisionModuleManagerTest {
@Override
public void setGain(int gain) {}
@Override
public void setLowExposureOptimization(boolean mode) {}
@Override
public VideoMode getCurrentVideoMode() {
return new VideoMode(0, 320, 240, 254);
@@ -97,6 +94,9 @@ public class VisionModuleManagerTest {
ret.put(0, getCurrentVideoMode());
return ret;
}
@Override
public void setAutoExposure(boolean cameraAutoExposure) {}
}
private static class TestDataConsumer implements CVPipelineResultConsumer {