Back to index

octave-image  1.0.8
imadjust.m
Go to the documentation of this file.
00001 ## Copyright (C) 2004 Josep Mones i Teixidor
00002 ##
00003 ## This program is free software; you can redistribute it and/or modify
00004 ## it under the terms of the GNU General Public License as published by
00005 ## the Free Software Foundation; either version 2 of the License, or
00006 ## (at your option) any later version.
00007 ##
00008 ## This program is distributed in the hope that it will be useful,
00009 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 ## GNU General Public License for more details.
00012 ##
00013 ## You should have received a copy of the GNU General Public License
00014 ## along with this program; If not, see <http://www.gnu.org/licenses/>.
00015 ##
00016 ##
00017 ## Based on old imadjust.m (GPL):
00018 ## Copyright (C) 1999,2000  Kai Habel
00019 
00020 
00021 ## -*- texinfo -*-
00022 ## @deftypefn {Function File} @var{J}= imadjust (@var{I})
00023 ## @deftypefnx {Function File} @var{J}= imadjust (@var{I},[@var{low_in};@var{high_in}])
00024 ## @deftypefnx {Function File} @var{J}= imadjust (@var{I},[@var{low_in};@var{high_in}],[@var{low_out};@var{high_out}])
00025 ## @deftypefnx {Function File} @var{J}= imadjust (..., @var{gamma})
00026 ## @deftypefnx {Function File} @var{newmap}= imadjust (@var{map}, ...)
00027 ## @deftypefnx {Function File} @var{RGB_out}= imadjust (@var{RGB}, ...)
00028 ## Adjust image or colormap values to a specified range
00029 ##
00030 ## @code{J=imadjust(I)} adjusts intensity image @var{I} values so that
00031 ## 1% of data on lower and higher values (2% in total) of the image is
00032 ## saturated; choosing for that the corresponding lower and higher
00033 ## bounds (using @code{stretchlim}) and mapping them to 0 and 1. @var{J}
00034 ## is an image of the same size as @var{I} which contains mapped values.
00035 ## This is equivalent to @code{imadjust(I,stretchlim(I))}.
00036 ##
00037 ## @code{J=imadjust(I,[low_in;high_in])} behaves as described but uses
00038 ## @var{low_in} and @var{high_in} values instead of calculating them. It
00039 ## maps those values to 0 and 1; saturates values lower than first limit
00040 ## to 0 and values higher than second to 1; and finally maps all values
00041 ## between limits linearly to a value between 0 and 1. If @code{[]} is
00042 ## passes as @code{[low_in;high_in]} value, then @code{[0;1]} is taken
00043 ## as a default value.
00044 ##
00045 ## @code{J=imadjust(I,[low_in;high_in],[low_out;high_out])} behaves as
00046 ## described but maps output values between @var{low_out} and
00047 ## @var{high_out} instead of 0 and 1. A default value @code{[]} can also
00048 ## be used for this parameter, which is taken as @code{[0;1]}.
00049 ##
00050 ## @code{J=imadjust(...,gamma)} takes, in addition of 3 parameters
00051 ## explained above, an extra parameter @var{gamma}, which specifies the
00052 ## shape of the mapping curve between input elements and output
00053 ## elements, which is linear (as taken if this parameter is omitted). If
00054 ## @var{gamma} is above 1, then function is weighted towards lower
00055 ## values, and if below 1, towards higher values.
00056 ##
00057 ## @code{newmap=imadjust(map,...)} applies a transformation to a
00058 ## colormap @var{map}, which output is @var{newmap}. This transformation
00059 ## is the same as explained above, just using a map instead of an image.
00060 ## @var{low_in}, @var{high_in}, @var{low_out}, @var{high_out} and
00061 ## @var{gamma} can be scalars, in which case the same values are applied
00062 ## for all three color components of a map; or it can be 1-by-3
00063 ## vectors, to define unique mappings for each component.
00064 ##
00065 ## @code{RGB_out=imadjust(RGB,...)} adjust RGB image @var{RGB} (a
00066 ## M-by-N-by-3 array) the same way as specified in images and colormaps.
00067 ## Here too @var{low_in}, @var{high_in}, @var{low_out}, @var{high_out} and
00068 ## @var{gamma} can be scalars or 1-by-3 matrices, to specify the same
00069 ## mapping for all planes, or unique mappings for each.
00070 ##
00071 ## The formula used to realize the mapping (if we omit saturation) is:
00072 ##
00073 ## @code{J = low_out + (high_out - low_out) .* ((I - low_in) / (high_in - low_in)) .^ gamma;}
00074 ##
00075 ## @strong{Compatibility notes:}
00076 ##
00077 ## @itemize @bullet
00078 ## @item
00079 ## Prior versions of imadjust allowed @code{[low_in; high_in]} and
00080 ## @code{[low_out; high_out]} to be row vectors. Compatibility with this
00081 ## behaviour has been keeped, although preferred form is vertical vector
00082 ## (since it extends nicely to 2-by-3 matrices for RGB images and
00083 ## colormaps).
00084 ## @item
00085 ## Previous version of imadjust, if @code{low_in>high_in} it "negated" output.
00086 ## Now it is negated if @code{low_out>high_out}, for compatibility with
00087 ## MATLAB.
00088 ## @item
00089 ## Class of @var{I} is not considered, so limit values are not
00090 ## modified depending on class of the image, just treated "as is". When
00091 ## Octave 2.1.58 is out, limits will be multiplied by 255 for uint8
00092 ## images and by 65535 for uint16 as in MATLAB.
00093 ## @end itemize
00094 ## 
00095 ## @seealso{stretchlim, brighten}
00096 ## @end deftypefn
00097 
00098 ## Author:  Josep Mones i Teixidor <jmones@puntbarra.com>
00099 
00100 ## TODO: When Octave 2.1.58 is out multiply indices if input argument is
00101 ## TODO: of class int* or uint*.
00102 
00103 function ret = imadjust (image, in, out, gamma)
00104 
00105   if (nargin < 1 || nargin > 4)
00106     usage ("imadjust(...) number of arguments must be between 1 and 4");
00107   endif
00108 
00109   if (nargin < 4)
00110     gamma = 1;              ## default gamma
00111   endif
00112 
00113   if !(ismatrix(image))
00114     error ("imadjust(image,...) first parameter must be a image matrix or colormap");
00115   endif
00116 
00117   if (nargin==1)
00118     in=stretchlim(image);   ## this saturates 1% on lower and 1% on
00119     out=[0;1];              ## higher values
00120   endif
00121 
00122   if (nargin==2)
00123     out=[0;1];              ## default out
00124   endif
00125 
00126   if !((ismatrix(in) || isempty(in)) && (ismatrix(out) || isempty(out)) )
00127     usage("imadjust(image,[low high],[bottom top],gamma)");
00128   endif
00129 
00130   if (isempty(in))
00131     in=[0;1];               ## default in
00132   endif
00133 
00134   if (isempty(out))
00135     out=[0;1];              ## default out
00136   endif
00137   
00138   simage=size(image);
00139   if (length(simage)==3 && simage(3)==3)
00140     ## image is rgb
00141     [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma);
00142 
00143     ## make room
00144     ret=zeros(size(image));
00145 
00146     ## process each plane
00147     for i=1:3
00148       ret(:,:,i)=__imadjust_plane__(image(:,:,i),in(1,i),in(2,i),out(1,i),out(2,i),gamma(1,i));
00149     endfor
00150 
00151   elseif (length(simage)==2)
00152     if(simage(2)==3 && \ 
00153        (size(in)==[2,3] || size(out)==[2,3] || size(gamma)==[1,3]) )
00154       ## image is a colormap
00155       [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma);
00156 
00157       ret=[];
00158       ## process each color
00159       for i=1:3
00160        ret=horzcat(ret,__imadjust_plane__(image(:,i),in(1,i),in(2,i),out(1,i),out(2,i),gamma(i)));
00161       endfor
00162 
00163     else
00164       ## image is a intensity image
00165       if( !isvector(in) || length(in)!=2 || !isvector(out) || length(out)!=2 || !isscalar(gamma) || (gamma<0) || (gamma==Inf) )
00166        error("imadjust: on an intensity image, in and out must be 2-by-1 and gamma a positive scalar.");
00167       endif
00168       ret=__imadjust_plane__(image,in(1),in(2),out(1),out(2),gamma);
00169     endif
00170 
00171   else
00172     error("imadjust: first parameter must be a colormap, an intensity image or a RGB image");
00173   endif
00174 endfunction
00175 
00176 
00177 ## This does all the work. I has a plane; li and hi input low and high
00178 ## values; and lo and ho, output bottom and top values.
00179 ## Image negative is computed if ho<lo although nothing special is
00180 ## needed, since formula automatically handles it.
00181 function ret=__imadjust_plane__(I, li, hi, lo, ho, gamma)
00182   ret = (I < li) .* lo;
00183   ret = ret + (I >= li & I < hi) .* (lo + (ho - lo) .* ((I - li) / (hi - li)) .^ gamma);
00184   ret = ret + (I >= hi) .* ho;
00185 endfunction
00186 
00187 
00188 ## Checks in, out and gamma to see if they are ok for colormap and RGB
00189 ## cases.
00190 function [in, out, gamma]=__imadjust_check_3d_args__(in, out, gamma)
00191   switch(size(in)) 
00192     case([2,3]) 
00193       ## ok!
00194     case([2,1])
00195       in=repmat(in,1,3);
00196     case([1,2]) ## Compatibility behaviour!
00197       in=repmat(in',1,3);
00198     otherwise
00199       error("imadjust: in must be 2-by-3 or 2-by-1.");
00200   endswitch
00201   
00202   switch(size(out))
00203     case([2,3])
00204       ## ok!
00205     case([2,1])
00206       out=repmat(out,1,3);
00207     case([1,2]) ## Compatibility behaviour!
00208       out=repmat(out',1,3);
00209     otherwise
00210       error("imadjust: out must be 2-by-3 or 2-by-1.");
00211   endswitch
00212   
00213   switch(size(gamma))
00214     case([1,3])
00215       ## ok!
00216     case([1,1])
00217       gamma=repmat(gamma,1,3);
00218     otherwise
00219       error("imadjust: gamma must be a scalar or a 1-by-3 matrix.");
00220   endswitch
00221   
00222   ## check gamma allowed range
00223   if(!all((gamma>=0)&(gamma<Inf)))
00224     error("imadjust: gamma values must be in the range [0,Inf]");
00225   endif
00226 
00227 endfunction
00228 
00229 
00230 # bad arguments
00231 
00232 # bad images
00233 %!error(imadjust("bad argument"));
00234 %!error(imadjust(zeros(10,10,3,2)));
00235 %!error(imadjust(zeros(10,10,4)));
00236 %!error(imadjust("bad argument"));
00237 
00238 # bad 2d, 3d or 4th argument
00239 %!error(imadjust([1:100],"bad argument",[0;1],1));
00240 %!error(imadjust([1:100],[0,1,1],[0;1],1));
00241 %!error(imadjust([1:100],[0;1],[0,1,1],1));
00242 %!error(imadjust([1:100],[0;1],[0;1],[0;1]));
00243 %!error(imadjust([1:100],[0;1],[0;1],-1));
00244 
00245 %!# 1% on each end saturated
00246 %!assert(imadjust([1:100]),[0,linspace(0,1,98),1]);
00247 
00248 %!# test with only input arg
00249 %!assert(sum(abs((imadjust(linspace(0,1,100),[1/99;98/99]) - \
00250 %!                [0,linspace(0,1,98),1] )(:))) < 1e-10);
00251 
00252 %!# a test with input and output args
00253 %!assert(imadjust([1:100],[50;90],[-50;-30]), \
00254 %!      [-50*ones(1,49), linspace(-50,-30,90-50+1), -30*ones(1,10)]);
00255 
00256 %!# a test with input and output args in a row vector (Compatibility behaviour)
00257 %!assert(imadjust([1:100],[50,90],[-50,-30]), \
00258 %!      [-50*ones(1,49), linspace(-50,-30,90-50+1), -30*ones(1,10)]);
00259 
00260 %!# the previous test, "negated"
00261 %!assert(imadjust([1:100],[50;90],[-30;-50]), \
00262 %!      [-30*ones(1,49), linspace(-30,-50,90-50+1), -50*ones(1,10)]);
00263 
00264 
00265 %!shared cm,cmn
00266 %! cm=[[1:10]',[2:11]',[3:12]'];
00267 %! cmn=([[1:10]',[2:11]',[3:12]']-1)/11;
00268 
00269 %!# a colormap
00270 %!assert(imadjust(cmn,[0;1],[10;11]),cmn+10);
00271 
00272 %!# a colormap with params in row (Compatibility behaviour)
00273 %!assert(imadjust(cmn,[0,1],[10,11]),cmn+10);
00274 
00275 %!# a colormap, different output on each
00276 %!assert(imadjust(cmn,[0;1],[10,20,30;11,21,31]),cmn+repmat([10,20,30],10,1));
00277 
00278 %!# a colormap, different input on each, we need increased tolerance for this test
00279 %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0;1]) -                   \
00280 %!       [[0,linspace(0,1,6),1,1,1]',                                   \
00281 %!        [0,0,linspace(0,1,6),1,1]',                                   \
00282 %!        [0,0,0,linspace(0,1,6),1]']                                   \
00283 %!       ))(:)) < 1e-10                                                 \     
00284 %!       );
00285 
00286 %!# a colormap, different input and output on each
00287 %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0,1,2;1,2,3]) -           \
00288 %!       [[0,linspace(0,1,6),1,1,1]',                                   \
00289 %!        [0,0,linspace(0,1,6),1,1]'+1,                                 \
00290 %!        [0,0,0,linspace(0,1,6),1]'+2]                                 \
00291 %!       ))(:)) < 1e-10                                                 \
00292 %!       );
00293 
00294 %!# a colormap, different gamma, input and output on each
00295 %!assert(sum(abs((imadjust(cm,[2,4,6;7,9,11],[0,1,2;1,2,3],[1,2,3]) -   \
00296 %!       [[0,linspace(0,1,6),1,1,1]',                                   \
00297 %!        [0,0,linspace(0,1,6).^2,1,1]'+1,                              \
00298 %!        [0,0,0,linspace(0,1,6).^3,1]'+2]                              \
00299 %!       )(:))) < 1e-10                                                 \    
00300 %!       );
00301 
00302 
00303 %!shared iRGB,iRGBn,oRGB
00304 %! iRGB=zeros(10,1,3);
00305 %! iRGB(:,:,1)=[1:10]';
00306 %! iRGB(:,:,2)=[2:11]';
00307 %! iRGB(:,:,3)=[3:12]';
00308 %! iRGBn=(iRGB-1)/11;
00309 %! oRGB=zeros(10,1,3);
00310 %! oRGB(:,:,1)=[0,linspace(0,1,6),1,1,1]';
00311 %! oRGB(:,:,2)=[0,0,linspace(0,1,6),1,1]';
00312 %! oRGB(:,:,3)=[0,0,0,linspace(0,1,6),1]';
00313 
00314 %!# a RGB image
00315 %!assert(imadjust(iRGBn,[0;1],[10;11]),iRGBn+10);
00316 
00317 %!# a RGB image, params in row (compatibility behaviour)
00318 %!assert(imadjust(iRGBn,[0,1],[10,11]),iRGBn+10);
00319 
00320 %!# a RGB, different output on each
00321 %!test
00322 %! t=iRGBn;
00323 %! t(:,:,1)+=10;
00324 %! t(:,:,2)+=20;
00325 %! t(:,:,3)+=30;
00326 %! assert(imadjust(iRGBn,[0;1],[10,20,30;11,21,31]),t);
00327 
00328 
00329 %!# a RGB, different input on each, we need increased tolerance for this test
00330 %!assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0;1]) - oRGB)(:))) < 1e-10);
00331 
00332 %!# a RGB, different input and output on each
00333 %!test
00334 %! t=oRGB;
00335 %! t(:,:,2)+=1;
00336 %! t(:,:,3)+=2;
00337 %! assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0,1,2;1,2,3]) - t)(:))) < 1e-10);
00338 
00339 %!# a RGB, different gamma, input and output on each
00340 %!test
00341 %! t=oRGB;
00342 %! t(:,:,2)=t(:,:,2).^2+1;
00343 %! t(:,:,3)=t(:,:,3).^3+2;
00344 %! assert(sum(abs((imadjust(iRGB,[2,4,6;7,9,11],[0,1,2;1,2,3],[1,2,3]) - t)(:))) < 1e-10);
00345 
00346 
00347 %
00348 % $Log$
00349 % Revision 1.3  2007/03/23 16:14:37  adb014
00350 % Update the FSF address
00351 %
00352 % Revision 1.2  2007/01/04 23:47:43  hauberg
00353 % Put seealso before end deftypefn
00354 %
00355 % Revision 1.1  2006/08/20 12:59:33  hauberg
00356 % Changed the structure to match the package system
00357 %
00358 % Revision 1.3  2004/09/01 22:51:14  jmones
00359 % Fully recoded: NDArray support, docs, tests, more compatible with MATLAB, changed copyright
00360 %
00361 %