PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/matlab/src/CircularHough_Grd.m

http://matlabdb.googlecode.com/
MATLAB | 651 lines | 280 code | 65 blank | 306 comment | 35 complexity | e0400ede69347d234274267e0bb26cde MD5 | raw file
  1. %> @file CircularHough_Grd.m
  2. %> @brief HOUGH??????
  3. %=============================================
  4. %> ????,??????????????
  5. %>
  6. function [accum, varargout] = CircularHough_Grd(img, radrange, varargin)
  7. %Detect circular shapes in a grayscale image. Resolve their center
  8. %positions and radii.
  9. %
  10. % [accum, circen, cirrad, dbg_LMmask] = CircularHough_Grd(
  11. % img, radrange, grdthres, fltr4LM_R, multirad, fltr4accum)
  12. % Circular Hough transform based on the gradient field of an image.
  13. % NOTE: Operates on grayscale images, NOT B/W bitmaps.
  14. % NO loops in the implementation of Circular Hough transform,
  15. % which means faster operation but at the same time larger
  16. % memory consumption.
  17. %
  18. %%%%%%%% INPUT: (img, radrange, grdthres, fltr4LM_R, multirad, fltr4accum)
  19. %
  20. % img: A 2-D grayscale image (NO B/W bitmap)
  21. %
  22. % radrange: The possible minimum and maximum radii of the circles
  23. % to be searched, in the format of
  24. % [minimum_radius , maximum_radius] (unit: pixels)
  25. % **NOTE**: A smaller range saves computational time and
  26. % memory.
  27. %
  28. % grdthres: (Optional, default is 10, must be non-negative)
  29. % The algorithm is based on the gradient field of the
  30. % input image. A thresholding on the gradient magnitude
  31. % is performed before the voting process of the Circular
  32. % Hough transform to remove the 'uniform intensity'
  33. % (sort-of) image background from the voting process.
  34. % In other words, pixels with gradient magnitudes smaller
  35. % than 'grdthres' are NOT considered in the computation.
  36. % **NOTE**: The default parameter value is chosen for
  37. % images with a maximum intensity close to 255. For cases
  38. % with dramatically different maximum intensities, e.g.
  39. % 10-bit bitmaps in stead of the assumed 8-bit, the default
  40. % value can NOT be used. A value of 4% to 10% of the maximum
  41. % intensity may work for general cases.
  42. %
  43. % fltr4LM_R: (Optional, default is 8, minimum is 3)
  44. % The radius of the filter used in the search of local
  45. % maxima in the accumulation array. To detect circles whose
  46. % shapes are less perfect, the radius of the filter needs
  47. % to be set larger.
  48. %
  49. % multirad: (Optional, default is 0.5)
  50. % In case of concentric circles, multiple radii may be
  51. % detected corresponding to a single center position. This
  52. % argument sets the tolerance of picking up the likely
  53. % radii values. It ranges from 0.1 to 1, where 0.1
  54. % corresponds to the largest tolerance, meaning more radii
  55. % values will be detected, and 1 corresponds to the smallest
  56. % tolerance, in which case only the "principal" radius will
  57. % be picked up.
  58. %
  59. % fltr4accum: (Optional. A default filter will be used if not given)
  60. % Filter used to smooth the accumulation array. Depending
  61. % on the image and the parameter settings, the accumulation
  62. % array built has different noise level and noise pattern
  63. % (e.g. noise frequencies). The filter should be set to an
  64. % appropriately size such that it's able to suppress the
  65. % dominant noise frequency.
  66. %
  67. %%%%%%%% OUTPUT: [accum, circen, cirrad, dbg_LMmask]
  68. %
  69. % accum: The result accumulation array from the Circular Hough
  70. % transform. The accumulation array has the same dimension
  71. % as the input image.
  72. %
  73. % circen: (Optional)
  74. % Center positions of the circles detected. Is a N-by-2
  75. % matrix with each row contains the (x, y) positions
  76. % of a circle. For concentric circles (with the same center
  77. % position), say k of them, the same center position will
  78. % appear k times in the matrix.
  79. %
  80. % cirrad: (Optional)
  81. % Estimated radii of the circles detected. Is a N-by-1
  82. % column vector with a one-to-one correspondance to the
  83. % output 'circen'. A value 0 for the radius indicates a
  84. % failed detection of the circle's radius.
  85. %
  86. % dbg_LMmask: (Optional, for debugging purpose)
  87. % Mask from the search of local maxima in the accumulation
  88. % array.
  89. %
  90. %%%%%%%%% EXAMPLE #0:
  91. % rawimg = imread('TestImg_CHT_a2.bmp');
  92. % tic;
  93. % [accum, circen, cirrad] = CircularHough_Grd(rawimg, [15 60]);
  94. % toc;
  95. % figure(1); imagesc(accum); axis image;
  96. % title('Accumulation Array from Circular Hough Transform');
  97. % figure(2); imagesc(rawimg); colormap('gray'); axis image;
  98. % hold on;
  99. % plot(circen(:,1), circen(:,2), 'r+');
  100. % for k = 1 : size(circen, 1),
  101. % DrawCircle(circen(k,1), circen(k,2), cirrad(k), 32, 'b-');
  102. % end
  103. % hold off;
  104. % title(['Raw Image with Circles Detected ', ...
  105. % '(center positions and radii marked)']);
  106. % figure(3); surf(accum, 'EdgeColor', 'none'); axis ij;
  107. % title('3-D View of the Accumulation Array');
  108. %
  109. % COMMENTS ON EXAMPLE #0:
  110. % Kind of an easy case to handle. To detect circles in the image whose
  111. % radii range from 15 to 60. Default values for arguments 'grdthres',
  112. % 'fltr4LM_R', 'multirad' and 'fltr4accum' are used.
  113. %
  114. %%%%%%%%% EXAMPLE #1:
  115. % rawimg = imread('TestImg_CHT_a3.bmp');
  116. % tic;
  117. % [accum, circen, cirrad] = CircularHough_Grd(rawimg, [15 60], 10, 20);
  118. % toc;
  119. % figure(1); imagesc(accum); axis image;
  120. % title('Accumulation Array from Circular Hough Transform');
  121. % figure(2); imagesc(rawimg); colormap('gray'); axis image;
  122. % hold on;
  123. % plot(circen(:,1), circen(:,2), 'r+');
  124. % for k = 1 : size(circen, 1),
  125. % DrawCircle(circen(k,1), circen(k,2), cirrad(k), 32, 'b-');
  126. % end
  127. % hold off;
  128. % title(['Raw Image with Circles Detected ', ...
  129. % '(center positions and radii marked)']);
  130. % figure(3); surf(accum, 'EdgeColor', 'none'); axis ij;
  131. % title('3-D View of the Accumulation Array');
  132. %
  133. % COMMENTS ON EXAMPLE #1:
  134. % The shapes in the raw image are not very good circles. As a result,
  135. % the profile of the peaks in the accumulation array are kind of
  136. % 'stumpy', which can be seen clearly from the 3-D view of the
  137. % accumulation array. (As a comparison, please see the sharp peaks in
  138. % the accumulation array in example #0) To extract the peak positions
  139. % nicely, a value of 20 (default is 8) is used for argument 'fltr4LM_R',
  140. % which is the radius of the filter used in the search of peaks.
  141. %
  142. %%%%%%%%% EXAMPLE #2:
  143. % rawimg = imread('TestImg_CHT_b3.bmp');
  144. % fltr4img = [1 1 1 1 1; 1 2 2 2 1; 1 2 4 2 1; 1 2 2 2 1; 1 1 1 1 1];
  145. % fltr4img = fltr4img / sum(fltr4img(:));
  146. % imgfltrd = filter2( fltr4img , rawimg );
  147. % tic;
  148. % [accum, circen, cirrad] = CircularHough_Grd(imgfltrd, [15 80], 8, 10);
  149. % toc;
  150. % figure(1); imagesc(accum); axis image;
  151. % title('Accumulation Array from Circular Hough Transform');
  152. % figure(2); imagesc(rawimg); colormap('gray'); axis image;
  153. % hold on;
  154. % plot(circen(:,1), circen(:,2), 'r+');
  155. % for k = 1 : size(circen, 1),
  156. % DrawCircle(circen(k,1), circen(k,2), cirrad(k), 32, 'b-');
  157. % end
  158. % hold off;
  159. % title(['Raw Image with Circles Detected ', ...
  160. % '(center positions and radii marked)']);
  161. %
  162. % COMMENTS ON EXAMPLE #2:
  163. % The circles in the raw image have small scale irregularities along
  164. % the edges, which could lead to an accumulation array that is bad for
  165. % local maxima detection. A 5-by-5 filter is used to smooth out the
  166. % small scale irregularities. A blurred image is actually good for the
  167. % algorithm implemented here which is based on the image's gradient
  168. % field.
  169. %
  170. %%%%%%%%% EXAMPLE #3:
  171. % rawimg = imread('TestImg_CHT_c3.bmp');
  172. % fltr4img = [1 1 1 1 1; 1 2 2 2 1; 1 2 4 2 1; 1 2 2 2 1; 1 1 1 1 1];
  173. % fltr4img = fltr4img / sum(fltr4img(:));
  174. % imgfltrd = filter2( fltr4img , rawimg );
  175. % tic;
  176. % [accum, circen, cirrad] = ...
  177. % CircularHough_Grd(imgfltrd, [15 105], 8, 10, 0.7);
  178. % toc;
  179. % figure(1); imagesc(accum); axis image;
  180. % figure(2); imagesc(rawimg); colormap('gray'); axis image;
  181. % hold on;
  182. % plot(circen(:,1), circen(:,2), 'r+');
  183. % for k = 1 : size(circen, 1),
  184. % DrawCircle(circen(k,1), circen(k,2), cirrad(k), 32, 'b-');
  185. % end
  186. % hold off;
  187. % title(['Raw Image with Circles Detected ', ...
  188. % '(center positions and radii marked)']);
  189. %
  190. % COMMENTS ON EXAMPLE #3:
  191. % Similar to example #2, a filtering before circle detection works for
  192. % noisy image too. 'multirad' is set to 0.7 to eliminate the false
  193. % detections of the circles' radii.
  194. %
  195. %%%%%%%%% BUG REPORT:
  196. % This is a beta version. Please send your bug reports, comments and
  197. % suggestions to pengtao@glue.umd.edu . Thanks.
  198. %
  199. %
  200. %%%%%%%%% INTERNAL PARAMETERS:
  201. % The INPUT arguments are just part of the parameters that are used by
  202. % the circle detection algorithm implemented here. Variables in the code
  203. % with a prefix 'prm_' in the name are the parameters that control the
  204. % judging criteria and the behavior of the algorithm. Default values for
  205. % these parameters can hardly work for all circumstances. Therefore, at
  206. % occasions, the values of these INTERNAL PARAMETERS (parameters that
  207. % are NOT exposed as input arguments) need to be fine-tuned to make
  208. % the circle detection work as expected.
  209. % The following example shows how changing an internal parameter could
  210. % influence the detection result.
  211. % 1. Change the value of the internal parameter 'prm_LM_LoBndRa' to 0.4
  212. % (default is 0.2)
  213. % 2. Run the following matlab code:
  214. % fltr4accum = [1 2 1; 2 6 2; 1 2 1];
  215. % fltr4accum = fltr4accum / sum(fltr4accum(:));
  216. % rawimg = imread('Frame_0_0022_portion.jpg');
  217. % tic;
  218. % [accum, circen] = CircularHough_Grd(rawimg, ...
  219. % [4 14], 10, 4, 0.5, fltr4accum);
  220. % toc;
  221. % figure(1); imagesc(accum); axis image;
  222. % title('Accumulation Array from Circular Hough Transform');
  223. % figure(2); imagesc(rawimg); colormap('gray'); axis image;
  224. % hold on; plot(circen(:,1), circen(:,2), 'r+'); hold off;
  225. % title('Raw Image with Circles Detected (center positions marked)');
  226. % 3. See how different values of the parameter 'prm_LM_LoBndRa' could
  227. % influence the result.
  228. % Author: Tao Peng
  229. % Department of Mechanical Engineering
  230. % University of Maryland, College Park, Maryland 20742, USA
  231. % pengtao@glue.umd.edu
  232. % Version: Beta Revision: Mar. 07, 2007
  233. %%%%%%%% Arguments and parameters %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  234. % Validation of arguments
  235. if ndims(img) ~= 2 || ~isnumeric(img),
  236. error('CircularHough_Grd: ''img'' has to be 2 dimensional');
  237. end
  238. if ~all(size(img) >= 32),
  239. error('CircularHough_Grd: ''img'' has to be larger than 32-by-32');
  240. end
  241. if numel(radrange) ~= 2 || ~isnumeric(radrange),
  242. error(['CircularHough_Grd: ''radrange'' has to be ', ...
  243. 'a two-element vector']);
  244. end
  245. prm_r_range = sort(max( [0,0;radrange(1),radrange(2)] ));
  246. % Parameters (default values)
  247. prm_grdthres = 10;
  248. prm_fltrLM_R = 8;
  249. prm_multirad = 0.5;
  250. func_compu_cen = true;
  251. func_compu_radii = true;
  252. % Validation of arguments
  253. vap_grdthres = 1;
  254. if nargin > (1 + vap_grdthres),
  255. if isnumeric(varargin{vap_grdthres}) && ...
  256. varargin{vap_grdthres}(1) >= 0,
  257. prm_grdthres = varargin{vap_grdthres}(1);
  258. else
  259. error(['CircularHough_Grd: ''grdthres'' has to be ', ...
  260. 'a non-negative number']);
  261. end
  262. end
  263. vap_fltr4LM = 2; % filter for the search of local maxima
  264. if nargin > (1 + vap_fltr4LM),
  265. if isnumeric(varargin{vap_fltr4LM}) && varargin{vap_fltr4LM}(1) >= 3,
  266. prm_fltrLM_R = varargin{vap_fltr4LM}(1);
  267. else
  268. error(['CircularHough_Grd: ''fltr4LM_R'' has to be ', ...
  269. 'larger than or equal to 3']);
  270. end
  271. end
  272. vap_multirad = 3;
  273. if nargin > (1 + vap_multirad),
  274. if isnumeric(varargin{vap_multirad}) && ...
  275. varargin{vap_multirad}(1) >= 0.1 && ...
  276. varargin{vap_multirad}(1) <= 1,
  277. prm_multirad = varargin{vap_multirad}(1);
  278. else
  279. error(['CircularHough_Grd: ''multirad'' has to be ', ...
  280. 'within the range [0.1, 1]']);
  281. end
  282. end
  283. vap_fltr4accum = 4; % filter for smoothing the accumulation array
  284. if nargin > (1 + vap_fltr4accum),
  285. if isnumeric(varargin{vap_fltr4accum}) && ...
  286. ndims(varargin{vap_fltr4accum}) == 2 && ...
  287. all(size(varargin{vap_fltr4accum}) >= 3),
  288. fltr4accum = varargin{vap_fltr4accum};
  289. else
  290. error(['CircularHough_Grd: ''fltr4accum'' has to be ', ...
  291. 'a 2-D matrix with a minimum size of 3-by-3']);
  292. end
  293. else
  294. % Default filter (5-by-5)
  295. fltr4accum = ones(5,5);
  296. fltr4accum(2:4,2:4) = 2;
  297. fltr4accum(3,3) = 6;
  298. end
  299. func_compu_cen = ( nargout > 1 );
  300. func_compu_radii = ( nargout > 2 );
  301. % Reserved parameters
  302. dbg_on = false; % debug information
  303. dbg_bfigno = 4;
  304. if nargout > 3, dbg_on = true; end
  305. %%%%%%%% Building accumulation array %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  306. % Convert the image to single if it is not of
  307. % class float (single or double)
  308. img_is_double = isa(img, 'double');
  309. if ~(img_is_double || isa(img, 'single')),
  310. imgf = single(img);
  311. end
  312. % Compute the gradient and the magnitude of gradient
  313. if img_is_double,
  314. [grdx, grdy] = gradient(img);
  315. else
  316. [grdx, grdy] = gradient(imgf);
  317. end
  318. grdmag = sqrt(grdx.^2 + grdy.^2);
  319. % Get the linear indices, as well as the subscripts, of the pixels
  320. % whose gradient magnitudes are larger than the given threshold
  321. grdmasklin = find(grdmag > prm_grdthres);
  322. [grdmask_IdxI, grdmask_IdxJ] = ind2sub(size(grdmag), grdmasklin);
  323. % Compute the linear indices (as well as the subscripts) of
  324. % all the votings to the accumulation array.
  325. % The Matlab function 'accumarray' accepts only double variable,
  326. % so all indices are forced into double at this point.
  327. % A row in matrix 'lin2accum_aJ' contains the J indices (into the
  328. % accumulation array) of all the votings that are introduced by a
  329. % same pixel in the image. Similarly with matrix 'lin2accum_aI'.
  330. rr_4linaccum = double( prm_r_range );
  331. linaccum_dr = [ (-rr_4linaccum(2) + 0.5) : -rr_4linaccum(1) , ...
  332. (rr_4linaccum(1) + 0.5) : rr_4linaccum(2) ];
  333. lin2accum_aJ = floor( ...
  334. double(grdx(grdmasklin)./grdmag(grdmasklin)) * linaccum_dr + ...
  335. repmat( double(grdmask_IdxJ)+0.5 , [1,length(linaccum_dr)] ) ...
  336. );
  337. lin2accum_aI = floor( ...
  338. double(grdy(grdmasklin)./grdmag(grdmasklin)) * linaccum_dr + ...
  339. repmat( double(grdmask_IdxI)+0.5 , [1,length(linaccum_dr)] ) ...
  340. );
  341. % Clip the votings that are out of the accumulation array
  342. mask_valid_aJaI = ...
  343. lin2accum_aJ > 0 & lin2accum_aJ < (size(grdmag,2) + 1) & ...
  344. lin2accum_aI > 0 & lin2accum_aI < (size(grdmag,1) + 1);
  345. mask_valid_aJaI_reverse = ~ mask_valid_aJaI;
  346. lin2accum_aJ = lin2accum_aJ .* mask_valid_aJaI + mask_valid_aJaI_reverse;
  347. lin2accum_aI = lin2accum_aI .* mask_valid_aJaI + mask_valid_aJaI_reverse;
  348. clear mask_valid_aJaI_reverse;
  349. % Linear indices (of the votings) into the accumulation array
  350. lin2accum = sub2ind( size(grdmag), lin2accum_aI, lin2accum_aJ );
  351. lin2accum_size = size( lin2accum );
  352. lin2accum = reshape( lin2accum, [numel(lin2accum),1] );
  353. clear lin2accum_aI lin2accum_aJ;
  354. % Weights of the votings, currently using the gradient maginitudes
  355. % but in fact any scheme can be used (application dependent)
  356. weight4accum = ...
  357. repmat( double(grdmag(grdmasklin)) , [lin2accum_size(2),1] ) .* ...
  358. mask_valid_aJaI(:);
  359. clear mask_valid_aJaI;
  360. % Build the accumulation array using Matlab function 'accumarray'
  361. accum = accumarray( lin2accum , weight4accum );
  362. accum = [ accum ; zeros( numel(grdmag) - numel(accum) , 1 ) ];
  363. accum = reshape( accum, size(grdmag) );
  364. %%%%%%%% Locating local maxima in the accumulation array %%%%%%%%%%%%
  365. % Stop if no need to locate the center positions of circles
  366. if ~func_compu_cen,
  367. return;
  368. end
  369. clear lin2accum weight4accum;
  370. % Parameters to locate the local maxima in the accumulation array
  371. % -- Segmentation of 'accum' before locating LM
  372. prm_useaoi = true;
  373. prm_aoithres_s = 2;
  374. prm_aoiminsize = floor(min([ min(size(accum)) * 0.25, ...
  375. prm_r_range(2) * 1.5 ]));
  376. % -- Filter for searching for local maxima
  377. prm_fltrLM_s = 1.35;
  378. prm_fltrLM_r = ceil( prm_fltrLM_R * 0.6 );
  379. prm_fltrLM_npix = max([ 6, ceil((prm_fltrLM_R/2)^1.8) ]);
  380. % -- Lower bound of the intensity of local maxima
  381. prm_LM_LoBndRa = 0.2; % minimum ratio of LM to the max of 'accum'
  382. % Smooth the accumulation array
  383. fltr4accum = fltr4accum / sum(fltr4accum(:));
  384. accum = filter2( fltr4accum, accum );
  385. % Select a number of Areas-Of-Interest from the accumulation array
  386. if prm_useaoi,
  387. % Threshold value for 'accum'
  388. prm_llm_thres1 = prm_grdthres * prm_aoithres_s;
  389. % Thresholding over the accumulation array
  390. accummask = ( accum > prm_llm_thres1 );
  391. % Segmentation over the mask
  392. [accumlabel, accum_nRgn] = bwlabel( accummask, 8 );
  393. % Select AOIs from segmented regions
  394. accumAOI = ones(0,4);
  395. for k = 1 : accum_nRgn,
  396. accumrgn_lin = find( accumlabel == k );
  397. [accumrgn_IdxI, accumrgn_IdxJ] = ...
  398. ind2sub( size(accumlabel), accumrgn_lin );
  399. rgn_top = min( accumrgn_IdxI );
  400. rgn_bottom = max( accumrgn_IdxI );
  401. rgn_left = min( accumrgn_IdxJ );
  402. rgn_right = max( accumrgn_IdxJ );
  403. % The AOIs selected must satisfy a minimum size
  404. if ( (rgn_right - rgn_left + 1) >= prm_aoiminsize && ...
  405. (rgn_bottom - rgn_top + 1) >= prm_aoiminsize ),
  406. accumAOI = [ accumAOI; ...
  407. rgn_top, rgn_bottom, rgn_left, rgn_right ];
  408. end
  409. end
  410. else
  411. % Whole accumulation array as the one AOI
  412. accumAOI = [1, size(accum,1), 1, size(accum,2)];
  413. end
  414. % Thresholding of 'accum' by a lower bound
  415. prm_LM_LoBnd = max(accum(:)) * prm_LM_LoBndRa;
  416. % Build the filter for searching for local maxima
  417. fltr4LM = zeros(2 * prm_fltrLM_R + 1);
  418. [mesh4fLM_x, mesh4fLM_y] = meshgrid(-prm_fltrLM_R : prm_fltrLM_R);
  419. mesh4fLM_r = sqrt( mesh4fLM_x.^2 + mesh4fLM_y.^2 );
  420. fltr4LM_mask = ...
  421. ( mesh4fLM_r > prm_fltrLM_r & mesh4fLM_r <= prm_fltrLM_R );
  422. fltr4LM = fltr4LM - ...
  423. fltr4LM_mask * (prm_fltrLM_s / sum(fltr4LM_mask(:)));
  424. if prm_fltrLM_R >= 4,
  425. fltr4LM_mask = ( mesh4fLM_r < (prm_fltrLM_r - 1) );
  426. else
  427. fltr4LM_mask = ( mesh4fLM_r < prm_fltrLM_r );
  428. end
  429. fltr4LM = fltr4LM + fltr4LM_mask / sum(fltr4LM_mask(:));
  430. % **** Debug code (begin)
  431. if dbg_on,
  432. dbg_LMmask = zeros(size(accum));
  433. end
  434. % **** Debug code (end)
  435. % For each of the AOIs selected, locate the local maxima
  436. circen = zeros(0,2);
  437. for k = 1 : size(accumAOI, 1),
  438. aoi = accumAOI(k,:); % just for referencing convenience
  439. % Thresholding of 'accum' by a lower bound
  440. accumaoi_LBMask = ...
  441. ( accum(aoi(1):aoi(2), aoi(3):aoi(4)) > prm_LM_LoBnd );
  442. % Apply the local maxima filter
  443. candLM = conv2( accum(aoi(1):aoi(2), aoi(3):aoi(4)) , ...
  444. fltr4LM , 'same' );
  445. candLM_mask = ( candLM > 0 );
  446. % Clear the margins of 'candLM_mask'
  447. candLM_mask([1:prm_fltrLM_R, (end-prm_fltrLM_R+1):end], :) = 0;
  448. candLM_mask(:, [1:prm_fltrLM_R, (end-prm_fltrLM_R+1):end]) = 0;
  449. % **** Debug code (begin)
  450. if dbg_on,
  451. dbg_LMmask(aoi(1):aoi(2), aoi(3):aoi(4)) = ...
  452. dbg_LMmask(aoi(1):aoi(2), aoi(3):aoi(4)) + ...
  453. accumaoi_LBMask + 2 * candLM_mask;
  454. end
  455. % **** Debug code (end)
  456. % Group the local maxima candidates by adjacency, compute the
  457. % centroid position for each group and take that as the center
  458. % of one circle detected
  459. [candLM_label, candLM_nRgn] = bwlabel( candLM_mask, 8 );
  460. parfor ilabel = 1 : candLM_nRgn,
  461. % Indices (to current AOI) of the pixels in the group
  462. candgrp_masklin = find( candLM_label == ilabel );
  463. [candgrp_IdxI, candgrp_IdxJ] = ...
  464. ind2sub( size(candLM_label) , candgrp_masklin );
  465. % Indices (to 'accum') of the pixels in the group
  466. candgrp_IdxI = candgrp_IdxI + ( aoi(1) - 1 );
  467. candgrp_IdxJ = candgrp_IdxJ + ( aoi(3) - 1 );
  468. candgrp_idx2acm = ...
  469. sub2ind( size(accum) , candgrp_IdxI , candgrp_IdxJ );
  470. % Minimum number of qulified pixels in the group
  471. if sum(accumaoi_LBMask(candgrp_masklin)) < prm_fltrLM_npix,
  472. continue;
  473. end
  474. % Compute the centroid position
  475. candgrp_acmsum = sum( accum(candgrp_idx2acm) );
  476. cc_x = sum( candgrp_IdxJ .* accum(candgrp_idx2acm) ) / ...
  477. candgrp_acmsum;
  478. cc_y = sum( candgrp_IdxI .* accum(candgrp_idx2acm) ) / ...
  479. candgrp_acmsum;
  480. circen = [circen; [cc_x, cc_y]];
  481. end
  482. end
  483. % **** Debug code (begin)
  484. if dbg_on,
  485. figure(dbg_bfigno); imagesc(dbg_LMmask); axis image;
  486. title('Generated map of local maxima');
  487. if size(accumAOI, 1) == 1,
  488. figure(dbg_bfigno+1);
  489. surf(candLM, 'EdgeColor', 'none'); axis ij;
  490. title('Accumulation array after local maximum filtering');
  491. end
  492. end
  493. % **** Debug code (end)
  494. %%%%%%%% Estimation of the Radii of Circles %%%%%%%%%%%%
  495. % Stop if no need to estimate the radii of circles
  496. if ~func_compu_radii,
  497. varargout{1} = circen;
  498. return;
  499. end
  500. % Parameters for the estimation of the radii of circles
  501. fltr4SgnCv = [2 1 1];
  502. fltr4SgnCv = fltr4SgnCv / sum(fltr4SgnCv);
  503. % Find circle's radius using its signature curve
  504. cirrad = zeros( size(circen,1), 1 );
  505. for k = 1 : size(circen,1),
  506. % Neighborhood region of the circle for building the sgn. curve
  507. circen_round = round( circen(k,:) );
  508. SCvR_I0 = circen_round(2) - prm_r_range(2) - 1;
  509. if SCvR_I0 < 1,
  510. SCvR_I0 = 1;
  511. end
  512. SCvR_I1 = circen_round(2) + prm_r_range(2) + 1;
  513. if SCvR_I1 > size(grdx,1),
  514. SCvR_I1 = size(grdx,1);
  515. end
  516. SCvR_J0 = circen_round(1) - prm_r_range(2) - 1;
  517. if SCvR_J0 < 1,
  518. SCvR_J0 = 1;
  519. end
  520. SCvR_J1 = circen_round(1) + prm_r_range(2) + 1;
  521. if SCvR_J1 > size(grdx,2),
  522. SCvR_J1 = size(grdx,2);
  523. end
  524. % Build the sgn. curve
  525. SgnCvMat_dx = repmat( (SCvR_J0:SCvR_J1) - circen(k,1) , ...
  526. [SCvR_I1 - SCvR_I0 + 1 , 1] );
  527. SgnCvMat_dy = repmat( (SCvR_I0:SCvR_I1)' - circen(k,2) , ...
  528. [1 , SCvR_J1 - SCvR_J0 + 1] );
  529. SgnCvMat_r = sqrt( SgnCvMat_dx .^2 + SgnCvMat_dy .^2 );
  530. SgnCvMat_rp1 = round(SgnCvMat_r) + 1;
  531. f4SgnCv = abs( ...
  532. double(grdx(SCvR_I0:SCvR_I1, SCvR_J0:SCvR_J1)) .* SgnCvMat_dx + ...
  533. double(grdy(SCvR_I0:SCvR_I1, SCvR_J0:SCvR_J1)) .* SgnCvMat_dy ...
  534. ) ./ SgnCvMat_r;
  535. SgnCv = accumarray( SgnCvMat_rp1(:) , f4SgnCv(:) );
  536. SgnCv_Cnt = accumarray( SgnCvMat_rp1(:) , ones(numel(f4SgnCv),1) );
  537. SgnCv_Cnt = SgnCv_Cnt + (SgnCv_Cnt == 0);
  538. SgnCv = SgnCv ./ SgnCv_Cnt;
  539. % Suppress the undesired entries in the sgn. curve
  540. % -- Radii that correspond to short arcs
  541. SgnCv = SgnCv .* ( SgnCv_Cnt >= (pi/4 * [0:(numel(SgnCv_Cnt)-1)]') );
  542. % -- Radii that are out of the given range
  543. SgnCv( 1 : (round(prm_r_range(1))+1) ) = 0;
  544. SgnCv( (round(prm_r_range(2))+1) : end ) = 0;
  545. % Get rid of the zero radius entry in the array
  546. SgnCv = SgnCv(2:end);
  547. % Smooth the sgn. curve
  548. SgnCv = filtfilt( fltr4SgnCv , [1] , SgnCv );
  549. % Get the maximum value in the sgn. curve
  550. SgnCv_max = max(SgnCv);
  551. if SgnCv_max <= 0,
  552. cirrad(k) = 0;
  553. continue;
  554. end
  555. % Find the local maxima in sgn. curve by 1st order derivatives
  556. % -- Mark the ascending edges in the sgn. curve as 1s and
  557. % -- descending edges as 0s
  558. SgnCv_AscEdg = ( SgnCv(2:end) - SgnCv(1:(end-1)) ) > 0;
  559. % -- Mark the transition (ascending to descending) regions
  560. SgnCv_LMmask = [ 0; 0; SgnCv_AscEdg(1:(end-2)) ] & (~SgnCv_AscEdg);
  561. SgnCv_LMmask = SgnCv_LMmask & [ SgnCv_LMmask(2:end) ; 0 ];
  562. % Incorporate the minimum value requirement
  563. SgnCv_LMmask = SgnCv_LMmask & ...
  564. ( SgnCv(1:(end-1)) >= (prm_multirad * SgnCv_max) );
  565. % Get the positions of the peaks
  566. SgnCv_LMPos = sort( find(SgnCv_LMmask) );
  567. % Save the detected radii
  568. if isempty(SgnCv_LMPos),
  569. cirrad(k) = 0;
  570. else
  571. cirrad(k) = SgnCv_LMPos(end);
  572. for i_radii = (length(SgnCv_LMPos) - 1) : -1 : 1,
  573. circen = [ circen; circen(k,:) ];
  574. cirrad = [ cirrad; SgnCv_LMPos(i_radii) ];
  575. end
  576. end
  577. end
  578. % Output
  579. varargout{1} = circen;
  580. varargout{2} = cirrad;
  581. if nargout > 3,
  582. varargout{3} = dbg_LMmask;
  583. end