Coverage Report

Created: 2025-01-28 06:34

/src/libvips/libvips/arithmetic/stats.c
Line
Count
Source (jump to first uncovered line)
1
/* stats.c ... many image stats in a single pass
2
 *
3
(C) Kirk Martinez 1993
4
23/4/93 J.Cupitt
5
  - adapted to partial images
6
  - special struct abandoned; now returns DOUBLEMASK.
7
1/7/93 JC
8
  - adapted for partial v2
9
  - ANSIfied
10
27/7/93 JC
11
  - init of mask changed to use actual values, not IM_MAXDOUBLE and
12
    (-IM_MAXDOUBLE). These caused problems when assigned to floats.
13
    funny business with offset==42, yuk!
14
31/8/93 JC
15
  - forgot to init global max/min properly! sorry.
16
21/6/95 JC
17
  - still did not init max and min correctly --- now fixed for good
18
19
 * 13/1/05
20
 *  - use 64 bit arithmetic
21
 * 1/9/09
22
 *  - argh nope min/max was broken again for >1CPU in short pipelines on
23
 *      some architectures
24
 * 7/9/09
25
 *  - rework based on new im__wrapscan() / im_max() ideas for a proper fix
26
 *  - gtkdoc comment
27
 * 7/11/11
28
 *  - redone as a class
29
 *  - track maxpos / minpos too
30
 */
31
32
/*
33
34
  This file is part of VIPS.
35
36
  VIPS is free software; you can redistribute it and/or modify
37
  it under the terms of the GNU Lesser General Public License as published by
38
  the Free Software Foundation; either version 2 of the License, or
39
  (at your option) any later version.
40
41
  This program is distributed in the hope that it will be useful,
42
  but WITHOUT ANY WARRANTY; without even the implied warranty of
43
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44
  GNU Lesser General Public License for more details.
45
46
  You should have received a copy of the GNU Lesser General Public License
47
  along with this program; if not, write to the Free Software
48
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
49
  02110-1301  USA
50
51
 */
52
53
/*
54
55
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
56
57
 */
58
59
/*
60
#define VIPS_DEBUG
61
 */
62
63
#ifdef HAVE_CONFIG_H
64
#include <config.h>
65
#endif /*HAVE_CONFIG_H*/
66
#include <glib/gi18n-lib.h>
67
68
#include <stdio.h>
69
#include <stdlib.h>
70
#include <math.h>
71
72
#include <vips/vips.h>
73
#include <vips/internal.h>
74
75
#include "statistic.h"
76
77
typedef struct _VipsStats {
78
  VipsStatistic parent_instance;
79
80
  VipsImage *out;
81
82
  gboolean set; /* FALSE means no value yet */
83
} VipsStats;
84
85
typedef VipsStatisticClass VipsStatsClass;
86
87
G_DEFINE_TYPE(VipsStats, vips_stats, VIPS_TYPE_STATISTIC);
88
89
/* Names for our columns.
90
 */
91
enum {
92
  COL_MIN = 0,
93
  COL_MAX = 1,
94
  COL_SUM = 2,
95
  COL_SUM2 = 3,
96
  COL_AVG = 4,
97
  COL_SD = 5,
98
  COL_XMIN = 6,
99
  COL_YMIN = 7,
100
  COL_XMAX = 8,
101
  COL_YMAX = 9,
102
  COL_LAST = 10
103
};
104
105
static int
106
vips_stats_build(VipsObject *object)
107
0
{
108
0
  VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(object);
109
0
  VipsStatistic *statistic = VIPS_STATISTIC(object);
110
0
  VipsStats *stats = (VipsStats *) object;
111
112
0
  gint64 vals, pels;
113
0
  double *row0, *row;
114
0
  int b, y, i;
115
116
0
  if (vips_object_argument_isset(object, "in")) {
117
0
    int bands = vips_image_get_bands(statistic->in);
118
119
0
    if (vips_check_noncomplex(class->nickname, statistic->in))
120
0
      return -1;
121
122
0
    g_object_set(object,
123
0
      "out", vips_image_new_matrix(COL_LAST, bands + 1),
124
0
      NULL);
125
0
  }
126
127
0
  if (VIPS_OBJECT_CLASS(vips_stats_parent_class)->build(object))
128
0
    return -1;
129
130
0
  pels = (gint64) vips_image_get_width(statistic->in) *
131
0
    vips_image_get_height(statistic->in);
132
0
  vals = pels * vips_image_get_bands(statistic->in);
133
134
0
  row0 = VIPS_MATRIX(stats->out, 0, 0);
135
0
  row = VIPS_MATRIX(stats->out, 0, 1);
136
0
  for (i = 0; i < COL_LAST; i++)
137
0
    row0[i] = row[i];
138
139
0
  for (b = 1; b < vips_image_get_bands(statistic->in); b++) {
140
0
    row = VIPS_MATRIX(stats->out, 0, b + 1);
141
142
0
    if (row[COL_MIN] < row0[COL_MIN]) {
143
0
      row0[COL_MIN] = row[COL_MIN];
144
0
      row0[COL_XMIN] = row[COL_XMIN];
145
0
      row0[COL_YMIN] = row[COL_YMIN];
146
0
    }
147
148
0
    if (row[COL_MAX] > row0[COL_MAX]) {
149
0
      row0[COL_MAX] = row[COL_MAX];
150
0
      row0[COL_XMAX] = row[COL_XMAX];
151
0
      row0[COL_YMAX] = row[COL_YMAX];
152
0
    }
153
154
0
    row0[COL_SUM] += row[COL_SUM];
155
0
    row0[COL_SUM2] += row[COL_SUM2];
156
0
  }
157
158
0
  for (y = 1; y < vips_image_get_height(stats->out); y++) {
159
0
    double *row = VIPS_MATRIX(stats->out, 0, y);
160
161
0
    row[COL_AVG] = row[COL_SUM] / pels;
162
0
    row[COL_SD] = sqrt(
163
0
      VIPS_FABS(row[COL_SUM2] -
164
0
        (row[COL_SUM] * row[COL_SUM] / pels)) /
165
0
      (pels - 1));
166
0
  }
167
168
0
  row0[COL_AVG] = row0[COL_SUM] / vals;
169
0
  row0[COL_SD] = sqrt(
170
0
    VIPS_FABS(row0[COL_SUM2] -
171
0
      (row0[COL_SUM] * row0[COL_SUM] / vals)) /
172
0
    (vals - 1));
173
174
0
  return 0;
175
0
}
176
177
/* Stop function. Add these little stats to the main set of stats.
178
 */
179
static int
180
vips_stats_stop(VipsStatistic *statistic, void *seq)
181
0
{
182
0
  int bands = vips_image_get_bands(statistic->in);
183
0
  VipsStats *global = (VipsStats *) statistic;
184
0
  VipsStats *local = (VipsStats *) seq;
185
186
0
  int b;
187
188
0
  if (local->set && !global->set) {
189
0
    for (b = 0; b < bands; b++) {
190
0
      double *p = VIPS_MATRIX(local->out, 0, b + 1);
191
0
      double *q = VIPS_MATRIX(global->out, 0, b + 1);
192
193
0
      int i;
194
195
0
      for (i = 0; i < COL_LAST; i++)
196
0
        q[i] = p[i];
197
0
    }
198
199
0
    global->set = TRUE;
200
0
  }
201
0
  else if (local->set && global->set) {
202
0
    for (b = 0; b < bands; b++) {
203
0
      double *p = VIPS_MATRIX(local->out, 0, b + 1);
204
0
      double *q = VIPS_MATRIX(global->out, 0, b + 1);
205
206
0
      if (p[COL_MIN] < q[COL_MIN]) {
207
0
        q[COL_MIN] = p[COL_MIN];
208
0
        q[COL_XMIN] = p[COL_XMIN];
209
0
        q[COL_YMIN] = p[COL_YMIN];
210
0
      }
211
212
0
      if (p[COL_MAX] > q[COL_MAX]) {
213
0
        q[COL_MAX] = p[COL_MAX];
214
0
        q[COL_XMAX] = p[COL_XMAX];
215
0
        q[COL_YMAX] = p[COL_YMAX];
216
0
      }
217
218
0
      q[COL_SUM] += p[COL_SUM];
219
0
      q[COL_SUM2] += p[COL_SUM2];
220
0
    }
221
0
  }
222
223
0
  VIPS_FREEF(g_object_unref, local->out);
224
0
  VIPS_FREEF(g_free, seq);
225
226
0
  return 0;
227
0
}
228
229
/* Start function: make a dummy local stats for the private use of this thread.
230
 */
231
static void *
232
vips_stats_start(VipsStatistic *statistic)
233
0
{
234
0
  int bands = vips_image_get_bands(statistic->in);
235
236
0
  VipsStats *stats;
237
238
0
  stats = g_new(VipsStats, 1);
239
0
  if (!(stats->out = vips_image_new_matrix(COL_LAST, bands + 1))) {
240
0
    g_free(stats);
241
0
    return NULL;
242
0
  }
243
0
  stats->set = FALSE;
244
245
0
  return (void *) stats;
246
0
}
247
248
/* We scan lines bands times to avoid repeating band loops.
249
 * Use temp variables of same type for min/max for faster comparisons.
250
 */
251
#define LOOP(TYPE) \
252
0
  { \
253
0
    for (b = 0; b < bands; b++) { \
254
0
      TYPE *p = ((TYPE *) in) + b; \
255
0
      double *q = VIPS_MATRIX(local->out, 0, b + 1); \
256
0
      TYPE small, big; \
257
0
      double sum, sum2; \
258
0
      int xmin, ymin; \
259
0
      int xmax, ymax; \
260
0
\
261
0
      if (local->set) { \
262
0
        small = q[COL_MIN]; \
263
0
        big = q[COL_MAX]; \
264
0
        sum = q[COL_SUM]; \
265
0
        sum2 = q[COL_SUM2]; \
266
0
        xmin = q[COL_XMIN]; \
267
0
        ymin = q[COL_YMIN]; \
268
0
        xmax = q[COL_XMAX]; \
269
0
        ymax = q[COL_YMAX]; \
270
0
      } \
271
0
      else { \
272
0
        small = p[0]; \
273
0
        big = p[0]; \
274
0
        sum = 0; \
275
0
        sum2 = 0; \
276
0
        xmin = x; \
277
0
        ymin = y; \
278
0
        xmax = x; \
279
0
        ymax = y; \
280
0
      } \
281
0
\
282
0
      for (i = 0; i < n; i++) { \
283
0
        TYPE value = *p; \
284
0
\
285
0
        sum += value; \
286
0
        sum2 += (double) value * (double) value; \
287
0
        if (value > big) { \
288
0
          big = value; \
289
0
          xmax = x + i; \
290
0
          ymax = y; \
291
0
        } \
292
0
        else if (value < small) { \
293
0
          small = value; \
294
0
          xmin = x + i; \
295
0
          ymin = y; \
296
0
        } \
297
0
\
298
0
        p += bands; \
299
0
      } \
300
0
\
301
0
      q[COL_MIN] = small; \
302
0
      q[COL_MAX] = big; \
303
0
      q[COL_SUM] = sum; \
304
0
      q[COL_SUM2] = sum2; \
305
0
      q[COL_XMIN] = xmin; \
306
0
      q[COL_YMIN] = ymin; \
307
0
      q[COL_XMAX] = xmax; \
308
0
      q[COL_YMAX] = ymax; \
309
0
    } \
310
0
\
311
0
    local->set = TRUE; \
312
0
  }
313
314
/* As above, but for float/double types where we have to avoid NaN.
315
 */
316
#define LOOPF(TYPE) \
317
  { \
318
    for (b = 0; b < bands; b++) { \
319
      TYPE *p = ((TYPE *) in) + b; \
320
      double *q = VIPS_MATRIX(local->out, 0, b + 1); \
321
      TYPE small, big; \
322
      double sum, sum2; \
323
      int xmin, ymin; \
324
      int xmax, ymax; \
325
\
326
      if (local->set) { \
327
        small = q[COL_MIN]; \
328
        big = q[COL_MAX]; \
329
        sum = q[COL_SUM]; \
330
        sum2 = q[COL_SUM2]; \
331
        xmin = q[COL_XMIN]; \
332
        ymin = q[COL_YMIN]; \
333
        xmax = q[COL_XMAX]; \
334
        ymax = q[COL_YMAX]; \
335
      } \
336
      else { \
337
        small = p[0]; \
338
        big = p[0]; \
339
        sum = 0; \
340
        sum2 = 0; \
341
        xmin = x; \
342
        ymin = y; \
343
        xmax = x; \
344
        ymax = y; \
345
      } \
346
\
347
      for (i = 0; i < n; i++) { \
348
        TYPE value = *p; \
349
\
350
        sum += value; \
351
        sum2 += (double) value * (double) value; \
352
        if (value > big) { \
353
          big = value; \
354
          xmax = x + i; \
355
          ymax = y; \
356
        } \
357
        else if (value < small) { \
358
          small = value; \
359
          xmin = x + i; \
360
          ymin = y; \
361
        } \
362
\
363
        p += bands; \
364
      } \
365
\
366
      q[COL_MIN] = small; \
367
      q[COL_MAX] = big; \
368
      q[COL_SUM] = sum; \
369
      q[COL_SUM2] = sum2; \
370
      q[COL_XMIN] = xmin; \
371
      q[COL_YMIN] = ymin; \
372
      q[COL_XMAX] = xmax; \
373
      q[COL_YMAX] = ymax; \
374
    } \
375
\
376
    local->set = TRUE; \
377
  }
378
379
/* Loop over region, accumulating a sum in *tmp.
380
 */
381
static int
382
vips_stats_scan(VipsStatistic *statistic, void *seq,
383
  int x, int y, void *in, int n)
384
0
{
385
0
  const int bands = vips_image_get_bands(statistic->in);
386
0
  VipsStats *local = (VipsStats *) seq;
387
388
0
  int b, i;
389
390
0
  switch (vips_image_get_format(statistic->in)) {
391
0
  case VIPS_FORMAT_UCHAR:
392
0
    LOOP(unsigned char);
393
0
    break;
394
0
  case VIPS_FORMAT_CHAR:
395
0
    LOOP(signed char);
396
0
    break;
397
0
  case VIPS_FORMAT_USHORT:
398
0
    LOOP(unsigned short);
399
0
    break;
400
0
  case VIPS_FORMAT_SHORT:
401
0
    LOOP(signed short);
402
0
    break;
403
0
  case VIPS_FORMAT_UINT:
404
0
    LOOP(unsigned int);
405
0
    break;
406
0
  case VIPS_FORMAT_INT:
407
0
    LOOP(signed int);
408
0
    break;
409
0
  case VIPS_FORMAT_FLOAT:
410
0
    LOOP(float);
411
0
    break;
412
0
  case VIPS_FORMAT_DOUBLE:
413
0
    LOOP(double);
414
0
    break;
415
416
0
  default:
417
0
    g_assert_not_reached();
418
0
  }
419
420
0
  return 0;
421
0
}
422
423
static void
424
vips_stats_class_init(VipsStatsClass *class)
425
1
{
426
1
  GObjectClass *gobject_class = (GObjectClass *) class;
427
1
  VipsObjectClass *object_class = (VipsObjectClass *) class;
428
1
  VipsStatisticClass *sclass = VIPS_STATISTIC_CLASS(class);
429
430
1
  gobject_class->set_property = vips_object_set_property;
431
1
  gobject_class->get_property = vips_object_get_property;
432
433
1
  object_class->nickname = "stats";
434
1
  object_class->description = _("find many image stats");
435
1
  object_class->build = vips_stats_build;
436
437
1
  sclass->start = vips_stats_start;
438
1
  sclass->scan = vips_stats_scan;
439
1
  sclass->stop = vips_stats_stop;
440
441
1
  VIPS_ARG_IMAGE(class, "out", 100,
442
1
    _("Output"),
443
1
    _("Output array of statistics"),
444
1
    VIPS_ARGUMENT_REQUIRED_OUTPUT,
445
1
    G_STRUCT_OFFSET(VipsStats, out));
446
1
}
447
448
static void
449
vips_stats_init(VipsStats *stats)
450
0
{
451
0
}
452
453
/**
454
 * vips_stats: (method)
455
 * @in: image to scan
456
 * @out: (out): image of statistics
457
 * @...: %NULL-terminated list of optional named arguments
458
 *
459
 * Find many image statistics in a single pass through the data. @out is a
460
 * one-band #VIPS_FORMAT_DOUBLE image of at least 10 columns by n + 1
461
 * (where n is number of bands in image @in)
462
 * rows. Columns are statistics, and are, in order: minimum, maximum, sum,
463
 * sum of squares, mean, standard deviation, x coordinate of minimum, y
464
 * coordinate of minimum, x coordinate of maximum, y coordinate of maximum.
465
 * Later versions of vips_stats() may add more columns.
466
 *
467
 * Row 0 has statistics for all
468
 * bands together, row 1 has stats for band 1, and so on.
469
 *
470
 * If there is more than one maxima or minima, one of them will be chosen at
471
 * random.
472
 *
473
 * See also: vips_avg(), vips_min().
474
 *
475
 * Returns: 0 on success, -1 on error
476
 */
477
int
478
vips_stats(VipsImage *in, VipsImage **out, ...)
479
0
{
480
0
  va_list ap;
481
0
  int result;
482
483
0
  va_start(ap, out);
484
0
  result = vips_call_split("stats", ap, in, out);
485
0
  va_end(ap);
486
487
0
  return result;
488
0
}