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