comparison points2binaryimage.py @ 4:4c0f16fb9f8d draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points2binaryimage/ commit a3c77a79db469c9ad18b666f2c64d6e8f573945f
author imgteam
date Wed, 25 Sep 2024 08:29:59 +0000
parents 90385ec28b34
children
comparison
equal deleted inserted replaced
3:a80f92ec6f89 4:4c0f16fb9f8d
1 import argparse 1 import argparse
2 import os 2 import os
3 import warnings 3 import warnings
4 from typing import List
4 5
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
8 10
9 11
10 def points2binaryimage(point_file, out_file, shape=[500, 500], has_header=False, invert_xy=False): 12 def find_column(df: pd.DataFrame, candidates: List[str]) -> str:
13 """
14 Returns the column name present in `df` and the list of `candidates`.
11 15
12 img = np.zeros(shape, dtype=np.int16) 16 Raises:
17 KeyError: If there is no candidate column name present in `df`, or more than one.
18 """
19 intersection = frozenset(df.columns) & frozenset(candidates)
20 if len(intersection) == 0:
21 raise KeyError(f'No such column: {", ".join(candidates)}')
22 elif len(intersection) > 1:
23 raise KeyError(f'The column names {", ".join(intersection)} are ambiguous')
24 else:
25 return next(iter(intersection))
26
27
28 def points2binaryimage(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=0xffff):
29
30 img = np.full(shape, dtype=np.uint16, fill_value=bg_value)
13 if os.path.exists(point_file) and os.path.getsize(point_file) > 0: 31 if os.path.exists(point_file) and os.path.getsize(point_file) > 0:
32
33 # Read the tabular file with information from the header
14 if has_header: 34 if has_header:
15 df = pd.read_csv(point_file, skiprows=1, header=None, delimiter="\t") 35 df = pd.read_csv(point_file, delimiter='\t')
36 pos_x_column = find_column(df, ['pos_x', 'POS_X'])
37 pos_y_column = find_column(df, ['pos_y', 'POS_Y'])
38 pos_x_list = df[pos_x_column].round().astype(int)
39 pos_y_list = df[pos_y_column].round().astype(int)
40 assert len(pos_x_list) == len(pos_y_list)
41 try:
42 radius_column = find_column(df, ['radius', 'RADIUS'])
43 radius_list = df[radius_column]
44 except KeyError:
45 radius_list = [0] * len(pos_x_list)
46
47 # Read the tabular file without header
16 else: 48 else:
17 df = pd.read_csv(point_file, header=None, delimiter="\t") 49 df = pd.read_csv(point_file, header=None, delimiter='\t')
50 pos_x_list = df[0].round().astype(int)
51 pos_y_list = df[1].round().astype(int)
52 assert len(pos_x_list) == len(pos_y_list)
53 radius_list = [0] * len(pos_x_list)
18 54
19 for i in range(0, len(df)): 55 # Optionally swap the coordinates
20 a_row = df.iloc[i] 56 if swap_xy:
21 if int(a_row[0]) < 0 or int(a_row[1]) < 0: 57 pos_x_list, pos_y_list = pos_y_list, pos_x_list
22 raise IndexError("Point {},{} is out of image with bounds {},{}.".format(int(a_row[0]), int(a_row[1]), shape[0], shape[1]))
23 58
24 if invert_xy: 59 # Perform the rasterization
25 if img.shape[0] <= int(a_row[0]) or img.shape[1] <= int(a_row[1]): 60 for y, x, radius in zip(pos_y_list, pos_x_list, radius_list):
26 raise IndexError("Point {},{} is out of image with bounds {},{}.".format(int(a_row[0]), int(a_row[1]), shape[0], shape[1])) 61
27 else: 62 if y < 0 or x < 0 or y >= shape[0] or x >= shape[1]:
28 img[int(a_row[1]), int(a_row[0])] = 32767 63 raise IndexError(f'The point x={x}, y={y} exceeds the bounds of the image (width: {shape[1]}, height: {shape[0]})')
64
65 if radius > 0:
66 mask = np.ones(shape, dtype=bool)
67 mask[y, x] = False
68 mask = (ndi.distance_transform_edt(mask) <= radius)
69 img[mask] = fg_value
29 else: 70 else:
30 if img.shape[0] <= int(a_row[1]) or img.shape[1] <= int(a_row[0]): 71 img[y, x] = fg_value
31 raise IndexError("Point {},{} is out of image with bounds {},{}.".format(int(a_row[1]), int(a_row[0]), shape[0], shape[1])) 72
32 else:
33 img[int(a_row[0]), int(a_row[1])] = 32767
34 else: 73 else:
35 raise Exception("{} is empty or does not exist.".format(point_file)) # appropriate built-in error? 74 raise Exception("{} is empty or does not exist.".format(point_file)) # appropriate built-in error?
36 75
37 with warnings.catch_warnings(): 76 with warnings.catch_warnings():
38 warnings.simplefilter("ignore") 77 warnings.simplefilter("ignore")
39 skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension 78 skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension
40 79
41 80
42 if __name__ == "__main__": 81 if __name__ == "__main__":
43 parser = argparse.ArgumentParser() 82 parser = argparse.ArgumentParser()
44 parser.add_argument('point_file', type=argparse.FileType('r'), help='label file') 83 parser.add_argument('point_file', type=argparse.FileType('r'), help='point file')
45 parser.add_argument('out_file', type=str, help='out file (TIFF)') 84 parser.add_argument('out_file', type=str, help='out file (TIFF)')
46 parser.add_argument('shapex', type=int, help='shapex') 85 parser.add_argument('shapex', type=int, help='shapex')
47 parser.add_argument('shapey', type=int, help='shapey') 86 parser.add_argument('shapey', type=int, help='shapey')
48 parser.add_argument('--has_header', dest='has_header', default=False, help='set True if CSV has header') 87 parser.add_argument('--has_header', dest='has_header', default=False, help='set True if point file has header')
49 parser.add_argument('--invert_xy', dest='invert_xy', default=False, help='invert x and y in CSV') 88 parser.add_argument('--swap_xy', dest='swap_xy', default=False, help='Swap X and Y coordinates')
50 89
51 args = parser.parse_args() 90 args = parser.parse_args()
52 91
53 # TOOL 92 # TOOL
54 points2binaryimage(args.point_file.name, args.out_file, [args.shapey, args.shapex], has_header=args.has_header, invert_xy=args.invert_xy) 93 points2binaryimage(args.point_file.name, args.out_file, (args.shapey, args.shapex), has_header=args.has_header, swap_xy=args.swap_xy)