PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/R2007-12-15/main/image/inst/imadjust.m

#
MATLAB | 362 lines | 204 code | 55 blank | 103 comment | 33 complexity | b01487d27723bf8cfba4fcd35458268f MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. ## Copyright (C) 2004 Josep Mones i Teixidor
  2. ##
  3. ## This program is free software; you can redistribute it and/or modify
  4. ## it under the terms of the GNU General Public License as published by
  5. ## the Free Software Foundation; either version 2 of the License, or
  6. ## (at your option) any later version.
  7. ##
  8. ## This program is distributed in the hope that it will be useful,
  9. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ## GNU General Public License for more details.
  12. ##
  13. ## You should have received a copy of the GNU General Public License
  14. ## along with this program; if not, write to the Free Software
  15. ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. ##
  17. ##
  18. ## Based on old imadjust.m (GPL):
  19. ## Copyright (C) 1999,2000 Kai Habel
  20. ## -*- texinfo -*-
  21. ## @deftypefn {Function File} @var{J}= imadjust (@var{I})
  22. ## @deftypefnx {Function File} @var{J}= imadjust (@var{I},[@var{low_in};@var{high_in}])
  23. ## @deftypefnx {Function File} @var{J}= imadjust (@var{I},[@var{low_in};@var{high_in}],[@var{low_out};@var{high_out}])
  24. ## @deftypefnx {Function File} @var{J}= imadjust (..., @var{gamma})
  25. ## @deftypefnx {Function File} @var{newmap}= imadjust (@var{map}, ...)
  26. ## @deftypefnx {Function File} @var{RGB_out}= imadjust (@var{RGB}, ...)
  27. ## Adjust image or colormap values to a specified range
  28. ##
  29. ## @code{J=imadjust(I)} adjusts intensity image @var{I} values so that
  30. ## 1% of data on lower and higher values (2% in total) of the image is
  31. ## saturated; choosing for that the corresponding lower and higher
  32. ## bounds (using @code{stretchlim}) and mapping them to 0 and 1. @var{J}
  33. ## is an image of the same size as @var{I} which contains mapped values.
  34. ## This is equivalent to @code{imadjust(I,stretchlim(I))}.
  35. ##
  36. ## @code{J=imadjust(I,[low_in;high_in])} behaves as described but uses
  37. ## @var{low_in} and @var{high_in} values instead of calculating them. It
  38. ## maps those values to 0 and 1; saturates values lower than first limit
  39. ## to 0 and values higher than second to 1; and finally maps all values
  40. ## between limits linearly to a value between 0 and 1. If @code{[]} is
  41. ## passes as @code{[low_in;high_in]} value, then @code{[0;1]} is taken
  42. ## as a default value.
  43. ##
  44. ## @code{J=imadjust(I,[low_in;high_in],[low_out;high_out])} behaves as
  45. ## described but maps output values between @var{low_out} and
  46. ## @var{high_out} instead of 0 and 1. A default value @code{[]} can also
  47. ## be used for this parameter, which is taken as @code{[0;1]}.
  48. ##
  49. ## @code{J=imadjust(...,gamma)} takes, in addition of 3 parameters
  50. ## explained above, an extra parameter @var{gamma}, which specifies the
  51. ## shape of the mapping curve between input elements and output
  52. ## elements, which is linear (as taken if this parameter is omitted). If
  53. ## @var{gamma} is above 1, then function is weighted towards lower
  54. ## values, and if below 1, towards higher values.
  55. ##
  56. ## @code{newmap=imadjust(map,...)} applies a transformation to a
  57. ## colormap @var{map}, which output is @var{newmap}. This transformation
  58. ## is the same as explained above, just using a map instead of an image.
  59. ## @var{low_in}, @var{high_in}, @var{low_out}, @var{high_out} and
  60. ## @var{gamma} can be scalars, in which case the same values are applied
  61. ## for all three color components of a map; or it can be 1-by-3
  62. ## vectors, to define unique mappings for each component.
  63. ##
  64. ## @code{RGB_out=imadjust(RGB,...)} adjust RGB image @var{RGB} (a
  65. ## M-by-N-by-3 array) the same way as specified in images and colormaps.
  66. ## Here too @var{low_in}, @var{high_in}, @var{low_out}, @var{high_out} and
  67. ## @var{gamma} can be scalars or 1-by-3 matrices, to specify the same
  68. ## mapping for all planes, or unique mappings for each.
  69. ##
  70. ## The formula used to realize the mapping (if we omit saturation) is:
  71. ##
  72. ## @code{J = low_out + (high_out - low_out) .* ((I - low_in) / (high_in - low_in)) .^ gamma;}
  73. ##
  74. ## @strong{Compatibility notes:}
  75. ##
  76. ## @itemize @bullet
  77. ## @item
  78. ## Prior versions of imadjust allowed @code{[low_in; high_in]} and
  79. ## @code{[low_out; high_out]} to be row vectors. Compatibility with this
  80. ## behaviour has been keeped, although preferred form is vertical vector
  81. ## (since it extends nicely to 2-by-3 matrices for RGB images and
  82. ## colormaps).
  83. ## @item
  84. ## Previous version of imadjust, if @code{low_in>high_in} it "negated" output.
  85. ## Now it is negated if @code{low_out>high_out}, for compatibility with
  86. ## MATLAB.
  87. ## @item
  88. ## Class of @var{I} is not considered, so limit values are not
  89. ## modified depending on class of the image, just treated "as is". When
  90. ## Octave 2.1.58 is out, limits will be multiplied by 255 for uint8
  91. ## images and by 65535 for uint16 as in MATLAB.
  92. ## @end itemize
  93. ##
  94. ## @seealso{stretchlim, brighten}
  95. ## @end deftypefn
  96. ## Author: Josep Mones i Teixidor <jmones@puntbarra.com>
  97. ## TODO: When Octave 2.1.58 is out multiply indices if input argument is
  98. ## TODO: of class int* or uint*.
  99. function ret = imadjust (image, in, out, gamma)
  100. if (nargin < 1 || nargin > 4)
  101. usage ("imadjust(...) number of arguments must be between 1 and 4");
  102. endif
  103. if (nargin < 4)
  104. gamma = 1; ## default gamma
  105. endif
  106. if !(ismatrix(image))
  107. error ("imadjust(image,...) first parameter must be a image matrix or colormap");
  108. endif
  109. if (nargin==1)
  110. in=stretchlim(image); ## this saturates 1% on lower and 1% on
  111. out=[0;1]; ## higher values
  112. endif
  113. if (nargin==2)
  114. out=[0;1]; ## default out
  115. endif
  116. if !((ismatrix(in) || isempty(in)) && (ismatrix(out) || isempty(out)) )
  117. usage("imadjust(image,[low high],[bottom top],gamma)");
  118. endif
  119. if (isempty(in))
  120. in=[0;1]; ## default in
  121. endif
  122. if (isempty(out))
  123. out=[0;1]; ## default out
  124. endif
  125. simage=size(image);
  126. if (length(simage)==3 && simage(3)==3)
  127. ## image is rgb
  128. [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma);
  129. ## make room
  130. ret=zeros(size(image));
  131. ## process each plane
  132. for i=1:3
  133. ret(:,:,i)=__imadjust_plane__(image(:,:,i),in(1,i),in(2,i),out(1,i),out(2,i),gamma(1,i));
  134. endfor
  135. elseif (length(simage)==2)
  136. if(simage(2)==3 && \
  137. (size(in)==[2,3] || size(out)==[2,3] || size(gamma)==[1,3]) )
  138. ## image is a colormap
  139. [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma);
  140. ret=[];
  141. ## process each color
  142. for i=1:3
  143. ret=horzcat(ret,__imadjust_plane__(image(:,i),in(1,i),in(2,i),out(1,i),out(2,i),gamma(i)));
  144. endfor
  145. else
  146. ## image is a intensity image
  147. if( !isvector(in) || length(in)!=2 || !isvector(out) || length(out)!=2 || !isscalar(gamma) || (gamma<0) || (gamma==Inf) )
  148. error("imadjust: on an intensity image, in and out must be 2-by-1 and gamma a positive scalar.");
  149. endif
  150. ret=__imadjust_plane__(image,in(1),in(2),out(1),out(2),gamma);
  151. endif
  152. else
  153. error("imadjust: first parameter must be a colormap, an intensity image or a RGB image");
  154. endif
  155. endfunction
  156. ## This does all the work. I has a plane; li and hi input low and high
  157. ## values; and lo and ho, output bottom and top values.
  158. ## Image negative is computed if ho<lo although nothing special is
  159. ## needed, since formula automatically handles it.
  160. function ret=__imadjust_plane__(I, li, hi, lo, ho, gamma)
  161. ret = (I < li) .* lo;
  162. ret = ret + (I >= li & I < hi) .* (lo + (ho - lo) .* ((I - li) / (hi - li)) .^ gamma);
  163. ret = ret + (I >= hi) .* ho;
  164. endfunction
  165. ## Checks in, out and gamma to see if they are ok for colormap and RGB
  166. ## cases.
  167. function [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma)
  168. switch(size(in))
  169. case([2,3])
  170. ## ok!
  171. case([2,1])
  172. in=repmat(in,1,3);
  173. case([1,2]) ## Compatibility behaviour!
  174. in=repmat(in',1,3);
  175. otherwise
  176. error("imadjust: in must be 2-by-3 or 2-by-1.");
  177. endswitch
  178. switch(size(out))
  179. case([2,3])
  180. ## ok!
  181. case([2,1])
  182. out=repmat(out,1,3);
  183. case([1,2]) ## Compatibility behaviour!
  184. out=repmat(out',1,3);
  185. otherwise
  186. error("imadjust: out must be 2-by-3 or 2-by-1.");
  187. endswitch
  188. switch(size(gamma))
  189. case([1,3])
  190. ## ok!
  191. case([1,1])
  192. gamma=repmat(gamma,1,3);
  193. otherwise
  194. error("imadjust: gamma must be a scalar or a 1-by-3 matrix.");
  195. endswitch
  196. ## check gamma allowed range
  197. if(!all((gamma>=0)&(gamma<Inf)))
  198. error("imadjust: gamma values must be in the range [0,Inf]");
  199. endif
  200. endfunction
  201. # bad arguments
  202. # bad images
  203. %!error(imadjust("bad argument"));
  204. %!error(imadjust(zeros(10,10,3,2)));
  205. %!error(imadjust(zeros(10,10,4)));
  206. %!error(imadjust("bad argument"));
  207. # bad 2d, 3d or 4th argument
  208. %!error(imadjust([1:100],"bad argument",[0;1],1));
  209. %!error(imadjust([1:100],[0,1,1],[0;1],1));
  210. %!error(imadjust([1:100],[0;1],[0,1,1],1));
  211. %!error(imadjust([1:100],[0;1],[0;1],[0;1]));
  212. %!error(imadjust([1:100],[0;1],[0;1],-1));
  213. %!# 1% on each end saturated
  214. %!assert(imadjust([1:100]),[0,linspace(0,1,98),1]);
  215. %!# test with only input arg
  216. %!assert(sum(abs((imadjust(linspace(0,1,100),[1/99;98/99]) - \
  217. %! [0,linspace(0,1,98),1] )(:))) < 1e-10);
  218. %!# a test with input and output args
  219. %!assert(imadjust([1:100],[50;90],[-50;-30]), \
  220. %! [-50*ones(1,49), linspace(-50,-30,90-50+1), -30*ones(1,10)]);
  221. %!# a test with input and output args in a row vector (Compatibility behaviour)
  222. %!assert(imadjust([1:100],[50,90],[-50,-30]), \
  223. %! [-50*ones(1,49), linspace(-50,-30,90-50+1), -30*ones(1,10)]);
  224. %!# the previous test, "negated"
  225. %!assert(imadjust([1:100],[50;90],[-30;-50]), \
  226. %! [-30*ones(1,49), linspace(-30,-50,90-50+1), -50*ones(1,10)]);
  227. %!shared cm,cmn
  228. %! cm=[[1:10]',[2:11]',[3:12]'];
  229. %! cmn=([[1:10]',[2:11]',[3:12]']-1)/11;
  230. %!# a colormap
  231. %!assert(imadjust(cmn,[0;1],[10;11]),cmn+10);
  232. %!# a colormap with params in row (Compatibility behaviour)
  233. %!assert(imadjust(cmn,[0,1],[10,11]),cmn+10);
  234. %!# a colormap, different output on each
  235. %!assert(imadjust(cmn,[0;1],[10,20,30;11,21,31]),cmn+repmat([10,20,30],10,1));
  236. %!# a colormap, different input on each, we need increased tolerance for this test
  237. %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0;1]) - \
  238. %! [[0,linspace(0,1,6),1,1,1]', \
  239. %! [0,0,linspace(0,1,6),1,1]', \
  240. %! [0,0,0,linspace(0,1,6),1]'] \
  241. %! ))(:)) < 1e-10 \
  242. %! );
  243. %!# a colormap, different input and output on each
  244. %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0,1,2;1,2,3]) - \
  245. %! [[0,linspace(0,1,6),1,1,1]', \
  246. %! [0,0,linspace(0,1,6),1,1]'+1, \
  247. %! [0,0,0,linspace(0,1,6),1]'+2] \
  248. %! ))(:)) < 1e-10 \
  249. %! );
  250. %!# a colormap, different gamma, input and output on each
  251. %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0,1,2;1,2,3],[1,2,3]) - \
  252. %! [[0,linspace(0,1,6),1,1,1]', \
  253. %! [0,0,linspace(0,1,6).^2,1,1]'+1, \
  254. %! [0,0,0,linspace(0,1,6).^3,1]'+2] \
  255. %! )(:))) < 1e-10 \
  256. %! );
  257. %!shared iRGB,iRGBn,oRGB
  258. %! iRGB=zeros(10,1,3);
  259. %! iRGB(:,:,1)=[1:10]';
  260. %! iRGB(:,:,2)=[2:11]';
  261. %! iRGB(:,:,3)=[3:12]';
  262. %! iRGBn=(iRGB-1)/11;
  263. %! oRGB=zeros(10,1,3);
  264. %! oRGB(:,:,1)=[0,linspace(0,1,6),1,1,1]';
  265. %! oRGB(:,:,2)=[0,0,linspace(0,1,6),1,1]';
  266. %! oRGB(:,:,3)=[0,0,0,linspace(0,1,6),1]';
  267. %!# a RGB image
  268. %!assert(imadjust(iRGBn,[0;1],[10;11]),iRGBn+10);
  269. %!# a RGB image, params in row (compatibility behaviour)
  270. %!assert(imadjust(iRGBn,[0,1],[10,11]),iRGBn+10);
  271. %!# a RGB, different output on each
  272. %!test
  273. %! t=iRGBn;
  274. %! t(:,:,1)+=10;
  275. %! t(:,:,2)+=20;
  276. %! t(:,:,3)+=30;
  277. %! assert(imadjust(iRGBn,[0;1],[10,20,30;11,21,31]),t);
  278. %!# a RGB, different input on each, we need increased tolerance for this test
  279. %!assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0;1]) - oRGB)(:))) < 1e-10);
  280. %!# a RGB, different input and output on each
  281. %!test
  282. %! t=oRGB;
  283. %! t(:,:,2)+=1;
  284. %! t(:,:,3)+=2;
  285. %! assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0,1,2;1,2,3]) - t)(:))) < 1e-10);
  286. %!# a RGB, different gamma, input and output on each
  287. %!test
  288. %! t=oRGB;
  289. %! t(:,:,2)=t(:,:,2).^2+1;
  290. %! t(:,:,3)=t(:,:,3).^3+2;
  291. %! assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0,1,2;1,2,3],[1,2,3]) - t)(:))) < 1e-10);
  292. %
  293. % $Log$
  294. % Revision 1.3 2007/03/23 16:14:37 adb014
  295. % Update the FSF address
  296. %
  297. % Revision 1.2 2007/01/04 23:47:43 hauberg
  298. % Put seealso before end deftypefn
  299. %
  300. % Revision 1.1 2006/08/20 12:59:33 hauberg
  301. % Changed the structure to match the package system
  302. %
  303. % Revision 1.3 2004/09/01 22:51:14 jmones
  304. % Fully recoded: NDArray support, docs, tests, more compatible with MATLAB, changed copyright
  305. %
  306. %