Further unification of camera array functionality
This commit is contained in:
parent
5484b51a1e
commit
7cd6b11411
4 changed files with 40 additions and 37 deletions
|
|
@ -8,7 +8,6 @@ See repository root for licensing information.
|
|||
|
||||
from __future__ import annotations
|
||||
from typing import Literal, Optional, Tuple, Any
|
||||
from types import EllipsisType
|
||||
import json
|
||||
import io
|
||||
import time
|
||||
|
|
@ -263,7 +262,7 @@ class BaseCamera(lt.Thing):
|
|||
metadata_getter: lt.deps.GetThingStates,
|
||||
logger: lt.deps.InvocationLogger,
|
||||
stream_name: str = "main",
|
||||
wait: Optional[float | EllipsisType] = ...,
|
||||
wait: Optional[float] = None,
|
||||
) -> JPEGBlob:
|
||||
"""Acquire one image from the camera as a JPEG.
|
||||
|
||||
|
|
@ -274,20 +273,15 @@ class BaseCamera(lt.Thing):
|
|||
injected.
|
||||
:param logger: LabThings InvocationLogger dependency, automatically injected.
|
||||
:param stream_name: A stream name supported by this camera.
|
||||
:param wait: (Optional, float) Set a timeout in seconds. If not set it will
|
||||
:param wait: (Optional, float) Set a timeout in seconds. If None it will
|
||||
use the default for the underlying camera.
|
||||
"""
|
||||
fname = datetime.now().strftime("%Y-%m-%d-%H%M%S.jpeg")
|
||||
directory = tempfile.TemporaryDirectory()
|
||||
jpeg_path = os.path.join(directory.name, fname)
|
||||
|
||||
# Using Ellipsis to specify no input specified. As `None` set for wait may
|
||||
# have a meaning as it does for the Picamera. If wait is Ellipsis then
|
||||
# do not specify.
|
||||
if wait is Ellipsis:
|
||||
img = self.capture_image(stream_name)
|
||||
else:
|
||||
img = self.capture_image(stream_name, wait)
|
||||
img = self.capture_image(stream_name, wait)
|
||||
|
||||
self._save_capture(
|
||||
jpeg_path=jpeg_path,
|
||||
image=img,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ class OpenCVCamera(BaseCamera):
|
|||
@lt.thing_action
|
||||
def capture_array(
|
||||
self,
|
||||
resolution: Literal["main", "full"] = "full",
|
||||
stream_name: Literal["main", "full"] = "full",
|
||||
wait: Optional[float] = None,
|
||||
) -> NDArray:
|
||||
"""Acquire one image from the camera and return as an array.
|
||||
|
||||
|
|
@ -91,7 +92,9 @@ class OpenCVCamera(BaseCamera):
|
|||
It's likely to be highly inefficient - raw and/or uncompressed captures using
|
||||
binary image formats will be added in due course.
|
||||
"""
|
||||
logging.warning(f"OpenCV camera doesn't respect {resolution} setting")
|
||||
if wait is not None:
|
||||
logging.warning("OpenCV camera has no wait option. Use None.")
|
||||
logging.warning(f"OpenCV camera doesn't respect {stream_name=}")
|
||||
ret, frame = self.cap.read()
|
||||
if not ret:
|
||||
raise RuntimeError(
|
||||
|
|
@ -108,7 +111,7 @@ class OpenCVCamera(BaseCamera):
|
|||
|
||||
This function will produce a JPEG image.
|
||||
"""
|
||||
logging.warning(
|
||||
f"Simulation camera doesn't respect {stream_name=} or {wait=} arguments."
|
||||
)
|
||||
if wait is not None:
|
||||
logging.warning("OpenCV camera has no wait option. Use None.")
|
||||
logging.warning(f"OpenCV camera doesn't respect {stream_name=}")
|
||||
return Image.fromarray(self.capture_array())
|
||||
|
|
|
|||
|
|
@ -541,12 +541,15 @@ class StreamingPiCamera2(BaseCamera):
|
|||
|
||||
:param stream_name: (Optional) The PiCamera2 stream to use, should be one of
|
||||
["main", "lores", "raw", "full"]. Default = "main"
|
||||
:param wait: (Optional, float) Set a timeout in seconds.
|
||||
|
||||
:rasises TimeoutError: if this time is exceeded during capture. Default = 0.9s,
|
||||
:param wait: (Optional, float) Set a timeout in seconds. Default = 0.9s,
|
||||
lower than the 1s timeout for the camera. This ensures that our code times
|
||||
out and returns before the camera times out.
|
||||
out and returns before the camera times out. If None is set the default
|
||||
value of 0.9 will be used to prevent the possibility of the camera locking.
|
||||
|
||||
:raises TimeoutError: if this time is exceeded during capture.
|
||||
"""
|
||||
if wait is None:
|
||||
wait = 0.9
|
||||
if stream_name in ["main", "lores", "raw"]:
|
||||
with self._streaming_picamera() as cam:
|
||||
return cam.capture_image(stream_name, wait=wait)
|
||||
|
|
@ -572,20 +575,20 @@ class StreamingPiCamera2(BaseCamera):
|
|||
It's likely to be highly inefficient - raw and/or uncompressed captures using
|
||||
binary image formats will be added in due course.
|
||||
|
||||
stream_name: (Optional) The PiCamera2 stream to use, should be one of ["main",
|
||||
"lores", "raw", "full"]. Default = "main"
|
||||
wait: (Optional, float) Set a timeout in seconds.
|
||||
A TimeoutError is raised if this time is exceeded during capture.
|
||||
Default = 0.9s, lower than the 1s timeout default in picamera yaml settings
|
||||
:param stream_name: (Optional) The PiCamera2 stream to use, should be one of
|
||||
["main", "lores", "raw", "full"]. Default = "main"
|
||||
:param wait: (Optional, float) Set a timeout in seconds. Default = 0.9s,
|
||||
lower than the 1s timeout for the camera. This ensures that our code times
|
||||
out and returns before the camera times out. If None is set the default
|
||||
value of 0.9 will be used to prevent the possibility of the camera locking.
|
||||
|
||||
:raises TimeoutError: if this time is exceeded during capture.
|
||||
"""
|
||||
# This was slower than capture_image for our use case, but directly returning
|
||||
# an image as an array is still a useful feature
|
||||
if stream_name == "full":
|
||||
with self._streaming_picamera(pause_stream=True) as picam2:
|
||||
capture_config = picam2.create_still_configuration()
|
||||
return picam2.switch_mode_and_capture_array(capture_config, wait=wait)
|
||||
with self._streaming_picamera() as cam:
|
||||
return cam.capture_array(stream_name, wait=wait)
|
||||
# Note that internally the PiCamera creates a PIL image and then converts to
|
||||
# numpy with ``np.array(Image.open(io.BytesIO(self.make_buffer(name))))``.
|
||||
# As such we use capture_image to get an Image from the picamera and return
|
||||
# as array
|
||||
return np.array(self.capture_image(stream_name, wait))
|
||||
|
||||
@lt.thing_property
|
||||
def camera_configuration(self) -> Mapping:
|
||||
|
|
|
|||
|
|
@ -277,7 +277,8 @@ class SimulatedCamera(BaseCamera):
|
|||
@lt.thing_action
|
||||
def capture_array(
|
||||
self,
|
||||
resolution: Literal["main", "full"] = "full",
|
||||
stream_name: Literal["main", "full"] = "full",
|
||||
wait: Optional[float] = None,
|
||||
) -> ArrayModel:
|
||||
"""Acquire one image from the camera and return as an array.
|
||||
|
||||
|
|
@ -285,7 +286,9 @@ class SimulatedCamera(BaseCamera):
|
|||
It's likely to be highly inefficient - raw and/or uncompressed captures using
|
||||
binary image formats will be added in due course.
|
||||
"""
|
||||
logging.warning(f"Simulation camera doesn't respect {resolution=} setting")
|
||||
if wait is not None:
|
||||
logging.warning("Simulation camera has no wait option. Use None.")
|
||||
logging.warning(f"Simulation camera camera doesn't respect {stream_name=}")
|
||||
return self.generate_frame()
|
||||
|
||||
def capture_image(
|
||||
|
|
@ -297,9 +300,9 @@ class SimulatedCamera(BaseCamera):
|
|||
|
||||
It is used for capture to memory.
|
||||
"""
|
||||
logging.warning(
|
||||
f"Simulation camera doesn't respect {stream_name=} or {wait=} arguments."
|
||||
)
|
||||
if wait is not None:
|
||||
logging.warning("Simulation camera has no wait option. Use None.")
|
||||
logging.warning(f"Simulation camera camera doesn't respect {stream_name=}")
|
||||
return Image.fromarray(self.generate_frame())
|
||||
|
||||
@lt.thing_action
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue