USB camera exposure too high with setExposureManual()

We’ve been using setExposureManual(10) to set our camera exposure low for detecting vision targets on a Raspberry Pi. However, recently we started seeing a problem where the camera images are overexposed. The exposure seems wrong even when configuring via the web UI.

image

Exposure auto:
55%20PM

Exposure 1:
18%20PM

Exposure 0:
10%20PM

Any value higher than 1 looks the same as 1. (My initial guess would be that it’s expecting a value between 0–1 rather than 0–100, but the API accepts an integer and when I make a manual web request to change the configuration setting to 0.2, it complains “invalid integer”.)

Not sure it’s correlated, but we did recently install the 2019.3.1 Raspberry Pi software image (and copied the new .jar files into our existing code). However we think we might’ve seen the problem even before installing the update. Is there something in the update which could have broken manual exposure settings, or is there some other camera configuration that affects how the exposure value (0-100) works?

Below is the contents of http://frcvision.local:1181/settings.json when the exposure is set to Manual Mode with a value of 1:

{
"controls": [
{
"name": "connect_verbose",
"id": "1",
"type": "2",
"min": "0",
"max": "1",
"step": "1",
"default": "1",
"value": "1"},
{
"name": "raw_brightness",
"id": "2",
"type": "2",
"min": "30",
"max": "255",
"step": "1",
"default": "-8193",
"value": "138"},
{
"name": "brightness",
"id": "3",
"type": "2",
"min": "0",
"max": "100",
"step": "1",
"default": "-3654",
"value": "48"},
{
"name": "raw_contrast",
"id": "4",
"type": "2",
"min": "0",
"max": "10",
"step": "1",
"default": "57343",
"value": "5"},
{
"name": "contrast",
"id": "5",
"type": "2",
"min": "0",
"max": "100",
"step": "1",
"default": "573430",
"value": "50"},
{
"name": "raw_saturation",
"id": "6",
"type": "2",
"min": "0",
"max": "200",
"step": "1",
"default": "57343",
"value": "83"},
{
"name": "saturation",
"id": "7",
"type": "2",
"min": "0",
"max": "100",
"step": "1",
"default": "28671",
"value": "41"},
{
"name": "white_balance_temperature_auto",
"id": "8",
"type": "1",
"min": "0",
"max": "1",
"step": "1",
"default": "1",
"value": "1"},
{
"name": "power_line_frequency",
"id": "9",
"type": "8",
"min": "0",
"max": "2",
"step": "1",
"default": "2",
"value": "2",
"menu": {"0": "Disabled", "1": "50 Hz", "2": "60 Hz"}
},
{
"name": "white_balance_temperature",
"id": "10",
"type": "2",
"min": "2800",
"max": "10000",
"step": "1",
"default": "57343",
"value": "4500"},
{
"name": "raw_sharpness",
"id": "11",
"type": "2",
"min": "0",
"max": "50",
"step": "1",
"default": "57343",
"value": "25"},
{
"name": "sharpness",
"id": "12",
"type": "2",
"min": "0",
"max": "100",
"step": "1",
"default": "114686",
"value": "50"},
{
"name": "backlight_compensation",
"id": "13",
"type": "2",
"min": "0",
"max": "10",
"step": "1",
"default": "57343",
"value": "0"},
{
"name": "exposure_auto",
"id": "14",
"type": "8",
"min": "0",
"max": "3",
"step": "1",
"default": "0",
"value": "1",
"menu": {"0": "", "1": "Manual Mode", "2": "", "3": "Aperture Priority Mode"}
},
{
"name": "raw_exposure_absolute",
"id": "15",
"type": "2",
"min": "5",
"max": "20000",
"step": "1",
"default": "156",
"value": "204"},
{
"name": "exposure_absolute",
"id": "16",
"type": "2",
"min": "0",
"max": "100",
"step": "1",
"default": "0",
"value": "1"},
{
"name": "pan_absolute",
"id": "17",
"type": "2",
"min": "-201600",
"max": "201600",
"step": "3600",
"default": "0",
"value": "0"},
{
"name": "tilt_absolute",
"id": "18",
"type": "2",
"min": "-201600",
"max": "201600",
"step": "3600",
"default": "0",
"value": "0"},
{
"name": "zoom_absolute",
"id": "19",
"type": "2",
"min": "0",
"max": "10",
"step": "1",
"default": "57343",
"value": "0"}
],
"modes": [
{
"pixelFormat": "YUYV",
"width": "640",
"height": "480",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "480",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "480",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "480",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "480",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "1280",
"height": "720",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "1280",
"height": "720",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "960",
"height": "544",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "960",
"height": "544",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "960",
"height": "544",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "448",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "448",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "448",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "448",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "360",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "360",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "360",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "360",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "640",
"height": "360",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "424",
"height": "240",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "424",
"height": "240",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "424",
"height": "240",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "424",
"height": "240",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "424",
"height": "240",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "352",
"height": "288",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "352",
"height": "288",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "352",
"height": "288",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "352",
"height": "288",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "352",
"height": "288",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "320",
"height": "240",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "320",
"height": "240",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "320",
"height": "240",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "320",
"height": "240",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "320",
"height": "240",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "600",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "600",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "800",
"height": "600",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "176",
"height": "144",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "176",
"height": "144",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "176",
"height": "144",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "176",
"height": "144",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "176",
"height": "144",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "160",
"height": "120",
"fps": "30"},
{
"pixelFormat": "YUYV",
"width": "160",
"height": "120",
"fps": "20"},
{
"pixelFormat": "YUYV",
"width": "160",
"height": "120",
"fps": "15"},
{
"pixelFormat": "YUYV",
"width": "160",
"height": "120",
"fps": "10"},
{
"pixelFormat": "YUYV",
"width": "160",
"height": "120",
"fps": "7"},
{
"pixelFormat": "YUYV",
"width": "1280",
"height": "800",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "480",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "480",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "480",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "480",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "480",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "1280",
"height": "720",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "1280",
"height": "720",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "1280",
"height": "720",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "1280",
"height": "720",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "1280",
"height": "720",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "960",
"height": "544",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "960",
"height": "544",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "960",
"height": "544",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "960",
"height": "544",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "960",
"height": "544",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "448",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "448",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "448",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "448",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "448",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "360",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "360",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "360",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "360",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "640",
"height": "360",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "600",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "600",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "600",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "600",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "800",
"height": "600",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "416",
"height": "240",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "416",
"height": "240",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "416",
"height": "240",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "416",
"height": "240",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "416",
"height": "240",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "352",
"height": "288",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "352",
"height": "288",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "352",
"height": "288",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "352",
"height": "288",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "352",
"height": "288",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "176",
"height": "144",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "176",
"height": "144",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "176",
"height": "144",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "176",
"height": "144",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "176",
"height": "144",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "320",
"height": "240",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "320",
"height": "240",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "320",
"height": "240",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "320",
"height": "240",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "320",
"height": "240",
"fps": "7"},
{
"pixelFormat": "MJPEG",
"width": "160",
"height": "120",
"fps": "30"},
{
"pixelFormat": "MJPEG",
"width": "160",
"height": "120",
"fps": "20"},
{
"pixelFormat": "MJPEG",
"width": "160",
"height": "120",
"fps": "15"},
{
"pixelFormat": "MJPEG",
"width": "160",
"height": "120",
"fps": "10"},
{
"pixelFormat": "MJPEG",
"width": "160",
"height": "120",
"fps": "7"}
]
}

setExposureManual should take a number from 0 to 100, way more range than the 0…1. Do you also call setBrightness?
With our camera, the automatic exposure turned out to be too slow. When the camera was hit by a bright light, it would take a long time until it recovers. So we set manual exposure of 50 and a brightness of 25 to get better behavior, and we had to set both exposure and brightness to get there.

We aren’t setting the brightness manually. I just tried lowering it using the web configuration page and it didn’t help; the image is still washed out. We had been using the manual exposure setting of 10 and it was working just fine (making the image darker) until recently.

Which camera? My team is using the ELP 180degree camera (I think), and they’ve had a hard time trying to override the camera’s settings in code.

We just started manually adjusting it on the driver station laptop.

It’s the Microsoft LifeCam HD-3000. We’ve been able to change the settings in code for several weeks and it just recently stopped working.

We don’t use the lifecam any longer, but when we did we controlled exposure using a pair of cheap sunglasses which we cut to cover the lens. It was MUCH more reliable than using setExposure().

I can’t speak to whether something changed recently in the FRC Vision image, but it sounds like you’re coming up against a limitation of the LifeCam HD-3000 – it only accepts certain values for manual exposure, and using any other value results in an overexposed image.

From our startup script:

# Possible exposure values for the Microsoft LifeCam HD-3000/5000:
# 5, 9-10 (same exposure?), 19-20 (same exposure?) 39, 78, 156, 312, 625, 1250,
# 2500, 5000, 10000
# (per http://comments.gmane.org/gmane.linux.drivers.uvc.devel/5717)

Based on the values, it looks like these correspond with “raw_exposure_absolute” rather than “exposure_absolute”. Can you verify that your call to setExposureAbsolute(10) is setting raw_exposure_absolute to 10? Does the web GUI give you a way to manipulate raw_exposure_absolute, or can you manipulate exposure_absolute to force one of the acceptable values to appear in raw_exposure_absolute?

1 Like

The behavior and handling of exposure settings for HD-3000 cameras in CameraServer has not changed since its inception ~2 years ago. The CameraServer code automatically maps between the setExposureAbsolute() / web “exposure_absolute” 0-100 “percentage” range and the valid raw settings for HD-3000 cameras (see here). The raw_ values are not exposed through the web interface.

It is theoretically possible the camera in question is malfunctioning in such a way that it isn’t reporting itself as a HD-3000 camera, which would disable this “quirk” handling. You could try setting the raw_exposure_absolute value directly using VideoSource.getProperty("raw_exposure_absolute").set(/*raw value*/); instead of VideoSource.setExposureAbsolute(/*percent value*/);

2 Likes

Thanks for your responses; will try this out soon. We also recently changed from using /dev/video0 to an id-based device in order to add a second camera; wonder if that might be related to whether the quirk handling is enabled.

No, using the alternate paths won’t affect that; at the Linux level the alternate path is literally a symbolic link to /dev/videoN.

Thanks so much! The tip about raw_exposure_absolute seems to have done the trick.

When I set exposure_absolute to 10 using the web UI, the resulting raw_exposure_absolute is 2004 — and generally, when setting exposure_absolute, it doesn’t seem to result in a raw_exposure_absolute value that actually comes from this array.

When I explicitly set raw_exposure_absolute to one of the supported values, it works as expected. Why the automatic translation would’ve stopped working is anyone’s guess…

There’s a very clear condition for automatic translation to work: the camera description must end in “LifeCam HD-3000”. What does VideoSource.getDescription() return for the camera?

getDescription() returns "Microsoft? LifeCam HD-3000: Mi" (guessing the ? is actually a ®, but that’s how it shows up in the Vision Status / Console Output screen) — so that explains that.

I spoke a bit too soon; while setting raw_exposure_absolute worked via HTTP request, camera.getProperty("raw_exposure_absolute").set(10) failed with a “property write failed” error…

Try calling setExposureAbsolute() first. It also sets the exposure to manual mode, which just setting the raw property doesn’t do by itself.

That worked. Thanks!

Where is this happening? The closest I find to where the property change is actually handled is here: https://github.com/wpilibsuite/allwpilib/blob/a9371a75867d73882394a663f21c63db26dc86a1/cscore/src/main/native/windows/UsbCameraImpl.cpp#L130-L132

And that doesn’t do anything other than setting the single exposure value.

It’s only necessary on Linux and happens Here.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.