/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 | } |