Better fix for exposure drift with more complete testing. Also fix return value for capture_to_memory
This commit is contained in:
parent
376780ea28
commit
b90b02cf7d
3 changed files with 58 additions and 8 deletions
|
|
@ -16,11 +16,19 @@ from openflexure_microscope_server.things.camera.picamera import StreamingPiCame
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
# The tolerance used to be a setting of the camera, and any changes within tolerance
|
||||||
|
# were ignored. Now this isn't needed we always set a value 1 larger than the last
|
||||||
|
# accepted value as the PiCamera always rounds down.
|
||||||
|
# The tolerance is needed in this test as when setting an arbitrary value it is rounded
|
||||||
|
# down to a hardware compatible one.
|
||||||
|
EXPOSURE_TOL = 30
|
||||||
|
|
||||||
|
|
||||||
def _test_exposure_time_drift(desired_time):
|
def _test_exposure_time_drift(desired_time):
|
||||||
"""Capture 10 full resolution images and check that the exposure time remains constant.
|
"""Capture 10 full res images and check that the exposure time remains constant.
|
||||||
|
|
||||||
This confirms that automatic exposure time adjustment is fully turned off
|
This confirms that automatic exposure time adjustment is fully turned off during
|
||||||
|
capture.
|
||||||
"""
|
"""
|
||||||
cam = StreamingPiCamera2()
|
cam = StreamingPiCamera2()
|
||||||
server = ThingServer()
|
server = ThingServer()
|
||||||
|
|
@ -28,21 +36,20 @@ def _test_exposure_time_drift(desired_time):
|
||||||
|
|
||||||
with TestClient(server.app) as test_client:
|
with TestClient(server.app) as test_client:
|
||||||
client = ThingClient.from_url("/camera/", client=test_client)
|
client = ThingClient.from_url("/camera/", client=test_client)
|
||||||
exposure_tol = cam.persistent_control_tolerances["ExposureTime"]
|
|
||||||
client.exposure_time = desired_time
|
client.exposure_time = desired_time
|
||||||
print(f"Setting desired time of {desired_time}")
|
print(f"Setting desired time of {desired_time}")
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
pre_capture_et = client.exposure_time
|
pre_capture_et = client.exposure_time
|
||||||
print(f"Pre-capture the time is set to {pre_capture_et}")
|
print(f"Pre-capture the time is set to {pre_capture_et}")
|
||||||
# Check exp is set correctly within known tolerance
|
# Check exp is set correctly within known tolerance
|
||||||
assert abs(pre_capture_et - desired_time) < exposure_tol
|
assert abs(pre_capture_et - desired_time) < EXPOSURE_TOL
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
client.capture_jpeg(resolution="full")
|
client.capture_jpeg(resolution="full")
|
||||||
if i == 0:
|
if i == 0:
|
||||||
# Exposure can update on first capture, due to frame rate restrictions
|
# Exposure can update on first capture, due to frame rate restrictions
|
||||||
first_et = client.exposure_time
|
first_et = client.exposure_time
|
||||||
assert abs(first_et - pre_capture_et) < exposure_tol
|
assert abs(first_et - desired_time) < EXPOSURE_TOL
|
||||||
else:
|
else:
|
||||||
frame_et = client.exposure_time
|
frame_et = client.exposure_time
|
||||||
print(f"Frame {i} captured with exposure time {frame_et}")
|
print(f"Frame {i} captured with exposure time {frame_et}")
|
||||||
|
|
@ -65,3 +72,45 @@ def test_exposure_time_drift():
|
||||||
"""Performs the exposure time test for a range of exposure time values."""
|
"""Performs the exposure time test for a range of exposure time values."""
|
||||||
for desired_time in [100, 1000, 10000]:
|
for desired_time in [100, 1000, 10000]:
|
||||||
_test_exposure_time_drift(desired_time)
|
_test_exposure_time_drift(desired_time)
|
||||||
|
|
||||||
|
|
||||||
|
def test_exposure_time_on_start_and_stop_stream():
|
||||||
|
"""Capture 10 full res images and check that the exposure time remains constant.
|
||||||
|
|
||||||
|
This confirms that automatic exposure time adjustment is fully turned off during
|
||||||
|
capture.
|
||||||
|
"""
|
||||||
|
cam = StreamingPiCamera2()
|
||||||
|
server = ThingServer()
|
||||||
|
server.add_thing(cam, "/camera/")
|
||||||
|
desired_time = 1000
|
||||||
|
# Create a test client
|
||||||
|
with TestClient(server.app) as test_client:
|
||||||
|
client = ThingClient.from_url("/camera/", client=test_client)
|
||||||
|
# Set a desired exposure time
|
||||||
|
client.exposure_time = desired_time
|
||||||
|
print(f"Setting desired time of {desired_time}")
|
||||||
|
time.sleep(0.5)
|
||||||
|
# Take a couple of images to make sure that the exposure is adjusted to
|
||||||
|
# a hardware compatible value.
|
||||||
|
for i in range(2):
|
||||||
|
client.capture_jpeg(resolution="full")
|
||||||
|
# Save this time.
|
||||||
|
set_time = client.exposure_time
|
||||||
|
assert abs(set_time - desired_time) < EXPOSURE_TOL
|
||||||
|
|
||||||
|
# Mimick doing 10 smart scans. Change to full res, take some images. Return
|
||||||
|
# to standard preview resolution.
|
||||||
|
for i in range(10):
|
||||||
|
print(f"Starting simulation scan {i}")
|
||||||
|
# This will need updating if we start supporting other Picamera models
|
||||||
|
# It is currently used here to mimick the behaviour in in scanning.
|
||||||
|
client.start_streaming(main_resolution=(3280, 2464))
|
||||||
|
time.sleep(0.5)
|
||||||
|
for _j in range(5):
|
||||||
|
client.capture_to_memory(buffer_max=1)
|
||||||
|
# Reset to main resolution
|
||||||
|
time.sleep(0.5)
|
||||||
|
client.start_streaming()
|
||||||
|
# Check after all of this the exposure time is the same.
|
||||||
|
assert abs(client.exposure_time - set_time) < EXPOSURE_TOL
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,7 @@ class BaseCamera(lt.Thing):
|
||||||
logger: lt.deps.InvocationLogger,
|
logger: lt.deps.InvocationLogger,
|
||||||
metadata_getter: lt.deps.GetThingStates,
|
metadata_getter: lt.deps.GetThingStates,
|
||||||
buffer_max: int = 1,
|
buffer_max: int = 1,
|
||||||
) -> None:
|
) -> int:
|
||||||
"""Capture an image to memory. This can be saved later with ``save_from_memory``.
|
"""Capture an image to memory. This can be saved later with ``save_from_memory``.
|
||||||
|
|
||||||
Note that only one image is held in memory so this will overwrite any image
|
Note that only one image is held in memory so this will overwrite any image
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
if not self._setting_save_in_progress and self.streaming:
|
if not self._setting_save_in_progress and self.streaming:
|
||||||
with self._streaming_picamera() as cam:
|
with self._streaming_picamera() as cam:
|
||||||
cam_value = cam.capture_metadata()["ExposureTime"]
|
cam_value = cam.capture_metadata()["ExposureTime"]
|
||||||
if abs(cam_value - self._exposure_time) > 30:
|
if cam_value != self._exposure_time:
|
||||||
self._exposure_time = cam_value
|
self._exposure_time = cam_value
|
||||||
self.save_settings()
|
self.save_settings()
|
||||||
return self._exposure_time
|
return self._exposure_time
|
||||||
|
|
@ -250,7 +250,8 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
"Brightness": 0,
|
"Brightness": 0,
|
||||||
"ColourGains": self.colour_gains,
|
"ColourGains": self.colour_gains,
|
||||||
"Contrast": 1,
|
"Contrast": 1,
|
||||||
"ExposureTime": self.exposure_time,
|
# Must also set plus 1 or the exposure drifts with start and stop stream.
|
||||||
|
"ExposureTime": self.exposure_time + 1,
|
||||||
"Saturation": 1,
|
"Saturation": 1,
|
||||||
"Sharpness": 1,
|
"Sharpness": 1,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue