1
1
% Estimate center of fetal brain and largest slices from 2D-EPI scout.
2
- function [bcenout ,ecenout ,outmask ] = landmarks(mri , ga , doplot )
2
+ function [bcenout ,ecenout ,outmask ] = landmarks(mri , ga , doplot , par )
3
3
4
4
if nargin() < 3
5
5
doplot = [];
6
6
end
7
+ if nargin() < 4
8
+ par = [];
9
+ end
7
10
8
11
if ischar(mri )
9
12
mri = MRIread(mri ); % FreeSurfer.
42
45
%% Stage 1.
43
46
44
47
% MSERs and filtering.
45
- maxarea = pi * bpd / 2 * ofd / 2 / prod(vsz(1 : 2 ));
46
- [bw , numbw , snum ] = slcmser(pre , 5 , maxarea * 0.2 , maxarea * 1.0 );
48
+ par = setdefault(par , ' maxratio1' , 1.5 , ' minarea1' , 0.2 , ' maxarea1' , 1.1 , ...
49
+ ' maxsemi1' , 1.1 , ' minfill1' , 0.5 );
50
+ refarea = pi * bpd / 2 * ofd / 2 / prod(vsz(1 : 2 ));
51
+ [bw , numbw , snum ] = slcmser(pre , 5 , refarea * par .minarea1 , refarea * par .maxarea1 );
47
52
[cen ,semi ,rot ,~ ,inside ,outside ] = fitellipse(bw ); % #ok
48
53
ratio = semi(: ,1 ) ./ semi(: ,2 );
49
54
area = pi * prod(semi , 2 );
50
55
keep = true(numbw , 1 );
51
- keep = keep & ratio < ofd / bpd * 1.5 ;
52
- keep = keep & area < maxarea * 1.1 ;
53
- keep = keep & semi(: ,1 )*vsz(1 ) < ofd / 2 * 1.1 ;
54
- keep = keep & inside - outside > 0.5 ;
56
+ keep = keep & ratio < ofd / bpd * par .maxratio1 ;
57
+ keep = keep & area > refarea * par .minarea1 ;
58
+ keep = keep & area < refarea * par .maxarea1 ;
59
+ keep = keep & semi(: ,1 )*vsz(1 ) < ofd / 2 * par .maxsemi1 ;
60
+ keep = keep & inside - outside > par .minfill1 ;
55
61
56
62
% Mean-shift clustering: remove near-duplicate points first.
63
+ par = setdefault(par , ' minsep1' , 1 , ' diam1' , 1 , ' steptol1' , 0.1 );
57
64
points = [cen(keep ,: ) snum(keep )] .* vsz ;
58
65
i = 1 ;
59
66
while i <= size(points ,1 )
60
- ind = sqrt(sum((points(i ,: )-points ).^2 ,2 )) < 1 ; % In mm.
67
+ ind = sqrt(sum((points(i ,: )-points ).^2 ,2 )) < par . minsep1 ; % In mm.
61
68
ind(i ) = 0 ;
62
69
points(ind ,: ) = [];
63
70
i = i + 1 ;
64
71
end
65
- [clusters ,numclust ] = meanshift(points , ofd , 0.1 );
72
+ [clusters ,numclust ] = meanshift(points , par .diam1 * ofd , par .steptol1 , ...
73
+ par .minsep1 );
66
74
if isfield(doplot , ' clust1' ) && doplot .clust1
67
75
wait(' Brain localization: mean-shift clustering of MSER centers' , ...
68
76
' before cluster selection' );
69
- showclust(points , clusters , ofd );
77
+ showclust(points , clusters , par . diam1 * ofd );
70
78
end
71
79
72
80
% Cluster selection.
81
+ par = setdefault(par , ' rad1' , 1 );
82
+ len = ofd / 2 * par .rad1 ;
73
83
xyz = ndarray([1 1 1 ], dim ) .* vsz ;
74
84
ind1d = reshape(1 : prod(dim ), dim );
75
85
points = [cen snum ] .* vsz ;
78
88
for i = 1 : numclust
79
89
centroid = clusters(i ,: );
80
90
dist = sqrt(sum((centroid - points ).^2 , 2 ));
81
- ind = find(keep & dist < ofd / 2 );
91
+ ind = find(keep & dist < len );
82
92
volmask = false(dim );
83
93
for j = ind '
84
94
volmask(: ,: ,snum(j )) = volmask(: ,: ,snum(j )) | bw{j };
85
95
end
86
- low = floor((centroid - ofd / 2 ) ./ vsz );
87
- upp = ceil((centroid + ofd / 2 ) ./ vsz );
96
+ low = floor((centroid - len ) ./ vsz );
97
+ upp = ceil((centroid + len ) ./ vsz );
88
98
low = max(1 , low );
89
99
upp = min(dim , upp );
90
100
subset = ind1d(low(1 ): upp(1 ),low(2 ): upp(2 ),low(3 ): upp(3 ));
91
101
sphere = false(dim );
92
- insphere = sqrt(sum((xyz(subset ,: )-centroid ).^2 , 2 )) < ofd / 2 ;
102
+ insphere = sqrt(sum((xyz(subset ,: )-centroid ).^2 , 2 )) < len ;
93
103
sphere(subset ) = insphere ;
94
104
numvoxin(i ) = nnz(sphere & volmask );
95
105
numvoxout(i ) = nnz(~sphere & volmask );
104
114
linedist = linedist{order(1 )};
105
115
dist = sqrt(sum((centroid - points(keep ,: )).^2 , 2 ));
106
116
keeppreclust = keep ; % #ok
107
- keep(keep ) = dist < ofd / 2 ;
117
+ keep(keep ) = dist < len ;
108
118
109
119
% Filter distance from line fit.
110
120
zscore = (linedist - mean(linedist )) / std(linedist );
111
- zlim = 3 ;
112
121
keeppreline = keep ;
113
- keep(keep ) = zscore < zlim ;
122
+ par = setdefault(par , ' zline1' , 3 );
123
+ keep(keep ) = zscore < par .zline1 ;
114
124
if isfield(doplot , ' line1' ) && doplot .line1
115
125
wait(' Brain localization: rejection of MSERs based on the distance' , ...
116
126
' to a line fitted through their centers' );
117
- showfit(snum(keeppreline ), semi(keeppreline ,1 ), zscore , zlim );
127
+ showfit(snum(keeppreline ), semi(keeppreline ,1 ), zscore , par . zline1 );
118
128
end
119
129
if isfield(doplot , ' mser1' ) && doplot .mser1
120
130
wait(' Brain localization: retained MSERs on each slice before' , ...
150
160
rawloc = rawloc - boxshift ;
151
161
152
162
% MSER detection and filtering.
153
- maxarea = pi * bpd / 2 * ofd / 2 / prod(vsz(1 : 2 ));
154
- [bw , numbw , snum ] = slcmser(boxdat , 5 , maxarea * 0.05 , maxarea * 1.1 );
163
+ par = setdefault(par , ' maxratio2' , 1.5 , ' minarea2' , 0.05 , ' maxarea2' , 1.1 , ...
164
+ ' maxsemi2' , 1.1 , ' minfill2' , 0.5 , ' maxdist2' , 1.1 );
165
+ refarea = pi * bpd / 2 * ofd / 2 / prod(vsz(1 : 2 ));
166
+ [bw , numbw , snum ] = slcmser(boxdat , 5 , refarea * par .minarea2 , ...
167
+ refarea * par .maxarea2 );
155
168
[cen ,semi ,~ ,~ ,inside ,outside ] = fitellipse(bw );
156
169
ratio = semi(: ,1 ) ./ semi(: ,2 );
157
170
area = pi * prod(semi , 2 );
158
171
dist = sqrt(sum((vsz .*([cen snum ]-rawloc )).^2 , 2 ));
159
172
keep = true([numbw 1 ]);
160
- keep = keep & ratio < ofd / bpd * 1.5 ;
161
- keep = keep & area < maxarea * 1.1 ;
162
- keep = keep & semi(: ,1 )*vsz(1 ) < ofd / 2 * 1.1 ;
163
- keep = keep & inside - outside > 0.5 ;
164
- keep = keep & dist < ofd / 2 * 1.1 ;
173
+ keep = keep & ratio < ofd / bpd * par .maxratio2 ;
174
+ keep = keep & area > refarea * par .minarea2 ;
175
+ keep = keep & area < refarea * par .maxarea2 ;
176
+ keep = keep & semi(: ,1 )*vsz(1 ) < ofd / 2 * par .maxsemi2 ;
177
+ keep = keep & inside - outside > par .minfill2 ;
178
+ keep = keep & dist < ofd / 2 * par .maxdist2 ;
165
179
if isfield(doplot , ' mser2' ) && doplot .mser2
166
180
wait(' Brain-mask creation: pre-filtered MSERs on each slice before' , ...
167
181
' addition to the preliminary brain mask' );
186
200
% Fit line to average centers.
187
201
[~ ,~ ,dist ] = fitline(scen .* vsz );
188
202
zscoreline = (dist - mean(dist )) / std(dist );
189
- zlimline = 2 ;
203
+ par = setdefault( par , ' zline2 ' , 2 ) ;
190
204
[scenpreline , ssemipreline ] = deal(scen , ssemi );
191
- ind = zscoreline < zlimline ;
205
+ ind = zscoreline < par . zline2 ;
192
206
volmask(: ,: ,scen(~ind ,3 )) = 0 ;
193
207
[scen , ssemi ] = deal(scen(ind ,: ), ssemi(ind ,: ));
194
208
if isfield(doplot , ' line2' ) && doplot .line2
195
209
wait(' Brain-mask creation: rejection of brain-mask slices based on' , ...
196
210
' the distance to a line fitted through their centers' );
197
- showfit(scenpreline(: ,3 ), ssemipreline(: ,1 ), zscoreline , zlimline );
211
+ showfit(scenpreline(: ,3 ), ssemipreline(: ,1 ), zscoreline , par . zline2 );
198
212
end
199
213
200
214
% Fit polynomial to average axes.
201
- polywithin = ofd / vsz(3 )/2 * 0.5 ;
215
+ par = setdefault(par , ' polywithin2' , 0.5 );
216
+ polywithin = ofd / vsz(3 )/2 * par .polywithin2 ;
202
217
ind = abs(scen(: ,3 )-boxloc(3 )) < polywithin ;
203
218
xdat = scen(ind ,3 );
204
219
ydat = ssemi(ind ,1 );
205
220
coef = polyfit(xdat , ydat , 2 );
206
221
dist = ssemi(: ,1 ) - polyval(coef , scen(: ,3 ));
207
222
zscorepoly = (dist - mean(dist(ind ))) / std(dist );
208
- zlimpoly = 1.5 ;
223
+ par = setdefault( par , ' zpoly2 ' , 1.5 ) ;
209
224
[scenprepoly , ssemiprepoly , boxlocprepoly ] = deal(scen , ssemi , boxloc ); % #ok
210
225
if coef(1 ) < 0 % Want bad weather.
211
- ind = zscorepoly < zlimpoly ;
226
+ ind = zscorepoly < par . zpoly2 ;
212
227
volmask(: ,: ,scen(~ind ,3 )) = 0 ;
213
228
scen = scen(ind ,: ); % #ok
214
229
ssemi = ssemi(ind ,: ); % #ok
217
232
wait(' Brain-mask creation: rejection of brain-mask slices based on' , ...
218
233
' the deviation between their major-axis length and a quadratic' , ...
219
234
' fit across slices' );
220
- showfit(scenprepoly(: ,3 ), ssemiprepoly(: ,1 ), zscorepoly , zlimpoly , ...
235
+ showfit(scenprepoly(: ,3 ), ssemiprepoly(: ,1 ), zscorepoly , par . zpoly2 , ...
221
236
coef , boxloc(3 )+[-1 1 ]*polywithin );
222
237
end
223
238
244
259
%% Stage 3.
245
260
246
261
% Image cropping and interpolation.
247
- % low = max(1, floor(rawloc-halflen));
248
- % upp = min(dim, floor(rawloc+halflen));
249
- boxvsz = [1.49 1.49 vsz(3 )];
262
+ par = setdefault(par , ' voxsize3' , 1.49 );
263
+ boxvsz = [par .voxsize3 par .voxsize3 vsz(3 )];
250
264
lenvox = floor(sqrt(2 ) * ofd ./ boxvsz ); % Side length in new voxels.
251
265
scaledown = boxvsz ./ vsz ;
252
266
shift = bcenvox - (lenvox + 1 )/2 .* scaledown ; % In old voxels.
266
280
boxloc = boxloc(1 : 3 )' ;
267
281
268
282
% MSER detection and filtering.
269
- maxarea = pi * (odiam / 2 )^2 / prod(boxvsz(1 : 2 )); % In voxels.
270
- [bw , numbw , snum ] = slcmser(boxdat , 5 , maxarea * 0.5 , maxarea * 1.1 );
283
+ par = setdefault(par , ' maxratio3' , 1.5 , ' minarea3' , 0.5 , ' maxarea3' , 1.1 , ...
284
+ ' maxsemi3' , 1.3 , ' minfill3' , 0.8 , ' mindist3' , 0.5 , ' maxdist3' , 1.5 , ...
285
+ ' mindrop3' , 0.5 , ' int3' , 0.5 );
286
+ refarea = pi * (odiam / 2 )^2 / prod(boxvsz(1 : 2 )); % In voxels.
287
+ [bw , numbw , snum ] = slcmser(boxdat , 5 , refarea * par .minarea3 , ...
288
+ refarea * par .maxarea3 );
271
289
[cen ,semi ,rot ,ell ,inside ,outside ] = fitellipse(bw ); % #ok
272
290
ratio = semi(: ,1 ) ./ semi(: ,2 );
273
291
area = pi * prod(semi , 2 );
276
294
for i = 1 : numbw
277
295
im = boxdat(: ,: ,snum(i ));
278
296
inribbon = find(ellbig{i } & ~ell{i });
279
- threshold = 0.5 * median(im(ell{i }(: )));
297
+ threshold = par . int3 * median(im(ell{i }(: )));
280
298
drop(i ) = nnz(im(inribbon ) < threshold ) / numel(inribbon );
281
299
end
282
300
dist = sqrt(sum((boxvsz .* ([cen snum ]-boxloc )).^2 , 2 ));
283
-
284
- % Individual filtering.
285
301
keep = true(numbw , 1 );
286
- keep = keep & ratio < 1.5 ;
287
- keep = keep & area < maxarea * 1.1 ;
288
- keep = keep & semi(: ,1 ).*boxvsz(1 ) < odiam / 2 * 1.3 ;
289
- keep = keep & inside - outside > 0.8 ;
290
- keep = keep & drop > 0.5 ;
291
- keep = keep & dist > ofd / 2 * 0.5 ;
292
- keep = keep & dist < ofd / 2 * 1.5 ;
302
+ keep = keep & ratio < par .maxratio3 ;
303
+ keep = keep & area > refarea * par .minarea3 ;
304
+ keep = keep & area < refarea * par .maxarea3 ;
305
+ keep = keep & semi(: ,1 ).*boxvsz(1 ) < odiam / 2 * par .maxsemi3 ;
306
+ keep = keep & inside - outside > par .minfill3 ;
307
+ keep = keep & drop > par .mindrop3 ;
308
+ keep = keep & dist > ofd / 2 * par .mindist3 ;
309
+ keep = keep & dist < ofd / 2 * par .maxdist3 ;
293
310
if isfield(doplot , ' mser3' ) && doplot .mser3
294
311
wait(' Eye detection: pre-filtered MSERs on each slice before 3D' , ...
295
312
' clustering' );
296
313
showmsers(boxdat , bw , snum , ofd / boxvsz(1 ), keep );
297
314
end
298
315
299
316
% Clusters of points in 3D.
317
+ par = setdefault(par , ' group3' , 0.7 );
300
318
ind = find(keep );
301
319
cluster = arrayfun(@(p )p , ind , ' uniformoutput' , 0 );
302
320
numclust = numel(cluster );
303
321
for i = 1 : numclust
304
322
cen1 = [cen(ind(i ),: ) snum(ind(i ))] .* boxvsz ;
305
323
for j = 1 : numclust
306
324
cen2 = [cen(ind(j ),: ) snum(ind(j ))] .* boxvsz ;
307
- if i ~= j && sqrt(sum((cen1 - cen2 ).^2 )) < odiam * 0.7
325
+ if i ~= j && sqrt(sum((cen1 - cen2 ).^2 )) < odiam * par . group3
308
326
cluster{i } = [cluster{i } ind(j )];
309
327
end
310
328
end
358
376
end
359
377
360
378
% Centroid pair scores.
379
+ par = setdefault(par , ' lambda3' , 3 , ' angle3' , 40 );
361
380
numpair = numclust *(numclust - 1 )/2 ;
362
381
score = zeros([numpair 1 ]);
363
382
pair = zeros([numpair 2 ]);
367
386
for j = i + 1 : numclust
368
387
tmp = 0 ;
369
388
tmp = tmp + 1.0 * fun(mean(bdismm([i j ]))/(ofd / 2 ) - 1 );
370
- tmp = tmp + 3.0 * fun((bdismm(i )-bdismm(j ))/mean(bdismm([i j ])));
389
+ tmp = tmp + fun((bdismm(i )-bdismm(j ))/mean(bdismm([i j ])))* par . lambda3 ;
371
390
tmp = tmp + 1.0 * fun(edismm(i ,j )/odist - 1 );
372
- tmp = tmp + 1.0 * fun(ang(i ,j )/40 - 1 );
391
+ tmp = tmp + 1.0 * fun(ang(i ,j )/par . angle3 - 1 );
373
392
tmp = tmp + 1.0 * fun(mean(quality([i j ]))/max(quality ) - 1 );
374
393
tmp = tmp + 1.0 * fun(mean(numslc([i j ]))/max(numslc ) - 1 );
375
394
score(n ) = tmp ;
422
441
xsub = 1 + cropshift(1 ) : mri .volsize(1 )-cropshift(1 );
423
442
ysub = 1 + cropshift(2 ) : mri .volsize(2 )-cropshift(2 );
424
443
zsub = 1 + cropshift(3 ) : mri .volsize(3 )-cropshift(3 );
425
- outmask(xsub ,ysub ,zsub ) = brainmask ;
444
+ outmask(xsub ,ysub ,zsub ) = brainmask ;
0 commit comments