Unify JPEG capture, metadata, and saving; reducing duplication
This commit is contained in:
parent
4450fda945
commit
5484b51a1e
5 changed files with 83 additions and 135 deletions
|
|
@ -8,10 +8,14 @@ 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
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
import numpy as np
|
||||
from pydantic import RootModel
|
||||
|
|
@ -257,14 +261,42 @@ class BaseCamera(lt.Thing):
|
|||
def capture_jpeg(
|
||||
self,
|
||||
metadata_getter: lt.deps.GetThingStates,
|
||||
resolution: Literal["lores", "main", "full"] = "main",
|
||||
wait: Optional[float] = 5,
|
||||
logger: lt.deps.InvocationLogger,
|
||||
stream_name: str = "main",
|
||||
wait: Optional[float | EllipsisType] = ...,
|
||||
) -> JPEGBlob:
|
||||
"""Acquire one image from the camera and return as a JPEG blob."""
|
||||
raise NotImplementedError(
|
||||
"CameraThings must define their own capture_jpeg method"
|
||||
"""Acquire one image from the camera as a JPEG.
|
||||
|
||||
This will use the internal capture image functionally of capture_image if
|
||||
the specific camera being used.
|
||||
|
||||
:param metadata_getter: LabThings GetThingStates dependency, automatically
|
||||
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
|
||||
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)
|
||||
self._save_capture(
|
||||
jpeg_path=jpeg_path,
|
||||
image=img,
|
||||
metadata=metadata_getter(),
|
||||
logger=logger,
|
||||
)
|
||||
|
||||
return JPEGBlob.from_temporary_directory(directory, fname)
|
||||
|
||||
@lt.thing_action
|
||||
def grab_jpeg(
|
||||
self,
|
||||
|
|
@ -331,7 +363,7 @@ class BaseCamera(lt.Thing):
|
|||
def capture_image(
|
||||
self,
|
||||
stream_name: Literal["main", "lores", "raw"],
|
||||
wait: Optional[float],
|
||||
wait: Optional[float] = None,
|
||||
) -> Image:
|
||||
"""Capture a PIL image from stream stream_name with timeout wait."""
|
||||
raise NotImplementedError(
|
||||
|
|
@ -486,10 +518,10 @@ class BaseCamera(lt.Thing):
|
|||
metadata
|
||||
).encode("utf-8")
|
||||
piexif.insert(piexif.dump(exif_dict), jpeg_path)
|
||||
except: # noqa: E722
|
||||
except Exception:
|
||||
# We need to capture any exception as there are many reasons metadata
|
||||
# might not be added. We warn rather than log the error.
|
||||
logger.warning(f"Failed to add metadata to {jpeg_path}")
|
||||
logger.exception(f"Failed to add metadata to {jpeg_path}")
|
||||
except Exception as e:
|
||||
raise IOError(f"An error occurred while saving {jpeg_path}") from e
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue