-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFindNeurons.m
251 lines (185 loc) · 7.46 KB
/
FindNeurons.m
1
2
3
4
5
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
function [position, radius, I] = FindNeurons(I, RADIUS_LIST, EDGE_WIDTH, THETA_THRESHOLD)
% FINDNEURONS Implements image processing techniques for extracting
% neuron locations and sizes from gray images
%
% Input parameters:
% I Gray image containing the neurons
% RADIUS_LIST List of integers indicating the radii of the neurons to
% be checked for detectio
% EDGE_WIDTH The width in pixels of the neuron edge used to compute
% the THETA_THRESHOLD
% THETA_THRESHOLD The arc ratio defined by the edge pixels. Set to 1 for
% 360 degrees
%
% Return values:
% position An N-by-2 matrix with the coordinates of the neuron
% centers found
% radius An N-vector containing the estimated radius in pixels
%% Parameters
% Fixed parameters that depend on the properties of the images containing
% the neurons for detection
% Preprocessing
BW_THRESHOLD = 45;
INTENSITY_THRESHOLD = 190;
AREA_THRESHOLD = 500;
EXTENT_THRESHOLD = 0.4;
% Detection
DENSITY_LIST = 0.3:0.05:1.0;
R_SOMA_SCALE = 0.6;
SOMA_THRESHOLD = 0.4;
%% Image preprocessing
wh = waitbar(0, 'Preprocessing the image...', ...
'Name', 'Neuron Detection in Progress', ...
'WindowStyle', 'modal');
% Step 1.
% -----------------
% Sharpen the image by assigning to each pixel the value of the local
% max or min, whichever is closer.
H = strel('disk', 1).Neighborhood;
I_max = ordfilt2(I, sum(H(:)), H);
I_min = ordfilt2(I, 1, H);
I_idx = I_max - I > I - I_min;
I = I_max;
I(I_idx) = I_min(I_idx);
% Step 2.
% -----------------
% Remove small neurons with no connection that appear on the images as
% small white dots.
% Label regions so we can filter them later
L = bwlabel(I > BW_THRESHOLD);
stats = regionprops(L, I, 'Extent', 'Area', 'MeanIntensity'); %#ok<MRPBW>
idx = find([stats.MeanIntensity] > INTENSITY_THRESHOLD & ...
[stats.Area] < AREA_THRESHOLD & [stats.Extent] > EXTENT_THRESHOLD);
% Set the pixels of the regions that satisfy the above criteria to 0
I(ismember(L, idx)) = 0;
% Step 3.
% -----------------
% Prepare the black and white image for neuron detection.
% Remove small unconnected white patches on the image and slightly extend
% everything else.
I = imclose(I > BW_THRESHOLD, strel('disk', 2));
I = bwareaopen(I, 50);
% Step 4.
% -----------------
% fill holes in neurons which caused when removing dots
I_f = imfill(I, 'holes') & ~I;
% preventing from fill very large holes
I = I | I_f & ~bwareaopen(I_f, 120);
% Free memory
clearvars I_max I_min I_idx L I_f;
%% Neuron Detection
% Step 1.
% -----------------
% Convolve the image with disk of different radius that match the neuron
% sizes. This produces peaks at locations where the disk covers the most
% number of white pixels. Use theresholding to find regions containing
% peaks and add their centroid to the list of potential neurons.
% Remark
% -----------------
% Potential improvement is to replace the centroid with the local maximas
% in the image which are above a threshold.
points = cell(1, numel(RADIUS_LIST) * numel(DENSITY_LIST));
k = 1;
for radius = RADIUS_LIST
waitbar(0.1 + 0.45 * k / (numel(RADIUS_LIST) * numel(DENSITY_LIST)), ...
wh, sprintf('Finding neurons of radius %i...', radius));
I_d = conv2(I, single(fspecial('disk', radius)), 'same');
for T = DENSITY_LIST
stats = regionprops(I_d > T, 'Centroid');
points{k} = reshape([stats.Centroid], 2, []);
k = k + 1;
end
end
points = round(transpose(cell2mat(points)));
% Free memory
clearvars I_d;
% Step 2.
% -----------------
% For each posible point and radius in the list, check if the ratio of
% pixels at the edge forming a different angle with the center is above the
% specified threshold
% Output
radius = zeros(size(points,1), 1);
theta = zeros(size(points,1), 1);
% Intermediate varibles
R_MAX = RADIUS_LIST(end);
D = [];
num_angles = zeros(1, numel(RADIUS_LIST));
for k = 1:size(points,1)
if mod(k, 79) == 0
waitbar(0.55 + 0.45 * k / size(points,1), wh,...
sprintf('Evaluating neuron %i of %i...', k, size(points,1)));
end
row = points(k, 2);
col = points(k, 1);
rows = max(1, row - R_MAX - EDGE_WIDTH) : min(size(I, 1), row + R_MAX + EDGE_WIDTH);
cols = max(1, col - R_MAX - EDGE_WIDTH) : min(size(I, 2), col + R_MAX + EDGE_WIDTH);
Img = I(rows, cols);
% Recompute the distance and angle matrix when the dimensions changed
if size(D, 1) ~= numel(rows) || size(D, 2) ~= numel(cols)
[mc, mr] = meshgrid(cols - col, rows - row);
D = sqrt(single(mc .^ 2 + mr .^ 2));
angles = int16(atan2d(mc, mr));
edge_pixels = zeros(size(D, 1), size(D, 2), numel(RADIUS_LIST), 'logical');
for i = 1:numel(RADIUS_LIST)
% For each radius compute the maximum number of angles that can
% be found inside
inside_angles = zeros(1, 361, 'uint8');
inside_angles(angles(D <= RADIUS_LIST(i)) + 181) = 1;
num_angles(i) = sum(inside_angles);
% Create the mask of edge pixels for each radius
edge_pixels(:, :, i) = (D <= RADIUS_LIST(i) + EDGE_WIDTH) & ...
(D >= RADIUS_LIST(i) - EDGE_WIDTH);
end
end
for i = 1:numel(RADIUS_LIST)
% Compute the ratio of angles found on the edge compared to number
% of posible angles for that radius
edge_angles = zeros(1, 361, 'uint8');
edge_angles(angles(edge_pixels(:, :, i) & Img) + 181) = 1;
ratio = sum(edge_angles) / num_angles(i);
% Save the radius with the maximum ratio
if ratio > theta(k)
theta(k) = ratio;
radius(k) = RADIUS_LIST(i);
end
end
% compute the bright ratio in soma area
radius_soma = radius(k) * R_SOMA_SCALE;
rows_soma = max(1, row - radius_soma) : min(size(I, 1), row + radius_soma);
cols_soma = max(1, col - radius_soma) : min(size(I, 2), col + radius_soma);
[mcs, mrs] = meshgrid(cols_soma, rows_soma);
% bright num in circle / num in circle
D = sqrt(single((mcs - col) .^ 2 + (mrs - row) .^ 2));
in_soma = D < radius_soma;
img_soma = I(round(rows_soma), round(cols_soma));
soma_ratio = sum(sum(img_soma & in_soma)) / sum(in_soma(:));
% if not meet the need of soma, theta = 0
if soma_ratio < SOMA_THRESHOLD
theta(k) = 0;
end
end
% Select only points above the threshold
idx = theta > THETA_THRESHOLD;
position = points(idx, :);
radius = radius(idx);
theta = theta(idx);
% Step 3.
% -----------------
% Remove neighbors with smaller theta within R_MAX
for i = 1:size(position, 1)
if isnan(position(i, 1))
continue;
end
d = sqrt((position(:, 1) - position(i, 1)) .^ 2 + ...
(position(:, 2) - position(i, 2)) .^ 2);
idx = (d < R_MAX) & (theta <= theta(i));
% Prevent romoving current point
idx(i) = 0;
position(idx,:) = NaN;
end
idx = ~isnan(position(:, 1));
radius = radius(idx);
position = position(idx, :);
close(wh);
end