Skip to content

Commit 2f471fe

Browse files
committed
add regionEquivalentEllipses.m
1 parent c991512 commit 2f471fe

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

src/@Image/regionCentroids.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
%
2525
% See also
2626
% analyzeRegions, findRegionLabels, componentLabeling, regionprops
27+
% regionEquivalentEllipses
2728

2829
% ------
2930
% Author: David Legland
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
function [ellipse, labels] = regionEquivalentEllipses(obj, varargin)
2+
% Equivalent ellipse of region(s) in a binary or label image.
3+
%
4+
% ELLI = regionEquivalentEllipses(IMG)
5+
% Computes the ellipse with same second order moments for each region in
6+
% label image IMG. If the case of a binary image, a single ellipse
7+
% corresponding to the foreground (i.e. to the region with pixel value 1)
8+
% will be computed.
9+
%
10+
% The result is a N-by-5 array ELLI = [XC YC A B THETA], containing
11+
% coordinates of ellipse center, lengths of major and minor semi-axes,
12+
% and the orientation of the largest axis (in degrees, and
13+
% counter-clockwise).
14+
%
15+
% ELLI = regionEquivalentEllipses(..., LABELS)
16+
% Specifies the labels for which the equivalent ellipse needs to be
17+
% computed. The result is a N-by-5 array with as many rows as the number
18+
% of labels.
19+
%
20+
%
21+
% Example
22+
% % Draw a commplex particle together with its equivalent ellipse
23+
% img = Image.read('circles.png');
24+
% show(img); hold on;
25+
% elli = regionEquivalentEllipses(img);
26+
% drawEllipse(elli)
27+
%
28+
% % Compute and display the equivalent ellipses of several particles
29+
% img = Image.read('rice.png');
30+
% img2 = img - opening(img, ones(30, 30));
31+
% lbl = componentLabeling(img2 > 50, 4);
32+
% ellipses = regionEquivalentEllipses(lbl);
33+
% show(img); hold on;
34+
% drawEllipse(ellipses, 'linewidth', 2, 'color', 'g');
35+
%
36+
% See also
37+
% regionCentroids, regionBoxes
38+
%
39+
40+
% ------
41+
% Author: David Legland
42+
% e-mail: david.legland@inrae.fr
43+
% INRAE - BIA Research Unit - BIBS Platform (Nantes)
44+
% Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a)
45+
% Copyright 2021 INRAE.
46+
47+
48+
%% extract spatial calibration
49+
50+
% extract calibration
51+
spacing = obj.Spacing;
52+
origin = obj.Origin;
53+
calib = isCalibrated(obj);
54+
55+
56+
%% Initialisations
57+
58+
% check if labels are specified
59+
labels = [];
60+
if ~isempty(varargin) && size(varargin{1}, 2) == 1
61+
labels = varargin{1};
62+
end
63+
64+
% extract the set of labels, without the background
65+
if isempty(labels)
66+
labels = findRegionLabels(obj);
67+
end
68+
nLabels = length(labels);
69+
70+
% allocate memory for result
71+
ellipse = zeros(nLabels, 5);
72+
73+
74+
%% Extract ellipse corresponding to each label
75+
76+
for i = 1:nLabels
77+
% extract points of the current particle
78+
[x, y] = find(obj.Data==labels(i));
79+
80+
% transform to physical space if needed
81+
if calib
82+
x = (x-1) * spacing(1) + origin(1);
83+
y = (y-1) * spacing(2) + origin(2);
84+
end
85+
86+
% compute centroid, used as center of equivalent ellipse
87+
xc = mean(x);
88+
yc = mean(y);
89+
90+
% recenter points (should be better for numerical accuracy)
91+
x = x - xc;
92+
y = y - yc;
93+
94+
% number of points
95+
n = length(x);
96+
97+
% compute second order parameters. 1/12 is the contribution of a single
98+
% pixel, then for regions with only one pixel the resulting ellipse has
99+
% positive radii.
100+
Ixx = sum(x.^2) / n + spacing(1)^2/12;
101+
Iyy = sum(y.^2) / n + spacing(2)^2/12;
102+
Ixy = sum(x.*y) / n;
103+
104+
% compute semi-axis lengths of ellipse
105+
common = sqrt( (Ixx - Iyy)^2 + 4 * Ixy^2);
106+
ra = sqrt(2) * sqrt(Ixx + Iyy + common);
107+
rb = sqrt(2) * sqrt(Ixx + Iyy - common);
108+
109+
% compute ellipse angle and convert into degrees
110+
theta = atan2(2 * Ixy, Ixx - Iyy) / 2;
111+
theta = theta * 180 / pi;
112+
113+
% create the resulting equivalent ellipse
114+
ellipse(i,:) = [xc yc ra rb theta];
115+
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
function tests = test_regionEquivalentEllipses
2+
% Test suite for the file regionEquivalentEllipses.
3+
%
4+
% Test suite for the file regionEquivalentEllipses
5+
%
6+
% Example
7+
% test_regionEquivalentEllipses
8+
%
9+
% See also
10+
% regionEquivalentEllipses
11+
12+
% ------
13+
% Author: David Legland
14+
% e-mail: david.legland@inrae.fr
15+
% Created: 2021-02-02, using Matlab 9.8.0.1323502 (R2020a)
16+
% Copyright 2021 INRAE - BIA-BIBS.
17+
18+
tests = functiontests(localfunctions);
19+
20+
function test_Simple(testCase) %#ok<*DEFNU>
21+
% Test call of function without argument.
22+
23+
img = Image.read('circles.png');
24+
25+
elli = regionEquivalentEllipses(img);
26+
27+
assertEqual(testCase, size(elli), [1 5]);
28+
29+
30+
function test_severalLabels(testCase) %#ok<*DEFNU>
31+
32+
% create a label image with four regions
33+
data = zeros([10 10], 'uint8');
34+
data(2:4, 2:4) = 3;
35+
data(6:10, 2:4) = 4;
36+
data(2:4, 6:10) = 7;
37+
data(6:10, 6:10) = 8;
38+
img = Image('Data', data, 'Type', 'Label');
39+
40+
[elli, labels] = regionEquivalentEllipses(img);
41+
42+
assertEqual(testCase, size(elli), [4 5]);
43+
assertEqual(testCase, size(labels), [4 1]);

0 commit comments

Comments
 (0)