Skip to content

Commit 47ca68c

Browse files
committed
Handle ExposureTimeMode and AnalogueGainMode controls transparently
libcamera now has ExposureTimeMode and AnalogueGainMode mode controls, which must be set to "manual" before you can set fixed exposure or gain values. Likewise, the modes must be "cleared" to return to auto mode (setting zero no longer does anything). We're going to handle all this transparently for users, because in all practical cases it adds nothing useful and would be an unwelcome API change. Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
1 parent 28bc57e commit 47ca68c

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

picamera2/request.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pathlib import Path
88
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
99

10+
import libcamera
1011
import numpy as np
1112
import piexif
1213
import simplejpeg
@@ -115,7 +116,25 @@ def release(self) -> None:
115116
self.request.reuse()
116117
controls = self.picam2.controls.get_libcamera_controls()
117118
for id, value in controls.items():
119+
120+
# libcamera now has "ExposureTimeMode" and "AnalogueGainMode" which must be set to
121+
# manual for the fixed exposure time or gain to have any effect, and cleared to return
122+
# to "auto " mode. We're going to hide that by supplying them automatically as needed.
123+
if id == libcamera.controls.ExposureTime:
124+
if value:
125+
self.request.set_control(libcamera.controls.ExposureTimeMode, 1) # manual
126+
else:
127+
self.request.set_control(libcamera.controls.ExposureTimeMode, 0) # auto
128+
continue # no need to set the zero value!
129+
elif id == libcamera.controls.AnalogueGain:
130+
if value:
131+
self.request.set_control(libcamera.controls.AnalogueGainMode, 1) # manual
132+
else:
133+
self.request.set_control(libcamera.controls.AnalogueGainMode, 0) # auto
134+
continue # no need to set the zero value!
135+
118136
self.request.set_control(id, value)
137+
119138
self.picam2.controls = Controls(self.picam2)
120139
self.picam2.camera.queue_request(self.request)
121140
[sync.__exit__() for sync in self.syncs]

tests/aegc.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/python3
2+
3+
import time
4+
5+
from picamera2 import Picamera2
6+
7+
8+
def test_control_fixed(control, value):
9+
picam2.set_controls({control: value})
10+
time.sleep(1.0)
11+
check = picam2.capture_metadata()[control]
12+
if abs(check - value) > value * 0.05:
13+
print(f"ERROR: requested {control} of {value} but got {check}")
14+
else:
15+
print(f"Requested {control} of {value}, got {check}")
16+
17+
18+
def test_control_auto(control):
19+
current = picam2.capture_metadata()[control]
20+
picam2.set_controls({control: 0})
21+
time.sleep(1.0)
22+
for _ in range(5):
23+
check = picam2.capture_metadata()[control]
24+
if abs(check - current) > current * 0.05:
25+
print(f"Control {control} changed from {current} to {check}")
26+
return
27+
print(f"ERROR: {control} has not returned to auto - still {check}")
28+
29+
30+
picam2 = Picamera2()
31+
picam2.start()
32+
33+
test_control_fixed("ExposureTime", 5000)
34+
test_control_fixed("ExposureTime", 10000)
35+
test_control_auto("ExposureTime")
36+
37+
test_control_fixed("AnalogueGain", 1.5)
38+
test_control_fixed("AnalogueGain", 3.0)
39+
test_control_auto("AnalogueGain")

tests/test_list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ examples/tuning_file.py
5151
examples/video_with_config.py
5252
examples/window_offset.py
5353
examples/zoom.py
54+
tests/aegc.py
5455
tests/alignment.py
5556
tests/app_dual.py
5657
tests/app_full_test.py

0 commit comments

Comments
 (0)