From 2db895a1fc4cd3df4157648b4a5e15af4d6cb36b Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Tue, 3 Dec 2024 11:16:27 +0000 Subject: [PATCH 1/4] Defocus and realign simulation axes --- .../things/camera/simulation.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/openflexure_microscope_server/things/camera/simulation.py b/src/openflexure_microscope_server/things/camera/simulation.py index 51ae32e0..1e87a4fe 100644 --- a/src/openflexure_microscope_server/things/camera/simulation.py +++ b/src/openflexure_microscope_server/things/camera/simulation.py @@ -17,6 +17,7 @@ import time import cv2 import numpy as np import piexif +from scipy.ndimage import gaussian_filter from labthings_fastapi.utilities import get_blocking_portal from labthings_fastapi.decorators import thing_action, thing_property @@ -95,15 +96,15 @@ class SimulatedCamera(BaseCamera): int(y) - h // 2 : int(y) - h // 2 + h, ] -= self.sprites[int(sprite)] - def generate_image(self, pos: tuple[int, int]): + def generate_image(self, pos: tuple[int, int, int]): """Generate an image with blobs based on supplied coordinates""" cw, ch, _ = self.canvas_shape w, h, _ = self.shape tl = (int(pos[0]) - w // 2 - cw // 2, int(pos[1]) - h // 2 - ch // 2) - image = self.canvas[ + image = gaussian_filter(self.canvas[ tuple(slice(tl[i], tl[i] + self.shape[i]) for i in range(2)) + (slice(None),) - ] + ], sigma = np.abs(pos[2])/5, axes = (0,1)) if image.shape != self.shape: raise ValueError( f"Image shape {image.shape} does not match intended shape {self.shape}" @@ -125,8 +126,8 @@ class SimulatedCamera(BaseCamera): pos = self.get_stage_position() except Exception as e: print(f"Failed to get stage position: {e}") - pos = {"x": 0, "y": 0} - return self.generate_image((pos["x"] / 10, pos["y"] / 10)) + pos = {"x": 0, "y": 0, "z": 0} + return self.generate_image((pos["y"]/10, pos["x"]/10, pos["z"]/10)) def __enter__(self): self._capture_enabled = True From 41f6c8879ab45f5ce3a4c1f049e62d049c7a537b Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Thu, 5 Jun 2025 13:28:51 +0100 Subject: [PATCH 2/4] RATIO as a global, better naming and splitting of generate_image() --- .../things/camera/simulation.py | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/openflexure_microscope_server/things/camera/simulation.py b/src/openflexure_microscope_server/things/camera/simulation.py index 1e87a4fe..5446fd3f 100644 --- a/src/openflexure_microscope_server/things/camera/simulation.py +++ b/src/openflexure_microscope_server/things/camera/simulation.py @@ -30,6 +30,10 @@ from pydantic import RootModel from . import BaseCamera, JPEGBlob from ..stage import StageProtocol as Stage +# The ratio between "motor" steps and pixels +# higher related to a faster movement +RATIO = 0.2 + class ArrayModel(RootModel): """A model for an array""" @@ -98,13 +102,22 @@ class SimulatedCamera(BaseCamera): def generate_image(self, pos: tuple[int, int, int]): """Generate an image with blobs based on supplied coordinates""" - cw, ch, _ = self.canvas_shape - w, h, _ = self.shape - tl = (int(pos[0]) - w // 2 - cw // 2, int(pos[1]) - h // 2 - ch // 2) - image = gaussian_filter(self.canvas[ - tuple(slice(tl[i], tl[i] + self.shape[i]) for i in range(2)) + canvas_width, canvas_height, _ = self.canvas_shape + image_width, image_height, _ = self.shape + pos = [x * RATIO for x in pos] + top_left = ( + int(pos[0]) - image_width // 2 - canvas_width // 2, + int(pos[1]) - image_height // 2 - canvas_height // 2, + ) + focused_image = self.canvas[ + tuple(slice(top_left[i], top_left[i] + self.shape[i]) for i in range(2)) + (slice(None),) - ], sigma = np.abs(pos[2])/5, axes = (0,1)) + ] + image = gaussian_filter( + focused_image, + sigma=np.abs(pos[2]) / 5, + axes=(0, 1), + ) if image.shape != self.shape: raise ValueError( f"Image shape {image.shape} does not match intended shape {self.shape}" @@ -127,7 +140,7 @@ class SimulatedCamera(BaseCamera): except Exception as e: print(f"Failed to get stage position: {e}") pos = {"x": 0, "y": 0, "z": 0} - return self.generate_image((pos["y"]/10, pos["x"]/10, pos["z"]/10)) + return self.generate_image((pos["y"], pos["x"], pos["z"])) def __enter__(self): self._capture_enabled = True From 69db7b40412d90afda4c29303aafd05d83f378cb Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Thu, 5 Jun 2025 17:09:37 +0000 Subject: [PATCH 3/4] Apply 2 suggestion(s) to 1 file(s) Co-authored-by: Julian Stirling --- .../things/camera/simulation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/openflexure_microscope_server/things/camera/simulation.py b/src/openflexure_microscope_server/things/camera/simulation.py index 5446fd3f..d40f3eb5 100644 --- a/src/openflexure_microscope_server/things/camera/simulation.py +++ b/src/openflexure_microscope_server/things/camera/simulation.py @@ -104,15 +104,15 @@ class SimulatedCamera(BaseCamera): """Generate an image with blobs based on supplied coordinates""" canvas_width, canvas_height, _ = self.canvas_shape image_width, image_height, _ = self.shape - pos = [x * RATIO for x in pos] + pos = tuple(x * RATIO for x in pos) top_left = ( int(pos[0]) - image_width // 2 - canvas_width // 2, int(pos[1]) - image_height // 2 - canvas_height // 2, ) - focused_image = self.canvas[ - tuple(slice(top_left[i], top_left[i] + self.shape[i]) for i in range(2)) - + (slice(None),) - ] + x_slice = slice(top_left[0], top_left[0] + self.shape[0]) + y_slice = slice(top_left[1], top_left[1] + self.shape[0]) + z_slice = slice(None) + focused_image = self.canvas[(x_slice, y_slice, z_slice)] image = gaussian_filter( focused_image, sigma=np.abs(pos[2]) / 5, From 08d13715693382dd1abe2331c7eb366347c1d6a4 Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Fri, 6 Jun 2025 10:25:34 +0000 Subject: [PATCH 4/4] Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Julian Stirling --- src/openflexure_microscope_server/things/camera/simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openflexure_microscope_server/things/camera/simulation.py b/src/openflexure_microscope_server/things/camera/simulation.py index d40f3eb5..ecef1b39 100644 --- a/src/openflexure_microscope_server/things/camera/simulation.py +++ b/src/openflexure_microscope_server/things/camera/simulation.py @@ -110,7 +110,7 @@ class SimulatedCamera(BaseCamera): int(pos[1]) - image_height // 2 - canvas_height // 2, ) x_slice = slice(top_left[0], top_left[0] + self.shape[0]) - y_slice = slice(top_left[1], top_left[1] + self.shape[0]) + y_slice = slice(top_left[1], top_left[1] + self.shape[1]) z_slice = slice(None) focused_image = self.canvas[(x_slice, y_slice, z_slice)] image = gaussian_filter(