Skip to content

Commit

Permalink
Image dimension stacking (PolusAI#495)
Browse files Browse the repository at this point in the history
* new plugin

* refactored code and added pytests

* adding plugin json and correcting documentation

* fixed documentation

* fixed documentation

* fixed documentation

* fixed documentation

* fixed documentation

* updating docker base container

* added multiple pattern support

* added docstring

* fix plugin name and test fixture

* test cli

* fixing multipattern test

* fix slow test

* fix slow test
  • Loading branch information
hamshkhawar committed Aug 15, 2024
1 parent 5cb9424 commit 5d2b925
Show file tree
Hide file tree
Showing 17 changed files with 818 additions and 0 deletions.
29 changes: 29 additions & 0 deletions formats/image-dimension-stacking-tool/.bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[bumpversion]
current_version = 0.1.1-dev
commit = False
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+)(?P<dev>\d+))?
serialize =
{major}.{minor}.{patch}-{release}{dev}
{major}.{minor}.{patch}

[bumpversion:part:release]
optional_value = _
first_value = dev
values =
dev
_

[bumpversion:part:dev]

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:VERSION]

[bumpversion:file:README.md]

[bumpversion:file:plugin.json]

[bumpversion:file:src/polus/images/formats/image_dimension_stacking/__init__.py]
4 changes: 4 additions & 0 deletions formats/image-dimension-stacking-tool/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.venv
out
tests
__pycache__
2 changes: 2 additions & 0 deletions formats/image-dimension-stacking-tool/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
poetry.lock
out
21 changes: 21 additions & 0 deletions formats/image-dimension-stacking-tool/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM polusai/bfio:2.3.6

# 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"

# Work directory defined in the base container
WORKDIR ${EXEC_DIR}

COPY pyproject.toml ${EXEC_DIR}
COPY VERSION ${EXEC_DIR}
COPY README.md ${EXEC_DIR}
COPY src ${EXEC_DIR}/src

RUN pip3 install ${EXEC_DIR} --no-cache-dir

# Default command. Additional arguments are provided through the command line
ENTRYPOINT ["python3", "-m", "polus.images.formats.image_dimension_stacking"]
CMD ["--help"]
50 changes: 50 additions & 0 deletions formats/image-dimension-stacking-tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Image dimension stacking(0.1.1-dev)

This plugin leverages the [filepattern](https://filepattern2.readthedocs.io/en/latest/Home.html) library and employs the filepattern `groupBy` functionality to enable the matching of image filenames, facilitating their stacking into multi-dimensional images.

The filepattern must include the variables `c`, `t`, and `z`. If all these variables are present in the pattern, the plugin will group images according to the order `z, c, t`. If only one variable is present in the file pattern, the plugin will group images according to that variable.


Currently, the plugin supports the following dimensions and user can choose the relevant variable for the `groupBy` input argument.
1. `tubhiswt_z{z:d+}_c{c:d+}_t{t:d+}.ome.tif`\
Images are grouped based on `z` variable
2. `tubhiswt_.*_.*_t{t:d+}.ome.tif`\
Images are grouped based on `t` variable
3. `00001_01_{c:d+}.ome.tif`\
Images are grouped based on `c` variable

#### Note:
Filename patterns may consist of any other filepattern variables, combined with other valid regular expression arguments, excluding the `groupBy` variable.

For more information on WIPP, visit the
[official WIPP page](https://isg.nist.gov/deepzoomweb/software/wipp).

## Building

To build the Docker image for the conversion plugin, run
`./build-docker.sh`.

## Install WIPP Plugin

If WIPP is running, navigate to the plugins page and add a new plugin. Paste the
contents of `plugin.json` into the pop-up window and submit.

## Options

This plugin takes three input argument and one output argument:

| Name | Description | I/O | Type |
|---------------|-------------------------|--------|--------|
| `--inpDir` | Input image collection | Input | Collection |
| `--filePattern` | Pattern to parse image files | Input | String |
| `--outDir` | Output image collection | Output | Collection |
| `--preview` | Generate a JSON file to view outputs | Output | Boolean |

### Run the Docker Container

```bash
docker run -v /path/to/data:/data polusai/image-dimension-stacking-plugin:0.1.1-dev \
--inpDir "Path/To/Data" \
--filePattern "tubhiswt_C1-z{z:d+}.ome.tif" \
--outDir "Path/To/Output/Dir"
```
1 change: 1 addition & 0 deletions formats/image-dimension-stacking-tool/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.1-dev
4 changes: 4 additions & 0 deletions formats/image-dimension-stacking-tool/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

version=$(<VERSION)
docker build . -t polusai/image-dimension-stacking-tool:${version}
62 changes: 62 additions & 0 deletions formats/image-dimension-stacking-tool/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "Image Dimension Stacking",
"version": "0.1.1-dev",
"title": "Stacking of image dimensions (c, z, t) to create a multidimensional image.",
"description": "Stacking of image dimensions (c, z, t) to create a multidimensional image.",
"author": "Nick Schaub (nick.schaub@nih.gov), Hamdah Shafqat Abbasi (hamdahshafqat.abbasi@nih.gov)",
"institution": "National Center for Advancing Translational Sciences, National Institutes of Health",
"repository": "https://github.com/PolusAI/image-tools",
"website": "https://ncats.nih.gov/preclinical/core/informatics",
"containerId": "polusai/image-dimension-stacking-tool:0.1.1-dev",
"baseCommand": [
"python3",
"-m",
"polus.images.formats.image_dimension_stacking"
],
"inputs": {
"inpDir": {
"type": "collection",
"title": "Input collection",
"description": "Input image collection to be processed by this plugin.",
"required": "True"
},
"filePattern": {
"type": "string",
"title": "Filename pattern",
"description": "Filename pattern used to separate data.",
"required": "True"
},
"preview": {
"type": "boolean",
"title": "Preview",
"description": "Generate an output preview.",
"required": "False"
}
},
"outputs": {
"outDir": {
"type": "collection",
"description": "Output collection."
}
},
"ui": {
"inpDir": {
"type": "collection",
"title": "Input collection",
"description": "Input image collection to be processed by this plugin.",
"required": "True"
},
"filePattern": {
"type": "string",
"title": "Filename pattern",
"description": "Filename pattern used to separate data.",
"required": "True"
},
"preview": {
"type": "boolean",
"title": "Preview example output of this plugin",
"description": "Generate an output preview.",
"required": "False"
}
}
}
42 changes: 42 additions & 0 deletions formats/image-dimension-stacking-tool/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[tool.poetry]
name = "polus-images-formats-image-dimension-stacking"
version = "0.1.1-dev"
description = "Stacking multi-dimensional images"
authors = [
"Nick Schaub <nick.schaub@nih.gov>",
"Hamdah Shafqat abbasi <hamdahshafqat.abbasi@nih.gov>"
]
readme = "README.md"
packages = [{include = "polus", from = "src"}]


[tool.poetry.dependencies]
python = ">=3.9,<3.12"
bfio = {version = "2.3.3", extras = ["all"]}
typer = "^0.7.0"
tqdm = "^4.66.1"
filepattern = "^2.0.4"
pydantic = "^1.10.4"
preadator="0.4.0.dev2"


[[tool.poetry.source]]
name = "test"
url = "https://test.pypi.org/simple/"
default = false
secondary = true

[tool.poetry.group.dev.dependencies]
bump2version = "^1.0.1"
flake8 = "^6.0.0"
pre-commit = "^3.2.1"
flake8-docstrings = "^1.7.0"
black = "^23.3.0"
mypy = "^1.1.1"
pytest = "^7.2.2"
ruff = "^0.0.270"
scikit-image = "^0.22.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
22 changes: 22 additions & 0 deletions formats/image-dimension-stacking-tool/run-plugin.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

version=$(<VERSION)
datapath=$(readlink --canonicalize ../data)

# Inputs
inpDir=/data/path_to_files

filePattern="tubhiswt_z{z:d+}_c{c:d+}_t{t:d+}.ome.tif"
# Output paths
outDir=/data/path_to_output

# Log level, must be one of ERROR, CRITICAL, WARNING, INFO, DEBUG
LOGLEVEL=INFO

docker run --mount type=bind,source=${datapath},target=/data/ \
--user $(id -u):$(id -g) \
--env POLUS_LOG=${LOGLEVEL} \
polusai/image-dimension-stacking-plugin:${version} \
--inpDir ${inpDir} \
--filePattern ${filePattern} \
--outDir ${outDir}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""Image dimension stacking package."""
__version__ = "0.1.1-dev"
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Image dimension stacking package."""
import json
import logging
import warnings
from os import environ
from pathlib import Path
from typing import Any

import filepattern as fp
import polus.images.formats.image_dimension_stacking.dimension_stacking as st
import typer

warnings.filterwarnings("ignore")

logging.basicConfig(
format="%(asctime)s - %(name)-8s - %(levelname)-8s - %(message)s",
datefmt="%d-%b-%y %H:%M:%S",
)
POLUS_LOG = getattr(logging, environ.get("POLUS_LOG", "INFO"))
logger = logging.getLogger("polus.plugins.formats.image_dimension_stacking")
logger.setLevel(POLUS_LOG)
logging.getLogger("bfio").setLevel(POLUS_LOG)


app = typer.Typer(help="Stack multi dimensional image into single image.")


def generate_preview(out_dir: Path, file_pattern: str) -> None:
"""Generate preview of the plugin outputs."""
with Path.open(Path(out_dir, "preview.json"), "w") as jfile:
out_json: dict[str, Any] = {
"filepattern": file_pattern,
"outDir": [],
}

fps = fp.FilePattern(out_dir, file_pattern)
out_name = fps.output_name()
out_json["outDir"].append(out_name)
json.dump(out_json, jfile, indent=2)


@app.command()
def main(
inp_dir: Path = typer.Option(
...,
"--inpDir",
"-i",
help="Path to input directory containing binary images.",
),
file_pattern: str = typer.Option(
".*",
"--filePattern",
"-f",
help="Filename pattern used to separate data.",
),
out_dir: Path = typer.Option(
...,
"--outDir",
"-o",
help="Output collection.",
),
preview: bool = typer.Option(
False,
"--preview",
"-p",
help="Generate preview of expected outputs.",
),
) -> None:
"""Image dimension stacking plugin."""
logger.info(f"--inpDir: {inp_dir}")
logger.info(f"--filePattern: {file_pattern}")
logger.info(f"--outDir: {out_dir}")

if not inp_dir.exists():
msg = "inpDir does not exist"
raise ValueError(msg, inp_dir)

if not out_dir.exists():
msg = "outDir does not exist"
raise ValueError(msg, out_dir)

fps = fp.FilePattern(inp_dir, file_pattern)
list_val = ["c", "t", "z"]
variables = sorted([f for f in fps.get_variables() if f in list_val])

if len(variables) == 0:
msg = "Could not detect c, t or z variables in a pattern"
raise ValueError(msg)

if variables == list_val or variables == ["z"]:
group_by = "z"

if variables == ["c", "t"] or variables == ["c"]:
group_by = "c"

if variables == ["t"]:
group_by = "t"

if preview:
generate_preview(out_dir=out_dir, file_pattern=file_pattern)

st.dimension_stacking(
inp_dir=inp_dir,
file_pattern=file_pattern,
group_by=group_by,
out_dir=out_dir,
)


if __name__ == "__main__":
app()
Loading

0 comments on commit 5d2b925

Please sign in to comment.