From 8502d79888eba377c65c08f88d1cc431de45c1a4 Mon Sep 17 00:00:00 2001 From: Roy Hoitink Date: Mon, 18 Mar 2024 10:13:49 +0100 Subject: [PATCH 1/4] Updated CITATION.cff file --- CITATION.cff | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 02e093b..12e8482 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,10 +2,10 @@ # Visit https://bit.ly/cffinit to generate yours today! cff-version: 1.2.0 -title: Simulated Microscopy -message: >- - If you use this software, please cite it using the - metadata from this file. +title: >- + Simulated Microscopy: A Python Package for Simulation of + Confocal Microscopy Data based on Particle Coordinates +message: 'If you use this software, please cite using the Zenodo DOI.' type: software authors: - given-names: Leroy Daniƫl @@ -15,6 +15,10 @@ authors: Soft Condensed Matter & Biophysics, Debye Institute for Nanomaterials Science, Utrecht University orcid: 'https://orcid.org/0000-0002-3470-0078' +identifiers: + - type: doi + value: 10.5281/zenodo.10678180 + description: Zenodo repository-code: 'https://github.com/rhoitink/simulatedmicroscopy' url: 'https://rhoitink.github.io/simulatedmicroscopy/' abstract: >- @@ -28,5 +32,5 @@ keywords: - point spread function - image generation license: MIT -version: "v1.6.1" +version: v1.6.1 date-released: '2024-02-19' From a863b84c4eb1252ad0996b439dd2d683363c870d Mon Sep 17 00:00:00 2001 From: Roy Hoitink Date: Mon, 18 Mar 2024 11:08:13 +0100 Subject: [PATCH 2/4] Added support for cupy convolution if installed --- src/simulatedmicroscopy/image.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/simulatedmicroscopy/image.py b/src/simulatedmicroscopy/image.py index e91aab0..762f554 100644 --- a/src/simulatedmicroscopy/image.py +++ b/src/simulatedmicroscopy/image.py @@ -6,7 +6,6 @@ import h5py import numpy as np -import scipy.signal import skimage.measure from .input import Coordinates @@ -398,9 +397,21 @@ def convolve(self, other: type[Image]) -> type[Image]: "Cannot convolve images with different pixel sizes" ) - self.image = scipy.signal.convolve( - self.image, other.image, mode="same" - ) + try: + import cupy as cp + from cupyx.scipy import signal as cusignal + + self.image = cusignal.fftconvolve( + cp.asarray(self.image), cp.asarray(other.image), mode="same" + ).get() + except ImportError: + # resort to scipy if cupy is not available + + import scipy.signal + + self.image = scipy.signal.convolve( + self.image, other.image, mode="same" + ) self.is_convolved = True self.metadata["is_convolved"] = True From 5cdbc236ce0beaedf1a1f1f694b5d729b14fedd6 Mon Sep 17 00:00:00 2001 From: Roy Hoitink Date: Wed, 20 Mar 2024 09:22:56 +0100 Subject: [PATCH 3/4] Automatically find root element for HuygensImage, fixes #13 --- src/simulatedmicroscopy/image.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/simulatedmicroscopy/image.py b/src/simulatedmicroscopy/image.py index e91aab0..e4401b6 100644 --- a/src/simulatedmicroscopy/image.py +++ b/src/simulatedmicroscopy/image.py @@ -566,9 +566,18 @@ def __init__(self, filename: str | Path) -> None: raise FileNotFoundError("Requested file does not exist") with h5py.File(filepath, "r") as f: - image = np.squeeze(f[filepath.stem + "/ImageData/Image"][()]) + root_elements = list(f.keys()) + if len(root_elements) == 1: + root_element = root_elements[0] + else: + # find root element with ImageData + for re in root_elements: + if "ImageData" in f[re].keys(): + root_element = re + break + image = np.squeeze(f[root_element + "/ImageData/Image"][()]) pixel_sizes = [ - float(f[filepath.stem + f"/ImageData/DimensionScale{dim}"][()]) + float(f[root_element + f"/ImageData/DimensionScale{dim}"][()]) for dim in list("ZYX") ] From 5b6d875804136212d0cd7ecae324179bc1aa9506 Mon Sep 17 00:00:00 2001 From: Roy Hoitink Date: Wed, 20 Mar 2024 09:43:07 +0100 Subject: [PATCH 4/4] Improve loading of h5file, automatically detect format --- src/simulatedmicroscopy/image.py | 48 ++++++++++++++++++++++++-------- tests/test_image.py | 21 +++++++++++++- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/simulatedmicroscopy/image.py b/src/simulatedmicroscopy/image.py index e4401b6..5858323 100644 --- a/src/simulatedmicroscopy/image.py +++ b/src/simulatedmicroscopy/image.py @@ -162,7 +162,7 @@ def save_h5file( @classmethod def load_h5file(cls, filename: str) -> type[Image]: - """Load data from h5 file (custom format) + """Load data from h5 file (custom format or HuygensImage) Parameters ---------- @@ -176,19 +176,40 @@ def load_h5file(cls, filename: str) -> type[Image]: """ with h5py.File(filename, "r") as f: - image = f["Image"][()] - pixel_sizes = [ - float(f[f"Metadata/DimensionScale{dim.upper()}"][()]) - for dim in list("zyx") - ] - if "PixelCoordinates" in f["Metadata"]: - pixel_coordinates = f["Metadata/PixelCoordinates"][()] + root_elements = list(f.keys()) + if "Image" in root_elements and "Metadata" in root_elements: + return cls._load_h5file_custom(f) else: - pixel_coordinates = None + return HuygensImage(filename) + + @staticmethod + def _load_h5file_custom(f: h5py.File) -> type[Image]: + """Load data from h5 file (custom format) + + Parameters + ---------- + f : h5py.File + File to load from + + Returns + ------- + Image + Resulting image with correct pixel sizes + """ + + image = f["Image"][()] + pixel_sizes = [ + float(f[f"Metadata/DimensionScale{dim.upper()}"][()]) + for dim in list("zyx") + ] + if "PixelCoordinates" in f["Metadata"]: + pixel_coordinates = f["Metadata/PixelCoordinates"][()] + else: + pixel_coordinates = None - metadata = dict(f["Metadata"].attrs) + metadata = dict(f["Metadata"].attrs) - im = cls(image=image, pixel_sizes=pixel_sizes, metadata=metadata) + im = Image(image=image, pixel_sizes=pixel_sizes, metadata=metadata) if pixel_coordinates is not None: im.pixel_coordinates = pixel_coordinates return im @@ -571,10 +592,15 @@ def __init__(self, filename: str | Path) -> None: root_element = root_elements[0] else: # find root element with ImageData + root_element = None for re in root_elements: if "ImageData" in f[re].keys(): root_element = re break + if root_element is None: + raise ValueError( + "No ImageData found in the file, cannot load image" + ) image = np.squeeze(f[root_element + "/ImageData/Image"][()]) pixel_sizes = [ float(f[root_element + f"/ImageData/DimensionScale{dim}"][()]) diff --git a/tests/test_image.py b/tests/test_image.py index 6c429be..c971da5 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -1,6 +1,6 @@ import numpy as np import pytest -from simulatedmicroscopy import HuygensImage, Image +from simulatedmicroscopy import HuygensImage, Image, HuygensPSF def create_demo_image(): @@ -40,6 +40,25 @@ def test_huygens_notexisting(tmp_path): with pytest.raises(FileNotFoundError): HuygensImage(tmp_path / "thisfiledoesnotexist.h5") +def test_huygens_loading(tmp_path): + import h5py + im = create_demo_image() + pixel_sizes = im.get_pixel_sizes() + filename = tmp_path / "testfile.hdf5" + with h5py.File(filename, "w") as f: + root = f.create_group("testfile") + root.create_dataset("ImageData/Image", data=im.image) + [root.create_dataset(f"ImageData/DimensionScale{dim}", data=pixel_sizes[i]) for i,dim in enumerate(list("ZYX"))] + + + im_loaded = im.load_h5file(filename) + im_loaded_huygens_im = HuygensImage(filename) + im_loaded_huygens_psf = HuygensPSF(filename) + + + assert im_loaded == im + assert im_loaded_huygens_im == im + assert im_loaded_huygens_psf == im @pytest.mark.parametrize( "unit,conversionfactor",