Skip to content

Commit eab2c19

Browse files
committed
AnalyzeImageRegions.m: add support for 3D images
1 parent 0b9e002 commit eab2c19

File tree

1 file changed

+237
-22
lines changed

1 file changed

+237
-22
lines changed

ImageM/+imagem/+actions/+analyze/AnalyzeImageRegions.m

Lines changed: 237 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,19 @@ function run(obj, frame)
4343

4444
% setup display
4545
obj.Viewer = frame;
46-
createFigure(obj);
46+
47+
% choose dialog accoding to image dimension
48+
nd = ndims(currentImage(obj.Viewer));
49+
if nd == 2
50+
createFigure2d(obj);
51+
elseif nd == 3
52+
createFigure3d(obj);
53+
else
54+
error('Can process only dimensions 2 and 3');
55+
end
4756
end
4857

49-
function tab = computeFeatures(obj)
58+
function tab = computeFeatures2d(obj)
5059
% compute the selected features on the current image and return table
5160

5261
% get current image
@@ -97,7 +106,7 @@ function run(obj, frame)
97106
tab = [tab Table(shapeFactor, {'ShapeFactor'})];
98107
end
99108

100-
% process inertia-based features
109+
% process moment-based features
101110
if centroidFlag || ellipseFlag || ellipseElongFlag
102111
elli = imEquivalentEllipse(imgData, spacing, origin, labels);
103112

@@ -157,14 +166,98 @@ function run(obj, frame)
157166

158167
tab.Name = [img.Name '-features'];
159168
end
169+
170+
function tab = computeFeatures3d(obj)
171+
% Compute the selected 3D features on the current image and return Table.
172+
173+
% get current image
174+
img = currentImage(obj.Viewer);
175+
176+
% identify features to compute
177+
volumeFlag = get(obj.Handles.VolumeCheckBox, 'Value');
178+
surfaceAreaFlag = get(obj.Handles.SurfaceAreaCheckBox, 'Value');
179+
meanBreadthFlag = get(obj.Handles.MeanBreadthCheckBox, 'Value');
180+
eulerFlag = get(obj.Handles.EulerNumberCheckBox, 'Value');
181+
sphericityFlag = get(obj.Handles.SphericityCheckBox, 'Value');
182+
183+
centroidFlag = get(obj.Handles.CentroidCheckBox, 'Value');
184+
ellipsoidFlag = get(obj.Handles.EllipsoidCheckBox, 'Value');
185+
ellipsoidShapeFlag = get(obj.Handles.EllipsoidShapeCheckBox, 'Value');
186+
187+
boundingBoxFlag = get(obj.Handles.BoundingBoxCheckBox, 'Value');
188+
% convexVolumeFlag = get(obj.Handles.ConvexVolumeCheckBox, 'Value');
189+
convexityFlag = get(obj.Handles.ConvexityCheckBox, 'Value');
190+
191+
192+
% retrieve image data
193+
imgData = permute(img.Data, [2 1 3]);
194+
spacing = img.Spacing;
195+
origin = img.Origin;
196+
197+
% initialize empty table
198+
labels = imFindLabels(imgData);
199+
tab = Table(labels, {'Label'});
200+
201+
% process global size features
202+
if volumeFlag || sphericityFlag
203+
volList = imVolume(imgData, labels, spacing);
204+
tab = [tab Table(volList, {'Volume'})];
205+
end
206+
if surfaceAreaFlag || sphericityFlag
207+
surfList = imSurfaceArea(imgData, labels, spacing);
208+
tab = [tab Table(surfList, {'SurfaceArea'})];
209+
end
210+
if meanBreadthFlag
211+
mbList = imMeanBreadth(imgData, labels, spacing);
212+
tab = [tab Table(mbList, {'MeanBreadth'})];
213+
end
214+
if eulerFlag
215+
tab = [tab Table(imEuler3d(imgData), {'EulerNumber'})];
216+
end
217+
if sphericityFlag
218+
shapeFactor = 36 * pi * volList.^2 ./ surfList .^3;
219+
tab = [tab Table(shapeFactor, {'Sphericity'})];
220+
end
221+
222+
if boundingBoxFlag
223+
boxes = imBoundingBox(imgData, spacing, origin, labels);
224+
colNames = {'XMin', 'XMax', 'YMin', 'YMax', 'ZMin', 'ZMax'};
225+
tab = [tab Table(boxes, colNames)];
226+
end
227+
228+
% process moment-based features
229+
if centroidFlag || ellipsoidFlag || ellipsoidShapeFlag
230+
elli = imEquivalentEllipsoid(imgData, spacing, origin, labels);
231+
232+
if centroidFlag || ellipsoidFlag
233+
tab = [tab Table(elli(:,1:3), {'CentroidX', 'CentroidY', 'CentroidZ'})];
234+
end
235+
if ellipsoidFlag
236+
tab = [tab Table(elli(:,4:6), {'SemiAxis1', 'SemiAxis2', 'SemiAxis3'})];
237+
tab = [tab Table(elli(:,7:9), {'Phi', 'Theta', 'Psi'})];
238+
end
239+
if ellipsoidShapeFlag
240+
elongs = [elli(:,4) ./ elli(:,6), elli(:,4) ./ elli(:,5), elli(:,5) ./ elli(:,6)] ;
241+
tab = [tab Table(elongs, {'R1/R3', 'R1/R2', 'R2/R3'})];
242+
end
243+
end
244+
245+
if convexityFlag
246+
convexityList = imConvexity(imgData, labels);
247+
tab = [tab Table(convexityList, {'Convexity'})];
248+
end
249+
250+
tab.Name = [img.Name '-features'];
251+
end
160252
end
161253

162254
methods
163-
function hf = createFigure(obj)
164-
% creates the figure
255+
256+
function hf = createFigure2d(obj)
257+
% creates the figure for 2D images.
165258

166259
hf = figure(...
167-
'Name', 'Analysis Particles', ...
260+
'Name', 'Analyze Regions 2D', ...
168261
'NumberTitle', 'off', ...
169262
'MenuBar', 'none', ...
170263
'Toolbar', 'none', ...
@@ -176,18 +269,18 @@ function run(obj, frame)
176269

177270
obj.Handles.Figure = hf;
178271
gui = obj.Viewer.Gui;
179-
272+
180273
% the list of images for choosing the overlay
181274
imageNames = getImageNames(gui.App);
182275

183-
276+
184277
% vertical layout, containing main panel and buttons panel
185278
vb = uix.VBox('Parent', hf, 'Spacing', 5, 'Padding', 5);
186279

187280
mainPanel = uix.VBox('Parent', vb);
188-
281+
189282
featuresPanel = uix.Grid('Parent', mainPanel);
190-
283+
191284

192285
% First column
193286

@@ -240,7 +333,7 @@ function run(obj, frame)
240333
'Style', 'Checkbox', ...
241334
'Parent', featuresPanel, ...
242335
'Visible', 'Off');
243-
336+
244337

245338
% Second column
246339

@@ -262,7 +355,7 @@ function run(obj, frame)
262355
'Parent', featuresPanel, ...
263356
'String', 'Ellipse Elongation', ...
264357
'Value', 1);
265-
358+
266359

267360
% Feret diameters
268361
uicontrol(...
@@ -281,19 +374,19 @@ function run(obj, frame)
281374
'Parent', featuresPanel, ...
282375
'String', 'Oriented Box', ...
283376
'Value', 1);
284-
377+
285378
obj.Handles.BoxElongationCheckBox = uicontrol(...
286379
'Style', 'Checkbox', ...
287380
'Parent', featuresPanel, ...
288381
'String', 'Box Elongation', ...
289382
'Value', 1);
290383

291-
obj.Handles.TortuosityCheckBox = uicontrol(...
384+
obj.Handles.TortuosityCheckBox = uicontrol(...
292385
'Style', 'Checkbox', ...
293386
'Parent', featuresPanel, ...
294387
'String', 'Tortuosity');
295388

296-
389+
297390
% use same widths for all columns
298391
set(featuresPanel, 'Widths', [-1 -1]);
299392
set(featuresPanel, 'Heights', [20 20 20 20 20 20 20 20 20]);
@@ -304,7 +397,7 @@ function run(obj, frame)
304397
'Overlay:', obj.OverlayTypeValues);
305398
set(obj.Handles.OverlayTypePopup, 'Value', 2);
306399

307-
% add combo box for choosing the image to overlay
400+
% add combo box for choosing the image to overlay
308401
obj.Handles.OverlayImagePopup = addComboBoxLine(gui, mainPanel, ...
309402
'Image to overlay:', imageNames);
310403

@@ -327,15 +420,126 @@ function run(obj, frame)
327420

328421
set(vb, 'Heights', [-1 40] );
329422
end
330-
end
423+
424+
function hf = createFigure3d(obj)
425+
% creates the figure for 3D images.
426+
427+
hf = figure(...
428+
'Name', 'Analyze Regions 3D', ...
429+
'NumberTitle', 'off', ...
430+
'MenuBar', 'none', ...
431+
'Toolbar', 'none', ...
432+
'CloseRequestFcn', @obj.closeFigure);
433+
set(hf, 'units', 'pixels');
434+
pos = get(hf, 'Position');
435+
pos(3:4) = [250 300];
436+
set(hf, 'Position', pos);
437+
438+
obj.Handles.Figure = hf;
439+
440+
% vertical layout, containing main panel and buttons panel
441+
vb = uix.VBox('Parent', hf, 'Spacing', 5, 'Padding', 5);
442+
443+
mainPanel = uix.VBox('Parent', vb);
444+
445+
featuresPanel = uix.Grid('Parent', mainPanel);
446+
447+
448+
% First column
449+
450+
% global morphometry
451+
obj.Handles.VolumeCheckBox = uicontrol(...
452+
'Style', 'Checkbox', ...
453+
'Parent', featuresPanel, ...
454+
'String', 'Volume', ...
455+
'Value', 1);
456+
obj.Handles.SurfaceAreaCheckBox = uicontrol(...
457+
'Style', 'Checkbox', ...
458+
'Parent', featuresPanel, ...
459+
'String', 'Surface Area', ...
460+
'Value', 1);
461+
obj.Handles.MeanBreadthCheckBox = uicontrol(...
462+
'Style', 'Checkbox', ...
463+
'Parent', featuresPanel, ...
464+
'String', 'Mean Breadth', ...
465+
'Value', 0);
466+
obj.Handles.EulerNumberCheckBox = uicontrol(...
467+
'Style', 'Checkbox', ...
468+
'Parent', featuresPanel, ...
469+
'String', 'Euler Number', ...
470+
'Value', 0);
471+
obj.Handles.SphericityCheckBox = uicontrol(...
472+
'Style', 'Checkbox', ...
473+
'Parent', featuresPanel, ...
474+
'String', 'Sphericity', ...
475+
'Value', 0);
476+
477+
478+
% Second column
479+
480+
% inertia-based parameters
481+
obj.Handles.BoundingBoxCheckBox = uicontrol(...
482+
'Style', 'Checkbox', ...
483+
'Parent', featuresPanel, ...
484+
'String', 'Bounding Box', ...
485+
'Value', 1);
486+
487+
% moment-based parameters
488+
obj.Handles.CentroidCheckBox = uicontrol(...
489+
'Style', 'Checkbox', ...
490+
'Parent', featuresPanel, ...
491+
'String', 'Centroid', ...
492+
'Value', 1);
493+
494+
obj.Handles.EllipsoidCheckBox = uicontrol(...
495+
'Style', 'Checkbox', ...
496+
'Parent', featuresPanel, ...
497+
'String', 'Equivalent Ellipsoid', ...
498+
'Value', 1);
499+
500+
obj.Handles.EllipsoidShapeCheckBox = uicontrol(...
501+
'Style', 'Checkbox', ...
502+
'Parent', featuresPanel, ...
503+
'String', 'Ellipsoid Shapes', ...
504+
'Value', 0);
505+
506+
% convexity
507+
obj.Handles.ConvexityCheckBox = uicontrol(...
508+
'Style', 'Checkbox', ...
509+
'Parent', featuresPanel, ...
510+
'String', 'Convexity', ...
511+
'Value', 0);
512+
513+
514+
% use same widths for all columns
515+
set(featuresPanel, 'Widths', [-1 -1]);
516+
set(featuresPanel, 'Heights', [20 20 20 20 20]);
517+
518+
519+
% setup layout for all widgets but control panel
520+
set(mainPanel, 'Heights', -1 );
521+
% set(mainPanel, 'Heights', [-1 35 35] );
522+
523+
% button for control panel
524+
buttonsPanel = uix.HButtonBox('Parent', vb, 'Padding', 5);
525+
uicontrol('Parent', buttonsPanel, ...
526+
'String', 'OK', ...
527+
'Callback', @obj.onButtonOK3d);
528+
uicontrol('Parent', buttonsPanel, ...
529+
'String', 'Cancel', ...
530+
'Callback', @obj.onButtonCancel);
531+
532+
set(vb, 'Heights', [-1 40] );
533+
end
534+
end
331535

332536

333537
%% Control buttons Callback
334538
methods
335539
function onButtonOK(obj, varargin)
336540

337541
% compute morphological features
338-
tab = computeFeatures(obj);
542+
tab = computeFeatures2d(obj);
339543

340544
% extract type of overlay
341545
overlayTypeIndex = get(obj.Handles.OverlayTypePopup, 'Value');
@@ -348,7 +552,7 @@ function onButtonOK(obj, varargin)
348552
docToOverlay = getDocument(gui.App, imageName);
349553

350554
closeFigure(obj);
351-
555+
352556
% display data table in its own window
353557
createTableFrame(obj.Viewer.Gui, tab);
354558

@@ -380,17 +584,28 @@ function onButtonOK(obj, varargin)
380584
'Style', {{'color', 'g'}});
381585
addShape(docToOverlay, shape);
382586
end
383-
587+
384588
otherwise
385589
error(['Unable to process overlay type: ' overlayType]);
386590
end
387-
591+
388592
% update all the views referenced by the document with overlay
389593
viewList = getViews(docToOverlay);
390594
for i = 1:length(viewList)
391595
updateDisplay(viewList{i});
392596
end
393-
597+
598+
end
599+
600+
function onButtonOK3d(obj, varargin)
601+
602+
% compute morphological features
603+
tab = computeFeatures3d(obj);
604+
605+
closeFigure(obj);
606+
607+
% display data table in its own window
608+
createTableFrame(obj.Viewer.Gui, tab);
394609
end
395610

396611
function onButtonCancel(obj, varargin)

0 commit comments

Comments
 (0)