Skip to content

Commit

Permalink
Further wip dynamic min/max
Browse files Browse the repository at this point in the history
  • Loading branch information
gerth2 committed Jun 27, 2024
1 parent b1d4d5f commit d46efd1
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,8 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
:disabled="useCameraSettingsStore().currentCameraSettings.pipelineSettings.cameraAutoExposure"
label="Exposure (μS)"
tooltip="Directly controls how long the camera shutter remains open (in microseconds)"
:min="1"
:max="80000"
:min="useCameraSettingsStore().minExposureUs"
:max="useCameraSettingsStore().maxExposureUs"
:slider-cols="8"
:step="1"
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraExposureUs: args }, false)"
Expand Down
4 changes: 2 additions & 2 deletions photon-client/src/components/dashboard/tabs/InputTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ const interactiveCols = computed(() =>
:disabled="useCameraSettingsStore().currentCameraSettings.pipelineSettings.cameraAutoExposure"
label="Exposure (μS)"
tooltip="Directly controls how long the camera shutter remains open (in microseconds)"
:min="1"
:max="80000"
:min="useCameraSettingsStore().minExposureUs"
:max="useCameraSettingsStore().maxExposureUs"
:slider-cols="interactiveCols"
:step="1"
@input="(args) => useCameraSettingsStore().changeCurrentPipelineSetting({ cameraExposureUs: args }, false)"
Expand Down
8 changes: 8 additions & 0 deletions photon-client/src/stores/settings/CameraSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
},
isCSICamera(): boolean {
return this.currentCameraSettings.isCSICamera;
},
minExposureUs(): number {
return this.currentCameraSettings.minExposureUs;
},
maxExposureUs(): number {
return this.currentCameraSettings.maxExposureUs;
}
},
actions: {
Expand Down Expand Up @@ -102,6 +108,8 @@ export const useCameraSettingsStore = defineStore("cameraSettings", {
})),
completeCalibrations: d.calibrations,
isCSICamera: d.isCSICamera,
minExposureUs: d.minExposureUs,
maxExposureUs: d.maxExposureUs,
pipelineNicknames: d.pipelineNicknames,
currentPipelineIndex: d.currentPipelineIndex,
pipelineSettings: d.currentPipelineSettings,
Expand Down
2 changes: 2 additions & 0 deletions photon-client/src/types/PipelineTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export interface PipelineSettings {
outputShowMultipleTargets: boolean;
contourSortMode: number;
cameraExposureUs: number;
cameraMinExposureUs: number;
cameraMaxExposureUs: number;
offsetSinglePoint: { x: number; y: number };
cameraBrightness: number;
offsetDualPointAArea: number;
Expand Down
7 changes: 6 additions & 1 deletion photon-client/src/types/SettingTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ export interface CameraSettings {

cameraQuirks: QuirkyCamera;
isCSICamera: boolean;

minExposureUs: number;
maxExposureUs: number;
}

export interface CameraSettingsChangeRequest {
Expand Down Expand Up @@ -287,7 +290,9 @@ export const PlaceholderCameraSettings: CameraSettings = {
StickyFPS: false
}
},
isCSICamera: false
isCSICamera: false,
minExposureUs: 1,
maxExposureUs: 100
};

export enum CalibrationBoardTypes {
Expand Down
2 changes: 2 additions & 0 deletions photon-client/src/types/WebsocketDataTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export interface WebsocketCameraSettingsUpdate {
pipelineNicknames: string[];
videoFormatList: WebsocketVideoFormat;
cameraQuirks: QuirkyCamera;
minExposureUs: number;
maxExposureUs: number;
}
export interface WebsocketNTUpdate {
connected: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,7 @@ public static class UICameraConfiguration {
public boolean isFovConfigurable = true;
public QuirkyCamera cameraQuirks;
public boolean isCSICamera;
public double minExposureUs;
public double maxExposureUs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,15 @@ protected void setVideoModeInternal(VideoMode videoMode) {
public HashMap<Integer, VideoMode> getAllVideoModes() {
return videoModes;
}

@Override
public double getMinExposureUs() {
return 1f;
}

@Override
public double getMaxExposureUs() {
return 100f;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public class LibcameraGpuSettables extends VisionSourceSettables {

private final LibCameraJNI.SensorModel sensorModel;

private double minExposure = 1;
private double maxExposure = 80000;

private ImageRotationMode m_rotationMode = ImageRotationMode.DEG_0;

public final Object CAMERA_LOCK = new Object();
Expand Down Expand Up @@ -100,6 +103,12 @@ public LibcameraGpuSettables(CameraConfiguration configuration) {
// TODO need to add more video modes for new sensors here

currentVideoMode = (FPSRatedVideoMode) videoModes.get(0);

if (sensorModel == LibCameraJNI.SensorModel.OV9281) {
minExposure = 7;
} else if (sensorModel == LibCameraJNI.SensorModel.OV5647) {
minExposure = 560;
}
}

@Override
Expand All @@ -123,15 +132,6 @@ public void setExposureUs(double exposureUs) {
// Store the exposure for use when we need to recreate the camera.
lastManualExposure = exposureUs;

// Minimum exposure can't be below 1uS cause otherwise it would be 0 and 0 is auto exposure.
double minExposure = 1;
double maxExposure = 80000;

if (sensorModel == LibCameraJNI.SensorModel.OV9281) {
minExposure = 7;
} else if (sensorModel == LibCameraJNI.SensorModel.OV5647) {
minExposure = 560;
}
// 80,000 uS seems like an exposure value that will be greater than ever needed while giving
// enough control over exposure.
exposureUs = MathUtils.limit(exposureUs, minExposure, maxExposure);
Expand Down Expand Up @@ -246,4 +246,14 @@ public HashMap<Integer, VideoMode> getAllVideoModes() {
public LibCameraJNI.SensorModel getModel() {
return sensorModel;
}

@Override
public double getMinExposureUs() {
return this.minExposure;
}

@Override
public double getMaxExposureUs() {
return this.maxExposure;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,13 @@ private void printCameraProperaties() {
private void setAllCamDefaults() {
// Common settings for all cameras to attempt to get their image
// as close as possible to what we want for image processing
// softSet("image_stabilization", 0); // No image stabilization, as this will throw off odometry
// softSet("power_line_frequency", 2); // Assume 60Hz USA
// softSet("scene_mode", 0); // no presets
// softSet("exposure_metering_mode", 0);
// softSet("exposure_dynamic_framerate", 0);
// softSet("focus_auto", 0);
// softSet("focus_absolute", 0); // Focus into infinity
softSet("image_stabilization", 0); // No image stabilization, as this will throw off odometry
softSet("power_line_frequency", 2); // Assume 60Hz USA
softSet("scene_mode", 0); // no presets
softSet("exposure_metering_mode", 0);
softSet("exposure_dynamic_framerate", 0);
softSet("focus_auto", 0);
softSet("focus_absolute", 0); // Focus into infinity
}

public QuirkyCamera getCameraQuirks() {
Expand All @@ -244,24 +244,36 @@ public class USBCameraSettables extends VisionSourceSettables {
// changing exposure
private int lastBrightness = -1;

double minExposure = 1;
double maxExposure = 80000;

protected USBCameraSettables(CameraConfiguration configuration) {
super(configuration);
getAllVideoModes();
if (!configuration.cameraQuirks.hasQuirk(CameraQuirk.StickyFPS))
if (!videoModes.isEmpty()) setVideoMode(videoModes.get(0)); // fixes double FPS set

this.minExposure = exposureAbsProp.getMin();
this.maxExposure = exposureAbsProp.getMax();

if (getCameraConfiguration().cameraQuirks.hasQuirk(CameraQuirk.ArduOV2311)) {
// Property limits are incorrect
this.minExposure = 1;
this.maxExposure = 75;
}
}

public void setAutoExposure(boolean cameraAutoExposure) {
logger.debug("Setting auto exposure to " + cameraAutoExposure);

if (!cameraAutoExposure) {
// Pick a bunch of reasonable setting defaults for vision processing
// softSet("auto_exposure_bias", 0);
// softSet("iso_sensitivity_auto", 0); // Disable auto ISO adjustment
// softSet("iso_sensitivity", 0); // Manual ISO adjustment
// softSet("white_balance_auto_preset", 2); // Auto white-balance disabled
// softSet("white_balance_automatic", 0);
// softSet("white_balance_temperature", 4000);
softSet("auto_exposure_bias", 0);
softSet("iso_sensitivity_auto", 0); // Disable auto ISO adjustment
softSet("iso_sensitivity", 0); // Manual ISO adjustment
softSet("white_balance_auto_preset", 2); // Auto white-balance disabled
softSet("white_balance_automatic", 0);
softSet("white_balance_temperature", 4000);
autoExposureProp.set(PROP_AUTO_EXPOSURE_ENABLED);

// Most cameras leave exposure time absolute at the last value from their AE algorithm.
Expand All @@ -270,32 +282,34 @@ public void setAutoExposure(boolean cameraAutoExposure) {

} else {
// Pick a bunch of reasonable setting to make the picture nice-for-humans
// softSet("auto_exposure_bias", 12);
// softSet("iso_sensitivity_auto", 1);
// softSet("iso_sensitivity", 1); // Manual ISO adjustment by default
// softSet("white_balance_auto_preset", 1); // Auto white-balance enabled
// softSet("white_balance_automatic", 1);
softSet("auto_exposure_bias", 12);
softSet("iso_sensitivity_auto", 1);
softSet("iso_sensitivity", 1); // Manual ISO adjustment by default
softSet("white_balance_auto_preset", 1); // Auto white-balance enabled
softSet("white_balance_automatic", 1);
autoExposureProp.set(PROP_AUTO_EXPOSURE_DISABLED);
camera.setExposureAuto(); // belt-and-suspenders with cscore's call too.
}
}

@Override
public double getMinExposureUs() {
return this.minExposure;
}

@Override
public double getMaxExposureUs() {
return this.maxExposure;
}

@Override
public void setExposureUs(double exposureUs) {
if (exposureUs >= 0.0) {
try {
autoExposureProp.set(PROP_AUTO_EXPOSURE_DISABLED);

var propMin = exposureAbsProp.getMin();
var propMax = exposureAbsProp.getMax();

if (getCameraConfiguration().cameraQuirks.hasQuirk(CameraQuirk.ArduOV2311)) {
// Property limits are incorrect
propMin = 1;
propMax = 75;
}
autoExposureProp.set(PROP_AUTO_EXPOSURE_DISABLED);

int propVal = (int) MathUtils.limit(exposureUs, propMin, propMax);
int propVal = (int) MathUtils.limit(exposureUs, this.minExposure, this.maxExposure);

if (getCameraConfiguration().cameraQuirks.hasQuirk(CameraQuirk.LifeCamExposure)) {
// Lifecam only allows certain settings for exposure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class CVPipelineSettings implements Cloneable {
public boolean cameraAutoExposure = false;
// manual exposure only used if cameraAutoExposure is false
public double cameraExposureUs = 20;
public double cameraMinExposureUs = 1;
public double cameraMaxExposureUs = 100;
public int cameraBrightness = 50;
// Currently only used by a few cameras (notably the zero-copy Pi Camera driver) with the Gain
// quirk
Expand All @@ -63,6 +65,8 @@ public boolean equals(Object o) {
CVPipelineSettings that = (CVPipelineSettings) o;
return pipelineIndex == that.pipelineIndex
&& Double.compare(that.cameraExposureUs, cameraExposureUs) == 0
&& Double.compare(that.cameraMinExposureUs, cameraMinExposureUs) == 0
&& Double.compare(that.cameraMaxExposureUs, cameraMaxExposureUs) == 0
&& Double.compare(that.cameraBrightness, cameraBrightness) == 0
&& Double.compare(that.cameraGain, cameraGain) == 0
&& Double.compare(that.cameraRedGain, cameraRedGain) == 0
Expand All @@ -85,6 +89,8 @@ public int hashCode() {
inputImageRotationMode,
pipelineNickname,
cameraExposureUs,
cameraMinExposureUs,
cameraMaxExposureUs,
cameraBrightness,
cameraGain,
cameraRedGain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ public PhotonConfiguration.UICameraConfiguration toUICameraConfig() {
ret.currentPipelineIndex = pipelineManager.getRequestedIndex();
ret.pipelineNicknames = pipelineManager.getPipelineNicknames();
ret.cameraQuirks = visionSource.getSettables().getConfiguration().cameraQuirks;
ret.maxExposureUs = visionSource.getSettables().getMaxExposureUs();
ret.minExposureUs = visionSource.getSettables().getMinExposureUs();

// TODO refactor into helper method
var temp = new HashMap<Integer, HashMap<String, Object>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ public CameraConfiguration getConfiguration() {

public abstract void setExposureUs(double exposureUs);

public abstract double getMinExposureUs();

public abstract double getMaxExposureUs();

public abstract void setAutoExposure(boolean cameraAutoExposure);

public abstract void setBrightness(int brightness);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ public HashMap<Integer, VideoMode> getAllVideoModes() {

@Override
public void setAutoExposure(boolean cameraAutoExposure) {}

@Override
public double getMinExposureUs() {
return 1;
}

@Override
public double getMaxExposureUs() {
return 1234;
}
}

private static class TestDataConsumer implements CVPipelineResultConsumer {
Expand Down

0 comments on commit d46efd1

Please sign in to comment.