Mercurial > repos > imgteam > binary2labelimage
comparison binary2label.py @ 6:364e235bf378 draft default tip
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/binary2labelimage/ commit f5a4de7535e433e3b0e96e0694e481b6643a54f8
| author | imgteam |
|---|---|
| date | Sat, 03 Jan 2026 14:14:28 +0000 |
| parents | 7f8102bdbfa1 |
| children |
comparison
equal
deleted
inserted
replaced
| 5:7f8102bdbfa1 | 6:364e235bf378 |
|---|---|
| 1 import argparse | 1 import giatools |
| 2 import numpy as np | |
| 3 import scipy.ndimage as ndi | |
| 2 | 4 |
| 3 import giatools | 5 # Fail early if an optional backend is not available |
| 4 import scipy.ndimage as ndi | 6 giatools.require_backend('omezarr') |
| 5 import tifffile | |
| 6 | 7 |
| 7 | 8 |
| 8 # Parse CLI parameters | 9 def label_watershed(arr: np.ndarray, **kwargs) -> np.ndarray: |
| 9 parser = argparse.ArgumentParser() | 10 import skimage.util |
| 10 parser.add_argument('input', type=str, help='input file') | 11 from skimage.feature import peak_local_max |
| 11 parser.add_argument('output', type=str, help='output file (TIFF)') | 12 from skimage.segmentation import watershed |
| 12 args = parser.parse_args() | 13 distance = ndi.distance_transform_edt(arr) |
| 14 local_max_indices = peak_local_max( | |
| 15 distance, | |
| 16 labels=arr, | |
| 17 **kwargs, | |
| 18 ) | |
| 19 local_max_mask = np.zeros(arr.shape, dtype=bool) | |
| 20 local_max_mask[tuple(local_max_indices.T)] = True | |
| 21 markers = ndi.label(local_max_mask)[0] | |
| 22 res = watershed(-distance, markers, mask=arr) | |
| 23 return skimage.util.img_as_uint(res) # converts to uint16 | |
| 13 | 24 |
| 14 # Read the input image with the original axes | |
| 15 img = giatools.Image.read(args.input) | |
| 16 img = img.normalize_axes_like( | |
| 17 img.original_axes, | |
| 18 ) | |
| 19 | 25 |
| 20 # Make sure the image is truly binary | 26 if __name__ == '__main__': |
| 21 img_arr_bin = (img.data > 0) | |
| 22 | 27 |
| 23 # Perform the labeling | 28 tool = giatools.ToolBaseplate() |
| 24 img.data = ndi.label(img_arr_bin)[0] | 29 tool.add_input_image('input') |
| 30 tool.add_output_image('output') | |
| 31 tool.parse_args() | |
| 25 | 32 |
| 26 # Write the result image (same axes as input image) | 33 # Validate the input image and the selected method |
| 27 tifffile.imwrite(args.output, img.data, metadata=dict(axes=img.axes)) | 34 try: |
| 35 input_image = tool.args.input_images['input'] | |
| 36 if (method := tool.args.params.pop('method')) == 'watershed' and input_image.shape[input_image.axes.index('Z')] > 1: | |
| 37 raise ValueError(f'Method "{method}" is not applicable to 3-D images.') | |
| 38 | |
| 39 elif input_image.shape[input_image.axes.index('C')] > 1: | |
| 40 raise ValueError('Multi-channel images are forbidden to avoid confusion with multi-channel labels (e.g., RGB labels).') | |
| 41 | |
| 42 else: | |
| 43 | |
| 44 # Choose the requested labeling method | |
| 45 match method: | |
| 46 | |
| 47 case 'cca': | |
| 48 joint_axes = 'ZYX' | |
| 49 label = lambda input_section_bin: ( # noqa: E731 | |
| 50 ndi.label(input_section_bin, **tool.args.params)[0].astype(np.uint16) | |
| 51 ) | |
| 52 | |
| 53 case 'watershed': | |
| 54 joint_axes = 'YX' | |
| 55 label = lambda input_section_bin: ( # noqa: E731 | |
| 56 label_watershed(input_section_bin, **tool.args.params) # already uint16 | |
| 57 ) | |
| 58 | |
| 59 case _: | |
| 60 raise ValueError(f'Unknown method: "{method}"') | |
| 61 | |
| 62 # Perform the labeling | |
| 63 for section in tool.run(joint_axes): | |
| 64 section['output'] = label( | |
| 65 section['input'].data > 0, # ensure that the input data is truly binary | |
| 66 ) | |
| 67 | |
| 68 # Exit and print error to stderr | |
| 69 except ValueError as err: | |
| 70 exit(err.args[0]) |
