This checks that a decent number of spots are detected on:
- Each channel across all rounds and tiles.
- Each tile across all rounds and channels.
- Each round across all tile and channels.
An error will be raised if any of these conditions are violated.
config['find_spots']['n_spots_warn_fraction']
and config['find_spots']['n_spots_error_fraction']
are the parameters which determine if warnings/errors will be raised.
Parameters:
Name |
Type |
Description |
Default |
nb |
Notebook
|
Notebook containing find_spots page. |
required
|
Source code in coppafish/find_spots/check_spots.py
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 | def check_n_spots(nb: Notebook):
"""
This checks that a decent number of spots are detected on:
* Each channel across all rounds and tiles.
* Each tile across all rounds and channels.
* Each round across all tile and channels.
An error will be raised if any of these conditions are violated.
`config['find_spots']['n_spots_warn_fraction']` and `config['find_spots']['n_spots_error_fraction']`
are the parameters which determine if warnings/errors will be raised.
Args:
nb: *Notebook* containing `find_spots` page.
"""
# TODO: show example of what error looks like in the docs
config = nb.get_config()['find_spots']
if nb.basic_info.is_3d:
n_spots_warn = config['n_spots_warn_fraction'] * config['max_spots_3d'] * nb.basic_info.nz
else:
n_spots_warn = config['n_spots_warn_fraction'] * config['max_spots_2d']
n_spots_warn = int(np.ceil(n_spots_warn))
use_tiles = np.asarray(nb.basic_info.use_tiles)
error_message = ""
if len(nb.basic_info.use_rounds) > 0:
use_rounds = np.asarray(nb.basic_info.use_rounds) # don't consider anchor in this analysis
use_channels = np.asarray(nb.basic_info.use_channels)
spot_no = nb.find_spots.spot_no[np.ix_(use_tiles, use_rounds, use_channels)]
# Consider bad channels first as most likely to have consistently low spot counts in a channel
n_images = len(use_tiles) * len(use_rounds)
n_images_error = int(np.floor(n_images * config['n_spots_error_fraction']))
n_bad_images = np.zeros(len(use_channels), dtype=int)
for c in range(len(use_channels)):
bad_images = np.vstack(np.where(spot_no[:, :, c] < n_spots_warn)).T
n_bad_images[c] = bad_images.shape[0]
if n_bad_images[c] > 0:
bad_images[:, 0] = use_tiles[bad_images[:, 0]]
bad_images[:, 1] = use_rounds[bad_images[:, 1]]
warnings.warn(f"\nChannel {use_channels[c]} - {n_bad_images[c]} tiles/rounds with n_spots < {n_spots_warn}:"
f"\n{bad_images}")
fail_inds = np.where(n_bad_images >= n_images_error)[0]
if len(fail_inds) > 0:
error_message = error_message + f"\nChannels that failed: {use_channels[fail_inds]}\n" \
f"This is because out of {n_images} tiles/rounds, these channels had " \
f"respectively:\n{n_bad_images[fail_inds]}\ntiles/rounds with " \
f"n_spots < {n_spots_warn}. These are all more than the error threshold of " \
f"{n_images_error}.\nConsider removing these from use_channels."
# don't consider failed channels for subsequent warnings/errors
use_channels = np.setdiff1d(use_channels, use_channels[fail_inds])
spot_no = nb.find_spots.spot_no[np.ix_(use_tiles, use_rounds, use_channels)]
# Consider bad tiles next as second most likely to have consistently low spot counts in a tile
n_images = len(use_channels) * len(use_rounds)
n_images_error = int(np.floor(n_images * config['n_spots_error_fraction']))
n_bad_images = np.zeros(len(use_tiles), dtype=int)
for t in range(len(use_tiles)):
bad_images = np.vstack(np.where(spot_no[t] < n_spots_warn)).T
n_bad_images[t] = bad_images.shape[0]
fail_inds = np.where(n_bad_images >= n_images_error)[0]
if len(fail_inds) > 0:
error_message = error_message + f"\nTiles that failed: {use_tiles[fail_inds]}\n" \
f"This is because out of {n_images} rounds/channels, these tiles had " \
f"respectively:\n{n_bad_images[fail_inds]}\nrounds/channels with " \
f"n_spots < {n_spots_warn}. These are all more than the error threshold of " \
f"{n_images_error}.\nConsider removing these from use_tiles."
# don't consider failed channels for subsequent warnings/errors
use_tiles = np.setdiff1d(use_tiles, use_tiles[fail_inds])
spot_no = nb.find_spots.spot_no[np.ix_(use_tiles, use_rounds, use_channels)]
# Consider bad rounds last as least likely to have consistently low spot counts in a round
n_images = len(use_channels) * len(use_tiles)
n_images_error = int(np.floor(n_images * config['n_spots_error_fraction']))
n_bad_images = np.zeros(len(use_rounds), dtype=int)
for r in range(len(use_rounds)):
bad_images = np.vstack(np.where(spot_no[:, r] < n_spots_warn)).T
n_bad_images[r] = bad_images.shape[0]
fail_inds = np.where(n_bad_images >= n_images_error)[0]
if len(fail_inds) > 0:
error_message = error_message + f"\nRounds that failed: {use_rounds[fail_inds]}\n" \
f"This is because out of {n_images} tiles/channels, these tiles had " \
f"respectively:\n{n_bad_images[fail_inds]}\ntiles/channels with " \
f"n_spots < {n_spots_warn}. These are all more than the error threshold " \
f"of {n_images_error}.\nConsider removing these from use_rounds."
# Consider anchor
if nb.basic_info.use_anchor:
spot_no = nb.find_spots.spot_no[use_tiles, nb.basic_info.anchor_round, nb.basic_info.anchor_channel]
n_images = len(use_tiles)
n_images_error = int(np.floor(n_images * config['n_spots_error_fraction']))
bad_images = np.where(spot_no < n_spots_warn)[0]
n_bad_images = len(bad_images)
if n_bad_images > 0:
bad_images = use_tiles[bad_images]
warnings.warn(
f"\nAnchor - {n_bad_images} tiles with n_spots < {n_spots_warn}:\n"
f"{bad_images}")
if n_bad_images >= n_images_error:
error_message = error_message + f"\nAnchor - tiles {bad_images} all had n_spots < {n_spots_warn}. " \
f"{n_bad_images}/{n_images} tiles failed which is more than the " \
f"error threshold of {n_images_error}.\n" \
f"Consider removing these tiles from use_tiles."
if len(error_message) > 0:
error_message = error_message + f"\nThe function coppafish.plot.view_find_spots may be useful for " \
f"investigating why the above tiles/rounds/channels had so few spots detected."
raise ValueError(error_message)
|