From 2ab6999e8a68ca853cb163a4cf5bf8f053dc3120 Mon Sep 17 00:00:00 2001 From: Alex Nimmo Smith Date: Thu, 18 Jun 2026 13:37:05 +0100 Subject: [PATCH 1/3] Use FilesToProcess instead of raw glob in holo.Initial glob() doesn't understand the .txt filelist convention supported by pyopia.pipeline.FilesToProcess, so pointing raw_files at an explicit filelist caused holo.Initial to try loading the .txt file itself as a hologram image. Fixes #388 --- pyopia/instrument/holo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyopia/instrument/holo.py b/pyopia/instrument/holo.py index 29d2a72..9301c4c 100644 --- a/pyopia/instrument/holo.py +++ b/pyopia/instrument/holo.py @@ -19,10 +19,10 @@ from skimage.io import imread from skimage.filters import sobel from skimage.morphology import disk, erosion, dilation +import pyopia.pipeline import pyopia.process import struct from datetime import timedelta -from glob import glob import logging logger = logging.getLogger() @@ -65,8 +65,8 @@ def __init__(self, wavelength, n, offset, minZ, maxZ, stepZ): def __call__(self, data): logger.info('Using first raw file from list in general settings to determine image dimensions') - raw_files = glob(data['settings']['general']['raw_files']) - self.filename = raw_files[0] + raw_files = pyopia.pipeline.FilesToProcess(data['settings']['general']['raw_files']) + self.filename = raw_files.files[0] imtmp = load_image(self.filename) self.pixel_size = data['settings']['general']['pixel_size'] logger.info(f'Build kernel with pixel_size = {self.pixel_size} um') From 0e712e31505b8786f67caddc4bb52cd6c4e05411 Mon Sep 17 00:00:00 2001 From: Alex Nimmo Smith Date: Thu, 18 Jun 2026 13:43:40 +0100 Subject: [PATCH 2/3] Bump version to 2.16.7 --- pyopia/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyopia/__init__.py b/pyopia/__init__.py index c4ed6cf..b15cdcb 100644 --- a/pyopia/__init__.py +++ b/pyopia/__init__.py @@ -1 +1 @@ -__version__ = "2.16.6" +__version__ = "2.16.7" From b4da1df59ff20e2e6c38c81b28275f61f07cee7c Mon Sep 17 00:00:00 2001 From: Alex Nimmo Smith Date: Thu, 18 Jun 2026 16:06:43 +0100 Subject: [PATCH 3/3] Build holo reconstruction kernel lazily instead of via file globbing Per nepstad's feedback on #388: rather than peeking at the raw file list just to determine image dimensions, store the reconstruction parameters on the pipeline data in Initial, and build kern/im_stack lazily inside Reconstruct the first time it runs, using the dimensions of the image actually loaded by the pipeline. This removes the need to list/glob raw files at all during holo setup, which is a more direct fix for the reported bug than swapping to FilesToProcess. Initial's public config interface (wavelength, n, offset, minZ, maxZ, stepZ) is unchanged, so no existing config.toml files or notebooks need updating. --- pyopia/instrument/holo.py | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/pyopia/instrument/holo.py b/pyopia/instrument/holo.py index 9301c4c..7e63743 100644 --- a/pyopia/instrument/holo.py +++ b/pyopia/instrument/holo.py @@ -19,7 +19,6 @@ from skimage.io import imread from skimage.filters import sobel from skimage.morphology import disk, erosion, dilation -import pyopia.pipeline import pyopia.process import struct from datetime import timedelta @@ -31,6 +30,11 @@ class Initial(): '''PyOpia pipline-compatible class for one-time setup of holograhic reconstruction + Stores the reconstruction parameters needed to build the reconstruction kernel. + The kernel itself is built lazily on the first call to :class:`Reconstruct`, using the + dimensions of the first hologram actually loaded by the pipeline. This avoids needing to + glob or otherwise list the raw files here just to peek at one for its dimensions. + Parameters ---------- wavelength : float @@ -48,11 +52,10 @@ class Initial(): Returns ------- - kern : np.arry - reconstruction kernel - im_stack : np.array - pre-allocated array to receive reconstruction + data : :class:`pyopia.pipeline.Data` + containing the following new keys: + :attr:`pyopia.pipeline.Data.holo_recon_params` ''' def __init__(self, wavelength, n, offset, minZ, maxZ, stepZ): @@ -64,17 +67,15 @@ def __init__(self, wavelength, n, offset, minZ, maxZ, stepZ): self.stepZ = stepZ def __call__(self, data): - logger.info('Using first raw file from list in general settings to determine image dimensions') - raw_files = pyopia.pipeline.FilesToProcess(data['settings']['general']['raw_files']) - self.filename = raw_files.files[0] - imtmp = load_image(self.filename) - self.pixel_size = data['settings']['general']['pixel_size'] - logger.info(f'Build kernel with pixel_size = {self.pixel_size} um') - kern = create_kernel(imtmp, self.pixel_size, self.wavelength, self.n, self.offset, self.minZ, self.maxZ, self.stepZ) - im_stack = np.zeros(np.shape(kern)).astype(np.float64) - logger.info('HoloInitial done') - data['kern'] = kern - data['im_stack'] = im_stack + data['holo_recon_params'] = { + 'wavelength': self.wavelength, + 'n': self.n, + 'offset': self.offset, + 'minZ': self.minZ, + 'maxZ': self.maxZ, + 'stepZ': self.stepZ, + } + logger.info('HoloInitial done. Kernel will be built on first call to Reconstruct.') return data @@ -135,6 +136,11 @@ class Reconstruct(): Required keys in :class:`pyopia.pipeline.Data`: - :attr:`pyopia.pipeline.Data.im_corrected` + - :attr:`pyopia.pipeline.Data.holo_recon_params` (set by :class:`Initial`) + + On the first call, builds the reconstruction kernel and image stack from the dimensions + of `im_corrected` and stores them on `data['kern']` / `data['im_stack']` for reuse on + subsequent calls. Parameters ---------- @@ -160,6 +166,14 @@ def __init__(self, stack_clean=0, forward_filter_option=0, inverse_output_option def __call__(self, data): imc = data['im_corrected'] + + if 'kern' not in data: + pixel_size = data['settings']['general']['pixel_size'] + logger.info(f'Build kernel with pixel_size = {pixel_size} um') + kern = create_kernel(imc, pixel_size, **data['holo_recon_params']) + data['kern'] = kern + data['im_stack'] = np.zeros(np.shape(kern)).astype(np.float64) + kern = data['kern'] im_stack = data['im_stack']