From 0e3f4305b5de9e5d8d97e8608657098da759b1d2 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Thu, 22 May 2025 09:13:42 +0100 Subject: [PATCH] Tidy post refactor of cameras --- pyproject.toml | 2 +- .../things/autofocus.py | 1 - .../things/camera/__init__.py | 3 +- .../things/camera/opencv.py | 1 - .../things/camera/picamera.py | 15 +++---- .../camera/picamera_recalibrate_utils.py | 43 ++++++------------- .../things/camera/simulation.py | 3 -- 7 files changed, 22 insertions(+), 46 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b00f07b..53f992e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,8 +83,8 @@ addopts = [ ] norecursedirs = [".git", "build", "node_modules"] - pythonpath = ["."] +testpaths = ["tests"] [tool.ruff.format] # Use native line endings for all files diff --git a/src/openflexure_microscope_server/things/autofocus.py b/src/openflexure_microscope_server/things/autofocus.py index 2e87aac1..4040ae7f 100644 --- a/src/openflexure_microscope_server/things/autofocus.py +++ b/src/openflexure_microscope_server/things/autofocus.py @@ -24,7 +24,6 @@ from labthings_fastapi.dependencies.blocking_portal import BlockingPortal from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.dependencies.metadata import GetThingStates from labthings_fastapi.types.numpy import NDArray -from labthings_fastapi.dependencies.thing import direct_thing_client_dependency from labthings_fastapi.dependencies.invocation import InvocationLogger from .camera import RawCameraDependency as Camera diff --git a/src/openflexure_microscope_server/things/camera/__init__.py b/src/openflexure_microscope_server/things/camera/__init__.py index 91a7efb1..7d771af6 100644 --- a/src/openflexure_microscope_server/things/camera/__init__.py +++ b/src/openflexure_microscope_server/things/camera/__init__.py @@ -7,11 +7,12 @@ See repository root for licensing information. """ from __future__ import annotations -import logging from typing import Literal, Optional, Tuple +import json from pydantic import RootModel from PIL import Image +import piexif from labthings_fastapi.thing import Thing from labthings_fastapi.decorators import thing_action, thing_property diff --git a/src/openflexure_microscope_server/things/camera/opencv.py b/src/openflexure_microscope_server/things/camera/opencv.py index 2278e2bb..f793f209 100644 --- a/src/openflexure_microscope_server/things/camera/opencv.py +++ b/src/openflexure_microscope_server/things/camera/opencv.py @@ -19,7 +19,6 @@ import piexif from labthings_fastapi.utilities import get_blocking_portal from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.dependencies.metadata import GetThingStates -from labthings_fastapi.outputs.mjpeg_stream import MJPEGStreamDescriptor from labthings_fastapi.types.numpy import NDArray from . import BaseCamera, JPEGBlob diff --git a/src/openflexure_microscope_server/things/camera/picamera.py b/src/openflexure_microscope_server/things/camera/picamera.py index 4c72c6ff..9920050d 100644 --- a/src/openflexure_microscope_server/things/camera/picamera.py +++ b/src/openflexure_microscope_server/things/camera/picamera.py @@ -1,7 +1,5 @@ from __future__ import annotations -from dataclasses import dataclass from datetime import datetime -import io import json import logging import os @@ -12,19 +10,14 @@ from tempfile import TemporaryDirectory from pydantic import BaseModel, BeforeValidator from labthings_fastapi.descriptors.property import PropertyDescriptor -from labthings_fastapi.thing import Thing from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.outputs.mjpeg_stream import MJPEGStream from labthings_fastapi.utilities import get_blocking_portal -from labthings_fastapi.types.numpy import NDArray from labthings_fastapi.dependencies.metadata import GetThingStates from labthings_fastapi.dependencies.blocking_portal import BlockingPortal -from typing import Annotated, Any, Iterator, Literal, Mapping, Optional, Self +from typing import Annotated, Any, Iterator, Literal, Mapping, Optional from contextlib import contextmanager import piexif -from scipy.ndimage import zoom -from scipy.interpolate import interp1d -from PIL import Image from threading import RLock import picamera2 from picamera2 import Picamera2 @@ -33,7 +26,7 @@ from picamera2.outputs import Output import numpy as np from . import picamera_recalibrate_utils as recalibrate_utils -from . import BaseCamera, JPEGBlob, PNGBlob, ArrayModel +from . import BaseCamera, JPEGBlob, ArrayModel class PicameraControl(PropertyDescriptor): @@ -636,7 +629,9 @@ class StreamingPiCamera2(BaseCamera): the processed images. It should not affect raw images. """ with self.picamera(pause_stream=True) as cam: - L, Cr, Cb = recalibrate_utils.lst_from_camera(cam) + # Suppress lint warning that L, Cr, and Cb are not lowercase, as this is the + # Standard format for these mathematical vars. + L, Cr, Cb = recalibrate_utils.lst_from_camera(cam) # noqa: N806 recalibrate_utils.set_static_lst(self.tuning, L, Cr, Cb) self.initialise_picamera() diff --git a/src/openflexure_microscope_server/things/camera/picamera_recalibrate_utils.py b/src/openflexure_microscope_server/things/camera/picamera_recalibrate_utils.py index ca2fa629..dee31127 100644 --- a/src/openflexure_microscope_server/things/camera/picamera_recalibrate_utils.py +++ b/src/openflexure_microscope_server/things/camera/picamera_recalibrate_utils.py @@ -30,6 +30,11 @@ picamera.lens_shading_table = lst ``` """ +# Disable N806 & 803, which checks that all variables and args are lowercase. +# This is due to the number of matrix calculations and colour channel +# calculations that are clearer using the standard R, G, B, or L, Cr, Cb terms. +# ruff: noqa: N806 N803 + from __future__ import annotations import gc import logging @@ -43,6 +48,9 @@ from picamera2 import Picamera2 import picamera2 +LensShadingTables = tuple[np.ndarray, np.ndarray, np.ndarray] + + def load_default_tuning(cam: Picamera2) -> dict: """Load the default tuning file for the camera @@ -58,12 +66,13 @@ def load_default_tuning(cam: Picamera2) -> dict: try: return cam.load_tuning_file(fname) except RuntimeError: - dir = "/usr/share/libcamera/ipa/raspberrypi" # from picamera2 v0.3.9 - # The directory above has been removed from the search path, which I + tuning_dir = "/usr/share/libcamera/ipa/raspberrypi" + # from picamera2 v0.3.9 + # The directory above has been removed from the search path seems # find odd - as that's where the files currently are on a default # Raspbian image. This may need updating if the files have moved # in future updates to the system libcamera package - return cam.load_tuning_file(fname, dir=dir) + return cam.load_tuning_file(fname, dir=tuning_dir) def set_minimum_exposure(camera: Picamera2): @@ -312,9 +321,6 @@ def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray: return channels -LensShadingTables = tuple[np.ndarray, np.ndarray, np.ndarray] - - def get_16x12_grid(chan: np.ndarray, dx: int, dy: int): """Compresses channel down to a 16x12 grid - from libcamera @@ -527,8 +533,8 @@ def raw_channels_from_camera(camera: Picamera2) -> LensShadingTables: # raw_image is a 3D array, with full resolution and 3 colour channels. No # de-mosaicing has been done, so 2/3 of the values are zero (3/4 for R and B # channels, 1/2 for green because there's twice as many green pixels). - format = camera.camera_configuration()["raw"]["format"] - print(f"Acquired a raw image in format {format}") + raw_format = camera.camera_configuration()["raw"]["format"] + print(f"Acquired a raw image in format {raw_format}") return channels_from_bayer_array(raw_image) @@ -540,24 +546,3 @@ def recreate_camera_manager(): del Picamera2._cm gc.collect() Picamera2._cm = picamera2.picamera2.CameraManager() - - -if __name__ == "__main__": - """This block is untested but has been updated.""" - with Picamera2() as cam: - tuning = load_default_tuning(cam) - f = np.ones((12, 16)) - set_static_lst(tuning, f, f, f) - set_static_geq(tuning) - with Picamera2(tuning=tuning) as cam: - cam.start_preview() - time.sleep(3) - logging.info("Recalibrating...") - adjust_shutter_and_gain_from_raw(cam) - adjust_white_balance_from_raw(cam) - lst = lst_from_camera(cam) - set_static_lst(tuning, *lst) - logging.info("Done.") - with Picamera2(tuning=tuning) as cam: - cam.start_preview() - time.sleep(2) diff --git a/src/openflexure_microscope_server/things/camera/simulation.py b/src/openflexure_microscope_server/things/camera/simulation.py index def8f443..2494c6c6 100644 --- a/src/openflexure_microscope_server/things/camera/simulation.py +++ b/src/openflexure_microscope_server/things/camera/simulation.py @@ -22,9 +22,7 @@ from scipy.ndimage import gaussian_filter from labthings_fastapi.utilities import get_blocking_portal from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.dependencies.metadata import GetThingStates -from labthings_fastapi.types.numpy import NDArray from labthings_fastapi.server import ThingServer -from pydantic import RootModel from . import BaseCamera, JPEGBlob, ArrayModel from ..stage import StageProtocol as Stage @@ -33,7 +31,6 @@ from ..stage import StageProtocol as Stage # higher related to a faster movement RATIO = 0.2 - class SimulatedCamera(BaseCamera): """A Thing representing an OpenCV camera"""