|
| 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 |
0 commit comments