comparison points2label.py @ 3:de611b3b5ae8 draft default tip

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points2labelimage/ commit 6fc9ab8db9ef72ac7ded30d7373768feeae9390d
author imgteam
date Fri, 27 Sep 2024 17:41:21 +0000
parents 30ca5d5d03ec
children
comparison
equal deleted inserted replaced
2:30ca5d5d03ec 3:de611b3b5ae8
1 import argparse 1 import argparse
2 import sys 2 import os
3 import warnings 3 import warnings
4 4
5 import giatools.pandas
5 import numpy as np 6 import numpy as np
6 import pandas as pd 7 import pandas as pd
8 import scipy.ndimage as ndi
7 import skimage.io 9 import skimage.io
10 import skimage.segmentation
8 11
9 12
10 def points2label(labels, shape, output_file=None, has_header=False, is_TSV=True): 13 def rasterize(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=None):
11 labelimg = np.zeros([shape[0], shape[1]], dtype=np.int32)
12 14
13 if is_TSV: 15 img = np.full(shape, dtype=np.uint16, fill_value=bg_value)
16 if os.path.exists(point_file) and os.path.getsize(point_file) > 0:
17
18 # Read the tabular file with information from the header
14 if has_header: 19 if has_header:
15 df = pd.read_csv(labels, sep='\t', skiprows=1, header=None) 20 df = pd.read_csv(point_file, delimiter='\t')
21
22 pos_x_column = giatools.pandas.find_column(df, ['pos_x', 'POS_X'])
23 pos_y_column = giatools.pandas.find_column(df, ['pos_y', 'POS_Y'])
24 pos_x_list = df[pos_x_column].round().astype(int)
25 pos_y_list = df[pos_y_column].round().astype(int)
26 assert len(pos_x_list) == len(pos_y_list)
27
28 try:
29 radius_column = giatools.pandas.find_column(df, ['radius', 'RADIUS'])
30 radius_list = df[radius_column]
31 assert len(pos_x_list) == len(radius_list)
32 except KeyError:
33 radius_list = [0] * len(pos_x_list)
34
35 try:
36 label_column = giatools.pandas.find_column(df, ['label', 'LABEL'])
37 label_list = df[label_column]
38 assert len(pos_x_list) == len(label_list)
39 except KeyError:
40 label_list = list(range(1, len(pos_x_list) + 1))
41
42 # Read the tabular file without header
16 else: 43 else:
17 df = pd.read_csv(labels, sep='\t', header=None) 44 df = pd.read_csv(point_file, header=None, delimiter='\t')
45 pos_x_list = df[0].round().astype(int)
46 pos_y_list = df[1].round().astype(int)
47 assert len(pos_x_list) == len(pos_y_list)
48 radius_list = [0] * len(pos_x_list)
49 label_list = list(range(1, len(pos_x_list) + 1))
50
51 # Optionally swap the coordinates
52 if swap_xy:
53 pos_x_list, pos_y_list = pos_y_list, pos_x_list
54
55 # Perform the rasterization
56 for y, x, radius, label in zip(pos_y_list, pos_x_list, radius_list, label_list):
57 if fg_value is not None:
58 label = fg_value
59
60 if y < 0 or x < 0 or y >= shape[0] or x >= shape[1]:
61 raise IndexError(f'The point x={x}, y={y} exceeds the bounds of the image (width: {shape[1]}, height: {shape[0]})')
62
63 # Rasterize circle and distribute overlapping image area
64 if radius > 0:
65 mask = np.ones(shape, dtype=bool)
66 mask[y, x] = False
67 mask = (ndi.distance_transform_edt(mask) <= radius)
68
69 # Compute the overlap (pretend there is none if the rasterization is binary)
70 if fg_value is None:
71 overlap = np.logical_and(img > 0, mask)
72 else:
73 overlap = np.zeros(shape, dtype=bool)
74
75 # Rasterize the part of the circle which is disjoint from other foreground.
76 #
77 # In the current implementation, the result depends on the order of the rasterized circles if somewhere
78 # more than two circles overlap. This is probably negligable for most applications. To achieve results
79 # that are invariant to the order, first all circles would need to be rasterized independently, and
80 # then blended together. This, however, would either strongly increase the memory consumption, or
81 # require a more complex implementation which exploits the sparsity of the rasterized masks.
82 #
83 disjoint_mask = np.logical_xor(mask, overlap)
84 if disjoint_mask.any():
85 img[disjoint_mask] = label
86
87 # Distribute the remaining part of the circle
88 if overlap.any():
89 dist = ndi.distance_transform_edt(overlap)
90 foreground = (img > 0)
91 img[overlap] = 0
92 img = skimage.segmentation.watershed(dist, img, mask=foreground)
93
94 # Rasterize point (there is no overlapping area to be distributed)
95 else:
96 img[y, x] = label
97
18 else: 98 else:
19 if has_header: 99 raise Exception("{} is empty or does not exist.".format(point_file)) # appropriate built-in error?
20 df = pd.read_csv(labels, skiprows=1, header=None)
21 else:
22 df = pd.read_csv(labels, header=None)
23 100
24 for i in range(0, len(df)): 101 with warnings.catch_warnings():
25 a_row = df.iloc[i] 102 warnings.simplefilter("ignore")
26 labelimg[a_row[0], a_row[1]] = i + 1 103 skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension
27
28 if output_file is not None:
29 with warnings.catch_warnings():
30 warnings.simplefilter("ignore")
31 skimage.io.imsave(output_file, labelimg, plugin='tifffile')
32 else:
33 return labelimg
34 104
35 105
36 if __name__ == "__main__": 106 if __name__ == "__main__":
37 parser = argparse.ArgumentParser() 107 parser = argparse.ArgumentParser()
38 parser.add_argument('label_file', type=argparse.FileType('r'), default=sys.stdin, help='label file') 108 parser.add_argument('point_file', type=argparse.FileType('r'), help='point file')
39 parser.add_argument('out_file', type=argparse.FileType('w'), default=sys.stdin, help='out file') 109 parser.add_argument('out_file', type=str, help='out file (TIFF)')
40 parser.add_argument('org_file', type=argparse.FileType('r'), default=sys.stdin, help='input original file') 110 parser.add_argument('shapex', type=int, help='shapex')
41 parser.add_argument('--has_header', dest='has_header', type=bool, default=False, help='label file has header') 111 parser.add_argument('shapey', type=int, help='shapey')
42 parser.add_argument('--is_tsv', dest='is_tsv', type=bool, default=True, help='label file is TSV') 112 parser.add_argument('--has_header', dest='has_header', default=False, help='set True if point file has header')
113 parser.add_argument('--swap_xy', dest='swap_xy', default=False, help='Swap X and Y coordinates')
114 parser.add_argument('--binary', dest='binary', default=False, help='Produce binary image')
115
43 args = parser.parse_args() 116 args = parser.parse_args()
44 117
45 original_shape = skimage.io.imread(args.org_file.name, plugin='tifffile').shape 118 rasterize(
46 119 args.point_file.name,
47 points2label(args.label_file.name, original_shape, args.out_file.name, args.has_header, args.is_tsv) 120 args.out_file,
121 (args.shapey, args.shapex),
122 has_header=args.has_header,
123 swap_xy=args.swap_xy,
124 fg_value=0xffff if args.binary else None,
125 )