Subversion Repositories seema-scanner

Rev

Rev 209 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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

Generated by GNU Enscript 1.6.6.
192

Generated by GNU Enscript 1.6.6.
193
 
193
 
194
 
194
 
195
 
195