Camera settings exposed in UI defined by server.
This commit is contained in:
parent
3651013440
commit
e7e3a08210
7 changed files with 159 additions and 80 deletions
|
|
@ -20,7 +20,7 @@ import piexif
|
|||
import labthings_fastapi as lt
|
||||
from labthings_fastapi.types.numpy import NDArray
|
||||
|
||||
from openflexure_microscope_server.ui import ActionButton
|
||||
from openflexure_microscope_server.ui import ActionButton, PropertyControl
|
||||
from openflexure_microscope_server.background_detect import (
|
||||
ColourChannelDetectLUV,
|
||||
BackgroundDetectAlgorithm,
|
||||
|
|
@ -487,6 +487,11 @@ class BaseCamera(lt.Thing):
|
|||
"""The calibration actions that appear only in settings panel."""
|
||||
return []
|
||||
|
||||
@lt.thing_property
|
||||
def manual_camera_settings(self) -> list[PropertyControl]:
|
||||
"""The camera settings to expose as property controls in the settings panel."""
|
||||
return []
|
||||
|
||||
# Note that the default detector name is set at init. This is over written if
|
||||
# setting is loaded from disk.
|
||||
@lt.thing_setting
|
||||
|
|
|
|||
|
|
@ -35,7 +35,12 @@ from picamera2.outputs import Output
|
|||
import labthings_fastapi as lt
|
||||
from labthings_fastapi.exceptions import NotConnectedToServerError
|
||||
|
||||
from openflexure_microscope_server.ui import ActionButton, action_button_for
|
||||
from openflexure_microscope_server.ui import (
|
||||
ActionButton,
|
||||
PropertyControl,
|
||||
action_button_for,
|
||||
property_control_for,
|
||||
)
|
||||
from . import picamera_recalibrate_utils as recalibrate_utils
|
||||
from . import BaseCamera, JPEGBlob, ArrayModel
|
||||
|
||||
|
|
@ -833,6 +838,33 @@ class StreamingPiCamera2(BaseCamera):
|
|||
),
|
||||
]
|
||||
|
||||
@lt.thing_property
|
||||
def manual_camera_settings(self) -> list[PropertyControl]:
|
||||
"""The camera settings to expose as property controls in the settings panel."""
|
||||
return [
|
||||
property_control_for(
|
||||
self,
|
||||
"exposure_time",
|
||||
label="Exposure Time (0-33251)",
|
||||
read_back=True,
|
||||
read_back_delay=1000,
|
||||
),
|
||||
property_control_for(
|
||||
self,
|
||||
"analogue_gain",
|
||||
label="Analogue Gain",
|
||||
read_back=True,
|
||||
read_back_delay=1000,
|
||||
),
|
||||
property_control_for(
|
||||
self,
|
||||
"colour_gains",
|
||||
label="Colour Gains",
|
||||
read_back=True,
|
||||
read_back_delay=1000,
|
||||
),
|
||||
]
|
||||
|
||||
@lt.thing_property
|
||||
def lens_shading_tables(self) -> Optional[LensShading]:
|
||||
"""The current lens shading (i.e. flat-field correction).
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ from scipy.ndimage import gaussian_filter
|
|||
|
||||
import labthings_fastapi as lt
|
||||
|
||||
from openflexure_microscope_server.ui import ActionButton, action_button_for
|
||||
from openflexure_microscope_server.ui import (
|
||||
ActionButton,
|
||||
PropertyControl,
|
||||
action_button_for,
|
||||
property_control_for,
|
||||
)
|
||||
|
||||
from . import BaseCamera, JPEGBlob, ArrayModel
|
||||
from ..stage import BaseStage
|
||||
|
|
@ -166,7 +171,7 @@ class SimulatedCamera(BaseCamera):
|
|||
)
|
||||
|
||||
# Add noise and convert to uint8
|
||||
image += RNG.normal(scale=2, size=self.shape).astype("int16")
|
||||
image += RNG.normal(scale=self.noise_level, size=self.shape).astype("int16")
|
||||
image[image < 0] = 0
|
||||
image[image > 255] = 255
|
||||
return image.astype("uint8")
|
||||
|
|
@ -221,6 +226,8 @@ class SimulatedCamera(BaseCamera):
|
|||
return self._capture_thread.is_alive()
|
||||
return False
|
||||
|
||||
noise_level = lt.ThingProperty(float, 2.0)
|
||||
|
||||
def _capture_frames(self):
|
||||
portal = lt.get_blocking_portal(self)
|
||||
while self._capture_enabled:
|
||||
|
|
@ -306,3 +313,8 @@ class SimulatedCamera(BaseCamera):
|
|||
action_button_for(self.load_sample, submit_label="Load Sample"),
|
||||
action_button_for(self.remove_sample, submit_label="Remove Sample"),
|
||||
]
|
||||
|
||||
@lt.thing_property
|
||||
def manual_camera_settings(self) -> list[PropertyControl]:
|
||||
"""The camera settings to expose as property controls in the settings panel."""
|
||||
return [property_control_for(self, "noise_level", label="Noise Level")]
|
||||
|
|
|
|||
|
|
@ -3,18 +3,6 @@
|
|||
from pydantic import BaseModel
|
||||
|
||||
|
||||
def action_button_for(action, **kwargs):
|
||||
"""Create an action button for the specified Thing Action.
|
||||
|
||||
:param action: The thing action to create a button for.
|
||||
:param kwargs: Any attribute of `ActionButton` except for ``thing`` or ``action``.
|
||||
"""
|
||||
thing = action.args[0]
|
||||
thing_path = thing.path.strip("/")
|
||||
action_name = action.func.__name__
|
||||
return ActionButton(thing=thing_path, action=action_name, **kwargs)
|
||||
|
||||
|
||||
class ActionButton(BaseModel):
|
||||
"""The data required for creating an actionButton in Vue.
|
||||
|
||||
|
|
@ -53,3 +41,54 @@ class ActionButton(BaseModel):
|
|||
|
||||
success_message: str = "Success!"
|
||||
"""The message to show on successful completion."""
|
||||
|
||||
|
||||
def action_button_for(action, **kwargs) -> ActionButton:
|
||||
"""Create a ActionButton data for the specified Thing Action.
|
||||
|
||||
:param action: The thing action to create a button for.
|
||||
:param kwargs: Any attribute of `ActionButton` except for ``thing`` or ``action``.
|
||||
"""
|
||||
thing = action.args[0]
|
||||
thing_path = thing.path.strip("/")
|
||||
action_name = action.func.__name__
|
||||
return ActionButton(thing=thing_path, action=action_name, **kwargs)
|
||||
|
||||
|
||||
class PropertyControl(BaseModel):
|
||||
"""The data required for creating an actionButton in Vue.
|
||||
|
||||
Currently this cannot be used to submitData, or to submitOnEvent.
|
||||
"""
|
||||
|
||||
thing: str
|
||||
"""The Thing "path" for the Thing instance."""
|
||||
|
||||
property_name: str
|
||||
"""The name of the property (or setting)."""
|
||||
|
||||
label: str
|
||||
"""The label to show in the UI"""
|
||||
|
||||
read_back: bool = False
|
||||
"""Whether or not to read back the property after setting.
|
||||
|
||||
This is useful for hardware settings that may be coerced to the closest value.
|
||||
"""
|
||||
|
||||
read_back_delay: int = 1000
|
||||
"""The delay in ms before reading back the property."""
|
||||
|
||||
|
||||
def property_control_for(thing, property_name, **kwargs) -> PropertyControl:
|
||||
"""Create an PropertyControl data for the specified Thing Property.
|
||||
|
||||
:param thing: The instance of the thing that has the property to be controlled.
|
||||
:param property_name: The name of the property to create a control for.
|
||||
:param kwargs: Any attribute of `PropertyControl` except for ``thing`` or
|
||||
``property_name``. If label is not set here it will be the property name.
|
||||
"""
|
||||
thing_path = thing.path.strip("/")
|
||||
if "label" not in kwargs:
|
||||
kwargs["label"] = property_name
|
||||
return PropertyControl(thing=thing_path, property_name=property_name, **kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue