Subversion Repositories seema-scanner

Rev

Rev 210 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
209 jakw 1
function bestFits = ellipseDetection(img, params)
2
% ellipseDetection: Ellipse detection
3
%
4
% Overview:
5
% --------
6
% Fits an ellipse by examining all possible major axes (all pairs of points) and
7
% getting the minor axis using Hough transform. The algorithm complexity depends on 
8
% the number of valid non-zero points, therefore it is beneficial to provide as many 
9
% restrictions in the "params" input arguments as possible if there is any prior
10
% knowledge about the problem.
11
%
12
% The code is reasonably fast due to (optional) randomization and full code vectorization.
13
% However, as the algorithm needs to compute pairwise point distances, it can be quite memory
14
% intensive. If you get out of memory errors, either downsample the input image or somehow 
15
% decrease the number of non-zero points in it.
16
% It can deal with big amount of noise but can have severe problem with occlusions (major axis
17
% end points need to be visible)
18
%
19
% Input arguments:
20
% --------    
21
% img
22
%   - One-channel input image (greyscale or binary).
23
% params
24
%   - Parameters of the algorithm:
25
%       minMajorAxis: Minimal length of major axis accepted.
26
%       maxMajorAxis: Maximal length of major axis accepted.
27
%        rotation, rotationSpan: Specification of restriction on the angle of the major axis in degrees.
28
%                                If rotationSpan is in (0,90), only angles within [rotation-rotationSpan,
29
%                               rotation+rotationSpan] are accepted.
30
%       minAspectRatio: Minimal aspect ratio of an ellipse (in (0,1))
31
%       randomize: Subsampling of all possible point pairs. Instead of examining all N*N pairs, runs
32
%                  only on N*randomize pairs. If 0, randomization is turned off.
33
%       numBest: Top numBest to return
34
%       uniformWeights: Used to prefer some points over others. If false, accumulator points are weighted 
35
%                       by their grey intensity in the image. If true, the input image is regarded as binary.
36
%       smoothStddev: In order to provide more stability of the solution, the accumulator is convolved with
37
%                     a gaussian kernel. This parameter specifies its standard deviation in pixels.
38
%
39
% Return value:
40
% --------    
41
% Returns a matrix of best fits. Each row (there are params.numBest of them) contains six elements:
42
% [x0 y0 a b alpha score] being the center of the ellipse, its major and minor axis, its angle in degrees and score.
43
%
44
% Based on:
45
% --------    
46
% - "A New Efficient Ellipse Detection Method" (Yonghong Xie Qiang , Qiang Ji / 2002)
47
% - random subsampling inspired by "Randomized Hough Transform for Ellipse Detection with Result Clustering"
48
%   (CA Basca, M Talos, R Brad / 2005)
49
%
50
% Update log:
51
% --------
52
% 1.1: More memory efficient code, better documentation, more parameters, more solutions possible, example code.
53
% 1.0: Initial version
54
%
55
%
56
% Author: Martin Simonovsky
57
% e-mail: <mys007@seznam.cz>
58
% Release: 1.1
59
% Release date: 25.7.2013
60
%
61
%    
62
% --------    
63
%
64
% Redistribution and use in source and binary forms, with or without 
65
% modification, are permitted provided that the following conditions are 
66
% met:
67
% 
68
%     * Redistributions of source code must retain the above copyright 
69
%       notice, this list of conditions and the following disclaimer.
70
%     * Redistributions in binary form must reproduce the above copyright 
71
%       notice, this list of conditions and the following disclaimer in 
72
%       the documentation and/or other materials provided with the distribution
73
%       
74
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
75
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
76
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
77
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
78
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
79
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
80
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
81
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
82
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
83
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
84
% POSSIBILITY OF SUCH DAMAGE.
85
 
86
    % default values
87
    if nargin==1; params=[]; end
88
    % - parameters to contrain the search
89
    if ~isfield(params,'minMajorAxis');     params.minMajorAxis = 10; end
90
    if ~isfield(params,'maxMajorAxis');     params.maxMajorAxis = 200; end
91
    if ~isfield(params,'rotation');            params.rotation = 0; end
92
    if ~isfield(params,'rotationSpan');        params.rotationSpan = 0; end
93
    if ~isfield(params,'minAspectRatio');    params.minAspectRatio = 0.1; end
94
    if ~isfield(params,'randomize');        params.randomize = 2; end
95
    % - others
96
    if ~isfield(params,'numBest');            params.numBest = 3; end
97
    if ~isfield(params,'uniformWeights');   params.uniformWeights = true; end
98
    if ~isfield(params,'smoothStddev');        params.smoothStddev = 1; end
99
 
100
    eps = 0.0001;
101
    bestFits = zeros(params.numBest,6);
102
    params.rotationSpan = min(params.rotationSpan, 90);    
103
    H = fspecial('gaussian', [params.smoothStddev*6 1], params.smoothStddev);
104
 
105
    [Y,X]=find(img);
106
    Y = single(Y); X = single(X);
107
    N = length(Y);
108
 
109
    fprintf('Possible major axes: %d * %d = %d\n', N, N, N*N);
110
 
111
    % compute pairwise distances between points (memory intensive!) and filter
112
    % TODO: do this block-wise, just appending the filtered results (I,J)
113
    distsSq = bsxfun(@minus,X,X').^2 + bsxfun(@minus,Y,Y').^2;
114
    [I,J] = find(distsSq>=params.minMajorAxis^2 & distsSq<=params.maxMajorAxis^2);
115
    idx = I<J;
116
    I = uint32(I(idx)); J = uint32(J(idx));
117
 
118
    fprintf('..after distance constraint: %d\n', length(I));
119
 
120
    % compute pairwise angles and filter
121
    if params.rotationSpan>0
122
        tangents = (Y(I)-Y(J)) ./ (X(I)-X(J));
123
        tanLo = tand(params.rotation-params.rotationSpan);
124
        tanHi = tand(params.rotation+params.rotationSpan);    
125
        if tanLo<tanHi
126
            idx = tangents > tanLo & tangents < tanHi;
127
        else
128
            idx = tangents > tanLo | tangents < tanHi;
129
        end
130
        I = I(idx); J = J(idx);
131
        fprintf('..after angular constraint: %d\n', length(I));
132
    else
133
        fprintf('..angular constraint not used\n');
134
    end
135
 
136
    npairs = length(I);
137
 
138
    % compute random choice and filter
139
    if params.randomize>0
140
        perm = randperm(npairs);
141
        pairSubset = perm(1:min(npairs,N*params.randomize));
142
        clear perm;
143
        fprintf('..after randomization: %d\n', length(pairSubset));
144
    else
145
        pairSubset = 1:npairs;
146
    end
147
 
148
    % check out all hypotheses
149
    for p=pairSubset
150
        x1=X(I(p)); y1=Y(I(p));
151
        x2=X(J(p)); y2=Y(J(p));
152
 
153
        %compute center & major axis
154
        x0=(x1+x2)/2; y0=(y1+y2)/2;
155
        aSq = distsSq(I(p),J(p))/4;
156
        thirdPtDistsSq = (X-x0).^2 + (Y-y0).^2;
157
        K = thirdPtDistsSq <= aSq; % (otherwise the formulae in paper do not work)
158
 
159
        %get minor ax propositions for all other points
160
        fSq = (X(K)-x2).^2 + (Y(K)-y2).^2;
161
        cosTau = (aSq + thirdPtDistsSq(K) - fSq) ./ (2*sqrt(aSq*thirdPtDistsSq(K)));
162
        cosTau = min(1,max(-1,cosTau)); %inexact float arithmetic?!
163
        sinTauSq = 1 - cosTau.^2;
164
        b = sqrt( (aSq * thirdPtDistsSq(K) .* sinTauSq) ./ (aSq - thirdPtDistsSq(K) .* cosTau.^2 + eps) );
165
 
166
        %proper bins for b
167
        idxs = ceil(b+eps);
168
 
169
        if params.uniformWeights
170
            weights = 1;
171
        else
172
            weights = img(sub2ind(size(img),Y(K),X(K)));
173
        end
174
        accumulator = accumarray(idxs, weights, [params.maxMajorAxis 1]);
175
 
176
        %a bit of smoothing and finding the most busy bin
177
        accumulator = conv(accumulator,H,'same');
178
        accumulator(1:ceil(sqrt(aSq)*params.minAspectRatio)) = 0;
179
        [score, idx] = max(accumulator);
180
 
181
        %keeping only the params.numBest best hypothesis (no non-maxima suppresion)
182
        if (bestFits(end,end) < score)
183
            bestFits(end,:) = [x0 y0 sqrt(aSq) idx atand((y1-y2)/(x1-x2)) score];
184
            if params.numBest>1
185
                [~,si]=sort(bestFits(:,end),'descend');
186
                bestFits = bestFits(si,:);
187
            end
188
        end
189
    end
190
end