Mercurial > repos > imgteam > points2labelimage
comparison points2label.py @ 4:64c155acb864 draft default tip
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points2labelimage/ commit 9a40a5d1e1008c26cc327c6d163df2a1af22a1a0
author | imgteam |
---|---|
date | Mon, 12 May 2025 14:01:09 +0000 |
parents | 2ae122d5d85a |
children |
comparison
equal
deleted
inserted
replaced
3:2ae122d5d85a | 4:64c155acb864 |
---|---|
1 import argparse | 1 import argparse |
2 import json | |
2 import os | 3 import os |
3 import warnings | 4 import warnings |
5 from typing import ( | |
6 Dict, | |
7 List, | |
8 Tuple, | |
9 Union, | |
10 ) | |
4 | 11 |
5 import giatools.pandas | 12 import giatools.pandas |
6 import numpy as np | 13 import numpy as np |
14 import numpy.typing as npt | |
7 import pandas as pd | 15 import pandas as pd |
8 import scipy.ndimage as ndi | 16 import scipy.ndimage as ndi |
9 import skimage.io | 17 import skimage.io |
10 import skimage.segmentation | 18 import skimage.segmentation |
19 | |
20 | |
21 def is_rectangular(points: Union[List[Tuple[float, float]], npt.NDArray]) -> bool: | |
22 points = np.asarray(points) | |
23 | |
24 # Rectangle must have 5 points, where first and last are identical | |
25 if len(points) != 5 or not (points[0] == points[-1]).all(): | |
26 return False | |
27 | |
28 # Check that all edges align with the axes | |
29 edges = points[1:] - points[:-1] | |
30 if any((edge == 0).sum() != 1 for edge in edges): | |
31 return False | |
32 | |
33 # All checks have passed, the geometry is rectangular | |
34 return True | |
35 | |
36 | |
37 def geojson_to_tabular(geojson: Dict): | |
38 rows = [] | |
39 labels = [] | |
40 for feature in geojson['features']: | |
41 assert feature['geometry']['type'].lower() == 'polygon', ( | |
42 f'Unsupported geometry type: "{feature["geometry"]["type"]}"' | |
43 ) | |
44 coords = feature['geometry']['coordinates'][0] | |
45 | |
46 # Properties and name (label) are optional | |
47 try: | |
48 label = feature['properties']['name'] | |
49 except KeyError: | |
50 label = max(labels, default=0) + 1 | |
51 labels.append(label) | |
52 | |
53 # Read geometry | |
54 xs = [pt[0] for pt in coords] | |
55 ys = [pt[1] for pt in coords] | |
56 | |
57 x = min(xs) | |
58 y = min(ys) | |
59 | |
60 width = max(xs) + 1 - x | |
61 height = max(ys) + 1 - y | |
62 | |
63 # Validate geometry (must be rectangular) | |
64 assert is_rectangular(list(zip(xs, ys))) | |
65 | |
66 # Append the rectangle | |
67 rows.append({ | |
68 'pos_x': x, | |
69 'pos_y': y, | |
70 'width': width, | |
71 'height': height, | |
72 'label': label, | |
73 }) | |
74 df = pd.DataFrame(rows) | |
75 point_file = './point_file.tabular' | |
76 df.to_csv(point_file, sep='\t', index=False) | |
77 return point_file | |
11 | 78 |
12 | 79 |
13 def rasterize(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=None): | 80 def rasterize(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=None): |
14 | 81 |
15 img = np.full(shape, dtype=np.uint16, fill_value=bg_value) | 82 img = np.full(shape, dtype=np.uint16, fill_value=bg_value) |
120 # Rasterize point (there is no overlapping area to be distributed) | 187 # Rasterize point (there is no overlapping area to be distributed) |
121 else: | 188 else: |
122 img[y, x] = label | 189 img[y, x] = label |
123 | 190 |
124 else: | 191 else: |
125 raise Exception("{} is empty or does not exist.".format(point_file)) # appropriate built-in error? | 192 raise Exception('{} is empty or does not exist.'.format(point_file)) # appropriate built-in error? |
126 | 193 |
127 with warnings.catch_warnings(): | 194 with warnings.catch_warnings(): |
128 warnings.simplefilter("ignore") | 195 warnings.simplefilter("ignore") |
129 skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension | 196 skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension |
130 | 197 |
131 | 198 |
132 if __name__ == "__main__": | 199 if __name__ == '__main__': |
133 parser = argparse.ArgumentParser() | 200 parser = argparse.ArgumentParser() |
134 parser.add_argument('point_file', type=argparse.FileType('r'), help='point file') | 201 parser.add_argument('in_file', type=argparse.FileType('r'), help='Input point file or GeoJSON file') |
135 parser.add_argument('out_file', type=str, help='out file (TIFF)') | 202 parser.add_argument('out_file', type=str, help='out file (TIFF)') |
136 parser.add_argument('shapex', type=int, help='shapex') | 203 parser.add_argument('shapex', type=int, help='shapex') |
137 parser.add_argument('shapey', type=int, help='shapey') | 204 parser.add_argument('shapey', type=int, help='shapey') |
138 parser.add_argument('--has_header', dest='has_header', default=False, help='set True if point file has header') | 205 parser.add_argument('--has_header', dest='has_header', default=False, help='set True if point file has header') |
139 parser.add_argument('--swap_xy', dest='swap_xy', default=False, help='Swap X and Y coordinates') | 206 parser.add_argument('--swap_xy', dest='swap_xy', default=False, help='Swap X and Y coordinates') |
140 parser.add_argument('--binary', dest='binary', default=False, help='Produce binary image') | 207 parser.add_argument('--binary', dest='binary', default=False, help='Produce binary image') |
141 | 208 |
142 args = parser.parse_args() | 209 args = parser.parse_args() |
143 | 210 |
211 point_file = args.in_file.name | |
212 has_header = args.has_header | |
213 | |
214 try: | |
215 with open(args.in_file.name, 'r') as f: | |
216 content = json.load(f) | |
217 if isinstance(content, dict) and content.get('type') == 'FeatureCollection' and isinstance(content.get('features'), list): | |
218 point_file = geojson_to_tabular(content) | |
219 has_header = True # header included in the converted file | |
220 else: | |
221 raise ValueError('Input is a JSON file but not a valid GeoJSON file') | |
222 except json.JSONDecodeError: | |
223 print('Input is not a valid JSON file. Assuming it a tabular file.') | |
224 | |
144 rasterize( | 225 rasterize( |
145 args.point_file.name, | 226 point_file, |
146 args.out_file, | 227 args.out_file, |
147 (args.shapey, args.shapex), | 228 (args.shapey, args.shapex), |
148 has_header=args.has_header, | 229 has_header=has_header, |
149 swap_xy=args.swap_xy, | 230 swap_xy=args.swap_xy, |
150 fg_value=0xffff if args.binary else None, | 231 fg_value=0xffff if args.binary else None, |
151 ) | 232 ) |