Skip to content

Commit

Permalink
imMeasures/imInscribedCircle.m: takes into account spatial calibration
Browse files Browse the repository at this point in the history
  • Loading branch information
dlegland committed Jan 11, 2021
1 parent 19f4107 commit 58e6c6e
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 5 deletions.
46 changes: 41 additions & 5 deletions matImage/imMeasures/imInscribedCircle.m
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
function [circle, labels] = imInscribedCircle(lbl, varargin)
%IMINSCRIBEDCIRCLE Maximal circle inscribed in a particle
% Maximal circle inscribed in a region.
%
% CIRC = imInscribedCircle(IMG)
% Computes the maximal circle inscribed in a given particle, or
% around each labeled particle in the input image.
%
% CIRC = imInscribedCircle(IMG, LABELS)
% CIRC = imInscribedCircle(IMG, SPACING)
% CIRC = imInscribedCircle(IMG, SPACING, ORIGIN)
% Takes into account the spatical calibration to compute the results in
% physical units. Both SPACING and ORIGIN are 1-by-2 row vectors, that
% correspond to the spacing between pixels and to the position of the
% first pixel, respectively.
%
% CIRC = imInscribedCircle(..., LABELS)
% Specify the labels for which the inscribed circle needs to be computed.
% The result is a N-by-3 array with as many rows as the number of labels.
%
Expand All @@ -15,15 +22,15 @@
% img = imFillHoles(imread('circles.png'));
% imshow(img); hold on;
% circ = imInscribedCircle(img);
% drawCircle(circ, 'linewidth', 2)
% drawCircle(circ, 'LineWidth', 2)
%
% % Compute and display the equivalent ellipses of several particles
% img = imread('rice.png');
% img2 = img - imopen(img, ones(30, 30));
% lbl = bwlabel(img2 > 50, 4);
% circles = imInscribedCircle(lbl);
% imshow(img); hold on;
% drawCircle(circles, 'linewidth', 2, 'color', 'g');
% drawCircle(circles, 'LineWidth', 2, 'Color', 'g');
%
% See also
% drawCircle, imEnclosingCircle, imInertiaEllipse, imInscribedBall
Expand All @@ -35,6 +42,27 @@
% Created: 2012-07-08, using Matlab 7.9.0.529 (R2009b)
% Copyright 2012 INRA - Cepia Software Platform.

%% Process input arguments

% default values
spacing = [1 1];
origin = [1 1];
calib = false;

% extract spacing
if ~isempty(varargin) && sum(size(varargin{1}) == [1 2]) == 2
spacing = varargin{1};
varargin(1) = [];
calib = true;
origin = [0 0];
end

% extract origin
if ~isempty(varargin) && sum(size(varargin{1}) == [1 2]) == 2
origin = varargin{1};
varargin(1) = [];
end

% check if labels are specified
labels = [];
if ~isempty(varargin) && size(varargin{1}, 2) == 1
Expand All @@ -47,12 +75,15 @@
end
nLabels = length(labels);


%% Main processing

% allocate memory for result
circle = zeros(nLabels, 3);

for iLabel = 1:nLabels
% compute distance map
distMap = imDistanceMap(lbl==iLabel);
distMap = imDistanceMap(lbl==labels(iLabel));

% find value and position of the maximum
maxi = max(distMap(:));
Expand All @@ -61,3 +92,8 @@
circle(iLabel,:) = [xc yc maxi];
end

% apply spatial calibration
if calib
circle(:,1:2) = bsxfun(@plus, bsxfun(@times, circle(:,1:2) - 1, spacing), origin);
circle(:,3) = circle(:,3) * spacing(1);
end
83 changes: 83 additions & 0 deletions tests/imMeasures/test_imInscribedCircle.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
function tests = test_imInscribedCircle
% Test suite for the file imInscribedCircle.
%
% Test suite for the file imInscribedCircle
%
% Example
% test_imInscribedCircle
%
% See also
% imInscribedCircle

% ------
% Author: David Legland
% e-mail: [email protected]
% Created: 2021-01-11, using Matlab 9.8.0.1323502 (R2020a)
% Copyright 2021 INRAE - BIA-BIBS.

tests = functiontests(localfunctions);

function test_SimpleRect(testCase) %#ok<*DEFNU>
% Test call of function without argument.

img = zeros(100, 100, 'uint8');
img(30:60, 20:80) = 1;

circle = imInscribedCircle(img);

assertEqual(testCase, size(circle), [1 3]);
assertEqual(testCase, circle(3), 16, 'AbsTol', 0.01);


function test_MultiLabels(testCase) %#ok<*DEFNU>
% Test call of function without argument.

img = [ ...
0 0 0 0 0 0 0 0 0 0 0; ...
0 1 1 1 0 4 4 4 4 4 0; ...
0 1 1 1 0 4 4 4 4 4 0; ...
0 1 1 1 0 4 4 4 4 4 0; ...
0 0 0 0 0 0 0 0 0 0 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 6 6 6 6 6 6 6 6 6 0; ...
0 0 0 0 0 0 0 0 0 0 0] ; ...

[circles, labels] = imInscribedCircle(img);

assertEqual(testCase, size(circles), [3 3]);
assertEqual(testCase, length(labels), 3);

expRadius = [2 2 5]';
assertEqual(testCase, circles(:,3), expRadius, 'AbsTol', 0.01);


function test_LabelsTouchingImageBorder(testCase) %#ok<*DEFNU>
% Test call of function without argument.

img = [ ...
1 1 1 0 4 4 4 4 4; ...
1 1 1 0 4 4 4 4 4; ...
1 1 1 0 4 4 4 4 4; ...
0 0 0 0 0 0 0 0 0; ...
6 6 6 6 6 6 6 6 6; ...
6 6 6 6 6 6 6 6 6; ...
6 6 6 6 6 6 6 6 6; ...
6 6 6 6 6 6 6 6 6; ...
6 6 6 6 6 6 6 6 6] ; ...

[circles, labels] = imInscribedCircle(img);

assertEqual(testCase, size(circles), [3 3]);
assertEqual(testCase, length(labels), 3);

expRadius = [3 3 5]';
assertEqual(testCase, circles(:,3), expRadius, 'AbsTol', 0.01);

0 comments on commit 58e6c6e

Please sign in to comment.