Fix issues found by linter in PiCamera Code

Issue #423 created based on lens shading TODOs
This commit is contained in:
Julian Stirling 2025-06-23 12:33:42 +01:00
parent 0e3f4305b5
commit 72faba0272
4 changed files with 26 additions and 29 deletions

View file

@ -41,8 +41,7 @@ class PicameraControl(PropertyDescriptor):
def _getter(self, obj: StreamingPiCamera2):
with obj.picamera() as cam:
ret = cam.capture_metadata()[self.control_name]
return ret
return cam.capture_metadata()[self.control_name]
def _setter(self, obj: StreamingPiCamera2, value: Any):
with obj.picamera() as cam:
@ -171,6 +170,7 @@ class StreamingPiCamera2(BaseCamera):
initial_value=(820, 616),
description="Resolution to use for the MJPEG stream",
)
mjpeg_bitrate = PropertyDescriptor(
Optional[int],
initial_value=100000000,
@ -368,8 +368,6 @@ class StreamingPiCamera2(BaseCamera):
lower may cause dropped frames. Defaults to 6.
"""
with self.picamera() as picam:
# TODO: Filip: can we use the lores output to keep preview stream going
# while recording? According to picamera2 docs 4.2.1.6 this should work
try:
if picam.started:
picam.stop()

View file

@ -80,16 +80,15 @@ def set_minimum_exposure(camera: Picamera2):
We set exposure mode to manual, analog and digital gain
to 1, and shutter speed to the minimum (8us for Pi Camera v2)
NB ISO is left at auto, because this is needed for the gains
Note ISO is left at auto, because this is needed for the gains
to be set correctly.
"""
camera.set_controls({"AeEnable": False, "AnalogueGain": 1, "ExposureTime": 1})
# camera.iso = 0 # We must set ISO=0 (auto) or we can't set gain
# camera.analog_gain = 1
# camera.digital_gain = 1 (not configurable)
# Disable Automatic exposure and gain algoritm (AeEnable), and set analogue
# gain and exposure time.
# Setting the shutter speed to 1us will result in it being set
# to the minimum possible, which is probably 8us for PiCamera v2
# camera.shutter_speed = 1
# to the minimum possible, which is ~8us for PiCamera v2
camera.set_controls({"AeEnable": False, "AnalogueGain": 1, "ExposureTime": 1})
time.sleep(0.5)
@ -138,8 +137,7 @@ def test_exposure_settings(camera: Picamera2, percentile: float) -> ExposureTest
def check_convergence(test: ExposureTest, target: int, tolerance: float):
"""Check whether the brightness is within the specified target range"""
converged = abs(test.level - target) < target * tolerance
return converged
return abs(test.level - target) < target * tolerance
def adjust_shutter_and_gain_from_raw(
@ -257,7 +255,6 @@ def adjust_white_balance_from_raw(
camera.configure(config)
camera.start()
channels = channels_from_bayer_array(camera.capture_array("raw"))
# logging.info(f"White balance: channels were retrieved with shape {channels.shape}.")
if luminance is not None and Cr is not None and Cb is not None:
# Reconstruct a low-resolution image from the lens shading tables
# and use it to normalise the raw image, to compensate for
@ -328,10 +325,9 @@ def get_16x12_grid(chan: np.ndarray, dx: int, dy: int):
for consistency.
"""
grid = []
"""
since left and bottom border will not necessarily have rectangles of
dimension dx x dy, the 32nd iteration has to be handled separately.
"""
# since left and bottom border will not necessarily have rectangles of
# dimension dx x dy, the final iteration has to be handled separately.
for i in range(11):
for j in range(15):
grid.append(np.mean(chan[dy * i : dy * (1 + i), dx * j : dx * (1 + j)]))
@ -339,9 +335,7 @@ def get_16x12_grid(chan: np.ndarray, dx: int, dy: int):
for j in range(15):
grid.append(np.mean(chan[11 * dy :, dx * j : dx * (1 + j)]))
grid.append(np.mean(chan[11 * dy :, 15 * dx :]))
"""
return as np.array, ready for further manipulation
"""
# return as np.array, ready for further manipulation
return np.reshape(np.array(grid), (12, 16))
@ -387,24 +381,27 @@ def lst_from_channels(channels: np.ndarray) -> LensShadingTables:
def lst_from_grids(grids: np.ndarray) -> LensShadingTables:
"""Given 4 downsampled grids, generate the luminance and chrominance tables
The grids are the 4 BAYER channels RGGB
The LST format has changed with `picamera2` and now uses a fixed resolution,
and is in luminance, Cr, Cb format. This function returns three ndarrays of
luminance, Cr, Cb, each with shape (12, 16).
# TODO: make consistent with
https://git.linuxtv.org/libcamera.git/tree/utils/raspberrypi/ctt/ctt_alsc.py
"""
# Calculated red, green, and blue channels from Bayer data
r: np.ndarray = grids[3, ...]
g: np.ndarray = np.mean(grids[1:3, ...], axis=0)
b: np.ndarray = grids[0, ...]
# What we actually want to calculate is the gains needed to compensate for the
# lens shading - that's 1/lens_shading_table_float as we currently have it.
luminance_gains: np.ndarray = np.max(g) / g # Minimum luminance gain is 1
# Minimum luminance gain is 1
luminance_gains: np.ndarray = np.max(g) / g
cr_gains: np.ndarray = g / r
# cr_gains /= cr_gains[5, 7] # Normalise so the central colour doesn't change
cb_gains: np.ndarray = g / b
# cb_gains /= cb_gains[5, 7]
return luminance_gains, cr_gains, cb_gains
@ -491,11 +488,12 @@ def _geq_is_static(tuning: dict) -> bool:
return geq["offset"] == 65535
def index_of_algorithm(algorithms: list[dict], algorithm: str):
def index_of_algorithm(algorithms: list[dict], algorithm: str) -> int:
"""Find the index of an algorithm's section in the tuning file"""
for i, a in enumerate(algorithms):
if algorithm in a:
return i
raise ValueError(f"Algorithm {algorithm} is not available.")
def copy_alsc_section(from_tuning: dict, to_tuning: dict):

View file

@ -31,6 +31,7 @@ from ..stage import StageProtocol as Stage
# higher related to a faster movement
RATIO = 0.2
class SimulatedCamera(BaseCamera):
"""A Thing representing an OpenCV camera"""