Fix camera type issues
This commit is contained in:
parent
9976c5cb84
commit
99eee2e912
5 changed files with 22 additions and 42 deletions
|
|
@ -288,7 +288,7 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
"Sharpness": 1,
|
"Sharpness": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
_sensor_modes = None
|
_sensor_modes: Optional[list[SensorMode]] = None
|
||||||
|
|
||||||
@lt.property
|
@lt.property
|
||||||
def sensor_modes(self) -> list[SensorMode]:
|
def sensor_modes(self) -> list[SensorMode]:
|
||||||
|
|
@ -363,6 +363,9 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
camera_num=self._camera_num,
|
camera_num=self._camera_num,
|
||||||
tuning=self.tuning,
|
tuning=self.tuning,
|
||||||
)
|
)
|
||||||
|
if self._picamera is None:
|
||||||
|
# Type narrow (error if failure)
|
||||||
|
raise RuntimeError("Failed to start Picamera")
|
||||||
if check_sensor_model:
|
if check_sensor_model:
|
||||||
hw_sensor_model = self._picamera.camera_properties["Model"]
|
hw_sensor_model = self._picamera.camera_properties["Model"]
|
||||||
if hw_sensor_model != self._sensor_info.sensor_model:
|
if hw_sensor_model != self._sensor_info.sensor_model:
|
||||||
|
|
@ -861,7 +864,7 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
]
|
]
|
||||||
|
|
||||||
@lt.property
|
@lt.property
|
||||||
def lens_shading_tables(self) -> Optional[tf_utils.LensShading]:
|
def lens_shading_tables(self) -> Optional[tf_utils.LensShadingModel]:
|
||||||
"""The current lens shading (i.e. flat-field correction).
|
"""The current lens shading (i.e. flat-field correction).
|
||||||
|
|
||||||
Return the current lens shading correction, as three 2D lists each with
|
Return the current lens shading correction, as three 2D lists each with
|
||||||
|
|
@ -874,19 +877,6 @@ class StreamingPiCamera2(BaseCamera):
|
||||||
"""
|
"""
|
||||||
return tf_utils.get_lst(self.tuning)
|
return tf_utils.get_lst(self.tuning)
|
||||||
|
|
||||||
@lens_shading_tables.setter
|
|
||||||
def _set_lens_shading_tables(self, lst: tf_utils.LensShading) -> None:
|
|
||||||
"""Set the lens shading tables."""
|
|
||||||
with self._streaming_picamera(pause_stream=True):
|
|
||||||
self.tuning = tf_utils.set_lst(
|
|
||||||
self.tuning,
|
|
||||||
luminance=lst.luminance,
|
|
||||||
cr=lst.Cr,
|
|
||||||
cb=lst.Cb,
|
|
||||||
colour_temp=lst.colour_temp,
|
|
||||||
)
|
|
||||||
self._initialise_picamera()
|
|
||||||
|
|
||||||
@lt.action
|
@lt.action
|
||||||
def flat_lens_shading(self) -> None:
|
def flat_lens_shading(self) -> None:
|
||||||
"""Disable flat-field correction.
|
"""Disable flat-field correction.
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ import numpy as np
|
||||||
import picamera2
|
import picamera2
|
||||||
from picamera2 import Picamera2
|
from picamera2 import Picamera2
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from scipy.ndimage import zoom
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -322,18 +321,7 @@ def _get_16x12_grid(chan: np.ndarray, dx: int, dy: int) -> np.ndarray:
|
||||||
return np.reshape(np.array(grid), (12, 16))
|
return np.reshape(np.array(grid), (12, 16))
|
||||||
|
|
||||||
|
|
||||||
def _upsample_channels(grids: np.ndarray, shape: tuple[int]) -> np.ndarray:
|
def _downsampled_channels(channels: np.ndarray, blacklevel: int) -> np.ndarray:
|
||||||
"""Zoom an image in the last two dimensions.
|
|
||||||
|
|
||||||
This is effectively the inverse operation of ``_get_16x12_grid``
|
|
||||||
"""
|
|
||||||
zoom_factors = [
|
|
||||||
1,
|
|
||||||
] + list(np.ceil(np.array(shape) / np.array(grids.shape[1:])))
|
|
||||||
return zoom(grids, zoom_factors, order=1)[:, : shape[0], : shape[1]]
|
|
||||||
|
|
||||||
|
|
||||||
def _downsampled_channels(channels: np.ndarray, blacklevel: int) -> list[np.ndarray]:
|
|
||||||
"""Generate a downsampled, un-normalised image from which to calculate the LST."""
|
"""Generate a downsampled, un-normalised image from which to calculate the LST."""
|
||||||
channel_shape = np.array(channels.shape[1:])
|
channel_shape = np.array(channels.shape[1:])
|
||||||
lst_shape = np.array([12, 16])
|
lst_shape = np.array([12, 16])
|
||||||
|
|
@ -398,9 +386,7 @@ def _grids_from_lst(lum: np.ndarray, Cr: np.ndarray, Cb: np.ndarray) -> np.ndarr
|
||||||
return np.stack([B, G, G, R], axis=0)
|
return np.stack([B, G, G, R], axis=0)
|
||||||
|
|
||||||
|
|
||||||
def _raw_channels_from_camera(
|
def _raw_channels_from_camera(camera: Picamera2, sensor_info: SensorInfo) -> np.ndarray:
|
||||||
camera: Picamera2, sensor_info: SensorInfo
|
|
||||||
) -> LensShadingTables:
|
|
||||||
"""Acquire a raw image and return a 4xNxM array of the colour channels."""
|
"""Acquire a raw image and return a 4xNxM array of the colour channels."""
|
||||||
if camera.started:
|
if camera.started:
|
||||||
camera.stop_recording()
|
camera.stop_recording()
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@ CALIBRATED_COLOUR_TEMP = 5000
|
||||||
DEFAULT_COLOUR_TEMP = 1234
|
DEFAULT_COLOUR_TEMP = 1234
|
||||||
|
|
||||||
|
|
||||||
class LensShading(BaseModel):
|
class LensShadingModel(BaseModel):
|
||||||
"""A Pydantic model holding the lens shading tables.
|
"""A Pydantic model holding the lens shading tables.
|
||||||
|
|
||||||
|
Note this shouldn't be confused with the typehint for LensShadingTables in
|
||||||
|
recalibrate utils which is for the arrays.
|
||||||
|
|
||||||
PiCamera needs three numpy arrays for lens shading correction. Each array is
|
PiCamera needs three numpy arrays for lens shading correction. Each array is
|
||||||
(12, 16) in size. The arrays are luminance, red-difference chroma (Cr), and
|
(12, 16) in size. The arrays are luminance, red-difference chroma (Cr), and
|
||||||
blue-difference chroma (Cb).
|
blue-difference chroma (Cb).
|
||||||
|
|
@ -165,7 +168,7 @@ def flatten_lst(tuning: dict, keep_luminance: bool = False) -> dict:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_lst(tuning: dict) -> LensShading:
|
def get_lst(tuning: dict) -> LensShadingModel:
|
||||||
"""Return the lens shading as a LenSading Base Model."""
|
"""Return the lens shading as a LenSading Base Model."""
|
||||||
# Note "alsc" is the Picamera2 term for "Automatic Lens Shading Correction"
|
# Note "alsc" is the Picamera2 term for "Automatic Lens Shading Correction"
|
||||||
alsc = find_tuning_algo(tuning, "rpi.alsc")
|
alsc = find_tuning_algo(tuning, "rpi.alsc")
|
||||||
|
|
@ -175,7 +178,7 @@ def get_lst(tuning: dict) -> LensShading:
|
||||||
w, h = 16, 12
|
w, h = 16, 12
|
||||||
return [lin[w * i : w * (i + 1)] for i in range(h)]
|
return [lin[w * i : w * (i + 1)] for i in range(h)]
|
||||||
|
|
||||||
return LensShading(
|
return LensShadingModel(
|
||||||
luminance=reshape_lst(alsc["luminance_lut"]),
|
luminance=reshape_lst(alsc["luminance_lut"]),
|
||||||
Cr=reshape_lst(alsc["calibrations_Cr"][0]["table"]),
|
Cr=reshape_lst(alsc["calibrations_Cr"][0]["table"]),
|
||||||
Cb=reshape_lst(alsc["calibrations_Cb"][0]["table"]),
|
Cb=reshape_lst(alsc["calibrations_Cb"][0]["table"]),
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ from . import BaseCamera
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__name__)
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
# The ratio between "motor" steps and pixels
|
# The ratio between "motor" steps and pixels in (x, y, z)
|
||||||
# higher related to a faster movement
|
# higher related to a faster movement
|
||||||
RATIO = (2, 2, 0.2)
|
RATIO = (2, 2, 0.2)
|
||||||
|
|
||||||
|
|
@ -203,10 +203,11 @@ class SimulatedCamera(BaseCamera):
|
||||||
"""
|
"""
|
||||||
canvas_width, canvas_height, _ = self.canvas_shape
|
canvas_width, canvas_height, _ = self.canvas_shape
|
||||||
image_width, image_height, _ = self.shape
|
image_width, image_height, _ = self.shape
|
||||||
pos = tuple(x * s for x, s in zip(pos, RATIO, strict=True))
|
# Scale position by RATIO to get position in base image.
|
||||||
|
im_pos = tuple(x * ratio for x, ratio in zip(pos, RATIO, strict=True))
|
||||||
top_left = (
|
top_left = (
|
||||||
int(pos[0]) - image_width // 2 + self.sample_limits[0] // 2,
|
int(im_pos[0]) - image_width // 2 + self.sample_limits[0] // 2,
|
||||||
int(pos[1]) - image_height // 2 + self.sample_limits[1] // 2,
|
int(im_pos[1]) - image_height // 2 + self.sample_limits[1] // 2,
|
||||||
)
|
)
|
||||||
# Create index list with modulo rather than slicing to handle wrapping at the
|
# Create index list with modulo rather than slicing to handle wrapping at the
|
||||||
# canvas edge.
|
# canvas edge.
|
||||||
|
|
@ -217,7 +218,7 @@ class SimulatedCamera(BaseCamera):
|
||||||
# Use npx to make each 1d index list 3D
|
# Use npx to make each 1d index list 3D
|
||||||
focused_image = canvas[np.ix_(x_indices, y_indices, z_indices)]
|
focused_image = canvas[np.ix_(x_indices, y_indices, z_indices)]
|
||||||
|
|
||||||
image = fast_pil_blur(focused_image, sigma=np.abs(pos[2]) / 5)
|
image = fast_pil_blur(focused_image, sigma=np.abs(im_pos[2]) / 5)
|
||||||
|
|
||||||
if image.shape != self.shape:
|
if image.shape != self.shape:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
@ -251,7 +252,7 @@ class SimulatedCamera(BaseCamera):
|
||||||
_traceback: Optional[TracebackType],
|
_traceback: Optional[TracebackType],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Close the capture thread when the Thing context manager is closed."""
|
"""Close the capture thread when the Thing context manager is closed."""
|
||||||
if self.stream_active:
|
if self._capture_thread is not None and self._capture_thread.is_alive():
|
||||||
self._capture_enabled = False
|
self._capture_enabled = False
|
||||||
self._capture_thread.join()
|
self._capture_thread.join()
|
||||||
|
|
||||||
|
|
@ -300,7 +301,7 @@ class SimulatedCamera(BaseCamera):
|
||||||
try:
|
try:
|
||||||
frame = self.generate_frame()
|
frame = self.generate_frame()
|
||||||
self.mjpeg_stream.add_frame(_frame2bytes(frame))
|
self.mjpeg_stream.add_frame(_frame2bytes(frame))
|
||||||
ds_frame = frame.resize((320, 240), resample=Image.NEAREST)
|
ds_frame = frame.resize((320, 240), resample=Image.Resampling.NEAREST)
|
||||||
self.lores_mjpeg_stream.add_frame(_frame2bytes(ds_frame))
|
self.lores_mjpeg_stream.add_frame(_frame2bytes(ds_frame))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ def test_tuning_lst_tools(imx219_tuning, imx477_tuning):
|
||||||
assert not tf_utils.lst_calibrated(tuning)
|
assert not tf_utils.lst_calibrated(tuning)
|
||||||
|
|
||||||
lst_model = tf_utils.get_lst(tuning)
|
lst_model = tf_utils.get_lst(tuning)
|
||||||
assert isinstance(lst_model, tf_utils.LensShading)
|
assert isinstance(lst_model, tf_utils.LensShadingModel)
|
||||||
# Check the tables are lists of lists
|
# Check the tables are lists of lists
|
||||||
assert isinstance(lst_model.luminance, list)
|
assert isinstance(lst_model.luminance, list)
|
||||||
assert isinstance(lst_model.luminance[0], list)
|
assert isinstance(lst_model.luminance[0], list)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue