Merge branch 'v3-png-capture' into 'v3'
PNG Capture See merge request openflexure/openflexure-microscope-server!569
This commit is contained in:
commit
1e3548e803
3 changed files with 51 additions and 16 deletions
Binary file not shown.
|
|
@ -437,13 +437,44 @@ class BaseCamera(OFMThing):
|
|||
capture_metadata = self._capture_metadata()
|
||||
|
||||
self._save_capture(
|
||||
jpeg_path=jpeg_path,
|
||||
path=jpeg_path,
|
||||
image=img,
|
||||
metadata=capture_metadata,
|
||||
)
|
||||
|
||||
return JPEGBlob.from_temporary_directory(directory, fname)
|
||||
|
||||
@lt.action
|
||||
def capture_png(
|
||||
self,
|
||||
stream_name: Literal["main", "lores", "full"] = "main",
|
||||
wait: Optional[float] = None,
|
||||
) -> PNGBlob:
|
||||
"""Acquire one image from the camera as a PNG.
|
||||
|
||||
This will use the internal capture image functionally of capture_image of
|
||||
the specific camera being used.
|
||||
|
||||
:param stream_name: A stream name supported by this camera.
|
||||
: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()
|
||||
png_path = os.path.join(directory.name, fname)
|
||||
|
||||
img = self.capture_image(stream_name, wait)
|
||||
|
||||
capture_metadata = self._capture_metadata()
|
||||
|
||||
self._save_capture(
|
||||
path=png_path,
|
||||
image=img,
|
||||
metadata=capture_metadata,
|
||||
)
|
||||
|
||||
return PNGBlob.from_temporary_directory(directory, fname)
|
||||
|
||||
@lt.action
|
||||
def grab_jpeg(
|
||||
self,
|
||||
|
|
@ -564,7 +595,7 @@ class BaseCamera(OFMThing):
|
|||
image, metadata = self._memory_buffer.get_image(buffer_id)
|
||||
|
||||
self._save_capture(
|
||||
jpeg_path=jpeg_path,
|
||||
path=jpeg_path,
|
||||
image=image,
|
||||
metadata=metadata,
|
||||
save_resolution=save_resolution,
|
||||
|
|
@ -656,7 +687,7 @@ class BaseCamera(OFMThing):
|
|||
|
||||
def _save_capture(
|
||||
self,
|
||||
jpeg_path: str,
|
||||
path: str,
|
||||
image: Image.Image,
|
||||
metadata: Mapping[str, Any],
|
||||
save_resolution: Optional[Tuple[int, int]] = None,
|
||||
|
|
@ -672,22 +703,25 @@ class BaseCamera(OFMThing):
|
|||
if save_resolution is not None and image.size != save_resolution:
|
||||
image = image.resize(save_resolution, Image.Resampling.BOX)
|
||||
try:
|
||||
# Per PIL documentation,
|
||||
# (https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg)
|
||||
# there are two factors when saving a JPEG. Subsampling affects the colour,
|
||||
# quality affects the pixels.
|
||||
# subsampling = 0 disables subsampling of colour
|
||||
# quality = 95 is the maximum recommended - above this, JPEG compression is
|
||||
# disabled, file size increases and quality is barely or not affected
|
||||
image.save(jpeg_path, quality=95, subsampling=0)
|
||||
save_kwargs: dict[str, Any] = {}
|
||||
if path.endswith("jpg"):
|
||||
# Per PIL documentation,
|
||||
# (https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg)
|
||||
# there are two factors when saving a JPEG. Subsampling affects the colour,
|
||||
# quality affects the pixels.
|
||||
# subsampling = 0 disables subsampling of colour
|
||||
# quality = 95 is the maximum recommended - above this, JPEG compression is
|
||||
# disabled, file size increases and quality is barely or not affected
|
||||
save_kwargs = {"quality": 95, "subsampling": 0}
|
||||
image.save(path, **save_kwargs)
|
||||
try:
|
||||
self._add_metadata_to_capture(jpeg_path, dict(metadata))
|
||||
self._add_metadata_to_capture(path, dict(metadata))
|
||||
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.
|
||||
self.logger.exception(f"Failed to add metadata to {jpeg_path}")
|
||||
self.logger.exception(f"Failed to add metadata to {path}")
|
||||
except Exception as e:
|
||||
raise IOError(f"An error occurred while saving {jpeg_path}") from e
|
||||
raise IOError(f"An error occurred while saving {path}") from e
|
||||
|
||||
settling_time: float = lt.setting(default=0.2, ge=0)
|
||||
"""The settling time when calling the ``settle()`` method."""
|
||||
|
|
|
|||
|
|
@ -40,10 +40,11 @@ def test_grab_jpeg(simulation_test_env):
|
|||
assert image.size == (820, 616)
|
||||
|
||||
|
||||
def test_capture_jpeg_metadata(simulation_test_env):
|
||||
@pytest.mark.parametrize("fmt", ["jpeg", "png"])
|
||||
def test_capture_and_metadata(simulation_test_env, fmt):
|
||||
"""Check that the position is encoded into the image metadata."""
|
||||
camera = simulation_test_env.get_thing_client("camera")
|
||||
blob = camera.capture_jpeg()
|
||||
blob = getattr(camera, f"capture_{fmt}")()
|
||||
image = Image.open(blob.open())
|
||||
exif_dict = piexif.load(image.info["exif"])
|
||||
encoded_metadata = exif_dict["Exif"][piexif.ExifIFD.UserComment]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue