Mercurial > repos > imgteam > morphological_operations
comparison morphological_operations.py @ 0:f10112b317a1 draft
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/morphological_operations commit c6d2a4fb19b54c804029ae3d52eb9fb2619e4265
| author | imgteam |
|---|---|
| date | Fri, 08 Mar 2024 11:00:41 +0000 |
| parents | |
| children | 4e25befab102 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:f10112b317a1 |
|---|---|
| 1 import argparse | |
| 2 | |
| 3 import numpy as np | |
| 4 import scipy.ndimage as ndi | |
| 5 import skimage.io | |
| 6 import skimage.morphology as morph | |
| 7 | |
| 8 | |
| 9 def create_selem(args): | |
| 10 """ | |
| 11 Creates structuring element based on commandline arguments. | |
| 12 """ | |
| 13 assert args.selem_shape in ( | |
| 14 'square', | |
| 15 'disk', | |
| 16 ) | |
| 17 | |
| 18 if args.selem_shape == 'square': | |
| 19 return np.ones((args.selem_size, args.selem_size)) | |
| 20 | |
| 21 elif args.selem_shape == 'disk': | |
| 22 return morph.disk(args.selem_size) | |
| 23 | |
| 24 | |
| 25 def apply_operation(args, im): | |
| 26 """ | |
| 27 Applies morphological operation to a 2-D single-channel image. | |
| 28 """ | |
| 29 assert im.ndim == 2 | |
| 30 selem = create_selem(args) | |
| 31 values_count = len(np.unique(im)) | |
| 32 if values_count <= 2: | |
| 33 im_proxy = np.zeros(im.shape, bool) | |
| 34 im_proxy[im == im.max()] = True | |
| 35 result_proxy = apply_binary_operation(args, im_proxy, selem) | |
| 36 result = np.full(im.shape, im.min(), im.dtype) | |
| 37 result[result_proxy] = im.max() | |
| 38 return result | |
| 39 else: | |
| 40 return apply_intensity_based_operation(args, im, selem) | |
| 41 | |
| 42 | |
| 43 def apply_intensity_based_operation(args, im, selem): | |
| 44 operations = { | |
| 45 'erosion': ndi.grey_erosion, | |
| 46 'dilation': ndi.grey_dilation, | |
| 47 'opening': ndi.grey_opening, | |
| 48 'closing': ndi.grey_closing, | |
| 49 } | |
| 50 if args.operation in operations: | |
| 51 operation = operations[args.operation] | |
| 52 return operation(input=im, structure=selem) | |
| 53 else: | |
| 54 raise ValueError(f'Operation "{args.operation}" not supported for this image type ({im.dtype}).') | |
| 55 | |
| 56 | |
| 57 def apply_binary_operation(args, im, selem): | |
| 58 operations = { | |
| 59 'erosion': ndi.binary_erosion, | |
| 60 'dilation': ndi.binary_dilation, | |
| 61 'opening': ndi.binary_opening, | |
| 62 'closing': ndi.binary_closing, | |
| 63 'fill_holes': ndi.binary_fill_holes, | |
| 64 } | |
| 65 operation = operations[args.operation] | |
| 66 return operation(input=im, structure=selem) | |
| 67 | |
| 68 | |
| 69 if __name__ == '__main__': | |
| 70 | |
| 71 parser = argparse.ArgumentParser() | |
| 72 parser.add_argument('--operation', type=str) | |
| 73 parser.add_argument('--selem-shape', type=str) | |
| 74 parser.add_argument('--selem-size', type=int) | |
| 75 parser.add_argument('input', type=str) | |
| 76 parser.add_argument('output', type=str) | |
| 77 args = parser.parse_args() | |
| 78 | |
| 79 im = skimage.io.imread(args.input) | |
| 80 assert im.ndim in (2, 3), 'Input image must be two-dimensional and either single-channel or multi-channel.' | |
| 81 | |
| 82 if im.ndim == 2: | |
| 83 im_result = apply_operation(args, im) | |
| 84 | |
| 85 else: | |
| 86 ch_result_list = [] | |
| 87 for ch_idx in range(im.shape[2]): | |
| 88 ch = im[:, :, ch_idx] | |
| 89 ch_result = apply_operation(args, ch) | |
| 90 ch_result_list.append(ch_result) | |
| 91 im_result = np.dstack(ch_result_list) | |
| 92 | |
| 93 skimage.io.imsave(args.output, im_result) |
