image2image-io 0.1.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. image2image_io/__init__.py +11 -0
  2. image2image_io/_version.py +21 -0
  3. image2image_io/cli/__init__.py +100 -0
  4. image2image_io/cli/_common.py +40 -0
  5. image2image_io/cli/convert.py +90 -0
  6. image2image_io/cli/czi2tiff.py +99 -0
  7. image2image_io/cli/merge.py +99 -0
  8. image2image_io/cli/thumbnail.py +54 -0
  9. image2image_io/cli/transform.py +305 -0
  10. image2image_io/config.py +174 -0
  11. image2image_io/enums.py +51 -0
  12. image2image_io/exceptions.py +6 -0
  13. image2image_io/merge.py +55 -0
  14. image2image_io/models/__init__.py +7 -0
  15. image2image_io/models/base.py +71 -0
  16. image2image_io/models/merge.py +82 -0
  17. image2image_io/models/preprocess.py +50 -0
  18. image2image_io/models/transform.py +223 -0
  19. image2image_io/py.typed +0 -0
  20. image2image_io/readers/__init__.py +806 -0
  21. image2image_io/readers/_base_reader.py +767 -0
  22. image2image_io/readers/_czi.py +351 -0
  23. image2image_io/readers/array_reader.py +45 -0
  24. image2image_io/readers/coordinate_reader.py +195 -0
  25. image2image_io/readers/czi_reader.py +177 -0
  26. image2image_io/readers/geojson_utils.py +600 -0
  27. image2image_io/readers/points_reader.py +122 -0
  28. image2image_io/readers/shapes_reader.py +343 -0
  29. image2image_io/readers/tiff_reader.py +159 -0
  30. image2image_io/readers/tiff_utils.py +112 -0
  31. image2image_io/readers/utilities.py +428 -0
  32. image2image_io/utils/__init__.py +0 -0
  33. image2image_io/utils/_appdirs.py +13 -0
  34. image2image_io/utils/_test.py +110 -0
  35. image2image_io/utils/itk.py +116 -0
  36. image2image_io/utils/lazy.py +66 -0
  37. image2image_io/utils/mask.py +286 -0
  38. image2image_io/utils/utilities.py +264 -0
  39. image2image_io/utils/warp.py +140 -0
  40. image2image_io/wrapper.py +286 -0
  41. image2image_io/writers/__init__.py +628 -0
  42. image2image_io/writers/merge_tiff_writer.py +445 -0
  43. image2image_io/writers/tiff_writer.py +769 -0
  44. image2image_io-0.1.10.dist-info/METADATA +176 -0
  45. image2image_io-0.1.10.dist-info/RECORD +49 -0
  46. image2image_io-0.1.10.dist-info/WHEEL +5 -0
  47. image2image_io-0.1.10.dist-info/entry_points.txt +2 -0
  48. image2image_io-0.1.10.dist-info/licenses/LICENSE +29 -0
  49. image2image_io-0.1.10.dist-info/top_level.txt +1 -0
@@ -0,0 +1,11 @@
1
+ """Various image readers."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("image2image-io")
7
+ except PackageNotFoundError:
8
+ __version__ = "uninstalled"
9
+
10
+ __author__ = "Lukasz G. Migas"
11
+ __email__ = "lukas.migas@yahoo.com"
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '0.1.10'
21
+ __version_tuple__ = version_tuple = (0, 1, 10)
@@ -0,0 +1,100 @@
1
+ """Main CLI."""
2
+
3
+ import click
4
+ import koyo.compat
5
+ from loguru import logger
6
+
7
+ from image2image_io import __version__
8
+ from image2image_io.cli.convert import convert
9
+ from image2image_io.cli.czi2tiff import czi2tiff
10
+ from image2image_io.cli.merge import merge
11
+ from image2image_io.cli.thumbnail import thumbnail
12
+ from image2image_io.cli.transform import transform
13
+
14
+ LOG_FMT = "[<level>{level: <8}</level>][{time:YYYY-MM-DD HH:mm:ss:SSS}][{extra[src]}] {message}"
15
+ COLOR_LOG_FMT = (
16
+ "<green>[<level>{level: <8}</level>]</green>"
17
+ "<cyan>[{time:YYYY-MM-DD HH:mm:ss:SSS}]</cyan>"
18
+ "<red>[{process}]</red>"
19
+ "<blue>[{extra[src]}]</blue>"
20
+ " {message}"
21
+ )
22
+
23
+
24
+ @click.group(
25
+ context_settings={
26
+ "help_option_names": ["-h", "--help"],
27
+ "max_content_width": 120,
28
+ "ignore_unknown_options": True,
29
+ }
30
+ )
31
+ @click.option(
32
+ "--dev",
33
+ help="Flat to indicate that CLI should run in development mode and catch all errors.",
34
+ default=False,
35
+ is_flag=True,
36
+ show_default=True,
37
+ )
38
+ @click.option(
39
+ "--no_color",
40
+ help="Flag to enable colored while doing tasks.",
41
+ default=False,
42
+ is_flag=True,
43
+ show_default=True,
44
+ )
45
+ @click.version_option(__version__)
46
+ @click.option(
47
+ "--verbose",
48
+ "-v",
49
+ "verbosity",
50
+ default=1,
51
+ count=True,
52
+ help="Verbose output. This is additive flag so `-vvv` will print `INFO` messages and -vvvv will print `DEBUG`"
53
+ " information.",
54
+ )
55
+ @click.option("--quiet", "-q", "verbosity", flag_value=0, help="Minimal output")
56
+ @click.option("--debug", "verbosity", flag_value=0.5, help="Maximum output")
57
+ def cli(verbosity: int, no_color: bool, dev: bool) -> None:
58
+ """image2image-io CLI."""
59
+ from koyo.hooks import install_debugger_hook, uninstall_debugger_hook
60
+ from koyo.logging import get_loguru_config, set_loguru_env, set_loguru_log
61
+
62
+ verbosity = 2 - int(verbosity) # default is INFO
63
+ if verbosity < 0:
64
+ verbosity = 0
65
+ if dev:
66
+ install_debugger_hook()
67
+ verbosity = 0
68
+ elif dev:
69
+ uninstall_debugger_hook()
70
+ level = verbosity * 10
71
+ level, fmt, colorize, enqueue = get_loguru_config(level, no_color=no_color)
72
+ set_loguru_env(fmt, level, colorize, enqueue)
73
+ set_loguru_log(
74
+ level=level.upper(),
75
+ no_color=no_color,
76
+ logger=logger,
77
+ fmt=LOG_FMT if no_color else COLOR_LOG_FMT,
78
+ )
79
+ logger.configure(extra={"src": "CLI"})
80
+ logger.enable("image2image_io")
81
+ logger.enable("koyo")
82
+ logger.debug(f"Activated logger with level '{level}'.")
83
+
84
+
85
+ # register commands
86
+ cli.add_command(czi2tiff)
87
+ cli.add_command(thumbnail)
88
+ cli.add_command(merge)
89
+ cli.add_command(convert)
90
+ if transform:
91
+ cli.add_command(transform)
92
+
93
+
94
+ def main():
95
+ """Execute the "imimspy" command line program."""
96
+ cli.main(windows_expand_args=False)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ cli.main(windows_expand_args=False)
@@ -0,0 +1,40 @@
1
+ import click
2
+
3
+
4
+ # noinspection PyUnusedLocal
5
+ def arg_split_bbox(ctx, param, value):
6
+ """Split arguments."""
7
+ if value is None:
8
+ return None
9
+ args = [int(arg.strip()) for arg in value.split(",")]
10
+ assert len(args) == 4, "Bounding box must have 4 values"
11
+ return args
12
+
13
+
14
+ ALLOW_EXTRA_ARGS = {"help_option_names": ["-h", "--help"], "ignore_unknown_options": True, "allow_extra_args": True}
15
+ overwrite_ = click.option(
16
+ "-W",
17
+ "--overwrite",
18
+ help="Overwrite existing data.",
19
+ is_flag=True,
20
+ default=False,
21
+ show_default=True,
22
+ )
23
+ as_uint8_ = click.option(
24
+ "-u/-U",
25
+ "--as_uint8/--no_as_uint8",
26
+ help="Downcast the image data format to uint8 which will substantially reduce the size of the files (unless it's"
27
+ " already in uint8...).",
28
+ is_flag=True,
29
+ default=None,
30
+ show_default=True,
31
+ )
32
+ fmt_ = click.option(
33
+ "-f",
34
+ "--fmt",
35
+ help="Output format.",
36
+ type=click.Choice(["ome-tiff"], case_sensitive=False),
37
+ default="ome-tiff",
38
+ show_default=True,
39
+ required=False,
40
+ )
@@ -0,0 +1,90 @@
1
+ """Utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+ from koyo.click import Parameter, cli_parse_paths_sort, print_parameters
7
+
8
+ from image2image_io.cli._common import ALLOW_EXTRA_ARGS
9
+
10
+
11
+ @click.option(
12
+ "-W",
13
+ "--overwrite",
14
+ help="Overwrite existing data.",
15
+ is_flag=True,
16
+ default=None,
17
+ show_default=True,
18
+ )
19
+ @click.option(
20
+ "-u/-U",
21
+ "--as_uint8/--no_as_uint8",
22
+ help="Downcast the image data format to uint8 which will substantially reduce the size of the files (unless it's"
23
+ " already in uint8...).",
24
+ is_flag=True,
25
+ default=None,
26
+ show_default=True,
27
+ )
28
+ @click.option(
29
+ "-t",
30
+ "--tile_size",
31
+ help="Tile size.",
32
+ type=click.Choice(["256", "512", "1024", "2048"], case_sensitive=False),
33
+ default="512",
34
+ show_default=True,
35
+ required=False,
36
+ )
37
+ @click.option(
38
+ "-f",
39
+ "--fmt",
40
+ help="Output format.",
41
+ type=click.Choice(["ome-tiff"], case_sensitive=False),
42
+ default="ome-tiff",
43
+ show_default=True,
44
+ required=False,
45
+ )
46
+ @click.option(
47
+ "-o",
48
+ "--output_dir",
49
+ help="Path to the output directory where images should be saved to.",
50
+ type=click.Path(file_okay=False, dir_okay=True, resolve_path=True),
51
+ show_default=True,
52
+ required=True,
53
+ )
54
+ @click.option(
55
+ "-i",
56
+ "--image",
57
+ help="Path(s) of images to be converted.",
58
+ type=click.UNPROCESSED,
59
+ show_default=True,
60
+ required=True,
61
+ multiple=True,
62
+ callback=cli_parse_paths_sort,
63
+ )
64
+ @click.command("convert", context_settings=ALLOW_EXTRA_ARGS)
65
+ def convert(image: list[str], output_dir: str, fmt: str, tile_size: str, as_uint8: bool, overwrite: bool) -> None:
66
+ """Convert images to pyramidal OME-TIFF."""
67
+ convert_runner(image, output_dir, fmt, tile_size, as_uint8, overwrite)
68
+
69
+
70
+ def convert_runner(
71
+ paths: list[str],
72
+ output_dir: str,
73
+ fmt: str = "ome-tiff",
74
+ tile_size: int | str = 512,
75
+ as_uint8: bool | None = None,
76
+ overwrite: bool = False,
77
+ ) -> None:
78
+ """Convert images to pyramidal OME-TIFF."""
79
+ from image2image_io.writers import images_to_ome_tiff
80
+
81
+ print_parameters(
82
+ Parameter("Images", "-i/--image", paths),
83
+ Parameter("Output directory", "-o/--output_dir", output_dir),
84
+ Parameter("Output format", "-f/--fmt", fmt),
85
+ Parameter("Tile size", "-t/--tile_size", tile_size),
86
+ Parameter("Downcast to uint8", "-u/--as_uint8", as_uint8),
87
+ Parameter("Overwrite", "-W/--overwrite", overwrite),
88
+ )
89
+ for _ in images_to_ome_tiff(paths, output_dir, tile_size=int(tile_size), as_uint8=as_uint8, overwrite=overwrite):
90
+ pass
@@ -0,0 +1,99 @@
1
+ """Convert images from one format to OME-TIFF format."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+ from koyo.click import arg_split_int, arg_split_str
7
+ from loguru import logger
8
+
9
+
10
+ @click.option(
11
+ "-N",
12
+ "--channel_names",
13
+ type=click.STRING,
14
+ default=None,
15
+ help="Specify a list of channel names to be used by the task. Names should be separated by commas. "
16
+ "e.g. 'DAPI,CD3,CD20'",
17
+ callback=arg_split_str,
18
+ show_default=True,
19
+ required=False,
20
+ )
21
+ @click.option(
22
+ "-I",
23
+ "--channel_ids",
24
+ type=click.STRING,
25
+ default=None,
26
+ help="Specify a list of channel indices to be used by the task. Channel IDs should be separated by comma."
27
+ "e.g. '1,2,4'.",
28
+ callback=arg_split_int,
29
+ show_default=True,
30
+ required=False,
31
+ )
32
+ @click.option(
33
+ "-u",
34
+ "--as_uint8",
35
+ is_flag=True,
36
+ help="Convert to uint8. If not specified, the original data type will be used.",
37
+ show_default=True,
38
+ required=False,
39
+ )
40
+ @click.option(
41
+ "-t",
42
+ "--tile_size",
43
+ help="Tile size.",
44
+ type=click.Choice(["256", "512", "1024", "2048"], case_sensitive=False),
45
+ default="512",
46
+ show_default=True,
47
+ required=False,
48
+ )
49
+ @click.option(
50
+ "-s",
51
+ "--scene",
52
+ "scene_index",
53
+ type=click.INT,
54
+ help="Specify the scene to be processed. If not specified, all scenes will be processed.",
55
+ default=None,
56
+ show_default=True,
57
+ required=True,
58
+ )
59
+ @click.option(
60
+ "-o",
61
+ "--output_dir",
62
+ help="Path to directory where OME-TIFF files will be saved.",
63
+ default=".", # cwd
64
+ type=click.Path(exists=False, resolve_path=True, file_okay=False, dir_okay=True),
65
+ show_default=True,
66
+ required=True,
67
+ )
68
+ @click.option(
69
+ "-i",
70
+ "--input",
71
+ "input_",
72
+ help="Path to the CZI file that should be converted.",
73
+ type=click.Path(exists=False, resolve_path=False, file_okay=True, dir_okay=False),
74
+ show_default=True,
75
+ required=True,
76
+ )
77
+ @click.command()
78
+ def czi2tiff(
79
+ input_: str,
80
+ output_dir: str,
81
+ scene_index: int,
82
+ tile_size: str,
83
+ as_uint8: bool,
84
+ channel_ids: list[int] | None,
85
+ channel_names: list[str | None],
86
+ ) -> None:
87
+ """Convert CZI to OME-TIFF."""
88
+ from image2image_io.writers import czi_to_ome_tiff
89
+
90
+ metadata = None
91
+ if channel_ids and channel_names:
92
+ if len(channel_ids) != len(channel_names):
93
+ raise ValueError("Number of channel IDs and channel names must be equal.")
94
+ metadata = {scene_index: {"channel_ids": channel_ids, "channel_names": channel_names}}
95
+
96
+ for key, scene_index, total, _ in czi_to_ome_tiff(
97
+ input_, output_dir, as_uint8=as_uint8, tile_size=int(tile_size), metadata=metadata, scenes=[scene_index]
98
+ ):
99
+ logger.info(f"Converted {key} scene {scene_index}/{total}")
@@ -0,0 +1,99 @@
1
+ """Merge command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typing as ty
6
+
7
+ import click
8
+ from koyo.click import Parameter, arg_parse_framelist_multi, cli_parse_paths_sort, print_parameters
9
+ from koyo.timer import MeasureTimer
10
+ from loguru import logger
11
+
12
+ from image2image_io.cli._common import as_uint8_, fmt_, overwrite_
13
+ from image2image_io.enums import WriterMode
14
+
15
+
16
+ @overwrite_
17
+ @as_uint8_
18
+ @fmt_
19
+ @click.option(
20
+ "-C",
21
+ "--channel_ids",
22
+ type=click.STRING,
23
+ default=None,
24
+ help="Specify channel ids in the format: 1,2,4-6. You can provide multiple. If you are providing any, make sure to"
25
+ " provide one for each file you are trying to merge.",
26
+ callback=arg_parse_framelist_multi,
27
+ show_default=True,
28
+ multiple=True,
29
+ required=False,
30
+ )
31
+ @click.option(
32
+ "-o",
33
+ "--output_dir",
34
+ help="Path to images to be merged.",
35
+ type=click.Path(file_okay=False, dir_okay=True, resolve_path=True),
36
+ show_default=True,
37
+ required=True,
38
+ )
39
+ @click.option(
40
+ "-p",
41
+ "--path",
42
+ help="Path to images to be merged.",
43
+ type=click.UNPROCESSED,
44
+ show_default=True,
45
+ required=True,
46
+ multiple=True,
47
+ callback=cli_parse_paths_sort,
48
+ )
49
+ @click.option(
50
+ "-n",
51
+ "--name",
52
+ help="Name of the merged image.",
53
+ type=click.STRING,
54
+ show_default=True,
55
+ required=True,
56
+ )
57
+ @click.command("merge")
58
+ def merge(
59
+ name: str,
60
+ path: ty.Sequence[str],
61
+ output_dir: str,
62
+ channel_ids: ty.Sequence[tuple] | None,
63
+ fmt: WriterMode,
64
+ as_uint8: bool | None,
65
+ overwrite: bool,
66
+ ) -> None:
67
+ """Export images."""
68
+ merge_runner(name, path, output_dir, channel_ids, fmt, as_uint8, overwrite)
69
+
70
+
71
+ def merge_runner(
72
+ name: str,
73
+ paths: ty.Sequence[str],
74
+ output_dir: str,
75
+ channel_ids: ty.Sequence[tuple] | None,
76
+ fmt: WriterMode = "ome-tiff",
77
+ as_uint8: bool | None = False,
78
+ overwrite: bool = False,
79
+ ) -> None:
80
+ """Register images."""
81
+ from image2image_io.workflows.merge import merge as merge_images
82
+
83
+ print_parameters(
84
+ Parameter("Name", "-n/--name", name),
85
+ Parameter("Image paths", "-p/--path", paths),
86
+ Parameter("Output directory", "-o/--output_dir", output_dir),
87
+ Parameter("Channel ids", "-C/--channel_ids", channel_ids),
88
+ Parameter("Output format", "-f/--fmt", fmt),
89
+ Parameter("Write images as uint8", "--as_uint8/--no_as_uint8", as_uint8),
90
+ Parameter("Overwrite", "-W/--overwrite", overwrite),
91
+ )
92
+
93
+ if channel_ids:
94
+ if len(channel_ids) != len(paths):
95
+ raise ValueError("Number of channel ids must match number of images.")
96
+
97
+ with MeasureTimer() as timer:
98
+ merge_images(name, list(paths), output_dir, fmt, as_uint8, channel_ids=channel_ids, overwrite=overwrite)
99
+ logger.info(f"Finished processing project in {timer()}.")
@@ -0,0 +1,54 @@
1
+ """Create thumbnail for image(s)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+ from koyo.click import cli_parse_paths_sort
7
+ from loguru import logger
8
+ from tqdm import tqdm
9
+
10
+
11
+ @click.option(
12
+ "-f/-F",
13
+ "--first_only/--no_first_only",
14
+ help="Export only the first channel from the stack.",
15
+ is_flag=True,
16
+ default=False,
17
+ show_default=True,
18
+ )
19
+ @click.option(
20
+ "-t/-T",
21
+ "--with_title/--no_with_title",
22
+ help="Export image with title.",
23
+ is_flag=True,
24
+ default=True,
25
+ show_default=True,
26
+ )
27
+ @click.option(
28
+ "-o",
29
+ "--output_dir",
30
+ help="Path to directory where OME-TIFF files will be saved.",
31
+ default=".", # cwd
32
+ type=click.Path(exists=False, resolve_path=True, file_okay=False, dir_okay=True),
33
+ show_default=True,
34
+ required=True,
35
+ )
36
+ @click.option(
37
+ "-i",
38
+ "--input",
39
+ "input_",
40
+ help="Path to supported file(s).",
41
+ type=click.UNPROCESSED,
42
+ show_default=True,
43
+ required=True,
44
+ multiple=True,
45
+ callback=cli_parse_paths_sort,
46
+ )
47
+ @click.command()
48
+ def thumbnail(input_: str, output_dir: str, with_title: bool, first_only: bool) -> None:
49
+ """Create a thumbnail for image(s)."""
50
+ from image2image_io.utils.utilities import write_thumbnail
51
+
52
+ for path in tqdm(input_, desc="Creating thumbnails..."):
53
+ write_thumbnail(path, output_dir, with_title=with_title, first_only=first_only)
54
+ logger.info(f"Created thumbnail for '{path}'.")