Static type analysis

This commit is contained in:
Joel Collins 2020-11-30 13:36:45 +00:00
parent 3aebb8bead
commit 7866ec0f47
63 changed files with 1825 additions and 2722 deletions

View file

@ -1,14 +1,15 @@
import logging
from contextlib import contextmanager
# Type hinting
from typing import Tuple
import picamerax
from flask import abort
from labthings import find_component
from labthings.extensions import BaseExtension
from labthings.views import ActionView
from openflexure_microscope.camera.base import BaseCamera
from openflexure_microscope.microscope import Microscope
from .recalibrate_utils import (
auto_expose_and_freeze_settings,
flat_lens_shading_table,
@ -17,7 +18,7 @@ from .recalibrate_utils import (
@contextmanager
def pause_stream(scamera, resolution: Tuple[int, int] = None):
def pause_stream(scamera: BaseCamera):
"""This context manager locks a streaming camera, and pauses the stream.
The stream is re-enabled, with the original resolution, once the with
@ -28,20 +29,20 @@ def pause_stream(scamera, resolution: Tuple[int, int] = None):
not scamera.record_active
), "We can't pause the camera's video stream while a recording is in progress."
streaming = scamera.stream_active
old_resolution = scamera.camera.resolution
old_resolution = scamera.stream_resolution
if streaming:
logging.info("Stopping stream in pause_stream context manager")
scamera.stop_stream_recording(resolution=resolution)
scamera.stop_stream()
try:
yield scamera
finally:
scamera.camera.resolution = old_resolution
scamera.stream_resolution = old_resolution
if streaming:
logging.info("Restarting stream in pause_stream context manager")
scamera.start_stream_recording()
scamera.start_stream()
def recalibrate(microscope):
def recalibrate(microscope: Microscope):
"""Reset the camera's settings.
This generates new gains, exposure time, and lens shading
@ -49,11 +50,15 @@ def recalibrate(microscope):
with a gray level of 230. It takes a little while to run.
"""
with pause_stream(microscope.camera) as scamera:
auto_expose_and_freeze_settings(
scamera.camera
) # scamera.camera is the PiCamera object
recalibrate_camera(scamera.camera)
microscope.save_settings()
if hasattr(scamera, "picamera"):
picamera_obj: picamerax.PiCamera = getattr(scamera, "picamera")
auto_expose_and_freeze_settings(picamera_obj)
recalibrate_camera(picamera_obj)
microscope.save_settings()
else:
raise RuntimeError(
"Recalibrate can only be used with a Raspberry Pi camera"
)
class RecalibrateView(ActionView):

View file

@ -1,19 +1,21 @@
import logging
import time
from fractions import Fraction
from typing import List, Optional, Tuple
import numpy as np
from picamerax import PiCamera
from picamerax.array import PiBayerArray, PiRGBArray
def rgb_image(camera, resize=None, **kwargs):
def rgb_image(camera: PiCamera, resize: Optional[Tuple[int, int]] = None, **kwargs):
"""Capture an image and return an RGB numpy array"""
with PiRGBArray(camera, size=resize) as output:
camera.capture(output, format="rgb", resize=resize, **kwargs)
return output.array
def flat_lens_shading_table(camera):
def flat_lens_shading_table(camera: PiCamera):
"""Return a flat (i.e. unity gain) lens shading table.
This is mostly useful because it makes it easy to get the size
@ -24,14 +26,11 @@ def flat_lens_shading_table(camera):
raise ImportError(
"This program requires the forked picamera library with lens shading support"
)
return (
# pylint: disable=W0212
np.zeros(camera._lens_shading_table_shape(), dtype=np.uint8)
+ 32
)
# pylint: disable=protected-access
return np.zeros(camera._lens_shading_table_shape(), dtype=np.uint8) + 32
def adjust_exposure_to_setpoint(camera, setpoint):
def adjust_exposure_to_setpoint(camera: PiCamera, setpoint: int):
"""Adjust the camera's exposure time until the maximum pixel value is <setpoint>."""
print("Adjusting shutter speed to hit setpoint {}".format(setpoint), end="")
for _ in range(3):
@ -43,7 +42,7 @@ def adjust_exposure_to_setpoint(camera, setpoint):
print("done")
def auto_expose_and_freeze_settings(camera):
def auto_expose_and_freeze_settings(camera: PiCamera):
"""Freeze the settings after auto-exposing to white illumination"""
logging.info("Allowing the camera to auto-expose")
if "greyworld" in camera.AWB_MODES:
@ -66,7 +65,7 @@ def auto_expose_and_freeze_settings(camera):
logging.info("Shutter speed = %s", (camera.shutter_speed))
camera.exposure_mode = "off"
logging.info("Auto exposure disabled")
g = camera.awb_gains
g: Tuple[Fraction, Fraction] = camera.awb_gains
camera.awb_mode = "off"
camera.awb_gains = g
logging.info("Auto white balance disabled, gains are %s", (g))
@ -76,10 +75,10 @@ def auto_expose_and_freeze_settings(camera):
adjust_exposure_to_setpoint(camera, 215)
def channels_from_bayer_array(bayer_array):
def channels_from_bayer_array(bayer_array: np.ndarray) -> np.ndarray:
"""Given the 'array' from a PiBayerArray, return the 4 channels."""
bayer_pattern = [(i // 2, i % 2) for i in range(4)]
channels = np.zeros(
bayer_pattern: List[Tuple[int, int]] = [(0, 0), (0, 1), (1, 0), (1, 1)]
channels: np.ndarray = np.zeros(
(4, bayer_array.shape[0] // 2, bayer_array.shape[1] // 2),
dtype=bayer_array.dtype,
)
@ -92,14 +91,19 @@ def channels_from_bayer_array(bayer_array):
return channels
def lst_from_channels(channels):
def lst_from_channels(channels: np.ndarray) -> np.ndarray:
"""Given the 4 Bayer colour channels from a white image, generate a LST."""
full_resolution = np.array(channels.shape[1:]) * 2 # channels have been binned
# lst_resolution = list(np.ceil(full_resolution / 64.0).astype(int))
lst_resolution = [(r // 64) + 1 for r in full_resolution]
# NB the size of the LST is 1/64th of the image, but rounded UP.
full_resolution: np.ndarray = np.array(
channels.shape[1:]
) * 2 # channels have been binned
# NOTE: the size of the LST is 1/64th of the image, but rounded UP.
lst_resolution: List[int] = [(r // 64) + 1 for r in full_resolution]
logging.info("Generating a lens shading table at %sx%s", *lst_resolution)
lens_shading = np.zeros([channels.shape[0]] + lst_resolution, dtype=np.float)
lens_shading: np.ndarray = np.zeros(
[channels.shape[0]] + lst_resolution, dtype=np.float
)
for i in range(lens_shading.shape[0]):
image_channel = channels[i, :, :]
iw, ih = image_channel.shape
@ -153,7 +157,7 @@ def lst_from_channels(channels):
return lens_shading_table[::-1, :, :].copy()
def recalibrate_camera(camera):
def recalibrate_camera(camera: PiCamera):
"""Reset the lens shading table and exposure settings.
This method first resets to a flat lens shading table, then auto-exposes,