Tidy post refactor of cameras

This commit is contained in:
Julian Stirling 2025-05-22 09:13:42 +01:00
parent 4368dc3d90
commit 0e3f4305b5
7 changed files with 22 additions and 46 deletions

View file

@ -83,8 +83,8 @@ addopts = [
] ]
norecursedirs = [".git", "build", "node_modules"] norecursedirs = [".git", "build", "node_modules"]
pythonpath = ["."] pythonpath = ["."]
testpaths = ["tests"]
[tool.ruff.format] [tool.ruff.format]
# Use native line endings for all files # Use native line endings for all files

View file

@ -24,7 +24,6 @@ from labthings_fastapi.dependencies.blocking_portal import BlockingPortal
from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.decorators import thing_action, thing_property
from labthings_fastapi.dependencies.metadata import GetThingStates from labthings_fastapi.dependencies.metadata import GetThingStates
from labthings_fastapi.types.numpy import NDArray 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 labthings_fastapi.dependencies.invocation import InvocationLogger
from .camera import RawCameraDependency as Camera from .camera import RawCameraDependency as Camera

View file

@ -7,11 +7,12 @@ See repository root for licensing information.
""" """
from __future__ import annotations from __future__ import annotations
import logging
from typing import Literal, Optional, Tuple from typing import Literal, Optional, Tuple
import json
from pydantic import RootModel from pydantic import RootModel
from PIL import Image from PIL import Image
import piexif
from labthings_fastapi.thing import Thing from labthings_fastapi.thing import Thing
from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.decorators import thing_action, thing_property

View file

@ -19,7 +19,6 @@ import piexif
from labthings_fastapi.utilities import get_blocking_portal from labthings_fastapi.utilities import get_blocking_portal
from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.decorators import thing_action, thing_property
from labthings_fastapi.dependencies.metadata import GetThingStates from labthings_fastapi.dependencies.metadata import GetThingStates
from labthings_fastapi.outputs.mjpeg_stream import MJPEGStreamDescriptor
from labthings_fastapi.types.numpy import NDArray from labthings_fastapi.types.numpy import NDArray
from . import BaseCamera, JPEGBlob from . import BaseCamera, JPEGBlob

View file

@ -1,7 +1,5 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
import io
import json import json
import logging import logging
import os import os
@ -12,19 +10,14 @@ from tempfile import TemporaryDirectory
from pydantic import BaseModel, BeforeValidator from pydantic import BaseModel, BeforeValidator
from labthings_fastapi.descriptors.property import PropertyDescriptor 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.decorators import thing_action, thing_property
from labthings_fastapi.outputs.mjpeg_stream import MJPEGStream from labthings_fastapi.outputs.mjpeg_stream import MJPEGStream
from labthings_fastapi.utilities import get_blocking_portal 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.metadata import GetThingStates
from labthings_fastapi.dependencies.blocking_portal import BlockingPortal 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 from contextlib import contextmanager
import piexif import piexif
from scipy.ndimage import zoom
from scipy.interpolate import interp1d
from PIL import Image
from threading import RLock from threading import RLock
import picamera2 import picamera2
from picamera2 import Picamera2 from picamera2 import Picamera2
@ -33,7 +26,7 @@ from picamera2.outputs import Output
import numpy as np import numpy as np
from . import picamera_recalibrate_utils as recalibrate_utils from . import picamera_recalibrate_utils as recalibrate_utils
from . import BaseCamera, JPEGBlob, PNGBlob, ArrayModel from . import BaseCamera, JPEGBlob, ArrayModel
class PicameraControl(PropertyDescriptor): class PicameraControl(PropertyDescriptor):
@ -636,7 +629,9 @@ class StreamingPiCamera2(BaseCamera):
the processed images. It should not affect raw images. the processed images. It should not affect raw images.
""" """
with self.picamera(pause_stream=True) as cam: 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) recalibrate_utils.set_static_lst(self.tuning, L, Cr, Cb)
self.initialise_picamera() self.initialise_picamera()

View file

@ -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 from __future__ import annotations
import gc import gc
import logging import logging
@ -43,6 +48,9 @@ from picamera2 import Picamera2
import picamera2 import picamera2
LensShadingTables = tuple[np.ndarray, np.ndarray, np.ndarray]
def load_default_tuning(cam: Picamera2) -> dict: def load_default_tuning(cam: Picamera2) -> dict:
"""Load the default tuning file for the camera """Load the default tuning file for the camera
@ -58,12 +66,13 @@ def load_default_tuning(cam: Picamera2) -> dict:
try: try:
return cam.load_tuning_file(fname) return cam.load_tuning_file(fname)
except RuntimeError: except RuntimeError:
dir = "/usr/share/libcamera/ipa/raspberrypi" # from picamera2 v0.3.9 tuning_dir = "/usr/share/libcamera/ipa/raspberrypi"
# The directory above has been removed from the search path, which I # 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 # find odd - as that's where the files currently are on a default
# Raspbian image. This may need updating if the files have moved # Raspbian image. This may need updating if the files have moved
# in future updates to the system libcamera package # 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): def set_minimum_exposure(camera: Picamera2):
@ -312,9 +321,6 @@ def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray:
return channels return channels
LensShadingTables = tuple[np.ndarray, np.ndarray, np.ndarray]
def get_16x12_grid(chan: np.ndarray, dx: int, dy: int): def get_16x12_grid(chan: np.ndarray, dx: int, dy: int):
"""Compresses channel down to a 16x12 grid - from libcamera """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 # 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 # 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). # channels, 1/2 for green because there's twice as many green pixels).
format = camera.camera_configuration()["raw"]["format"] raw_format = camera.camera_configuration()["raw"]["format"]
print(f"Acquired a raw image in format {format}") print(f"Acquired a raw image in format {raw_format}")
return channels_from_bayer_array(raw_image) return channels_from_bayer_array(raw_image)
@ -540,24 +546,3 @@ def recreate_camera_manager():
del Picamera2._cm del Picamera2._cm
gc.collect() gc.collect()
Picamera2._cm = picamera2.picamera2.CameraManager() 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)

View file

@ -22,9 +22,7 @@ from scipy.ndimage import gaussian_filter
from labthings_fastapi.utilities import get_blocking_portal from labthings_fastapi.utilities import get_blocking_portal
from labthings_fastapi.decorators import thing_action, thing_property from labthings_fastapi.decorators import thing_action, thing_property
from labthings_fastapi.dependencies.metadata import GetThingStates from labthings_fastapi.dependencies.metadata import GetThingStates
from labthings_fastapi.types.numpy import NDArray
from labthings_fastapi.server import ThingServer from labthings_fastapi.server import ThingServer
from pydantic import RootModel
from . import BaseCamera, JPEGBlob, ArrayModel from . import BaseCamera, JPEGBlob, ArrayModel
from ..stage import StageProtocol as Stage from ..stage import StageProtocol as Stage
@ -33,7 +31,6 @@ from ..stage import StageProtocol as Stage
# higher related to a faster movement # higher related to a faster movement
RATIO = 0.2 RATIO = 0.2
class SimulatedCamera(BaseCamera): class SimulatedCamera(BaseCamera):
"""A Thing representing an OpenCV camera""" """A Thing representing an OpenCV camera"""