This file is indexed.

/usr/share/octave/packages/image-2.6.1/stretchlim.m is in octave-image 2.6.1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
## Copyright (C) 2004 Josep Monés i Teixidor <jmones@puntbarra.com>
## Copyright (C) 2015 Carnë Draug <carandraug@octave.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {Function File} {} stretchlim (@var{I})
## @deftypefnx {Function File} {} stretchlim (@var{RGB})
## @deftypefnx {Function File} {} stretchlim (@dots{}, @var{tol})
## Find limits to contrast stretch an image.
##
## Returns a 2 element column vector, @code{[@var{low}; @var{high}]},
## with the pair of intensities to contrast stretch @var{I} which
## saturates at most @var{tol} of the image.  The output of this
## function matches the input expected by @code{imadjust}.
##
## The input argument @var{tol}, controls the fraction of the image to be
## saturated and defaults to @code{[0.01 0.99]}, i.e., a 1% saturation
## on both sides of the image histogram.  It can be specified in two ways:
##
## @itemize
## @item a two element vector with lower and higher fraction of the
## the image to be saturated.  These values must be in the range
## [0 1], the display range of images of floating point class.
##
## @item a scalar value with the fraction of image to be saturated on
## each side; e.g., a @var{tol} with a value of @code{0.05} is equivalent
## to @code{[0.05 0.95]}.  This value must be in the range @code{[0 0.5]}.
##
## @end itemize
##
## A common use case wanting to maximize contrast without causing any
## saturation.  In such case @var{tol} should be 0 (zero).  It is the
## equivalent to @code{[min(@var{I}(:)); max(@var{I}(:))]} for a single
## plane.
##
## The return values are of class double and in the range [0 1] regardless
## of the input image class.  These values are scaled in the image class
## range (see @code{im2double}).
##
## If the input is a RGB image, i.e., the 3rd dimension has length 3, then
## it returns a @code{[2 3]} matrix with separate limits for each colour.
## It will actually do this for each plane, so an input of size,
## @code{[M N P]} will return a @code{[2 P]} matrix.
##
## Higher dimensions of @var{I} are also valid.  As in the 3 dimensional
## case, the limits for each 2 dimensional plane are returned in a 2 row
## vector for each 2 dimensional plane, one dimension below.  That is, an
## input of size @code{[M N P Q R]} will return an array of size
## @code{[2 P Q R]}.
##
## Note the detail that @var{tol} is the maximum fraction of saturation.
## It is rare that there is a value for that exact saturation.  In such
## case, @code{stretchlim} will always round down and saturate less.
## @var{tol} is the saturation limit.  For example, if @var{tol} is
## @code{0.10}, but there are only values that will lead to 5 or 11%
## saturation, it will return the value for a 5% saturation.
##
## @seealso{brighten, contrast, histeq, imadjust}
## @end deftypefn

function low_high = stretchlim (img, tol = [0.01 0.99])

  if (nargin () <1 || nargin () > 2)
    print_usage ();
  endif

  if (! isimage (img))
    error("stretchlim: I or RGB must be an image");
  endif

  ## Handle tol
  if (nargin > 1)
    if (! isnumeric (tol))
      error ("stretchlim: TOL must be numeric");
    endif

    if (isscalar (tol))
      if (min (tol) < 0 || max (tol) > 0.5)
        error ("stretchlim: TOL must be in the [0 1] range");
      endif
      tol = [tol (1-tol)];

    elseif (isvector (tol))
      if (numel (tol) != 2)
        error ("stretchlim: TOL must be a 2 element vector");
      endif
    endif
  endif

  ## tol is about the percentage of values that will be saturated.
  ## So instead of percentages, we convert to the actual number of
  ## pixels that need to be saturated.  After sorting the values in
  ## the image, that number of pixels simply becomes the index for
  ## the limits.
  ##
  ## Note that the actual intensity value that we set the limits to,
  ## is not saturated.  Only the values below or above the lower and
  ## higher limits it will be considered saturated.
  ##
  ## And since most images will have repeated values in the pixels,
  ## chances are that there's not a limit that would cause only the
  ## exact percentage of pixels to be saturated.  In such cases, we
  ## must prefer a limit that would saturate less pixels than the
  ## requested, rather than the opposite.
  ##
  ## We want to compute this for each plane, so we reshape the image
  ## in order to have each plane into a single column, while respecting
  ## any other dimensions beyond the 3rd.

  sz = postpad (size (img), max (3, ndims (img)), 1);
  plane_length = sz(1) * sz(2);

  img = reshape (img, [plane_length sz(3:end)]);

  lo_idx = floor (tol(1) * plane_length) + 1;
  hi_idx = ceil (tol(2) * plane_length);

  if (lo_idx == 1 && hi_idx == plane_length)
    ## special case, equivalent to tol [0 1], even if tol was not
    ## actually [0 1] but the image size would effectively make it.
    low_high = [min(img, [], 1); max(img, [], 1)];
  else
    strides = reshape ((0:plane_length:(numel(img)-1)), [1 sz(3:end)]);
    lo_hi_idx = [lo_idx; hi_idx] .+ strides;
    sorted = sort (img, 1);
    low_high = sorted(lo_hi_idx);
  endif

  low_high = im2double (low_high);

  ## This will only happen if the input was floating point and with
  ## values already outside the [0 1] range (common for users to simply
  ## convert an uint8 image to double and forget that display is [0 1]
  ## only).
  low_high(low_high < 0) = 0;
  low_high(low_high > 1) = 1;

  ## If a plane has a single value, then both limits will have the same
  ## value.  In such case, we must return [0; 1]
  equal_cols = low_high(1,:) == low_high(2,:);
  low_high(:, equal_cols) = repmat ([0; 1], 1, nnz (equal_cols));
endfunction

%!error (stretchlim ());
%!error (stretchlim ("bad parameter"));
#%!error (stretchlim (zeros (10, 10, 3, 2)));
%!error (stretchlim (zeros (10, 10), "bad parameter"));
%!error (stretchlim (zeros (10, 10), 0.01, 2));

## default parameters
%!assert (stretchlim (0.01:.01:1), [0.02; 0.99])
%!assert (stretchlim (0.01:.01:1), stretchlim (0.01:.01:1, [0.01 0.99]))

## use scalar for tol
%!assert (stretchlim (0.01:.01:1, 0.15), stretchlim (0.01:.01:1, [0.15 0.85]))

## this is different than Matlab but it looks like it's a Matlab bug
## (Matlab returns [0.018997482261387; 0.951003280689708])
## We actually have differences from Matlab which sometimes returns
## values that are not present in the image.
%!assert (stretchlim (0.01:.01:1, [0.01,0.95]), [0.02; 0.95], eps)

## corner case of zero tolerance
%!assert (stretchlim (0.01:.01:1, 0), [0.01; 1])

%!test
%! im = rand (5);
%! assert (stretchlim (im, 0), [min(im(:)); max(im(:))])

%!test
%! im = rand (5, 5, 3);
%! assert (stretchlim (im, 0),
%!         [min(im(:,:,1)(:)) min(im(:,:,2)(:)) min(im(:,:,3)(:));
%!          max(im(:,:,1)(:)) max(im(:,:,2)(:)) max(im(:,:,3)(:))])


## corner case where tol is not zero but the image is so small that
## it might as well be.
%!test
%! im = rand (5);
%! assert (stretchlim (im, 0.03), [min(im(:)); max(im(:))])
%! assert (stretchlim (im, 0.0399), [min(im(:)); max(im(:))])


## Test with non double data-types
%!assert (stretchlim (uint8 (1:100)), im2double (uint8 ([2; 99])))
%!assert (stretchlim (uint8 (1:100), .25), im2double (uint8 ([26; 75])))
%!assert (stretchlim (uint16  (1:1000)), im2double (uint16 ([11; 990])))

%!assert (stretchlim (int16 (-100:100)), im2double (int16 ([-98; 98])))
%!assert (stretchlim (single (0.01:.01:1)),
%!         double (single (0.01:.01:1)([2; 99])).')


## non uniform histogram tests
%!assert (stretchlim (uint8 ([1 repmat(2, [1, 90]) 92:100]), 0.05),
%!        im2double (uint8 ([2; 95])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 4]) 6:100]), 0.05),
%!        im2double (uint8 ([6; 95])))

## test limit rounding (actually, lack of rounding, we always round down)
## Note that this tests were different in the image package before v2.6.
## Back then we performed rounding of the fraction that was saturated.
%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) 7:100]), 0.05),
%!        im2double (uint8 ([2; 95])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 6]) 8:100]), 0.05),
%!        im2double (uint8 ([2; 95])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 7]) 9:100]), 0.05),
%!        im2double (uint8 ([2; 95])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 8]) 10:100]), 0.05),
%!        im2double (uint8 ([2; 95])))

%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.04),
%!        im2double (uint8 ([2; 96])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.05),
%!        im2double (uint8 ([2; 95])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.06),
%!        im2double (uint8 ([3; 94])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.07),
%!        im2double (uint8 ([3; 93])))
%!assert (stretchlim (uint8 ([1 repmat(2, [1 5]) repmat(3, [1 5]) 9:100]), 0.08),
%!        im2double (uint8 ([3; 92])))

## test RGB
%!test
%! RGB = zeros (100, 1, 3, "uint16");
%! RGB(:,:,1) = [1:1:100];
%! RGB(:,:,2) = [2:2:200];
%! RGB(:,:,3) = [4:4:400];
%! assert (stretchlim (RGB) , im2double (uint16 ([2 4 8; 99 198 396])))

## test other 3D lengths
%!test
%! im6c = zeros (100, 1, 6, "uint16");
%! im6c(:,:,1) = [1:1:100];
%! im6c(:,:,2) = [2:2:200];
%! im6c(:,:,3) = [4:4:400];
%! im6c(:,:,4) = [8:8:800];
%! im6c(:,:,5) = [16:16:1600];
%! im6c(:,:,6) = [32:32:3200];
%! assert (stretchlim (im6c) ,
%!         im2double (uint16 ([2 4 8 16 32 64; 99 198 396 792 1584 3168])))

%!test
%! im = [0 0 .1 .1 .1 .1 .2 .2 .2 .4 .4 .6 .6 .7 .7 .9 .9 .9 1 1];
%!
%! assert (stretchlim (im), [0; 1])
%!
%! ## Consider the returned lower limit in this test.  A lower limit
%! ## of 0.1 will saturate two elements (10%), while 0.2 will saturate
%! ## 6 elements (30%).  Both have the same distance to 20% but returning
%! ## 0.1 is Matlab compatible.
%! ## Now looking at the higher limit.  A limit of .9 will saturate
%! ## 2 elements (10%), while a limit of 0.7 will saturate 5 elements (25%).
%! ## However, for Matlab compatibility we must return .9 even though
%! ## 25% would be closer to 20%.
%! ## Basically, it's not just rounded.
%! assert (stretchlim (im, .2),  [0.1; 0.9])
%!
%! assert (stretchlim (im, .15), [0.1; 0.9])
%! assert (stretchlim (im, .1),  [0.1; 0.9])
%! assert (stretchlim (im, .25), [0.1; 0.7])
%!
%! ## Reorder the vector of values (real images don't have the values
%! ## already sorted), just to be sure it all works.
%! im([6 3 16 11 7 17 14 8 5 19 15 1 2 4 18 13 9 20 10 12]) = im;
%! assert (stretchlim (im, .2),  [0.1; 0.9])
%! assert (stretchlim (im, .15), [0.1; 0.9])
%! assert (stretchlim (im, .1),  [0.1; 0.9])
%! assert (stretchlim (im, .25), [0.1; 0.7])

## odd length images to test rounding of saturated fraction.  With a 1%
## fraction to be saturated and 991 elements, that's 9.91 pixels.  Since
## TOL is the limit, we must saturate the top and bottom 9 pixels (not 10).
%!assert (stretchlim (0.01:.001:1), [0.019; 0.991], eps)
%!assert (stretchlim (0.01:.001:1, [0.01,0.95]), [0.019; 0.951], eps)
%!assert (stretchlim (0.01:.001:1, 0), [0.01; 1])
%!assert (stretchlim (single (0.01:.001:1)),
%!         double (single (0.01:.001:1)([10; 982])).')

## Test ignoring floating point values outside [0 1] and how they do
## count for the fraction of saturated values, but are simply clipped
## to the [0 1] range
%!test
%! assert (stretchlim ([(.05:.05:1) (2:4)], 0.2), [0.25; 0.95], eps)
%! assert (stretchlim ([(.05:.05:1) (2:5)], 0.2), [0.25; 1])
%! assert (stretchlim ([(.05:.05:1) (2:6)], 0.2), [0.3; 1])
%! assert (stretchlim ([(.05:.05:1) (2:7)], 0.2), [0.3; 1])

%!test
%! assert (stretchlim ([(-6:0) (.05:.05:1)], 0.2), [0; 0.75], eps)
%! assert (stretchlim ([(-5:0) (.05:.05:1)], 0.2), [0; 0.75], eps)

## Test N dimensions
%!test
%! im = rand (4, 4, 2, 3, 2);
%! rv = zeros (2, 2, 3, 2);
%! for p = 1:2
%!   for q = 1:3
%!     for r = 1:2
%!       rv(:,p,q,r) = stretchlim (im(:,:,p,q,r), 0.25);
%!     endfor
%!   endfor
%! endfor
%! assert (stretchlim (im, 0.25), rv)

## The same but important because of our special code path for tol == 0
%!test
%! im = rand (4, 4, 2, 3, 2);
%! rv = zeros (2, 2, 3, 2);
%! for p = 1:2
%!   for q = 1:3
%!     for r = 1:2
%!       rv(:,p,q,r) = stretchlim (im(:,:,p,q,r), 0);
%!     endfor
%!   endfor
%! endfor
%! assert (stretchlim (im, 0), rv)

## Corner case there's a single value in the whole image
%!assert (stretchlim (zeros (5)), [0; 1])
%!assert (stretchlim (ones (5)), [0; 1])
%!assert (stretchlim (.6 * ones (5)), [0; 1])
%!assert (stretchlim (zeros (3, 3, 3, 3)), repmat ([0; 1], [1 3 3]))
%!assert (stretchlim ([0 .5 .5 .5 .5 1], .2), [0; 1])

## Cornier case when some of the planes have a single unique value
%!test
%! im = repmat ((magic (5) -1) / 24, [1 1 3 3]);
%! im(:,:,1,1) = 0;
%! im(:,:,2,2) = .5;
%! im(:,:,3,3) = 1;
%! lims = stretchlim (im, 0.2);
%! assert (size (lims), [2 3 3])
%! assert (lims(:, [2 3 4 6 7 8]),
%!         repmat ([(1/24)*round(24*.2); 1-((1/24)*round(24*.2))], [1 6]), eps)
%! assert (lims(:, [1 5 9]), repmat ([0; 1], [1 3]))