1 ## Copyright (C) 2010 Soren Hauberg
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published by
5 ## the Free Software Foundation; either version 3 of the License, or
6 ## (at your option) any later version.
8 ## This program is distributed in the hope that it will be useful,
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 ## GNU General Public License for more details.
13 ## You should have received a copy of the GNU General Public License
14 ## along with this program; If not, see <http://www.gnu.org/licenses/>.
17 ## @deftypefn {Function File} {@var{props} = } regionprops (@var{BW})
18 ## @deftypefnx {Function File} {@var{props} = } regionprops (@var{BW}, @var{properties}, @dots{})
19 ## Compute object properties in a binary image.
21 ## @code{regionprops} computes various properties of the individual objects (as
22 ## identified by @code{bwlabel}) in the binary image @var{BW}. The result is a
23 ## structure array containing an entry per property per object.
25 ## The following properties can be computed.
29 ## The number of pixels in the object.
30 ## @item "EulerNumber"
31 ## @itemx "euler_number"
32 ## The Euler number of the object (see @code{bweuler} for details).
33 ## @item "BoundingBox"
34 ## @itemx "bounding_box"
35 ## The bounding box of the object. This is represented as a 4-vector where the
36 ## first two entries are the @math{x} and @math{y} coordinates of the upper left
37 ## corner of the bounding box, and the two last entries are the width and the
40 ## The area of the object divided by the area of the bounding box.
42 ## The length of the boundary of the object.
44 ## The center coordinate of the object.
45 ## @item "PixelIdxList"
46 ## @itemx "pixel_idx_list"
47 ## The indices of the pixels in the object.
49 ## @itemx "filled_area"
50 ## The area of the object including possible holes.
52 ## @itemx "pixel_list"
53 ## The actual pixel values inside the object. This is only useful for grey scale
55 ## @item "FilledImage"
56 ## @itemx "filled_image"
57 ## A binary image with the same size as the object's bounding box that contains
58 ## the object with all holes removed.
60 ## An image with the same size as the bounding box that contains the original pixels.
61 ## @item "MaxIntensity"
62 ## @itemx "max_intensity"
63 ## The maximum intensity inside the object.
64 ## @item "MinIntensity"
65 ## @itemx "min_intensity"
66 ## The minimum intensity inside the object.
67 ## @item "WeightedCentroid"
68 ## @itemx "weighted_centroid"
69 ## The centroid of the object where pixel values are used as weights.
70 ## @item "MeanIntensity"
71 ## @itemx "mean_intensity"
72 ## The mean intensity inside the object.
73 ## @item "PixelValues"
74 ## @itemx "pixel_values"
75 ## The pixel values inside the object represented as a vector.
78 ## The requested properties can either be specified as several input arguments
79 ## or as a cell array of strings. As a short-hand it is also possible to give
80 ## the following strings as arguments.
84 ## The following properties are computed: @t{"Area"}, @t{"Centroid"} and @t{"BoundingBox"}.
86 ## All properties are computed.
89 ## If no properties are given, @t{basic} is assumed.
90 ## @seealso{bwlabel, bwperim, bweuler}
93 function retval = regionprops (bw, varargin)
96 error ("regionprops: not enough input arguments");
99 if (!ismatrix (bw) || ndims (bw) != 2)
100 error ("regionprops: first input argument must be a NxM matrix");
103 if (numel (varargin) == 0)
104 properties = "basic";
105 elseif (numel (varargin) == 1 && iscellstr (varargin {1}))
106 properties = varargin {1};
107 elseif (iscellstr (varargin))
108 properties = varargin;
110 error ("regionprops: properties must be a cell array of strings");
113 if (ischar (properties) && strcmpi (properties, "basic"))
114 properties = {"Area", "Centroid", "BoundingBox"};
115 elseif (ischar (properties) && strcmpi (properties, "all"))
116 properties = {"area", "eulernumber", "boundingbox", "extent", "perimeter", ...
117 "centroid", "pixelidxlist", "filledarea", "pixellist", ...
118 "filledimage", "image", "maxintensity", "minintensity", ...
119 "weightedcentroid", "meanintensity", "pixelvalues"};
120 elseif (!iscellstr (properties))
121 error ("%s %s", "regionprops: properties must be specified as a list of",
122 "strings or a cell array of strings");
125 ## Get a labelled image
126 if (!islogical (bw) && all (bw >= 0) && all (bw == round (bw)))
127 L = bw; # the image was already labelled
128 num_labels = max (L (:));
130 [L, num_labels] = bwlabel (bw);
133 ## Compute the properties
135 for k = 1:numel (properties)
136 switch (lower (properties {k}))
139 retval (k).Area = local_area (L == k);
142 case {"eulernumber", "euler_number"}
144 retval (k).EulerNumber = bweuler (L == k);
147 case {"boundingbox", "bounding_box"}
149 retval (k).BoundingBox = local_boundingbox (L == k);
154 bb = local_boundingbox (L == k);
155 area = local_area (L == k);
156 retval (k).Extent = area / (bb (3) * bb (4));
161 retval (k).Perimeter = sum (bwperim (L == k) (:));
166 [Y, X] = find (L == k);
167 retval (k).Centroid = [mean(X), mean(Y)];
170 case {"pixelidxlist", "pixel_idx_list"}
172 retval (k).PixelIdxList = find (L == k);
175 case {"filledarea", "filled_area"}
177 retval (k).FilledArea = sum (bwfill (L == k, "holes") (:));
180 case {"pixellist", "pixel_list"}
182 [Y, X] = find (L == k);
183 retval (k).PixelList = [X, Y];
186 case {"filledimage", "filled_image"}
188 retval (k).FilledImage = bwfill (L == k, "holes");
195 retval (k).Image = tmp (min (R):max (R), min (C):max (C));
198 case {"maxintensity", "max_intensity"}
200 retval (k).MaxIntensity = max (bw (L == k) (:));
203 case {"minintensity", "min_intensity"}
205 retval (k).MaxIntensity = min (bw (L == k) (:));
208 case {"weightedcentroid", "weighted_centroid"}
210 [Y, X] = find (L == k);
211 vals = bw (L == k) (:);
213 retval (k).WeightedCentroid = [dot(X, vals), dot(Y, vals)];
216 case {"meanintensity", "mean_intensity"}
218 retval (k).MaxIntensity = mean (bw (L == k) (:));
221 case {"pixelvalues", "pixel_values"}
223 retval (k).PixelValues = bw (L == k)(:);
228 [Y, X] = find (L == k);
230 C = cov ([X(:), Y(:)]);
231 [V, lambda] = eig (C);
232 [max_val, max_idx] = max (diag (lambda));
234 retval (k).Orientation = 180 - 180 * atan2 (v (2), v (1)) / pi;
236 retval (k).Orientation = 0; # XXX: What does the other brand do?
241 case "majoraxislength"
243 [Y, X] = find (L == k);
245 C = cov ([X(:), Y(:)]);
247 retval (k).MajorAxisLength = (max (lambda));
249 retval (k).MajorAxisLength = 1;
253 case "minoraxislength"
255 [Y, X] = find (L == k);
257 C = cov ([X(:), Y(:)]);
259 retval (k).MinorAxisLength = (min (lambda));
261 retval (k).MinorAxisLength = 1;
273 #case "equivdiameter"
276 error ("regionprops: unsupported property '%s'", properties {k});
281 function retval = local_area (bw)
282 retval = sum (bw (:));
285 function retval = local_boundingbox (bw)
287 retval = [min(X)-0.5, min(Y)-0.5, max(X)-min(X)+1, max(Y)-min(Y)+1];