Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvips/libvips/arithmetic/min.c
Line
Count
Source
1
/* find image minimum
2
 *
3
 * Copyright: 1990, J. Cupitt
4
 *
5
 * Author: J. Cupitt
6
 * Written on: 02/05/1990
7
 * Modified on : 18/03/1991, N. Dessipris
8
 * 23/11/92 JC
9
 *  - correct result for more than 1 band now.
10
 * 23/7/93 JC
11
 *  - im_incheck() added
12
 * 20/6/95 JC
13
 *  - now returns double for value, like im_min()
14
 * 4/9/09
15
 *  - gtkdoc comment
16
 * 8/9/09
17
 *  - rewrite, from im_minpos()
18
 * 30/8/11
19
 *  - rewrite as a class
20
 * 5/9/11
21
 *  - abandon scan if we find minimum possible value
22
 * 24/2/12
23
 *  - avoid NaN in float/double/complex images
24
 *  - allow +/- INFINITY as a result
25
 * 4/12/12
26
 *  - from min.c
27
 *  - track and return bottom n values
28
 */
29
30
/*
31
32
  This file is part of VIPS.
33
34
  VIPS is free software; you can redistribute it and/or modify
35
  it under the terms of the GNU Lesser General Public License as published by
36
  the Free Software Foundation; either version 2 of the License, or
37
  (at your option) any later version.
38
39
  This program is distributed in the hope that it will be useful,
40
  but WITHOUT ANY WARRANTY; without even the implied warranty of
41
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42
  GNU Lesser General Public License for more details.
43
44
  You should have received a copy of the GNU Lesser General Public License
45
  along with this program; if not, write to the Free Software
46
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
47
  02110-1301  USA
48
49
 */
50
51
/*
52
53
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
54
55
 */
56
57
/*
58
#define DEBUG
59
 */
60
61
#ifdef HAVE_CONFIG_H
62
#include <config.h>
63
#endif /*HAVE_CONFIG_H*/
64
#include <glib/gi18n-lib.h>
65
66
#include <stdio.h>
67
#include <stdlib.h>
68
#include <math.h>
69
#include <limits.h>
70
71
#include <vips/vips.h>
72
#include <vips/internal.h>
73
74
#include "statistic.h"
75
76
/* Track min values and position here. We need one of these for each thread,
77
 * and one for the main value.
78
 *
79
 * We will generally only be tracking a small (<10?) number of values, so
80
 * simple arrays will be fastest.
81
 */
82
typedef struct _VipsValues {
83
  struct _VipsMin *min;
84
85
  /* The min number of values we track.
86
   */
87
  int size;
88
89
  /* How many values we have in the arrays.
90
   */
91
  int n;
92
93
  /* Position and values. We track mod**2 for complex and do a sqrt() at
94
   * the end. The three arrays are sorted by @value, largest first.
95
   */
96
  double *value;
97
  int *x_pos;
98
  int *y_pos;
99
} VipsValues;
100
101
typedef struct _VipsMin {
102
  VipsStatistic parent_instance;
103
104
  /* Number of values we track.
105
   */
106
  int size;
107
108
  /* The single min. Can be unset if, for example, the whole image is
109
   * NaN.
110
   */
111
  double min;
112
  int x;
113
  int y;
114
115
  /* And the positions and values we found as VipsArrays for returning
116
   * to our caller.
117
   */
118
  VipsArrayDouble *min_array;
119
  VipsArrayInt *x_array;
120
  VipsArrayInt *y_array;
121
122
  /* Global state here.
123
   */
124
  VipsValues values;
125
} VipsMin;
126
127
static void
128
vips_values_init(VipsValues *values, VipsMin *min)
129
11.6k
{
130
11.6k
  values->min = min;
131
132
11.6k
  values->size = min->size;
133
11.6k
  values->n = 0;
134
11.6k
  values->value = VIPS_ARRAY(min, values->size, double);
135
11.6k
  values->x_pos = VIPS_ARRAY(min, values->size, int);
136
11.6k
  values->y_pos = VIPS_ARRAY(min, values->size, int);
137
11.6k
}
138
139
/* Add a value. Do nothing if the value is too large.
140
 */
141
static void
142
vips_values_add(VipsValues *values, double v, int x, int y)
143
20.8k
{
144
20.8k
  int i, j;
145
146
  /* Find insertion point.
147
   */
148
30.1k
  for (i = 0; i < values->n; i++) {
149
9.25k
    if (v > values->value[i])
150
0
      break;
151
152
9.25k
    if (v == values->value[i]) {
153
0
      if (y < values->y_pos[i])
154
0
        break;
155
156
0
      if (y == values->y_pos[i])
157
0
        if (x <= values->x_pos[i])
158
0
          break;
159
0
    }
160
9.25k
  }
161
162
  /* Array full?
163
   */
164
20.8k
  if (values->n == values->size) {
165
9.25k
    if (i > 0) {
166
      /* We need to move stuff to the left to make space,
167
       * shunting the largest out.
168
       */
169
9.25k
      for (j = 0; j < i - 1; j++) {
170
0
        values->value[j] = values->value[j + 1];
171
0
        values->x_pos[j] = values->x_pos[j + 1];
172
0
        values->y_pos[j] = values->y_pos[j + 1];
173
0
      }
174
9.25k
      values->value[i - 1] = v;
175
9.25k
      values->x_pos[i - 1] = x;
176
9.25k
      values->y_pos[i - 1] = y;
177
9.25k
    }
178
9.25k
  }
179
11.6k
  else {
180
    /* Not full, move stuff to the right into empty space.
181
     */
182
11.6k
    for (j = values->n; j > i; j--) {
183
0
      values->value[j] = values->value[j - 1];
184
0
      values->x_pos[j] = values->x_pos[j - 1];
185
0
      values->y_pos[j] = values->y_pos[j - 1];
186
0
    }
187
11.6k
    values->value[i] = v;
188
11.6k
    values->x_pos[i] = x;
189
11.6k
    values->y_pos[i] = y;
190
11.6k
    values->n += 1;
191
11.6k
  }
192
20.8k
}
193
194
typedef VipsStatisticClass VipsMinClass;
195
196
34
G_DEFINE_TYPE(VipsMin, vips_min, VIPS_TYPE_STATISTIC);
197
34
198
34
static int
199
34
vips_min_build(VipsObject *object)
200
5.81k
{
201
5.81k
  VipsStatistic *statistic = VIPS_STATISTIC(object);
202
5.81k
  VipsMin *min = (VipsMin *) object;
203
5.81k
  VipsValues *values = &min->values;
204
205
5.81k
  vips_values_init(values, min);
206
207
5.81k
  if (VIPS_OBJECT_CLASS(vips_min_parent_class)->build(object))
208
0
    return -1;
209
210
  /* For speed we accumulate min ** 2 for complex.
211
   */
212
5.81k
  if (vips_band_format_iscomplex(
213
5.81k
      vips_image_get_format(statistic->in))) {
214
0
    int i;
215
216
0
    for (i = 0; i < values->n; i++)
217
0
      values->value[i] = sqrt(values->value[i]);
218
0
  }
219
220
  /* Don't set if there's no value (eg. if every pixel is NaN). This
221
   * will trigger an error later.
222
   */
223
5.81k
  if (values->n > 0) {
224
5.81k
    VipsArrayDouble *out_array;
225
5.81k
    VipsArrayInt *x_array;
226
5.81k
    VipsArrayInt *y_array;
227
228
5.81k
    out_array = vips_array_double_new(values->value, values->n);
229
5.81k
    x_array = vips_array_int_new(values->x_pos, values->n);
230
5.81k
    y_array = vips_array_int_new(values->y_pos, values->n);
231
232
    /* We have to set the props via g_object_set() to stop vips
233
     * complaining they are unset.
234
     */
235
5.81k
    g_object_set(min,
236
5.81k
      "out", values->value[values->n - 1],
237
5.81k
      "x", values->x_pos[values->n - 1],
238
5.81k
      "y", values->y_pos[values->n - 1],
239
5.81k
      "out_array", out_array,
240
5.81k
      "x_array", x_array,
241
5.81k
      "y_array", y_array,
242
5.81k
      NULL);
243
244
5.81k
    vips_area_unref(VIPS_AREA(out_array));
245
5.81k
    vips_area_unref(VIPS_AREA(x_array));
246
5.81k
    vips_area_unref(VIPS_AREA(y_array));
247
5.81k
  }
248
249
#ifdef DEBUG
250
  {
251
    int i;
252
253
    printf("vips_min_build: %d values found\n", values->n);
254
    for (i = 0; i < values->n; i++)
255
      printf("%d) %g\t%d\t%d\n",
256
        i,
257
        values->value[i],
258
        values->x_pos[i], values->y_pos[i]);
259
  }
260
#endif /*DEBUG*/
261
262
5.81k
  return 0;
263
5.81k
}
264
265
/* New sequence value. Make a private VipsValues for this thread.
266
 */
267
static void *
268
vips_min_start(VipsStatistic *statistic)
269
5.81k
{
270
5.81k
  VipsValues *values;
271
272
5.81k
  values = g_new(VipsValues, 1);
273
5.81k
  vips_values_init(values, (VipsMin *) statistic);
274
275
5.81k
  return (void *) values;
276
5.81k
}
277
278
/* Merge the sequence value back into the per-call state.
279
 */
280
static int
281
vips_min_stop(VipsStatistic *statistic, void *seq)
282
5.81k
{
283
5.81k
  VipsMin *min = (VipsMin *) statistic;
284
5.81k
  VipsValues *values = (VipsValues *) seq;
285
286
5.81k
  int i;
287
288
11.6k
  for (i = 0; i < values->n; i++)
289
5.81k
    vips_values_add(&min->values,
290
5.81k
      values->value[i], values->x_pos[i], values->y_pos[i]);
291
292
5.81k
  g_free(values);
293
294
5.81k
  return 0;
295
5.81k
}
296
297
/* Real min with a lower bound.
298
 *
299
 * Add values to the buffer if they are less than the buffer maximum. If
300
 * the buffer isn't full, there is no maximum.
301
 *
302
 * Avoid a double test by splitting the loop into two phases: before and after
303
 * the buffer fills.
304
 *
305
 * Stop if our array fills with minval.
306
 */
307
#define LOOPU(TYPE, LOWER) \
308
174k
  { \
309
174k
    TYPE *p = (TYPE *) in; \
310
174k
    TYPE m; \
311
174k
\
312
179k
    for (i = 0; i < sz && values->n < values->size; i++) \
313
174k
      vips_values_add(values, p[i], x + i / bands, y); \
314
174k
    m = values->value[0]; \
315
174k
\
316
13.5M
    for (; i < sz; i++) { \
317
13.3M
      if (p[i] < m) { \
318
8.41k
        vips_values_add(values, p[i], x + i / bands, y); \
319
8.41k
        m = values->value[0]; \
320
8.41k
\
321
8.41k
        if (m <= LOWER) { \
322
2.24k
          statistic->stop = TRUE; \
323
2.24k
          break; \
324
2.24k
        } \
325
8.41k
      } \
326
13.3M
    } \
327
174k
  }
328
329
/* float/double min ... no limits, and we have to avoid NaN.
330
 *
331
 * NaN compares false to every float value, so we don't need to test for NaN
332
 * in the second loop.
333
 */
334
#define LOOPF(TYPE) \
335
11.7k
  { \
336
11.7k
    TYPE *p = (TYPE *) in; \
337
11.7k
    TYPE m; \
338
11.7k
\
339
22.8k
    for (i = 0; i < sz && values->n < values->size; i++) \
340
11.7k
      if (!isnan(p[i])) \
341
11.1k
        vips_values_add(values, p[i], x + i / bands, y); \
342
11.7k
    m = values->value[0]; \
343
11.7k
\
344
1.02M
    for (; i < sz; i++) \
345
1.01M
      if (p[i] < m) { \
346
834
        vips_values_add(values, p[i], x + i / bands, y); \
347
834
        m = values->value[0]; \
348
834
      } \
349
11.7k
  }
350
351
/* As LOOPF, but complex. Track min(mod ** 2) to avoid sqrt().
352
 */
353
#define LOOPC(TYPE) \
354
0
  { \
355
0
    TYPE *p = (TYPE *) in; \
356
0
    TYPE m; \
357
0
\
358
0
    for (i = 0; i < sz && values->n < values->size; i++) { \
359
0
      TYPE mod2 = p[0] * p[0] + p[1] * p[1]; \
360
0
\
361
0
      if (!isnan(mod2)) \
362
0
        vips_values_add(values, p[i], x + i / bands, y); \
363
0
\
364
0
      p += 2; \
365
0
    } \
366
0
    m = values->value[0]; \
367
0
\
368
0
    for (; i < sz; i++) { \
369
0
      TYPE mod2 = p[0] * p[0] + p[1] * p[1]; \
370
0
\
371
0
      if (mod2 < m) { \
372
0
        vips_values_add(values, mod2, x + i / bands, y); \
373
0
        m = values->value[0]; \
374
0
      } \
375
0
\
376
0
      p += 2; \
377
0
    } \
378
0
  }
379
380
/* Loop over region, adding to seq.
381
 */
382
static int
383
vips_min_scan(VipsStatistic *statistic, void *seq,
384
  int x, int y, void *in, int n)
385
186k
{
386
186k
  VipsValues *values = (VipsValues *) seq;
387
186k
  const int bands = vips_image_get_bands(statistic->in);
388
186k
  const int sz = n * bands;
389
390
186k
  int i;
391
392
186k
  switch (vips_image_get_format(statistic->in)) {
393
139k
  case VIPS_FORMAT_UCHAR:
394
139k
    LOOPU(unsigned char, 0);
395
139k
    break;
396
4.41k
  case VIPS_FORMAT_CHAR:
397
4.41k
    LOOPU(signed char, SCHAR_MIN);
398
4.41k
    break;
399
9.85k
  case VIPS_FORMAT_USHORT:
400
9.85k
    LOOPU(unsigned short, 0);
401
9.85k
    break;
402
6.65k
  case VIPS_FORMAT_SHORT:
403
6.65k
    LOOPU(signed short, SHRT_MIN);
404
6.65k
    break;
405
8.76k
  case VIPS_FORMAT_UINT:
406
8.76k
    LOOPU(unsigned int, 0);
407
8.76k
    break;
408
4.73k
  case VIPS_FORMAT_INT:
409
4.73k
    LOOPU(signed int, INT_MIN);
410
4.73k
    break;
411
412
11.7k
  case VIPS_FORMAT_FLOAT:
413
11.7k
    LOOPF(float);
414
11.7k
    break;
415
0
  case VIPS_FORMAT_DOUBLE:
416
0
    LOOPF(double);
417
0
    break;
418
419
0
  case VIPS_FORMAT_COMPLEX:
420
0
    LOOPC(float);
421
0
    break;
422
0
  case VIPS_FORMAT_DPCOMPLEX:
423
0
    LOOPC(double);
424
0
    break;
425
426
0
  default:
427
0
    g_assert_not_reached();
428
186k
  }
429
430
186k
  return 0;
431
186k
}
432
433
static void
434
vips_min_class_init(VipsMinClass *class)
435
17
{
436
17
  GObjectClass *gobject_class = (GObjectClass *) class;
437
17
  VipsObjectClass *object_class = (VipsObjectClass *) class;
438
17
  VipsStatisticClass *sclass = VIPS_STATISTIC_CLASS(class);
439
440
17
  gobject_class->set_property = vips_object_set_property;
441
17
  gobject_class->get_property = vips_object_get_property;
442
443
17
  object_class->nickname = "min";
444
17
  object_class->description = _("find image minimum");
445
17
  object_class->build = vips_min_build;
446
447
17
  sclass->start = vips_min_start;
448
17
  sclass->scan = vips_min_scan;
449
17
  sclass->stop = vips_min_stop;
450
451
17
  VIPS_ARG_DOUBLE(class, "out", 1,
452
17
    _("Output"),
453
17
    _("Output value"),
454
17
    VIPS_ARGUMENT_REQUIRED_OUTPUT,
455
17
    G_STRUCT_OFFSET(VipsMin, min),
456
17
    -INFINITY, INFINITY, 0.0);
457
458
17
  VIPS_ARG_INT(class, "x", 2,
459
17
    _("x"),
460
17
    _("Horizontal position of minimum"),
461
17
    VIPS_ARGUMENT_OPTIONAL_OUTPUT,
462
17
    G_STRUCT_OFFSET(VipsMin, x),
463
17
    0, VIPS_MAX_COORD, 0);
464
465
17
  VIPS_ARG_INT(class, "y", 3,
466
17
    _("y"),
467
17
    _("Vertical position of minimum"),
468
17
    VIPS_ARGUMENT_OPTIONAL_OUTPUT,
469
17
    G_STRUCT_OFFSET(VipsMin, y),
470
17
    0, VIPS_MAX_COORD, 0);
471
472
17
  VIPS_ARG_INT(class, "size", 4,
473
17
    _("Size"),
474
17
    _("Number of minimum values to find"),
475
17
    VIPS_ARGUMENT_OPTIONAL_INPUT,
476
17
    G_STRUCT_OFFSET(VipsMin, size),
477
17
    1, 1000000, 1);
478
479
17
  VIPS_ARG_BOXED(class, "out_array", 6,
480
17
    _("Output array"),
481
17
    _("Array of output values"),
482
17
    VIPS_ARGUMENT_OPTIONAL_OUTPUT,
483
17
    G_STRUCT_OFFSET(VipsMin, min_array),
484
17
    VIPS_TYPE_ARRAY_DOUBLE);
485
486
17
  VIPS_ARG_BOXED(class, "x_array", 7,
487
17
    _("x array"),
488
17
    _("Array of horizontal positions"),
489
17
    VIPS_ARGUMENT_OPTIONAL_OUTPUT,
490
17
    G_STRUCT_OFFSET(VipsMin, x_array),
491
17
    VIPS_TYPE_ARRAY_INT);
492
493
17
  VIPS_ARG_BOXED(class, "y_array", 8,
494
17
    _("y array"),
495
17
    _("Array of vertical positions"),
496
17
    VIPS_ARGUMENT_OPTIONAL_OUTPUT,
497
17
    G_STRUCT_OFFSET(VipsMin, y_array),
498
17
    VIPS_TYPE_ARRAY_INT);
499
17
}
500
501
static void
502
vips_min_init(VipsMin *min)
503
5.81k
{
504
5.81k
  min->size = 1;
505
5.81k
}
506
507
/**
508
 * vips_min: (method)
509
 * @in: input [class@Image]
510
 * @out: (out): output pixel minimum
511
 * @...: `NULL`-terminated list of optional named arguments
512
 *
513
 * This operation finds the minimum value in an image.
514
 *
515
 * By default it finds the single smallest value. If @size is set >1, it will
516
 * find the @size smallest values. It will stop searching early if has found
517
 * enough values.
518
 * Equal values will be sorted by y then x.
519
 *
520
 * It operates on all
521
 * bands of the input image: use [method@Image.stats] if you need to find an
522
 * minimum for each band.
523
 *
524
 * For complex images, this operation finds the minimum modulus.
525
 *
526
 * You can read out the position of the minimum with @x and @y. You can read
527
 * out arrays of the values and positions of the top @size minima with
528
 * @out_array, @x_array and @y_array.
529
 * These values are returned sorted from
530
 * smallest to largest.
531
 *
532
 * If there are more than @size minima, the minima returned will be a random
533
 * selection of the minima in the image.
534
 *
535
 * ::: tip "Optional arguments"
536
 *     * @x: `gint`, output, horizontal position of minimum
537
 *     * @y: `gint`, output, vertical position of minimum
538
 *     * @size: `gint`, number of minima to find
539
 *     * @out_array: [struct@ArrayDouble], output, array of minimum values
540
 *     * @x_array: [struct@ArrayInt], output, corresponding horizontal positions
541
 *     * @y_array: [struct@ArrayInt], output, corresponding vertical positions
542
 *
543
 * ::: seealso
544
 *     [method@Image.min], [method@Image.stats].
545
 *
546
 * Returns: 0 on success, -1 on error
547
 */
548
int
549
vips_min(VipsImage *in, double *out, ...)
550
5.81k
{
551
5.81k
  va_list ap;
552
5.81k
  int result;
553
554
5.81k
  va_start(ap, out);
555
5.81k
  result = vips_call_split("min", ap, in, out);
556
5.81k
  va_end(ap);
557
558
5.81k
  return result;
559
5.81k
}