Mercurial > repos > imgteam > crop_image
changeset 0:f8bfa85cac4c draft default tip
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/crop_image/ commit 7a5037206d267aa7d9b7e5e062327c3464942471
| author | imgteam |
|---|---|
| date | Fri, 06 Jun 2025 12:46:50 +0000 |
| parents | |
| children | |
| files | creators.xml crop_image.py crop_image.xml test-data/yx_float32.tiff test-data/yx_float32_uint8_0.tiff test-data/yx_float32_uint8_1.tiff test-data/yx_float32_uint8_2.tiff test-data/yx_uint8.tiff test-data/yxc_uint8.png test-data/yxc_uint8_mask.png test-data/yxc_uint8_uint8_2.png test-data/yxz_uint8.tiff test-data/zyx_uint16.tiff test-data/zyx_uint16_uint8_1.tiff tests.xml |
| diffstat | 15 files changed, 301 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/creators.xml Fri Jun 06 12:46:50 2025 +0000 @@ -0,0 +1,33 @@ +<macros> + + <xml name="creators/bmcv"> + <organization name="Biomedical Computer Vision Group, Heidelberg Universtiy" alternateName="BMCV" url="http://www.bioquant.uni-heidelberg.de/research/groups/biomedical_computer_vision.html" /> + <yield /> + </xml> + + <xml name="creators/rmassei"> + <person givenName="Riccardo" familyName="Massei"/> + <yield/> + </xml> + + <xml name="creators/alliecreason"> + <person givenName="Allison" familyName="Creason"/> + <yield/> + </xml> + + <xml name="creators/bugraoezdemir"> + <person givenName="Bugra" familyName="Oezdemir"/> + <yield/> + </xml> + + <xml name="creators/thawn"> + <person givenName="Till" familyName="Korten"/> + <yield/> + </xml> + + <xml name="creators/pavanvidem"> + <person givenName="Pavan" familyName="Videm"/> + <yield/> + </xml> + +</macros>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crop_image.py Fri Jun 06 12:46:50 2025 +0000 @@ -0,0 +1,68 @@ +import argparse +import os + +import numpy as np +from giatools.image import Image + + +def crop_image( + image_filepath: str, + labelmap_filepath: str, + output_ext: str, + output_dir: str, + skip_labels: frozenset[int], +): + image = Image.read(image_filepath) + labelmap = Image.read(labelmap_filepath) + + if image.axes != labelmap.axes: + raise ValueError(f'Axes mismatch between image ({image.axes}) and label map ({labelmap.axes}).') + + if image.data.shape != labelmap.data.shape: + raise ValueError(f'Shape mismatch between image ({image.data.shape}) and label map ({labelmap.data.shape}).') + + for label in np.unique(labelmap.data): + if label in skip_labels: + continue + roi_mask = (labelmap.data == label) + roi = crop_image_to_mask(image.data, roi_mask) + roi_image = Image(roi, image.axes).normalize_axes_like(image.original_axes) + roi_image.write(os.path.join(output_dir, f'{label}.{output_ext}')) + + +def crop_image_to_mask(data: np.ndarray, mask: np.ndarray) -> np.ndarray: + """ + Crop the `data` array to the minimal bounding box in `mask`. + + The arguments are not modified. + """ + assert data.shape == mask.shape + + # Crop `data` to the convex hull of the mask in each dimension + for dim in range(data.ndim): + mask1d = mask.any(axis=tuple(i for i in range(mask.ndim) if i != dim)) + mask1d_indices = np.where(mask1d)[0] + mask1d_indices_cvxhull = np.arange(min(mask1d_indices), max(mask1d_indices) + 1) + data = data.take(axis=dim, indices=mask1d_indices_cvxhull) + + return data + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('image', type=str) + parser.add_argument('labelmap', type=str) + parser.add_argument('skip_labels', type=str) + parser.add_argument('output_ext', type=str) + parser.add_argument('output_dir', type=str) + args = parser.parse_args() + + crop_image( + image_filepath=args.image, + labelmap_filepath=args.labelmap, + output_ext=args.output_ext, + output_dir=args.output_dir, + skip_labels=frozenset( + int(label.strip()) for label in args.skip_labels.split(',') if label.strip() + ) if args.skip_labels.strip() else frozenset(), + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crop_image.xml Fri Jun 06 12:46:50 2025 +0000 @@ -0,0 +1,105 @@ +<tool id="ip_crop_image" name="Crop image" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05"> + <description>with giatools</description> + <macros> + <import>creators.xml</import> + <import>tests.xml</import> + <token name="@TOOL_VERSION@">0.4.1</token> + <token name="@VERSION_SUFFIX@">0</token> + </macros> + <creator> + <expand macro="creators/bmcv" /> + </creator> + <edam_operations> + <edam_operation>operation_3443</edam_operation> + </edam_operations> + <xrefs> + <xref type="bio.tools">giatools</xref> + </xrefs> + <requirements> + <requirement type="package" version="@TOOL_VERSION@">giatools</requirement> + </requirements> + <command detect_errors="aggressive"><![CDATA[ + + mkdir ./output && + python '$__tool_directory__/crop_image.py' + + '$image' + '$labelmap' + '$skip_labels' + '${image.ext}' + + ./output + + ]]></command> + <inputs> + <param name="image" type="data" format="png,tiff" label="Image file" help="The image to be cropped."/> + <param name="labelmap" type="data" format="png,tiff" label="Label map" help="Each label identifies an individual region of interest, for which a cropped image is produced."/> + <param name="skip_labels" type="text" label="Skip labels" value="0" optional="true" help="Comma-separated list of labels for which no cropped image shall be produced."> + <validator type="regex">^\d+(,\d+)*$|^$</validator> + </param> + </inputs> + <outputs> + <collection type="list" name="output" label="Crop ${on_string}" format_source="image"> + <discover_datasets directory="output" pattern="__name_and_ext__"/> + </collection> + </outputs> + <tests> + <!-- Test 2D TIFF --> + <test> + <param name="image" value="yx_float32.tiff" ftype="tiff"/> + <param name="labelmap" value="yx_uint8.tiff"/> + <output_collection name="output" type="list" count="2"> + <expand macro="tests/intensity_image_diff/element" name="1" value="yx_float32_uint8_1.tiff" ftype="tiff"/> + <expand macro="tests/intensity_image_diff/element" name="2" value="yx_float32_uint8_2.tiff" ftype="tiff"/> + </output_collection> + </test> + <!-- Test with `skip_labels` --> + <test> + <param name="image" value="yx_float32.tiff" ftype="tiff"/> + <param name="labelmap" value="yx_uint8.tiff"/> + <param name="skip_labels" value="0,1"/> + <output_collection name="output" type="list" count="1"> + <expand macro="tests/intensity_image_diff/element" name="2" value="yx_float32_uint8_2.tiff" ftype="tiff"/> + </output_collection> + </test> + <!-- Test with empty `skip_labels` --> + <test> + <param name="image" value="yx_float32.tiff" ftype="tiff"/> + <param name="labelmap" value="yx_uint8.tiff"/> + <param name="skip_labels" value=""/> + <output_collection name="output" type="list" count="3"> + <expand macro="tests/intensity_image_diff/element" name="0" value="yx_float32_uint8_0.tiff" ftype="tiff"/> + <expand macro="tests/intensity_image_diff/element" name="1" value="yx_float32_uint8_1.tiff" ftype="tiff"/> + <expand macro="tests/intensity_image_diff/element" name="2" value="yx_float32_uint8_2.tiff" ftype="tiff"/> + </output_collection> + </test> + <!-- Test 3D TIFF (multi-frame) --> + <test> + <param name="image" value="zyx_uint16.tiff" ftype="tiff"/> + <param name="labelmap" value="yxz_uint8.tiff"/> + <output_collection name="output" type="list" count="1"> + <expand macro="tests/intensity_image_diff/element" name="1" value="zyx_uint16_uint8_1.tiff" ftype="tiff"/> + </output_collection> + </test> + <!-- Test PNG --> + <test> + <param name="image" value="yxc_uint8.png" ftype="png"/> + <param name="labelmap" value="yxc_uint8_mask.png"/> + <output_collection name="output" type="list" count="1"> + <expand macro="tests/intensity_image_diff/element" name="2" value="yxc_uint8_uint8_2.png" ftype="png"/> + </output_collection> + </test> + </tests> + <help> + + **Crops an image using one or more regions of interest.** + + The image is cropped using a label map that identifies individual regions of interest. The image and the label map must be of equal size. + + This operation preserves the file type of the image, the brightness, and the range of values. + + </help> + <citations> + <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation> + </citations> +</tool>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests.xml Fri Jun 06 12:46:50 2025 +0000 @@ -0,0 +1,95 @@ +<macros> + + <!-- Macros for verification of image outputs --> + + <xml + name="tests/binary_image_diff" + tokens="name,value,ftype,metric,eps" + token_metric="mae" + token_eps="0.01"> + + <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="0"> + <assert_contents> + <has_image_n_labels n="2"/> + <yield/> + </assert_contents> + </output> + + </xml> + + <xml + name="tests/label_image_diff" + tokens="name,value,ftype,metric,eps,pin_labels" + token_metric="iou" + token_eps="0.01" + token_pin_labels="0"> + + <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="@PIN_LABELS@"> + <assert_contents> + <yield/> + </assert_contents> + </output> + + </xml> + + <xml + name="tests/intensity_image_diff" + tokens="name,value,ftype,metric,eps" + token_metric="rms" + token_eps="0.01"> + + <output name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@"> + <assert_contents> + <yield/> + </assert_contents> + </output> + + </xml> + + <!-- Variants of the above for verification of collection elements --> + + <xml + name="tests/binary_image_diff/element" + tokens="name,value,ftype,metric,eps" + token_metric="mae" + token_eps="0.01"> + + <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="0"> + <assert_contents> + <has_image_n_labels n="2"/> + <yield/> + </assert_contents> + </element> + + </xml> + + <xml + name="tests/label_image_diff/element" + tokens="name,value,ftype,metric,eps" + token_metric="iou" + token_eps="0.01" + token_pin_labels="0"> + + <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@" pin_labels="@PIN_LABELS@"> + <assert_contents> + <yield/> + </assert_contents> + </element> + + </xml> + + <xml + name="tests/intensity_image_diff/element" + tokens="name,value,ftype,metric,eps" + token_metric="rms" + token_eps="0.01"> + + <element name="@NAME@" value="@VALUE@" ftype="@FTYPE@" compare="image_diff" metric="@METRIC@" eps="@EPS@"> + <assert_contents> + <yield/> + </assert_contents> + </element> + + </xml> + +</macros>
