diff --git a/formats/ome-converter-tool/.bumpversion.cfg b/formats/ome-converter-tool/.bumpversion.cfg index d277ae70a..5486ac653 100644 --- a/formats/ome-converter-tool/.bumpversion.cfg +++ b/formats/ome-converter-tool/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.3 +current_version = 0.3.4-dev2 commit = True tag = False parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)(?P\d+))? diff --git a/formats/ome-converter-tool/Dockerfile b/formats/ome-converter-tool/Dockerfile index 53a844554..087bb74e4 100644 --- a/formats/ome-converter-tool/Dockerfile +++ b/formats/ome-converter-tool/Dockerfile @@ -1,10 +1,12 @@ -FROM polusai/bfio:2.4.4 +FROM polusai/bfio:2.5.0 # environment variables defined in polusai/bfio ENV EXEC_DIR="/opt/executables" ENV POLUS_IMG_EXT=".ome.tif" ENV POLUS_TAB_EXT=".csv" ENV POLUS_LOG="INFO" +ENV NUM_THREADS=2 +ENV NUM_WORKERS=4 # Work directory defined in the base container WORKDIR ${EXEC_DIR} diff --git a/formats/ome-converter-tool/README.md b/formats/ome-converter-tool/README.md index f1cf158cc..d012fb777 100644 --- a/formats/ome-converter-tool/README.md +++ b/formats/ome-converter-tool/README.md @@ -1,4 +1,4 @@ -# OME Converter (v0.3.3) +# OME Converter (v0.3.4-dev2) This WIPP plugin converts BioFormats supported data types to the OME Zarr or OME TIF file format. This is not a complete implementation, rather it implements a file @@ -35,4 +35,12 @@ This plugin takes 2 input arguments and 1 output argument: ## Docker Command ```bash -docker run -e POLUS_IMG_EXT=".ome.zarr" -v /Users/username/:/Users/username/ polusai/ome-converter-tool:0.3.3 --inpDir=/Users/path/to/Images/ --filePattern=".*.tif" --outDir=/Users/path/to/outputs +docker run \ + -e POLUS_IMG_EXT=".ome.zarr" \ + -e NUM_THREADS=4 \ + -e NUM_WORKERS=2 \ + -v /Users/username/:/Users/username/ \ + polusai/ome-converter-tool:0.3.4-dev2 \ + --inpDir=/Users/path/to/Images/ \ + --filePattern=".*.tif" \ + --outDir=/Users/path/to/outputs diff --git a/formats/ome-converter-tool/VERSION b/formats/ome-converter-tool/VERSION index 1c09c74e2..ee14bbbd0 100644 --- a/formats/ome-converter-tool/VERSION +++ b/formats/ome-converter-tool/VERSION @@ -1 +1 @@ -0.3.3 +0.3.4-dev2 diff --git a/formats/ome-converter-tool/ict.yaml b/formats/ome-converter-tool/ict.yaml index a522e96a9..a0899413a 100644 --- a/formats/ome-converter-tool/ict.yaml +++ b/formats/ome-converter-tool/ict.yaml @@ -2,13 +2,13 @@ author: - Nick Schaub - Hamdah Shafqat contact: nick.schaub@nih.gov -container: polusai/ome-converter-tool:0.3.3 +container: polusai/ome-converter-tool:0.3.4-dev2 description: Convert Bioformats supported format to OME Zarr or OME TIF entrypoint: python3 -m polus.images.formats.ome_converter inputs: - description: Input generic data collection to be processed by this plugin format: - - genericData + - string name: inpDir required: true type: path @@ -38,4 +38,4 @@ ui: key: inputs.filePattern title: Filepattern type: text -version: 0.3.3 +version: 0.3.4-dev2 diff --git a/formats/ome-converter-tool/mypy.ini b/formats/ome-converter-tool/mypy.ini new file mode 100644 index 000000000..2251e5c41 --- /dev/null +++ b/formats/ome-converter-tool/mypy.ini @@ -0,0 +1,16 @@ +[mypy] +files = src/polus/images/formats/ome_converter, tests/ +exclude = \.git/ +disable_error_code = import-untyped + +[mypy-bfio] +ignore_missing_imports = True + +[mypy-filepattern] +ignore_missing_imports = True + +[mypy-tqdm] +ignore_missing_imports = True + +# [mypy-polus.images.formats.ome_converter.*] +# ignore_missing_imports = False diff --git a/formats/ome-converter-tool/omeconverter.cwl b/formats/ome-converter-tool/omeconverter.cwl index 47ab1a4ef..c6f38b610 100644 --- a/formats/ome-converter-tool/omeconverter.cwl +++ b/formats/ome-converter-tool/omeconverter.cwl @@ -8,7 +8,7 @@ inputs: inpDir: inputBinding: prefix: --inpDir - type: Directory + type: string outDir: inputBinding: prefix: --outDir @@ -20,7 +20,7 @@ outputs: type: Directory requirements: DockerRequirement: - dockerPull: polusai/ome-converter-tool:0.3.3 + dockerPull: polusai/ome-converter-tool:0.3.4-dev2 InitialWorkDirRequirement: listing: - entry: $(inputs.outDir) diff --git a/formats/ome-converter-tool/plugin.json b/formats/ome-converter-tool/plugin.json index 6ebed8f30..60edecfc6 100644 --- a/formats/ome-converter-tool/plugin.json +++ b/formats/ome-converter-tool/plugin.json @@ -1,6 +1,6 @@ { "name": "OME Converter", - "version": "0.3.3", + "version": "0.3.4-dev2", "title": "OME Converter", "description": "Convert Bioformats supported format to OME Zarr or OME TIF", "author": "Nick Schaub (nick.schaub@nih.gov), Hamdah Shafqat Abbasi (hamdahshafqat.abbasi@nih.gov)", @@ -8,7 +8,7 @@ "repository": "https://github.com/PolusAI/image-tools", "website": "https://ncats.nih.gov/preclinical/core/informatics", "citation": "", - "containerId": "polusai/ome-converter-tool:0.3.3", + "containerId": "polusai/ome-converter-tool:0.3.4-dev2", "baseCommand": [ "python3", "-m", @@ -17,7 +17,7 @@ "inputs": [ { "name": "inpDir", - "type": "genericData", + "type": "string", "description": "Input generic data collection to be processed by this plugin", "required": true }, diff --git a/formats/ome-converter-tool/pyproject.toml b/formats/ome-converter-tool/pyproject.toml index d37c5ae4b..f68ae78f7 100644 --- a/formats/ome-converter-tool/pyproject.toml +++ b/formats/ome-converter-tool/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "polus-images-formats-ome-converter" -version = "0.3.3" +version = "0.3.4-dev2" description = "Convert BioFormats datatypes to ome.tif or ome.zarr file format" authors = [ "Nick Schaub ", @@ -11,13 +11,13 @@ readme = "README.md" packages = [{include = "polus", from = "src"}] [tool.poetry.dependencies] -python = ">=3.9,<3.12" -bfio = {version = "^2.4.4", extras = ["all"]} -filepattern = "^2.0.4" -typer = "^0.7.0" +python = ">=3.11,<3.13" +bfio = "2.5.0" +filepattern = "2.1.4" +typer = "0.24.1" tqdm = "^4.64.1" -preadator = "0.4.0-dev2" -numpy = "<2.0.0" +numpy = ">2.0.0" +scikit-image = "^0.26.0" [tool.poetry.group.dev.dependencies] bump2version = "^1.0.1" @@ -25,7 +25,17 @@ pre-commit = "^3.0.4" pytest = "^7.2.1" ipykernel = "^6.21.2" requests = "^2.28.2" -scikit-image = "^0.19.3" +scikit-image = ">=0.26.0" +matplotlib = "3.9.4" + +[tool.mypy] +files = "." + +# Use strict defaults +strict = true +warn_unreachable = true +warn_no_return = true + [build-system] requires = ["poetry-core"] diff --git a/formats/ome-converter-tool/run-plugin.sh b/formats/ome-converter-tool/run-plugin.sh index b579515f9..711222cf9 100755 --- a/formats/ome-converter-tool/run-plugin.sh +++ b/formats/ome-converter-tool/run-plugin.sh @@ -15,8 +15,12 @@ outDir=/data/output # docker run polusai/ome-converter-plugin:${version} # Run the plugin -docker run -e POLUS_IMG_EXT=${fileExtension} --mount type=bind,source=${datapath},target=/data/ \ - polusai/ome-converter-tool:${version} \ - --inpDir ${inpDir} \ - --filePattern ${filePattern} \ - --outDir ${outDir} +docker run \ + -e POLUS_IMG_EXT=${fileExtension} \ + -e NUM_THREADS=2 \ + -e NUM_WORKERS=4 \ + --mount type=bind,source=${datapath},target=/data/ \ + polusai/ome-converter-tool:${version} \ + --inpDir ${inpDir} \ + --filePattern ${filePattern} \ + --outDir ${outDir} diff --git a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__init__.py b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__init__.py index b0b13b640..439dcf7ad 100644 --- a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__init__.py +++ b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__init__.py @@ -1,6 +1,6 @@ """Ome Converter.""" -__version__ = "0.3.3" +__version__ = "0.3.4-dev2" from .image_converter import batch_convert from .image_converter import convert_image diff --git a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__main__.py b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__main__.py index 969c803ff..e8bd1f440 100644 --- a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__main__.py +++ b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/__main__.py @@ -3,16 +3,13 @@ import logging import os import pathlib -from concurrent.futures import as_completed from typing import Any from typing import Optional import filepattern as fp -import preadator import typer -from polus.images.formats.ome_converter.image_converter import NUM_THREADS -from polus.images.formats.ome_converter.image_converter import convert_image -from tqdm import tqdm +from polus.images.formats.ome_converter.image_converter import POLUS_IMG_EXT +from polus.images.formats.ome_converter.image_converter import batch_convert app = typer.Typer() @@ -23,22 +20,71 @@ ) logger = logging.getLogger("polus.images.formats.ome_converter") logger.setLevel(os.environ.get("POLUS_LOG", logging.INFO)) -POLUS_IMG_EXT = os.environ.get("POLUS_IMG_EXT", ".ome.tif") + + +def parse_input_dir(value: str) -> pathlib.Path | list[pathlib.Path]: + """Parse a directory or comma-separated file paths.""" + if "," in value: + files = [ + pathlib.Path(f.strip()).expanduser().resolve() + for f in value.split(",") + if f.strip() + ] + if not files: + msg = "Empty file list provided" + raise typer.BadParameter(msg) + return files + path = pathlib.Path(value).expanduser().resolve() + if not path.is_dir(): + msg = f"Directory {path} does not exist" + raise typer.BadParameter(msg) + return path + + +def _collect_files( + inp: str | pathlib.Path | list[pathlib.Path], + pattern: str, +) -> list[pathlib.Path]: + """Normalize input into a list of files.""" + if isinstance(inp, (str, pathlib.Path)): + dir_path = pathlib.Path(inp) if isinstance(inp, str) else inp + if not dir_path.is_dir(): + msg = f"Input path is not a directory: {dir_path}" + raise ValueError(msg) + fps = fp.FilePattern(dir_path, pattern) + return [files[1][0] for files in fps()] + + if isinstance(inp, list): + return [pathlib.Path(p) if isinstance(p, str) else p for p in inp] + msg = "Expected str, Path, or list[Path/str]" + raise TypeError(msg) + + +def write_preview( + out_dir: pathlib.Path, + file_pattern: str, + files: list[pathlib.Path], +) -> None: + """Write a JSON preview of the files that would be converted.""" + preview: dict[str, Any] = { + "filePattern": file_pattern, + "outDir": [f.stem + POLUS_IMG_EXT for f in files], + } + preview_path = out_dir / "preview.json" + with preview_path.open("w") as f: + json.dump(preview, f, indent=2) + logger.info(f"Preview written to {preview_path}") @app.command() def main( - inp_dir: pathlib.Path = typer.Option( + inp_dir: str = typer.Option( ..., "--inpDir", help="Input generic data collection to be processed by this plugin", - exists=True, - resolve_path=True, - readable=True, - file_okay=False, - dir_okay=True, + callback=parse_input_dir, ), - pattern: str = typer.Option( + file_pattern: str = typer.Option( ".+", "--filePattern", help="A filepattern defining the images to be converted", @@ -62,44 +108,22 @@ def main( """Convert bioformat supported image datatypes conversion to ome.tif or ome.zarr.""" logger.info(f"inpDir = {inp_dir}") logger.info(f"outDir = {out_dir}") - logger.info(f"filePattern = {pattern}") + logger.info(f"filePattern = {file_pattern}") + logger.info(f"preview = {preview}") - fps = fp.FilePattern(inp_dir, pattern) + files = _collect_files(inp_dir, file_pattern) # type: ignore + logger.info(f"Found {len(files)} file(s) to process.") if preview: - with out_dir.joinpath("preview.json").open("w") as jfile: - out_json: dict[str, Any] = { - "filepattern": pattern, - "outDir": [], - } - for file in fps(): - out_name = str(file[1][0].name.split(".")[0]) + POLUS_IMG_EXT - out_json["outDir"].append(out_name) - json.dump(out_json, jfile, indent=2) + write_preview(out_dir, file_pattern, files) return - with preadator.ProcessManager( - name="ome_converter", - num_processes=NUM_THREADS, - threads_per_process=2, - ) as executor: - threads = [] - for files in fps(): - file = files[1][0] - threads.append( - executor.submit_process(convert_image, file, POLUS_IMG_EXT, out_dir), - ) - - for f in tqdm( - as_completed(threads), - total=len(threads), - mininterval=5, - desc=f"converting images to {POLUS_IMG_EXT}", - initial=0, - unit_scale=True, - colour="cyan", - ): - f.result() + batch_convert( + inp_dir=inp_dir, + out_dir=out_dir, + file_pattern=file_pattern, + file_extension=POLUS_IMG_EXT, + ) if __name__ == "__main__": diff --git a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/image_converter.py b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/image_converter.py index 825df42b9..a2e7e5ab5 100644 --- a/formats/ome-converter-tool/src/polus/images/formats/ome_converter/image_converter.py +++ b/formats/ome-converter-tool/src/polus/images/formats/ome_converter/image_converter.py @@ -1,16 +1,14 @@ """Ome Converter.""" - import logging import os import pathlib +import platform +from concurrent.futures import ProcessPoolExecutor from concurrent.futures import as_completed from itertools import product -from sys import platform from typing import Optional import filepattern as fp -import numpy as np -import preadator from bfio import BioReader from bfio import BioWriter from tqdm import tqdm @@ -19,52 +17,66 @@ logger.setLevel(os.environ.get("POLUS_LOG", logging.INFO)) TILE_SIZE = 2**13 +POLUS_IMG_EXT = os.environ.get("POLUS_IMG_EXT", ".ome.tif") -if platform.startswith("linux"): - NUM_THREADS = len(os.sched_getaffinity(0)) // 2 # type: ignore -else: - NUM_THREADS = os.cpu_count() # type: ignore +def get_num_threads() -> int: + """Determine default number of threads (half CPU cores, min 1).""" + # Allow environment override + if "NUM_THREADS" in os.environ: + return max(1, int(os.environ["NUM_THREADS"])) + try: + if platform.system().lower() == "linux" and hasattr(os, "sched_getaffinity"): + cpu_count = len(os.sched_getaffinity(0)) + else: + cpu_count = os.cpu_count() or 1 # fallback if None + return max(1, cpu_count // 2) + except (AttributeError, OSError): + return 1 -def write_image( - br: BioReader, - c: int, - image: np.ndarray, - out_path: pathlib.Path, - max_workers: int, -) -> None: - """Write an image to OME-TIFF or OME-ZARR format using BioFormats. +NUM_THREADS: int = get_num_threads() + +# NUM_WORKERS: default to 1 process +NUM_WORKERS = max(1, int(os.environ.get("NUM_WORKERS", "1"))) - This function converts a given image to the OME-TIFF or OME-ZARR format, - ensuring that the data type is compatible and handling any necessary - byte order adjustments. It utilizes the BioWriter for writing the image - and manages channel names based on the provided BioReader. + +def get_num_series(inp_image: pathlib.Path) -> int: + """Get the number of series/levels in an individual image file. Args: - br: An instance of BioReader containing metadata for the image. - c: The index of the channel in the image. - image: The image data to be written. - out_path: Path to an output image. - max_workers: The maximum number of worker threads to use for writing. + inp_image: Path to the input image file. + + Returns: + int: Number of series/levels in the image. """ - if image.dtype == ">u2": - image = image.byteswap().newbyteorder("<") - - with BioWriter( - out_path, - max_workers, - ) as bw: - # Handling of parsing channels when channels names are not provided. - if bw.channel_names != [None]: - bw.channel_names = [br.channel_names[c]] - bw.C = 1 - bw.T = 1 - bw.Z = 1 - bw.X = image.shape[1] - bw.Y = image.shape[0] - bw.dtype = image.dtype - bw[:] = image + try: + # Open the specific file with BioReader + br = BioReader(inp_image, max_workers=NUM_THREADS) + + # Initialize series count + num_series = 1 + # For formats with metadata.images, verify it's for this file only + if hasattr(br.metadata, "images"): + # Check if images relate to this specific file + image_count = len(br.metadata.images) + num_series = image_count + logger.debug( + f"count:{num_series} for {inp_image.name}", + ) + + # Log and return result + logger.info(f"File {inp_image.name} has {num_series}") + + # Clean up + br.close() + del br + + return num_series + + except (OSError, ValueError) as e: + logger.error(f"Error determining number of series for {inp_image}: {e!s}") + return 1 def convert_image( @@ -72,79 +84,93 @@ def convert_image( file_extension: str, out_dir: pathlib.Path, ) -> None: - """Convert bioformats supported datatypes to ome.tif or ome.zarr file format. - - Args: - inp_image: Path of an input image. - file_extension: Type of data conversion. - out_dir: Path to output directory. - """ - # Loop through timepoints, channels and z-slices - with BioReader(inp_image, max_workers=NUM_THREADS) as br: - for t, c, z in product(range(br.T), range(br.C), range(br.Z)): - extension = "".join( - [ - suffix - for suffix in inp_image.suffixes[-2:] - if len(suffix) < 6 # noqa: PLR2004 - ], - ) - - out_path = out_dir.joinpath( - inp_image.name.replace(extension, file_extension), - ) - if br.C > 1: - out_path = out_dir.joinpath( - out_path.name.replace( - file_extension, - f"_c{c}" + file_extension, - ), - ) - if br.T > 1: - out_path = out_dir.joinpath( - out_path.name.replace( - file_extension, - f"_t{t}" + file_extension, - ), - ) - - if br.Z > 1: - out_path = out_dir.joinpath( - out_path.name.replace( - file_extension, - f"_z{z}" + file_extension, - ), + """Convert bioformats supported datatypes to ome.tif or ome.zarr file format.""" + # First, determine the number of series + num_series = get_num_series(inp_image) + + logger.info(f"Detected {num_series} series/levels in image {inp_image.name}") + + files_written = 0 + if inp_image.suffix: + extension_len = 6 + extension = "".join( + s for s in inp_image.suffixes[-2:] if len(s) < extension_len + ) + base_name = inp_image.name.replace(extension, file_extension) + else: + base_name = f"{inp_image.name}{file_extension}" + + # Process each series independently + for idx in range(num_series): + logger.info(f"Processing levels {idx} of {inp_image.name}") + + try: + # Explicitly set the series/level parameter when opening the file + with BioReader(inp_image, max_workers=NUM_THREADS, level=idx) as br: + logger.debug( + f"Level {idx}: T={br.T}, C={br.C}, Z={br.Z}, Y={br.Y}, X={br.X}", ) - # Initialize the complete_image if not done yet - final_image = np.zeros((br.Y, br.X, 1, 1, 1), dtype=br.dtype) - - # Process each tile in the image using itertools.product - for y, x in product(range(0, br.Y, TILE_SIZE), range(0, br.X, TILE_SIZE)): - y_max = min(br.Y, y + TILE_SIZE) - x_max = min(br.X, x + TILE_SIZE) - - image = br[ - y:y_max, - x:x_max, - z, - c, - t, - ] - # Place the tile into the correct position in the complete image - final_image[y:y_max, x:x_max, z, c, t] = image - - write_image( - br=br, - c=c, - image=final_image, - out_path=out_path, - max_workers=NUM_THREADS, + # Process each view (t, c, z) + for t, c, z in product(range(br.T), range(br.C), range(br.Z)): + # Build suffix components + suffix = "".join( + [ + f"_c{c}" if br.C > 1 else "", + f"_t{t}" if br.T > 1 else "", + f"_z{z}" if br.Z > 1 else "", + f"_s{idx}" if num_series > 1 else "", + ], + ) + + # Apply combined suffix + out_name = ( + base_name.replace(file_extension, f"{suffix}{file_extension}") + if suffix + else base_name + ) + out_path = out_dir.joinpath(out_name) + + with BioWriter( + out_path, + max_workers=NUM_THREADS, + metadata=br.metadata, + ) as bw: + bw.C = 1 + bw.T = 1 + bw.Z = 1 + if bw.channel_names != [None]: + bw.channel_names = [br.channel_names[c]] + + for y in range(0, br.Y, TILE_SIZE): + y_max = min(br.Y, y + TILE_SIZE) + for x in range(0, br.X, TILE_SIZE): + x_max = min(br.X, x + TILE_SIZE) + bw[ + y:y_max, + x:x_max, + 0, + 0, + 0, + ] = br[ + y:y_max, + x:x_max, + z : z + 1, + c, + t, + ] + + files_written += 1 + logger.debug(f"Written: {out_path.name}") + + except (OSError, ValueError) as e: + logger.error( + f"Failed to process series {idx} of {inp_image.name}: {e!s}", ) def batch_convert( - inp_dir: pathlib.Path, + inp_dir: pathlib.Path | list[pathlib.Path], out_dir: pathlib.Path, file_pattern: Optional[str], file_extension: str, @@ -162,29 +188,34 @@ def batch_convert( logger.info(f"file_pattern = {file_pattern}") logger.info(f"file_extension = {file_extension}") - file_pattern = ".+" if file_pattern is None else file_pattern + if isinstance(inp_dir, pathlib.Path): + file_pattern = ".+" if file_pattern is None else file_pattern + fps = fp.FilePattern(inp_dir, file_pattern) + files = [files[1][0] for files in fps()] + else: + files = list(inp_dir) - fps = fp.FilePattern(inp_dir, file_pattern) + if not files: + logger.warning("No files found to process.") + return - with preadator.ProcessManager( - name="ome_converter", - num_processes=NUM_THREADS, - threads_per_process=2, + with ProcessPoolExecutor( + max_workers=NUM_WORKERS, ) as executor: - threads = [] - for files in fps(): - file = files[1][0] - threads.append( - executor.submit(convert_image, file, file_extension, out_dir), - ) - + futures = [ + executor.submit(convert_image, file, POLUS_IMG_EXT, out_dir) + for file in files + ] for f in tqdm( - as_completed(threads), - total=len(threads), + as_completed(futures), + total=len(futures), mininterval=5, - desc=f"converting images to {file_extension}", + desc=f"converting images to {POLUS_IMG_EXT}", initial=0, unit_scale=True, colour="cyan", ): - f.result() + try: + f.result() + except (OSError, ValueError) as e: + logger.error(f"Failed to convert file: {e}") diff --git a/formats/ome-converter-tool/tests/conftest.py b/formats/ome-converter-tool/tests/conftest.py index b3dc8a7b9..3c84e76fb 100644 --- a/formats/ome-converter-tool/tests/conftest.py +++ b/formats/ome-converter-tool/tests/conftest.py @@ -9,8 +9,7 @@ import numpy import pytest import requests -import skimage.data -import skimage.measure +import skimage def pytest_addoption(parser: pytest.Parser) -> None: diff --git a/formats/ome-converter-tool/tests/test_main.py b/formats/ome-converter-tool/tests/test_main.py index 8e91d9480..0bec2775c 100644 --- a/formats/ome-converter-tool/tests/test_main.py +++ b/formats/ome-converter-tool/tests/test_main.py @@ -67,11 +67,11 @@ def test_cli( app, [ "--inpDir", - inp_dir, + str(inp_dir), "--filePattern", ".+", "--outDir", - output_directory, + str(output_directory), ], )