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