Skip to content

Commit c7d54dc

Browse files
authored
add_layer_from_images: allow single image folders, retry with bioformats (#829)
* add_layer_from_images: allow folders with single images, retry with bioformats * add changelog entry, update docs, add one more testcase * apply PR feedback * fix for iterables
1 parent 2c901d4 commit c7d54dc

File tree

5 files changed

+100
-38
lines changed

5 files changed

+100
-38
lines changed

webknossos/Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ For upgrade instructions, please check the respective *Breaking Changes* section
1717
### Added
1818

1919
### Changed
20+
- `Dataset.from_images` and `dataset.add_layer_from_images` now try to import the images via the [bioformats](https://www.openmicroscopy.org/bio-formats) after all other options as well. [#829](https://github.com/scalableminds/webknossos-libs/pull/829)
2021

2122
### Fixed
23+
- `dataset.add_layer_from_images` can now handle paths to folders which only contain a single image. [#829](https://github.com/scalableminds/webknossos-libs/pull/829)
2224

2325

2426
## [0.10.25](https://github.com/scalableminds/webknossos-libs/releases/tag/v0.10.25) - 2022-11-29
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../various_tiff_formats/test_C.tif

webknossos/tests/dataset/test_add_layer_from_images.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def test_compare_tifffile(tmp_path: Path) -> None:
6262
(64, 64, 2),
6363
),
6464
(
65-
"testdata/rgb_tiff/test_rgb.tif",
65+
"testdata/rgb_tiff",
6666
{"mag": 2, "channel": 1, "dtype": "uint32"},
6767
"uint32",
6868
1,
@@ -75,6 +75,16 @@ def test_compare_tifffile(tmp_path: Path) -> None:
7575
1,
7676
(1024, 1024, 12),
7777
),
78+
(
79+
"testdata/temca2",
80+
{"flip_z": True, "batch_size": 2048},
81+
"uint8",
82+
1,
83+
# The topmost folder contains an extra image,
84+
# which is included here as well, but not in
85+
# the glob pattern above. Therefore z is +1.
86+
(1024, 1024, 13),
87+
),
7888
(
7989
"testdata/tiff_with_different_dimensions/*",
8090
{"flip_y": True},
@@ -84,6 +94,8 @@ def test_compare_tifffile(tmp_path: Path) -> None:
8494
),
8595
("testdata/various_tiff_formats/test_CS.tif", {}, "uint8", 3, (128, 128, 320)),
8696
("testdata/various_tiff_formats/test_C.tif", {}, "uint8", 1, (128, 128, 320)),
97+
# same as test_C.tif above, but as a single file in a folder:
98+
("testdata/single_multipage_tiff_folder", {}, "uint8", 1, (128, 128, 320)),
8799
("testdata/various_tiff_formats/test_I.tif", {}, "uint32", 1, (64, 128, 64)),
88100
("testdata/various_tiff_formats/test_S.tif", {}, "uint16", 3, (128, 128, 64)),
89101
]

webknossos/webknossos/dataset/_utils/pims_images.py

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@
3232

3333
try:
3434
import pims
35-
except ImportError as e:
35+
except ImportError as import_error:
3636
raise RuntimeError(
3737
"Cannot import pims, please install it e.g. using 'webknossos[all]'"
38-
) from e
38+
) from import_error
3939

4040

4141
# Fix ImageIOReader not handling channels correctly. This might get fixed via
@@ -280,6 +280,71 @@ def __init__(
280280
self.num_channels = 3
281281
self._first_n_channels = 3
282282

283+
def _normalize_original_images(self) -> Union[str, List[str]]:
284+
original_images = self._original_images
285+
if isinstance(original_images, (str, Path)):
286+
original_images_path = Path(original_images)
287+
if original_images_path.is_dir():
288+
valid_suffixes = get_valid_pims_suffixes()
289+
original_images = [
290+
str(i)
291+
for i in original_images_path.glob("**/*")
292+
if i.is_file() and i.suffix.lstrip(".") in valid_suffixes
293+
]
294+
if len(original_images) == 1:
295+
original_images = original_images[0]
296+
if isinstance(original_images, str):
297+
return original_images
298+
elif isinstance(original_images, Iterable):
299+
return [str(i) for i in original_images]
300+
else:
301+
return str(original_images)
302+
303+
def _open_bioformats_images_raw(
304+
self,
305+
original_images: Union[str, List[str]],
306+
previous_exceptions: List[Exception],
307+
) -> pims.FramesSequence:
308+
try:
309+
# There is a wrong warning about jpype, supressing it here.
310+
# See issue https://github.com/soft-matter/pims/issues/384
311+
warnings.filterwarnings(
312+
"ignore",
313+
"Due to an issue with JPype 0.6.0, reading is slower.*",
314+
category=UserWarning,
315+
module="pims.bioformats",
316+
)
317+
try:
318+
pims.bioformats._find_jar()
319+
except HTTPError:
320+
# We cannot use the newest bioformats version,
321+
# since it does not include the necessary loci_tools.jar.
322+
# Updates to support newer bioformats jars with pims are in PR
323+
# https://github.com/soft-matter/pims/pull/403
324+
pims.bioformats.download_jar(version="6.7.0")
325+
326+
if "*" in str(original_images) or isinstance(original_images, list):
327+
images_context_manager = pims.ReaderSequence(
328+
original_images, pims.bioformats.BioformatsReader
329+
)
330+
else:
331+
images_context_manager = pims.bioformats.BioformatsReader(
332+
original_images
333+
)
334+
except Exception as e:
335+
if len(previous_exceptions) == 0:
336+
raise e
337+
else:
338+
previous_exceptions.append(e)
339+
previous_exceptions_str = "\n".join(
340+
f"{type(e).__name__}: {str(e)}" for e in previous_exceptions
341+
)
342+
raise ValueError(
343+
f"Tried to open the images {self._original_images} with different methods, "
344+
+ f"none succeded. The following errors were raised:\n{previous_exceptions_str}"
345+
) from None
346+
return images_context_manager
347+
283348
@contextmanager
284349
def _open_images(
285350
self,
@@ -290,48 +355,29 @@ def _open_images(
290355
For a 2D image this is achieved by wrapping it in a list.
291356
"""
292357
with warnings.catch_warnings():
293-
294358
if isinstance(self._original_images, pims.FramesSequence):
295359
images_context_manager = nullcontext(enter_result=self._original_images)
296360
else:
297-
if self._use_bioformats:
298-
# There is a wrong warning about jpype, supressing it here.
299-
# See issue https://github.com/soft-matter/pims/issues/384
300-
warnings.filterwarnings(
301-
"ignore",
302-
"Due to an issue with JPype 0.6.0, reading is slower.*",
303-
category=UserWarning,
304-
module="pims.bioformats",
305-
)
306-
try:
307-
pims.bioformats._find_jar()
308-
except HTTPError:
309-
# We cannot use the newest bioformats version,
310-
# since it does not include the necessary loci_tools.jar.
311-
# Updates to support newer bioformats jars with pims are in PR
312-
# https://github.com/soft-matter/pims/pull/403
313-
pims.bioformats.download_jar(version="6.7.0")
314-
if "*" in str(self._original_images) or isinstance(
315-
self._original_images, list
316-
):
317-
images_context_manager = pims.ReaderSequence(
318-
self._original_images, pims.bioformats.BioformatsReader
319-
)
320-
else:
321-
images_context_manager = pims.bioformats.BioformatsReader(
322-
self._original_images
323-
)
324-
else:
325-
original_images = self._original_images
326-
if isinstance(original_images, Path):
327-
original_images = str(original_images)
361+
pims_open_exceptions = []
362+
original_images = self._normalize_original_images()
363+
if not self._use_bioformats:
328364
try:
329365
open_kwargs = {}
330366
if self._czi_channel is not None:
331367
open_kwargs["czi_channel"] = self._czi_channel
332368
images_context_manager = pims.open(original_images)
333-
except Exception:
334-
images_context_manager = pims.ImageSequence(original_images)
369+
except Exception as e1:
370+
pims_open_exceptions.append(e1)
371+
try:
372+
images_context_manager = pims.ImageSequence(original_images)
373+
except Exception as e2:
374+
pims_open_exceptions.append(e2)
375+
self._use_bioformats = True
376+
377+
if self._use_bioformats:
378+
images_context_manager = self._open_bioformats_images_raw(
379+
original_images, pims_open_exceptions
380+
)
335381

336382
with images_context_manager as images:
337383
if isinstance(images, pims.FramesSequenceND):

webknossos/webknossos/dataset/dataset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,7 +1020,8 @@ def add_layer_from_images(
10201020
* `swap_xy`: set to `True` to interchange x and y axis before writing to disk
10211021
* `flip_x`, `flip_y`, `flip_z`: set to `True` to reverse the respective axis before writing to disk
10221022
* `dtype`: the read image data will be convertoed to this dtype using `numpy.ndarray.astype`
1023-
* `use_bioformats`: set to `True` to use the [pims bioformats adapter](https://soft-matter.github.io/pims/v0.6.1/bioformats.html), needs a JVM
1023+
* `use_bioformats`: set to `True` to only use the
1024+
[pims bioformats adapter](https://soft-matter.github.io/pims/v0.6.1/bioformats.html) directly, needs a JVM
10241025
* `channel`: may be used to select a single channel, if multiple are available
10251026
* `timepoint`: for timeseries, select a timepoint to use by specifying it as an int, starting from 0
10261027
* `czi_channel`: may be used to select a channel for .czi images, which differs from normal color-channels

0 commit comments

Comments
 (0)