PageRenderTime 28ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/support_programs/basic_image_processing/adapthisteq3d.m

http://dirart.googlecode.com/
MATLAB | 707 lines | 387 code | 102 blank | 218 comment | 55 complexity | 2a212447a1dcc3e69c19557bd6c742fc MD5 | raw file
Possible License(s): GPL-2.0
  1. function out = adapthisteq3d(varargin)
  2. %ADAPTHISTEQ Performs Contrast-Limited Adaptive Histogram Equalization (CLAHE).
  3. % ADAPTHISTEQ enhances the contrast of images by transforming the
  4. % values in the intensity image I. Unlike HISTEQ, it operates on small
  5. % data regions (tiles), rather than the entire image. Each tile's
  6. % contrast is enhanced, so that the histogram of the output region
  7. % approximately matches the specified histogram. The neighboring tiles
  8. % are then combined using bilinear interpolation in order to eliminate
  9. % artificially induced boundaries. The contrast, especially
  10. % in homogeneous areas, can be limited in order to avoid amplifying the
  11. % noise which might be present in the image.
  12. %
  13. % J = ADAPTHISTEQ(I) Performs CLAHE on the intensity image I.
  14. %
  15. % J = ADAPTHISTEQ(I,PARAM1,VAL1,PARAM2,VAL2...) sets various parameters.
  16. % Parameter names can be abbreviated, and case does not matter. Each
  17. % string parameter is followed by a value as indicated below:
  18. %
  19. % 'NumTiles' Two-element vector of positive integers: [M N].
  20. % [M N] specifies the number of tile rows and
  21. % columns. Both M and N must be at least 2.
  22. % The total number of image tiles is equal to M*N.
  23. %
  24. % Default: [8 8].
  25. %
  26. % 'ClipLimit' Real scalar from 0 to 1.
  27. % 'ClipLimit' limits contrast enhancement. Higher numbers
  28. % result in more contrast.
  29. %
  30. % Default: 0.01.
  31. %
  32. % 'NBins' Positive integer scalar.
  33. % Sets number of bins for the histogram used in building a
  34. % contrast enhancing transformation. Higher values result
  35. % in greater dynamic range at the cost of slower processing
  36. % speed.
  37. %
  38. % Default: 256.
  39. %
  40. % 'Range' One of the strings: 'original' or 'full'.
  41. % Controls the range of the output image data. If 'Range'
  42. % is set to 'original', the range is limited to
  43. % [min(I(:)) max(I(:))]. Otherwise, by default, or when
  44. % 'Range' is set to 'full', the full range of the output
  45. % image class is used (e.g. [0 255] for uint8).
  46. %
  47. % Default: 'full'.
  48. %
  49. % 'Distribution' Distribution can be one of three strings: 'uniform',
  50. % 'rayleigh', 'exponential'.
  51. % Sets desired histogram shape for the image tiles, by
  52. % specifying a distribution type.
  53. %
  54. % Default: 'uniform'.
  55. %
  56. % 'Alpha' Nonnegative real scalar.
  57. % 'Alpha' is a distribution parameter, which can be supplied
  58. % when 'Dist' is set to either 'rayleigh' or 'exponential'.
  59. %
  60. % Default: 0.4.
  61. %
  62. % Notes
  63. % -----
  64. % - 'NumTiles' specify the number of rectangular contextual regions (tiles)
  65. % into which the image is divided. The contrast transform function is
  66. % calculated for each of these regions individually. The optimal number of
  67. % tiles depends on the type of the input image, and it is best determined
  68. % through experimentation.
  69. %
  70. % - The 'ClipLimit' is a contrast factor that prevents over-saturation of the
  71. % image specifically in homogeneous areas. These areas are characterized
  72. % by a high peak in the histogram of the particular image tile due to many
  73. % pixels falling inside the same gray level range. Without the clip limit,
  74. % the adaptive histogram equalization technique could produce results that,
  75. % in some cases, are worse than the original image.
  76. %
  77. % - ADAPTHISTEQ can use Uniform, Rayleigh, or Exponential distribution as
  78. % the basis for creating the contrast transform function. The distribution
  79. % that should be used depends on the type of the input image.
  80. % For example, underwater imagery appears to look more natural when the
  81. % Rayleigh distribution is used.
  82. %
  83. % Class Support
  84. % -------------
  85. % Intensity image I can be uint8, uint16, int16, double, or single.
  86. % The output image J has the same class as I.
  87. %
  88. % Example 1
  89. % ---------
  90. % Apply Contrast-Limited Adaptive Histogram Equalization to the
  91. % rice.png image. Display the enhanced image.
  92. %
  93. % I = imread('tire.tif');
  94. % A = adapthisteq(I,'clipLimit',0.02,'Distribution','rayleigh');
  95. % figure, imshow(I);
  96. % figure, imshow(A);
  97. %
  98. % Example 2
  99. % ---------
  100. %
  101. % Apply Contrast-Limited Adaptive Histogram Equalization to a color
  102. % photograph.
  103. %
  104. % [X MAP] = imread('shadow.tif');
  105. % RGB = ind2rgb(X,MAP); % convert indexed image to truecolor format
  106. % cform2lab = makecform('srgb2lab');
  107. % LAB = applycform(RGB, cform2lab); %convert image to L*a*b color space
  108. % L = LAB(:,:,1)/100; % scale the values to range from 0 to 1
  109. % LAB(:,:,1) = adapthisteq(L,'NumTiles',[8 8],'ClipLimit',0.005)*100;
  110. % cform2srgb = makecform('lab2srgb');
  111. % J = applycform(LAB, cform2srgb); %convert back to RGB
  112. % figure, imshow(RGB); %display the results
  113. % figure, imshow(J);
  114. %
  115. % See also HISTEQ.
  116. % Copyright 1993-2004 The MathWorks, Inc.
  117. % $Revision: 1.1 $ $Date: 2009/01/20 15:31:02 $
  118. % References:
  119. % Karel Zuiderveld, "Contrast Limited Adaptive Histogram Equalization",
  120. % Graphics Gems IV, p. 474-485, code: p. 479-484
  121. %
  122. % Hanumant Singh, Woods Hole Oceanographic Institution, personal
  123. % communication
  124. %--------------------------- The algorithm ----------------------------------
  125. %
  126. % 1. Obtain all the inputs:
  127. % * image
  128. % * number of regions in row and column directions
  129. % * number of bins for the histograms used in building image transform
  130. % function (dynamic range)
  131. % * clip limit for contrast limiting (normalized from 0 to 1)
  132. % * other miscellaneous options
  133. % 2. Pre-process the inputs:
  134. % * determine real clip limit from the normalized value
  135. % * if necessary, pad the image before splitting it into regions
  136. % 3. Process each contextual region (tile) thus producing gray level mappings
  137. % * extract a single image region
  138. % * make a histogram for this region using the specified number of bins
  139. % * clip the histogram using clip limit
  140. % * create a mapping (transformation function) for this region
  141. % 4. Interpolate gray level mappings in order to assemble final CLAHE image
  142. % * extract cluster of four neighboring mapping functions
  143. % * process image region partly overlapping each of the mapping tiles
  144. % * extract a single pixel, apply four mappings to that pixel, and
  145. % interpolate between the results to obtain the output pixel; repeat
  146. % over the entire image
  147. %
  148. % See M-code for further details.
  149. %
  150. %-----------------------------------------------------------------------------
  151. [I, selectedRange, fullRange, numTiles, dimTile, clipLimit, numBins, ...
  152. noPadRect, distribution, alpha, int16ClassChange] = parseInputs(varargin{:});
  153. tileMappings = makeTileMappings(I, numTiles, dimTile, numBins, clipLimit, ...
  154. selectedRange, fullRange, distribution, alpha);
  155. %Synthesize the output image based on the individual tile mappings.
  156. out = makeClaheImage(I, tileMappings, numTiles, selectedRange, numBins,...
  157. dimTile);
  158. if int16ClassChange
  159. % Change uint16 back to int16 so output has same class as input.
  160. out = uint16toint16(out);
  161. end
  162. if ~isempty(noPadRect) %do we need to remove padding?
  163. out = out(noPadRect.ulRow:noPadRect.lrRow, ...
  164. noPadRect.ulCol:noPadRect.lrCol, ...
  165. noPadRect.ulZ:noPadRect.lrZ);
  166. end
  167. %-----------------------------------------------------------------------------
  168. function tileMappings = ...
  169. makeTileMappings(I, numTiles, dimTile, numBins, clipLimit,...
  170. selectedRange, fullRange, distribution, alpha)
  171. numPixInTile = prod(dimTile);
  172. tileMappings = cell(numTiles);
  173. % extract and process each tile
  174. imgCol = 1;
  175. for col=1:numTiles(2),
  176. imgRow = 1;
  177. for row=1:numTiles(1),
  178. imgZ = 1;
  179. for z=1:numTiles(3)
  180. tile = I(imgRow:imgRow+dimTile(1)-1,imgCol:imgCol+dimTile(2)-1,imgZ:imgZ+dimTile(3)-1);
  181. % for speed, call MEX file directly thus avoiding costly
  182. % input parsing of imhist
  183. tileHist = imhistc(tile, numBins, 1, fullRange(2));
  184. tileHist = clipHistogram(tileHist, clipLimit, numBins);
  185. tileMapping = makeMapping(tileHist, selectedRange, fullRange, ...
  186. numPixInTile, distribution, alpha);
  187. % assemble individual tile mappings by storing them in a cell array;
  188. tileMappings{row,col,z} = tileMapping;
  189. imgZ = imgZ + dimTile(3);
  190. end
  191. imgRow = imgRow + dimTile(1);
  192. end
  193. imgCol = imgCol + dimTile(2); % move to the next column of tiles
  194. end
  195. %-----------------------------------------------------------------------------
  196. % Calculate the equalized lookup table (mapping) based on cumulating the input
  197. % histogram. Note: lookup table is rescaled in the selectedRange [Min..Max].
  198. function mapping = makeMapping(imgHist, selectedRange, fullRange, ...
  199. numPixInTile, distribution, alpha)
  200. mapping = zeros(size(imgHist));
  201. histSum = cumsum(imgHist);
  202. valSpread = selectedRange(2) - selectedRange(1);
  203. switch distribution
  204. case 'uniform',
  205. scale = valSpread/numPixInTile;
  206. mapping = min(selectedRange(1) + histSum*scale,...
  207. selectedRange(2)); %limit to max
  208. case 'rayleigh', % suitable for underwater imagery
  209. % pdf = (t./alpha^2).*exp(-t.^2/(2*alpha^2))*U(t)
  210. % cdf = 1-exp(-t.^2./(2*alpha^2))
  211. hconst = 2*alpha^2;
  212. vmax = 1 - exp(-1/hconst);
  213. val = vmax*(histSum/numPixInTile);
  214. val(val>=1) = 1-eps; % avoid log(0)
  215. temp = sqrt(-hconst*log(1-val));
  216. mapping = min(selectedRange(1)+temp*valSpread,...
  217. selectedRange(2)); %limit to max
  218. case 'exponential',
  219. % pdf = alpha*exp(-alpha*t)*U(t)
  220. % cdf = 1-exp(-alpha*t)
  221. vmax = 1 - exp(-alpha);
  222. val = (vmax*histSum/numPixInTile);
  223. val(val>=1) = 1-eps;
  224. temp = -1/alpha*log(1-val);
  225. mapping = min(selectedRange(1)+temp*valSpread,selectedRange(2));
  226. otherwise,
  227. eid = sprintf('Images:%s:internalError', mfilename);
  228. msg = 'Unknown distribution type.'; %should never get here
  229. error(eid, msg);
  230. end
  231. %rescale the result to be between 0 and 1 for later use by the GRAYXFORM
  232. %private mex function
  233. mapping = mapping/fullRange(2);
  234. %-----------------------------------------------------------------------------
  235. % This function clips the histogram according to the clipLimit and
  236. % redistributes clipped pixels accross bins below the clipLimit
  237. function imgHist = clipHistogram(imgHist, clipLimit, numBins)
  238. % total number of pixels overflowing clip limit in each bin
  239. totalExcess = sum(max(imgHist - clipLimit,0));
  240. % clip the histogram and redistribute the excess pixels in each bin
  241. avgBinIncr = floor(totalExcess/numBins);
  242. upperLimit = clipLimit - avgBinIncr; % bins larger than this will be
  243. % set to clipLimit
  244. % this loop should speed up the operation by putting multiple pixels
  245. % into the "obvious" places first
  246. for k=1:numBins
  247. if imgHist(k) > clipLimit
  248. imgHist(k) = clipLimit;
  249. else
  250. if imgHist(k) > upperLimit % high bin count
  251. totalExcess = totalExcess - (clipLimit - imgHist(k));
  252. imgHist(k) = clipLimit;
  253. else
  254. totalExcess = totalExcess - avgBinIncr;
  255. imgHist(k) = imgHist(k) + avgBinIncr;
  256. end
  257. end
  258. end
  259. % this loops redistributes the remaining pixels, one pixel at a time
  260. k = 1;
  261. while (totalExcess ~= 0)
  262. %keep increasing the step as fewer and fewer pixels remain for
  263. %the redistribution (spread them evenly)
  264. stepSize = max(floor(numBins/totalExcess),1);
  265. for m=k:stepSize:numBins
  266. if imgHist(m) < clipLimit
  267. imgHist(m) = imgHist(m)+1;
  268. totalExcess = totalExcess - 1; %reduce excess
  269. if totalExcess == 0
  270. break;
  271. end
  272. end
  273. end
  274. k = k+1; %prevent from always placing the pixels in bin #1
  275. if k > numBins % start over if numBins was reached
  276. k = 1;
  277. end
  278. end
  279. %-----------------------------------------------------------------------------
  280. % This function interpolates between neighboring tile mappings to produce a
  281. % new mapping in order to remove artificially induced tile borders.
  282. % Otherwise, these borders would become quite visible. The resulting
  283. % mapping is applied to the input image thus producing a CLAHE processed
  284. % image.
  285. function claheI = makeClaheImage(I, tileMappings, numTiles, selectedRange,...
  286. numBins, dimTile)
  287. %initialize the output image to zeros (preserve the class of the input image)
  288. claheI = I;
  289. claheI(:) = 0;
  290. %compute the LUT for looking up original image values in the tile mappings,
  291. %which we created earlier
  292. if ~(isa(I,'double') || isa(I,'single'))
  293. k = selectedRange(1)+1 : selectedRange(2)+1;
  294. aLut = zeros(length(k),1);
  295. aLut(k) = (k-1)-selectedRange(1);
  296. aLut = aLut/(selectedRange(2)-selectedRange(1));
  297. else
  298. % remap from 0..1 to 0..numBins-1
  299. if numBins ~= 1
  300. binStep = 1/(numBins-1);
  301. start = ceil(selectedRange(1)/binStep);
  302. stop = floor(selectedRange(2)/binStep);
  303. k = start+1:stop+1;
  304. aLut(k) = 0:1/(length(k)-1):1;
  305. else
  306. aLut(1) = 0; %in case someone specifies numBins = 1, which is just silly
  307. end
  308. end
  309. imgTileRow=1;
  310. for k=1:numTiles(1)+1
  311. if k == 1 %special case: top row
  312. imgTileNumRows = dimTile(1)/2; %always divisible by 2 because of padding
  313. mapTileRows = [1 1];
  314. else
  315. if k == numTiles(1)+1 %special case: bottom row
  316. imgTileNumRows = dimTile(1)/2;
  317. mapTileRows = [numTiles(1) numTiles(1)];
  318. else %default values
  319. imgTileNumRows = dimTile(1);
  320. mapTileRows = [k-1, k]; %[upperRow lowerRow]
  321. end
  322. end
  323. % loop over columns of the tileMappings cell array
  324. imgTileCol=1;
  325. for l=1:numTiles(2)+1
  326. if l == 1 %special case: left column
  327. imgTileNumCols = dimTile(2)/2;
  328. mapTileCols = [1, 1];
  329. else
  330. if l == numTiles(2)+1 % special case: right column
  331. imgTileNumCols = dimTile(2)/2;
  332. mapTileCols = [numTiles(2), numTiles(2)];
  333. else %default values
  334. imgTileNumCols = dimTile(2);
  335. mapTileCols = [l-1, l]; % right left
  336. end
  337. end
  338. imgTileZ = 1;
  339. for m=1:numTiles(3)+1
  340. if m == 1 %special case: front column
  341. imgTileNumZs = dimTile(3)/2;
  342. mapTileZs = [1, 1];
  343. else
  344. if m == numTiles(3)+1 % special case: back column
  345. imgTileNumZs = dimTile(3)/2;
  346. mapTileZs = [numTiles(3), numTiles(3)];
  347. else %default values
  348. imgTileNumZs = dimTile(3);
  349. mapTileZs = [m-1, m]; % right left
  350. end
  351. end
  352. % Extract size tile mappings
  353. ulMapTile1 = tileMappings{mapTileRows(1), mapTileCols(1), mapTileZs(1)};
  354. urMapTile1 = tileMappings{mapTileRows(1), mapTileCols(2), mapTileZs(1)};
  355. blMapTile1 = tileMappings{mapTileRows(2), mapTileCols(1), mapTileZs(1)};
  356. brMapTile1 = tileMappings{mapTileRows(2), mapTileCols(2), mapTileZs(1)};
  357. ulMapTile2 = tileMappings{mapTileRows(1), mapTileCols(1), mapTileZs(2)};
  358. urMapTile2 = tileMappings{mapTileRows(1), mapTileCols(2), mapTileZs(2)};
  359. blMapTile2 = tileMappings{mapTileRows(2), mapTileCols(1), mapTileZs(2)};
  360. brMapTile2 = tileMappings{mapTileRows(2), mapTileCols(2), mapTileZs(2)};
  361. % Calculate the new greylevel assignments of pixels
  362. % within a submatrix of the image specified by imgTileIdx. This
  363. % is done by a bilinear interpolation between four different mappings
  364. % in order to eliminate boundary artifacts.
  365. normFactor = imgTileNumRows*imgTileNumCols*imgTileNumZs; %normalization factor
  366. imgTileIdx = {imgTileRow:imgTileRow+imgTileNumRows-1, ...
  367. imgTileCol:imgTileCol+imgTileNumCols-1,...
  368. imgTileZ:imgTileZ+imgTileNumZs-1};
  369. imgPixVals = grayxform(I(imgTileIdx{1},imgTileIdx{2},imgTileIdx{3}), aLut);
  370. % calculate the weights used for linear interpolation between the
  371. % four mappings
  372. rowW = repmat((0:imgTileNumRows-1)',[1 imgTileNumCols imgTileNumZs]);
  373. colW = repmat(0:imgTileNumCols-1,[imgTileNumRows 1 imgTileNumZs]);
  374. zW = repmat(reshape(0:imgTileNumZs-1,1,1,imgTileNumZs),[imgTileNumRows imgTileNumCols 1]);
  375. rowRevW = repmat((imgTileNumRows:-1:1)',[1 imgTileNumCols imgTileNumZs]);
  376. colRevW = repmat(imgTileNumCols:-1:1,[imgTileNumRows 1 imgTileNumZs]);
  377. zRevW = repmat(reshape(imgTileNumZs:-1:1,1,1,imgTileNumZs),[imgTileNumRows imgTileNumCols 1]);
  378. claheI(imgTileIdx{1}, imgTileIdx{2}, imgTileIdx{3}) = (...
  379. rowRevW .* ( colRevW .* zRevW .* double(grayxform(imgPixVals,ulMapTile1)) ...
  380. + colW .* zRevW .* double(grayxform(imgPixVals,urMapTile1)))...
  381. + rowW .* ( colRevW .* zRevW .* double(grayxform(imgPixVals,blMapTile1)) ...
  382. + colW .* zRevW .* double(grayxform(imgPixVals,brMapTile1)))...
  383. + rowRevW .* ( colRevW .* zW .* double(grayxform(imgPixVals,ulMapTile2)) ...
  384. + colW .* zW .* double(grayxform(imgPixVals,urMapTile2)))...
  385. + rowW .* ( colRevW .* zW .* double(grayxform(imgPixVals,blMapTile2)) ...
  386. + colW .* zW .* double(grayxform(imgPixVals,brMapTile2)))...
  387. )/normFactor;
  388. imgTileZ = imgTileZ + imgTileNumZs;
  389. end
  390. imgTileCol = imgTileCol + imgTileNumCols;
  391. end %over tile cols
  392. imgTileRow = imgTileRow + imgTileNumRows;
  393. end %over tile rows
  394. %-----------------------------------------------------------------------------
  395. function [I, selectedRange, fullRange, numTiles, dimTile, clipLimit,...
  396. numBins, noPadRect, distribution, alpha, ...
  397. int16ClassChange] = parseInputs(varargin)
  398. iptchecknargin(1,13,nargin,mfilename);
  399. I = varargin{1};
  400. iptcheckinput(I, {'uint8', 'uint16', 'double', 'int16', 'single'}, ...
  401. {'real', '3d', 'nonsparse', 'nonempty'}, ...
  402. mfilename, 'I', 1);
  403. % convert int16 to uint16
  404. if isa(I,'int16')
  405. I = int16touint16(I);
  406. int16ClassChange = true;
  407. else
  408. int16ClassChange = false;
  409. end
  410. if any(size(I) < 2)
  411. eid = sprintf('Images:%s:inputImageTooSmall', mfilename);
  412. msg = 'The input image width and height must be at least equal to 2.';
  413. error(eid, msg);
  414. end
  415. %Other options
  416. %%%%%%%%%%%%%%
  417. %Set the defaults
  418. distribution = 'uniform';
  419. alpha = 0.4;
  420. if isa(I, 'double') || isa(I,'single')
  421. fullRange = [0 1];
  422. else
  423. fullRange(1) = I(1); %copy class of the input image
  424. fullRange(1:2) = [-Inf Inf]; %will be clipped to min and max
  425. fullRange = double(fullRange);
  426. end
  427. selectedRange = fullRange;
  428. %Set the default to 256 bins regardless of the data type;
  429. %the user can override this value at any time
  430. numBins = 256;
  431. normClipLimit = 0.01;
  432. numTiles = [8 8 8];
  433. checkAlpha = false;
  434. validStrings = {'NumTiles','ClipLimit','NBins','Distribution',...
  435. 'Alpha','Range'};
  436. if nargin > 1
  437. done = false;
  438. idx = 2;
  439. while ~done
  440. input = varargin{idx};
  441. inputStr = iptcheckstrs(input, validStrings,mfilename,'PARAM',idx);
  442. idx = idx+1; %advance index to point to the VAL portion of the input
  443. if idx > nargin
  444. eid = sprintf('Images:%s:valFor%sMissing', mfilename, inputStr);
  445. msg = sprintf('Parameter ''%s'' must be followed by a value.', inputStr);
  446. error(eid, msg);
  447. end
  448. switch inputStr
  449. case 'NumTiles'
  450. numTiles = varargin{idx};
  451. iptcheckinput(numTiles, {'double'}, {'real', 'vector', ...
  452. 'integer', 'finite','positive'},...
  453. mfilename, inputStr, idx);
  454. if (any(size(numTiles) ~= [1,3]))
  455. eid = sprintf('Images:%s:invalidNumTiles', mfilename);
  456. msg = sprintf(['Value of parameter,''%s'', must be a three '...
  457. 'element row vector.'], inputStr);
  458. error(eid, msg);
  459. end
  460. if any(numTiles < 2)
  461. eid = sprintf('Images:%s:invalidNumTiles', mfilename);
  462. msg = sprintf(['Both elements of ''%s'' value ',...
  463. 'must be greater or equal to 2.'],...
  464. inputStr);
  465. error(eid, msg);
  466. end
  467. case 'ClipLimit'
  468. normClipLimit = varargin{idx};
  469. iptcheckinput(normClipLimit, {'double'}, ...
  470. {'scalar','real','nonnegative'},...
  471. mfilename, inputStr, idx);
  472. if normClipLimit > 1
  473. eid = sprintf('Images:%s:invalidClipLimit', mfilename);
  474. msg = sprintf(['Value of parameter ''%s'' must be in the range ',...
  475. 'from 0 to 1.'], inputStr);
  476. error(eid, msg);
  477. end
  478. case 'NBins'
  479. numBins = varargin{idx};
  480. iptcheckinput(numBins, {'double'}, {'scalar','real','integer',...
  481. 'positive'}, mfilename, 'NBins', idx);
  482. case 'Distribution'
  483. validDist = {'rayleigh','exponential','uniform'};
  484. distribution = iptcheckstrs(varargin{idx}, validDist, mfilename,...
  485. 'Distribution', idx);
  486. case 'Alpha'
  487. alpha = varargin{idx};
  488. iptcheckinput(alpha, {'double'},{'scalar','real',...
  489. 'nonnan','positive','finite'},...
  490. mfilename, 'Alpha',idx);
  491. checkAlpha = true;
  492. case 'Range'
  493. validRangeStrings = {'original','full'};
  494. rangeStr = iptcheckstrs(varargin{idx}, validRangeStrings,mfilename,...
  495. 'Range',idx);
  496. if strmatch(rangeStr,'original')
  497. selectedRange = double([min(I(:)), max(I(:))]);
  498. end
  499. otherwise
  500. eid = sprintf('Images:%s:internalError', mfilename);
  501. msg = 'Unknown input string.'; %should never get here
  502. error(eid, msg);
  503. end
  504. if idx >= nargin
  505. done = true;
  506. end
  507. idx=idx+1;
  508. end
  509. end
  510. %% Pre-process the inputs
  511. %%%%%%%%%%%%%%%%%%%%%%%%%%
  512. dimI = size(I);
  513. dimTile = dimI ./ numTiles;
  514. %check if tile size is reasonable
  515. if any(dimTile < 1)
  516. eid = sprintf('Images:%s:inputImageTooSmall', mfilename);
  517. msg = sprintf(['The image I is too small to be split into [%s] ',...
  518. 'number of tiles.'], num2str(numTiles));
  519. error(eid, msg);
  520. end
  521. if checkAlpha
  522. if strcmp(distribution,'uniform')
  523. eid = sprintf('Images:%s:alphaShouldNotBeSpecified', mfilename);
  524. msg = sprintf(['Parameter ''Alpha'' cannot be specified for',...
  525. ' ''%s'' distribution.'], distribution);
  526. error(eid, msg);
  527. end
  528. end
  529. %check if the image needs to be padded; pad if necessary;
  530. %padding occurs if any dimension of a single tile is an odd number
  531. %and/or when image dimensions are not divisible by the selected
  532. %number of tiles
  533. rowDiv = mod(dimI(1),numTiles(1)) == 0;
  534. colDiv = mod(dimI(2),numTiles(2)) == 0;
  535. zDiv = mod(dimI(3),numTiles(3)) == 0;
  536. if rowDiv && colDiv && zDiv
  537. rowEven = mod(dimTile(1),2) == 0;
  538. colEven = mod(dimTile(2),2) == 0;
  539. zEven = mod(dimTile(3),2) == 0;
  540. end
  541. noPadRect = [];
  542. if ~(rowDiv && colDiv && rowEven && colEven && zDiv && zEven)
  543. padRow = 0;
  544. padCol = 0;
  545. padZ = 0;
  546. if ~rowDiv
  547. rowTileDim = floor(dimI(1)/numTiles(1)) + 1;
  548. padRow = rowTileDim*numTiles(1) - dimI(1);
  549. else
  550. rowTileDim = dimI(1)/numTiles(1);
  551. end
  552. if ~colDiv
  553. colTileDim = floor(dimI(2)/numTiles(2)) + 1;
  554. padCol = colTileDim*numTiles(2) - dimI(2);
  555. else
  556. colTileDim = dimI(2)/numTiles(2);
  557. end
  558. if ~zDiv
  559. zTileDim = floor(dimI(3)/numTiles(3)) + 1;
  560. padZ = zTileDim*numTiles(3) - dimI(3);
  561. else
  562. zTileDim = dimI(3)/numTiles(3);
  563. end
  564. %check if tile dimensions are even numbers
  565. rowEven = mod(rowTileDim,2) == 0;
  566. colEven = mod(colTileDim,2) == 0;
  567. zEven = mod(zTileDim,2) == 0;
  568. if ~rowEven
  569. padRow = padRow+numTiles(1);
  570. end
  571. if ~colEven
  572. padCol = padCol+numTiles(2);
  573. end
  574. if ~zEven
  575. padZ = padZ+numTiles(3);
  576. end
  577. padRowPre = floor(padRow/2);
  578. padRowPost = ceil(padRow/2);
  579. padColPre = floor(padCol/2);
  580. padColPost = ceil(padCol/2);
  581. padZPre = floor(padZ/2);
  582. padZPost = ceil(padZ/2);
  583. I = padarray(I,[padRowPre padColPre padZPre],'symmetric','pre');
  584. I = padarray(I,[padRowPost padColPost padZPost],'symmetric','post');
  585. %UL corner (Row, Col), LR corner (Row, Col)
  586. noPadRect.ulRow = padRowPre+1;
  587. noPadRect.ulCol = padColPre+1;
  588. noPadRect.ulZ = padZPre+1;
  589. noPadRect.lrRow = padRowPre+dimI(1);
  590. noPadRect.lrCol = padColPre+dimI(2);
  591. noPadRect.lrZ = padZPre+dimI(3);
  592. end
  593. %redefine this variable to include the padding
  594. dimI = size(I);
  595. %size of the single tile
  596. dimTile = dimI ./ numTiles;
  597. %compute actual clip limit from the normalized value entered by the user
  598. %maximum value of normClipLimit=1 results in standard AHE, i.e. no clipping;
  599. %the minimum value minClipLimit would uniformly distribute the image pixels
  600. %across the entire histogram, which would result in the lowest possible
  601. %contrast value
  602. numPixInTile = prod(dimTile);
  603. minClipLimit = ceil(numPixInTile/numBins);
  604. clipLimit = minClipLimit + round(normClipLimit*(numPixInTile-minClipLimit));
  605. %-----------------------------------------------------------------------------