Lock access to picamera
The picamera object isn't thread safe; I have now wrapped all access in context managers that acquire the BaseCamera.lock I also added an endpoint to reset the LST without auto-gain.
This commit is contained in:
parent
a9709b14ea
commit
069d27bf15
1 changed files with 64 additions and 44 deletions
|
|
@ -6,6 +6,8 @@ from flask import abort
|
||||||
from labthings import find_component
|
from labthings import find_component
|
||||||
from labthings.extensions import BaseExtension
|
from labthings.extensions import BaseExtension
|
||||||
from labthings.views import ActionView
|
from labthings.views import ActionView
|
||||||
|
from picamerax import PiCamera
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
from openflexure_microscope.camera.base import BaseCamera
|
from openflexure_microscope.camera.base import BaseCamera
|
||||||
from openflexure_microscope.microscope import Microscope
|
from openflexure_microscope.microscope import Microscope
|
||||||
|
|
@ -78,9 +80,14 @@ class LSTExtension(BaseExtension):
|
||||||
"/auto_white_balance_from_raw",
|
"/auto_white_balance_from_raw",
|
||||||
endpoint="auto_white_balance_from_raw",
|
endpoint="auto_white_balance_from_raw",
|
||||||
)
|
)
|
||||||
|
self.add_view(
|
||||||
|
AutoLensShadingTableView,
|
||||||
|
"/auto_lens_shading_table",
|
||||||
|
endpoint="auto_lens_shading_table",
|
||||||
|
)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
def find_microscope() -> Microscope:
|
def find_picamera() -> Tuple[PiCamera, BaseCamera, Microscope]:
|
||||||
"""Locate the microscope and raise a sensible error if it's missing."""
|
"""Locate the microscope and raise a sensible error if it's missing."""
|
||||||
microscope = find_component("org.openflexure.microscope")
|
microscope = find_component("org.openflexure.microscope")
|
||||||
|
|
||||||
|
|
@ -89,23 +96,17 @@ def find_microscope() -> Microscope:
|
||||||
503, "No microscope connected. Unable to use camera calibration functions."
|
503, "No microscope connected. Unable to use camera calibration functions."
|
||||||
)
|
)
|
||||||
|
|
||||||
if not hasattr(microscope.camera, "picamera"):
|
scamera = microscope.camera
|
||||||
|
|
||||||
|
if not hasattr(scamera, "picamera"):
|
||||||
abort(503, "The PiCamera calibration plugin requires a Raspberry Pi camera.")
|
abort(503, "The PiCamera calibration plugin requires a Raspberry Pi camera.")
|
||||||
|
|
||||||
return microscope
|
with scamera.lock:
|
||||||
|
yield scamera.picamera, scamera, microscope
|
||||||
|
|
||||||
|
|
||||||
class RecalibrateView(ActionView):
|
class RecalibrateView(ActionView):
|
||||||
args = {
|
args = {}
|
||||||
"skip_auto_exposure": fields.Bool(
|
|
||||||
missing=False,
|
|
||||||
description=(
|
|
||||||
"By default we perform an auto-exposure and AWB "
|
|
||||||
"step before updating the lens shading table. Set "
|
|
||||||
"this argument to True to disable those steps."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
"""Reset the camera's settings.
|
"""Reset the camera's settings.
|
||||||
|
|
@ -113,35 +114,60 @@ class RecalibrateView(ActionView):
|
||||||
This generates new gains, exposure time, and lens shading
|
This generates new gains, exposure time, and lens shading
|
||||||
table such that the background is as uniform as possible
|
table such that the background is as uniform as possible
|
||||||
with a gray level of 230. It takes a little while to run.
|
with a gray level of 230. It takes a little while to run.
|
||||||
|
|
||||||
|
This consists of three steps:
|
||||||
|
|
||||||
|
* Reset gain and exposure time, then increase them until
|
||||||
|
we have a suitably bright image
|
||||||
|
* Set the auto white balance based on a raw image
|
||||||
|
* Set the lens shading table based on a raw image
|
||||||
|
|
||||||
|
Each of these steps has its own endpoint if you want to
|
||||||
|
perform them separately.
|
||||||
"""
|
"""
|
||||||
microscope = find_microscope()
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
logging.info("Starting microscope recalibration...")
|
logging.info("Starting microscope recalibration...")
|
||||||
picamera = microscope.camera.picamera
|
adjust_shutter_and_gain_from_raw(picamera)
|
||||||
with microscope.camera.lock:
|
adjust_white_balance_from_raw(picamera)
|
||||||
if not args.get("skip_auto_exposure"):
|
|
||||||
adjust_shutter_and_gain_from_raw(picamera)
|
|
||||||
adjust_white_balance_from_raw(picamera)
|
|
||||||
lst = lst_from_camera(picamera)
|
lst = lst_from_camera(picamera)
|
||||||
with pause_stream(microscope.camera) as scamera:
|
with pause_stream(scamera):
|
||||||
scamera.picamera.lens_shading_table = lst
|
picamera.lens_shading_table = lst
|
||||||
microscope.save_settings()
|
microscope.save_settings()
|
||||||
|
|
||||||
|
class AutoLensShadingTableView(ActionView):
|
||||||
|
args = {}
|
||||||
|
|
||||||
|
def post(self, args):
|
||||||
|
"""Perform flat-field correction
|
||||||
|
|
||||||
|
This routine acquires a new image (which should be an
|
||||||
|
empty field of view, i.e. a perfect microscope would
|
||||||
|
record a uniform white image), and then uses it to set
|
||||||
|
the "lens shading table" such that future images will
|
||||||
|
be corrected for vignetting.
|
||||||
|
"""
|
||||||
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
|
logging.info("Generating lens shading table")
|
||||||
|
lst = lst_from_camera(picamera)
|
||||||
|
with pause_stream(scamera):
|
||||||
|
picamera.lens_shading_table = lst
|
||||||
|
microscope.save_settings()
|
||||||
|
|
||||||
class FlattenLSTView(ActionView):
|
class FlattenLSTView(ActionView):
|
||||||
def post(self):
|
def post(self):
|
||||||
microscope = find_microscope()
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
with pause_stream(microscope.camera) as scamera:
|
with pause_stream(scamera):
|
||||||
flat_lst = flat_lens_shading_table(scamera.camera)
|
flat_lst = flat_lens_shading_table(picamera)
|
||||||
scamera.camera.lens_shading_table = flat_lst
|
picamera.lens_shading_table = flat_lst
|
||||||
microscope.save_settings()
|
microscope.save_settings()
|
||||||
|
|
||||||
|
|
||||||
class DeleteLSTView(ActionView):
|
class DeleteLSTView(ActionView):
|
||||||
def post(self):
|
def post(self):
|
||||||
microscope = find_microscope()
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
with pause_stream(microscope.camera) as scamera:
|
with pause_stream(scamera):
|
||||||
scamera.camera.lens_shading_table = None
|
picamera.lens_shading_table = None
|
||||||
microscope.save_settings()
|
microscope.save_settings()
|
||||||
|
|
||||||
|
|
||||||
class AutoExposureFromRawView(ActionView):
|
class AutoExposureFromRawView(ActionView):
|
||||||
|
|
@ -180,12 +206,8 @@ class AutoExposureFromRawView(ActionView):
|
||||||
}
|
}
|
||||||
|
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
camera = find_component("org.openflexure.microscope").camera
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
|
adjust_shutter_and_gain_from_raw(picamera, **args)
|
||||||
with camera.lock:
|
|
||||||
adjust_shutter_and_gain_from_raw(
|
|
||||||
camera.picamera, **args # I'm relying on the schema to fill in missing values
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AutoWhiteBalanceFromRawView(ActionView):
|
class AutoWhiteBalanceFromRawView(ActionView):
|
||||||
|
|
@ -203,10 +225,8 @@ class AutoWhiteBalanceFromRawView(ActionView):
|
||||||
}
|
}
|
||||||
|
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
camera = find_component("org.openflexure.microscope").camera.picamera
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
adjust_white_balance_from_raw(
|
adjust_white_balance_from_raw(picamera, **args)
|
||||||
camera, **args # I'm relying on the schema to fill in missing values
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GetRawChannelPercentilesView(ActionView):
|
class GetRawChannelPercentilesView(ActionView):
|
||||||
|
|
@ -219,5 +239,5 @@ class GetRawChannelPercentilesView(ActionView):
|
||||||
schema = fields.List(fields.Integer)
|
schema = fields.List(fields.Integer)
|
||||||
|
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
camera = find_component("org.openflexure.microscope").camera.picamera
|
with find_picamera() as (picamera, scamera, microscope):
|
||||||
return get_channel_percentiles(camera, args["percentile"])
|
return get_channel_percentiles(picamera, args["percentile"])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue