Tal_Rofe

circle_houghpeaks

Jan 21st, 2018
27
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
MatLab 7.32 KB | None | 0 0
  1. function peaks = circle_houghpeaks(h, radii, varargin)
  2. %CIRCLE_HOUGHPEAKS finds peaks in the output of CIRCLE_HOUGH
  3. %   PEAKS = CIRCLE_HOUGHPEAKS(H, RADII, MARGIN, OPTIONS) locates the
  4. %   positions of peaks in the output of CIRCLE_HOUGH. The result PEAKS is a
  5. %   3 x N array, where each column gives the position and radius of a
  6. %   possible circle in the original array. The first row of PEAKS has the
  7. %   x-coordinates, the second row has the y-coordinates, and the third row
  8. %   has the radii.
  9. %
  10. %   H is the 3D accumulator array returned by CIRCLE_HOUGH.
  11. %
  12. %   RADII is the array of radii which was passed as an argument to
  13. %   CIRCLE_HOUGH.
  14. %
  15. %   MARGIN is optional, and may be omitted if the 'same' option was used
  16. %   with CIRCLE_HOUGH. Otherwise, it should be the second result returned
  17. %   by CIRCLE_HOUGH.
  18. %
  19. %   OPTIONS is a comma-separated list of parameter/value pairs, with the
  20. %   following effects:
  21. %
  22. %   'Smoothxy' causes each x-y layer of H to be smoothed before peak
  23. %   detection using a 2D Gaussian kernel whose "sigma" parameter is given
  24. %   by the value of this argument.
  25. %
  26. %   'Smoothr' causes each radius column of H to be smoothed before peak
  27. %   detection using a 1D Gaussian kernel whose "sigma" parameter is given
  28. %   by the value of this argument.
  29. %
  30. %       Note: Smoothing may be useful to locate peaks in noisy accumulator
  31. %       arrays. However, it may also cause the performance to deteriorate
  32. %       if H contains sharp peaks. It is most likely to be useful if
  33. %       neighbourhood suppression (see below) is not used.
  34. %
  35. %       Both smoothing operations use reflecting boundary conditions to
  36. %       compute values close to the boundaries.
  37. %
  38. %   'Threshold' sets the minimum number of votes (after any smoothing)
  39. %   needed for a peak to be counted. The default is 0.5 * the maximum value
  40. %   in H.
  41. %
  42. %   'Npeaks' sets the maximum number of peaks to be found. The highest
  43. %   NPEAKS peaks are returned, unless the threshold causes fewer than
  44. %   NPEAKS peaks to be available.
  45. %
  46. %   'Nhoodxy' must be followed by an odd integer, which sets a minimum
  47. %   spatial separation between peaks. See below for a more precise
  48. %   statement. The default is 1.
  49. %
  50. %   'Nhoodr' must be followed by an odd integer, which sets a minimum
  51. %   separation in radius between peaks. See below for a more precise
  52. %   statement. The default is 1.
  53. %
  54. %       When a peak has been found, no other peak with a position within an
  55. %       NHOODXY x NHOODXY x NHOODR box centred on the first peak will be
  56. %       detected. Peaks are found sequentially; for example, after the
  57. %       highest peak has been found, the second will be found at the
  58. %       largest value in H excepting the exclusion box found the first
  59. %       peak. This is similar to the mechanism provided by the Toolbox
  60. %       function HOUGHPEAKS.
  61. %
  62. %       If both the 'Nhoodxy' and 'Nhoodr' options are omitted, the effect
  63. %       is not quite the same as setting each of them to 1. Instead of a
  64. %       sequential algorithm with repeated passes over H, the Toolbox
  65. %       function IMREGIONALMAX is used. This may produce slightly different
  66. %       results, since an above-threshold point adjacent to a peak will
  67. %       appear as an independent peak using the sequential suppression
  68. %       algorithm, but will not be a local maximum.
  69. %
  70. %   See also CIRCLE_HOUGH, CIRCLE_HOUGHDEMO
  71.  
  72. % check arguments
  73. params = checkargs(h, radii, varargin{:});
  74.  
  75. % smooth the accumulator - xy
  76. if params.smoothxy > 0
  77.     [m, hsize] = gaussmask1d(params.smoothxy);
  78.     % smooth each dimension separately, with reflection
  79.     h = cat(1, h(hsize:-1:1,:,:), h, h(end:-1:end-hsize+1,:,:));
  80.     h = convn(h, reshape(m, length(m), 1, 1), 'valid');
  81.    
  82.     h = cat(2, h(:,hsize:-1:1,:), h, h(:,end:-1:end-hsize+1,:));
  83.     h = convn(h, reshape(m, 1, length(m), 1), 'valid');
  84. end
  85.  
  86. % smooth the accumulator - r
  87. if params.smoothr > 0
  88.     [m, hsize] = gaussmask1d(params.smoothr);
  89.     h = cat(3, h(:,:,hsize:-1:1), h, h(:,:,end:-1:end-hsize+1));
  90.     h = convn(h, reshape(m, 1, 1, length(m)), 'valid');
  91. end
  92.  
  93. % set threshold
  94. if isempty(params.threshold)
  95.     params.threshold = 0.5 * max(h(:));
  96. end
  97.  
  98. if isempty(params.nhoodxy) && isempty(params.nhoodr)
  99.     % First approach to peak finding: local maxima
  100.    
  101.     % find the maxima
  102.     maxarr = imregionalmax(h);
  103.    
  104.     maxarr = maxarr & h >= params.threshold;
  105.    
  106.     % get array indices
  107.     peakind = find(maxarr);
  108.     [y, x, rind] = ind2sub(size(h), peakind);
  109.     peaks = [x'; y'; radii(rind)];
  110.    
  111.     % get strongest peaks
  112.     if ~isempty(params.npeaks) && params.npeaks < size(peaks,2)
  113.         [~, ind] = sort(h(peakind), 'descend');
  114.         ind = ind(1:params.npeaks);
  115.         peaks = peaks(:, ind);
  116.     end
  117.    
  118. else
  119.     % Second approach: iterative global max with suppression
  120.     if isempty(params.nhoodxy)
  121.         params.nhoodxy = 1;
  122.     elseif isempty(params.nhoodr)
  123.         params.nhoodr = 1;
  124.     end
  125.     nhood2 = ([params.nhoodxy params.nhoodxy params.nhoodr]-1) / 2;
  126.    
  127.     if isempty(params.npeaks)
  128.         maxpks = 0;
  129.         peaks = zeros(3, round(numel(h)/100));  % preallocate
  130.     else
  131.         maxpks = params.npeaks;  
  132.         peaks = zeros(3, maxpks);  % preallocate
  133.     end
  134.    
  135.     np = 0;
  136.     while true
  137.         [r, c, k, v] = max3(h);
  138.         % stop if peak height below threshold
  139.         if v < params.threshold || v == 0
  140.             break;
  141.         end
  142.         np = np + 1;
  143.         peaks(:, np) = [c; r; radii(k)];
  144.         % stop if done enough peaks
  145.         if np == maxpks
  146.             break;
  147.         end
  148.         % suppress this peak
  149.         r0 = max([1 1 1], [r c k]-nhood2);
  150.         r1 = min(size(h), [r c k]+nhood2);
  151.         h(r0(1):r1(1), r0(2):r1(2), r0(3):r1(3)) = 0;
  152.     end
  153.     peaks(:, np+1:end) = [];   % trim
  154. end
  155.  
  156. % adjust for margin
  157. if params.margin > 0
  158.     peaks([1 2], :) = peaks([1 2], :) - params.margin;
  159. end
  160. end
  161.  
  162. function params = checkargs(h, radii, varargin)
  163. % Argument checking
  164. ip = inputParser;
  165.  
  166. % required
  167. htest = @(h) validateattributes(h, {'double'}, {'real' 'nonnegative' 'nonsparse'});
  168. ip.addRequired('h', htest);
  169. rtest = @(radii) validateattributes(radii, {'double'}, {'real' 'positive' 'vector'});
  170. ip.addRequired('radii', rtest);
  171.  
  172. % optional
  173. mtest = @(n) validateattributes(n, {'double'}, {'real' 'nonnegative' 'integer' 'scalar'});
  174. ip.addOptional('margin', 0, mtest);
  175.  
  176. % parameter/value pairs
  177. stest = @(s) validateattributes(s, {'double'}, {'real' 'nonnegative' 'scalar'});
  178. ip.addParamValue('smoothxy', 0, stest);
  179. ip.addParamValue('smoothr', 0, stest);
  180. ip.addParamValue('threshold', [], stest);
  181. nptest = @(n) validateattributes(n, {'double'}, {'real' 'positive' 'integer' 'scalar'});
  182. ip.addParamValue('npeaks', [], nptest);
  183. nhtest = @(n) validateattributes(n, {'double'}, {'odd' 'positive' 'scalar'});
  184. ip.addParamValue('nhoodxy', [], nhtest);
  185. ip.addParamValue('nhoodr', [], nhtest);
  186. ip.parse(h, radii, varargin{:});
  187. params = ip.Results;
  188. end
  189.  
  190. function [m, hsize] = gaussmask1d(sigma)
  191. % truncated 1D Gaussian mask
  192. hsize = ceil(2.5*sigma);  % reasonable truncation
  193. x = (-hsize:hsize) / (sqrt(2) * sigma);
  194. m = exp(-x.^2);
  195. m = m / sum(m);  % normalise
  196. end
  197.  
  198. function [r, c, k, v] = max3(h)
  199. % location and value of global maximum of a 3D array
  200. [vr, r] = max(h);
  201. [vc, c] = max(vr);
  202. [v, k] = max(vc);
  203. c = c(1, 1, k);
  204. r = r(1, c, k);
  205. end
Add Comment
Please, Sign In to add comment