From fdae0b9226338aadcf822a48a080fdf7728a8ab1 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 20 Aug 2025 14:21:34 +0100 Subject: [PATCH] Add get_tuning_algo method to StreamingPiCamera2 --- .../picamera2/test_calibration.py | 20 +++++++++++++++++++ .../things/camera/picamera.py | 20 ++++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/hardware-specific-tests/picamera2/test_calibration.py b/hardware-specific-tests/picamera2/test_calibration.py index 6dc8ffc4..bd1e0ef9 100644 --- a/hardware-specific-tests/picamera2/test_calibration.py +++ b/hardware-specific-tests/picamera2/test_calibration.py @@ -38,6 +38,26 @@ def client(picamera_thing) -> lt.ThingClient: yield client +def test_get_tuning_algo(picamera_thing): + """Test that get_tuning algorithm retrieves tuning algorithms.""" + # Missing algorithm returns None + assert picamera_thing.get_tuning_algo("foo") is None + # Real algorithm is a dict and contains expected keys + assert isinstance(picamera_thing.get_tuning_algo("rpi.geq"), dict) + assert "offset" in picamera_thing.get_tuning_algo("rpi.geq") + + # Set the tuning to None as it is technically optional. And check this + # is handled gracefully. + try: + picamera_thing.tuning = None + except lt.exceptions.NotConnectedToServerError: + # Labthings will complain that it is not connected to a server + pass + # Check it is set to None. + assert picamera_thing.tuning is None + assert picamera_thing.get_tuning_algo("rpi.geq") is None + + def test_calibration(picamera_thing, client): """Check that full auto calibrate completes without an exception.""" tuning = picamera_thing.tuning diff --git a/src/openflexure_microscope_server/things/camera/picamera.py b/src/openflexure_microscope_server/things/camera/picamera.py index b12b888a..734e2a88 100644 --- a/src/openflexure_microscope_server/things/camera/picamera.py +++ b/src/openflexure_microscope_server/things/camera/picamera.py @@ -308,6 +308,19 @@ class StreamingPiCamera2(BaseCamera): tuning = lt.ThingSetting(Optional[dict], None, readonly=True) """The Raspberry PiCamera Tuning File JSON.""" + def get_tuning_algo(self, algorithm_name: str) -> Optional[dict]: + """Return the active tuning algorithm settings for the given algorithm. + + :returns: The algorithm dictionary if found, returns None if no tuning data + is loaded or if the tuning algorithm is not found. + """ + if self.tuning is None: + return None + try: + return Picamera2.find_tuning_algo(self.tuning, algorithm_name) + except StopIteration: + return None + def _initialise_picamera(self): """Acquire the picamera device and store it as ``self._picamera``. @@ -754,6 +767,7 @@ class StreamingPiCamera2(BaseCamera): * ``auto_expose_from_minimum`` * ``set_static_green_equalisation`` to set geq offset to max * ``calibrate_lens_shading`` + * ``reset_ccm`` * ``calibrate_white_balance`` * ``set_background`` """ @@ -761,8 +775,8 @@ class StreamingPiCamera2(BaseCamera): self.auto_expose_from_minimum() self.set_static_green_equalisation() self.calibrate_lens_shading() - self.calibrate_white_balance() self.reset_ccm() + self.calibrate_white_balance() self.set_background(portal) @lt.thing_action @@ -881,7 +895,7 @@ class StreamingPiCamera2(BaseCamera): return None # Note "alsc" is the Picamera2 term for "Automatic Lens Shading Correction" - alsc = Picamera2.find_tuning_algo(self.tuning, "rpi.alsc") + alsc = self.get_tuning_algo("rpi.alsc") # Check there is exactly 1 correction table for red-difference chroma (Cr) # and blue-difference chroma (Cb) @@ -952,7 +966,7 @@ class StreamingPiCamera2(BaseCamera): colour across the image. """ with self._streaming_picamera(pause_stream=True): - alsc = Picamera2.find_tuning_algo(self.tuning, "rpi.alsc") + alsc = self.get_tuning_algo("rpi.alsc") luminance = alsc["luminance_lut"] flat = np.ones((12, 16)) recalibrate_utils.set_static_lst(self.tuning, luminance, flat, flat)