Mercurial > repos > imgteam > points_association_nn
comparison points_association_nn.py @ 0:04e692ee53a8 draft
"planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points_association_nn/ commit db4c2a87a21f32e5d12d11e68f32773bfc06fcfd"
author | imgteam |
---|---|
date | Thu, 22 Jul 2021 22:29:47 +0000 |
parents | |
children | b30aa285ac0a |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:04e692ee53a8 |
---|---|
1 """ | |
2 Copyright 2021 Biomedical Computer Vision Group, Heidelberg University. | |
3 Author: Qi Gao (qi.gao@bioquant.uni-heidelberg.de) | |
4 | |
5 Distributed under the MIT license. | |
6 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT | |
7 | |
8 """ | |
9 | |
10 import argparse | |
11 | |
12 import numpy as np | |
13 import openpyxl # noqa: F401 | |
14 import pandas as pd | |
15 import skimage.util | |
16 | |
17 | |
18 def disk_mask(imsz, ir, ic, nbpx): | |
19 ys, xs = np.ogrid[-nbpx:nbpx + 1, -nbpx:nbpx + 1] | |
20 se = xs ** 2 + ys ** 2 <= nbpx ** 2 | |
21 mask = np.zeros(imsz, dtype=int) | |
22 if ir - nbpx < 0 or ic - nbpx < 0 or ir + nbpx + 1 > imsz[0] or ic + nbpx + 1 > imsz[1]: | |
23 mask = skimage.util.pad(mask, nbpx) | |
24 mask[ir:ir + 2 * nbpx + 1, ic:ic + 2 * nbpx + 1] = se | |
25 mask = skimage.util.crop(mask, nbpx) | |
26 else: | |
27 mask[ir - nbpx:ir + nbpx + 1, ic - nbpx:ic + nbpx + 1] = se | |
28 return mask | |
29 | |
30 | |
31 def find_nn(cim, icy, icx, nim, nbpx): | |
32 mask = disk_mask(cim.shape, icy, icx, nbpx) | |
33 iys_nim, ixs_nim = np.where(nim * mask) | |
34 if iys_nim.size == 0: | |
35 return np.NaN, np.NaN | |
36 | |
37 d2 = (icy - iys_nim) ** 2 + (icx - ixs_nim) ** 2 | |
38 I1 = np.argsort(d2) | |
39 iy_nim = iys_nim[I1[0]] | |
40 ix_nim = ixs_nim[I1[0]] | |
41 | |
42 mask = disk_mask(cim.shape, iy_nim, ix_nim, nbpx) | |
43 iys_cim, ixs_cim = np.where(cim * mask) | |
44 d2 = (iy_nim - iys_cim) ** 2 + (ix_nim - ixs_cim) ** 2 | |
45 I2 = np.argsort(d2) | |
46 if not iys_cim[I2[0]] == icy or not ixs_cim[I2[0]] == icx: | |
47 return np.NaN, np.NaN | |
48 | |
49 return iy_nim, ix_nim | |
50 | |
51 | |
52 def points_linking(fn_in, fn_out, nbpx=6, th=25, minlen=50): | |
53 data = pd.read_csv(fn_in, delimiter="\t") | |
54 all_data = np.array(data) | |
55 assert all_data.shape[1] in [3, 4], 'unknow collum(s) in input data!' | |
56 | |
57 coords = all_data[:, :3].astype('int64') | |
58 | |
59 frame_1st = np.min(coords[:, 0]) | |
60 frame_end = np.max(coords[:, 0]) | |
61 assert set([i for i in range(frame_1st, frame_end + 1)]).issubset(set(coords[:, 0].tolist())), "spots missing at some time point!" | |
62 | |
63 nSlices = frame_end | |
64 stack_h = np.max(coords[:, 2]) + nbpx | |
65 stack_w = np.max(coords[:, 1]) + nbpx | |
66 stack = np.zeros((stack_h, stack_w, nSlices), dtype='int8') | |
67 stack_r = np.zeros((stack_h, stack_w, nSlices), dtype='float64') | |
68 | |
69 for i in range(all_data.shape[0]): | |
70 iyxz = tuple(coords[i, ::-1] - 1) | |
71 stack[iyxz] = 1 | |
72 stack_r[iyxz] = all_data[i, -1] | |
73 | |
74 tracks_all = np.array([], dtype=float).reshape(0, nSlices, 4) | |
75 maxv = np.max(stack_r) | |
76 br_max = maxv | |
77 idx_max = np.argmax(stack_r) | |
78 while 1: | |
79 iyxz = np.unravel_index(idx_max, stack.shape) | |
80 | |
81 spot_br = np.empty((nSlices, 1)) | |
82 track = np.empty((nSlices, 3)) | |
83 for i in range(nSlices): | |
84 spot_br[i] = np.NaN | |
85 track[i, :] = np.array((np.NaN, np.NaN, np.NaN)) | |
86 | |
87 spot_br[iyxz[2]] = maxv | |
88 track[iyxz[2], :] = np.array(iyxz[::-1]) + 1 | |
89 | |
90 # forward | |
91 icy = iyxz[0] | |
92 icx = iyxz[1] | |
93 for inz in range(iyxz[2] + 1, nSlices): | |
94 iny, inx = find_nn(stack[:, :, inz - 1], icy, icx, stack[:, :, inz], nbpx) | |
95 if np.isnan(iny) and not inz == nSlices - 1: | |
96 iny, inx = find_nn(stack[:, :, inz - 1], icy, icx, stack[:, :, inz + 1], nbpx) | |
97 if np.isnan(iny): | |
98 break | |
99 else: | |
100 iny = icy | |
101 inx = icx | |
102 stack[iny, inx, inz] = 1 | |
103 stack_r[iny, inx, inz] = stack_r[iny, inx, inz - 1] | |
104 elif np.isnan(iny) and inz == nSlices - 1: | |
105 break | |
106 | |
107 track[inz, :] = np.array((inz, inx, iny)) + 1 | |
108 spot_br[inz] = stack_r[iny, inx, inz] | |
109 icy = iny | |
110 icx = inx | |
111 | |
112 # backward | |
113 icy = iyxz[0] | |
114 icx = iyxz[1] | |
115 for inz in range(iyxz[2] - 1, -1, -1): | |
116 iny, inx = find_nn(stack[:, :, inz + 1], icy, icx, stack[:, :, inz], nbpx) | |
117 if np.isnan(iny) and not inz == 0: | |
118 iny, inx = find_nn(stack[:, :, inz + 1], icy, icx, stack[:, :, inz - 1], nbpx) | |
119 if np.isnan(iny): | |
120 break | |
121 else: | |
122 iny = icy | |
123 inx = icx | |
124 stack[iny, inx, inz] = 1 | |
125 stack_r[iny, inx, inz] = stack_r[iny, inx, inz + 1] | |
126 elif np.isnan(iny) and inz == 0: | |
127 break | |
128 | |
129 track[inz, :] = np.array((inz, inx, iny)) + 1 | |
130 spot_br[inz] = stack_r[iny, inx, inz] | |
131 icy = iny | |
132 icx = inx | |
133 | |
134 for iz in range(nSlices): | |
135 if not np.isnan(track[iz, 0]): | |
136 stack[track[iz, 2].astype(int) - 1, track[iz, 1].astype(int) - 1, iz] = 0 | |
137 stack_r[track[iz, 2].astype(int) - 1, track[iz, 1].astype(int) - 1, iz] = 0 | |
138 | |
139 # discard short trajectories | |
140 if np.count_nonzero(~np.isnan(spot_br)) > minlen * (frame_end - frame_1st) / 100: | |
141 tmp = np.concatenate((track, spot_br), axis=1) | |
142 tracks_all = np.concatenate((tracks_all, tmp.reshape(1, -1, 4)), axis=0) | |
143 | |
144 maxv = np.max(stack_r) | |
145 idx_max = np.argmax(stack_r) | |
146 if maxv < th * br_max / 100: | |
147 break | |
148 | |
149 with pd.ExcelWriter(fn_out, engine="openpyxl") as writer: | |
150 for i in range(tracks_all.shape[0]): | |
151 df = pd.DataFrame() | |
152 df['FRAME'] = tracks_all[i, :, 0] | |
153 df['POS_X'] = tracks_all[i, :, 1] | |
154 df['POS_Y'] = tracks_all[i, :, 2] | |
155 df['INTENSITY'] = tracks_all[i, :, 3] | |
156 df.to_excel(writer, sheet_name='spot%s' % (i + 1), index=False, float_format='%.2f') | |
157 writer.save() | |
158 | |
159 | |
160 if __name__ == "__main__": | |
161 parser = argparse.ArgumentParser(description="Association of points in consecutive frames using the nearest neighbor algorithm") | |
162 parser.add_argument("fn_in", help="Name of input file (tsv tabular)") | |
163 parser.add_argument("fn_out", help="Name of output file (xlsx)") | |
164 parser.add_argument("nbpx", type=int, help="Neighborhood size in pixel") | |
165 parser.add_argument("thres", type=float, help="Percentage of the global maximal intensity for thresholding some event") | |
166 parser.add_argument("minlen", type=float, help="Minimum length of tracks (percentage of senquence length)") | |
167 args = parser.parse_args() | |
168 points_linking(args.fn_in, args.fn_out, args.nbpx, args.thres, args.minlen) |