1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimphistogram module Copyright (C) 1999 Jay Cox <jaycox@gimp.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <cairo.h>
24 #include <gegl.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26
27 #include "libgimpmath/gimpmath.h"
28 #include "libgimpcolor/gimpcolor.h"
29
30 #include "core-types.h"
31
32 #include "gegl/gimp-babl.h"
33 #include "gegl/gimp-gegl-loops.h"
34
35 #include "gimp-atomic.h"
36 #include "gimp-parallel.h"
37 #include "gimpasync.h"
38 #include "gimphistogram.h"
39 #include "gimpwaitable.h"
40
41
42 #define MAX_N_COMPONENTS 4
43 #define N_DERIVED_CHANNELS 2
44
45 #define PIXELS_PER_THREAD \
46 (/* each thread costs as much as */ 64.0 * 64.0 /* pixels */)
47
48
49 enum
50 {
51 PROP_0,
52 PROP_N_COMPONENTS,
53 PROP_N_BINS,
54 PROP_VALUES
55 };
56
57 struct _GimpHistogramPrivate
58 {
59 gboolean linear;
60 gint n_channels;
61 gint n_bins;
62 gdouble *values;
63 GimpAsync *calculate_async;
64 };
65
66 typedef struct
67 {
68 /* input */
69 GimpHistogram *histogram;
70 GeglBuffer *buffer;
71 GeglRectangle buffer_rect;
72 GeglBuffer *mask;
73 GeglRectangle mask_rect;
74
75 /* output */
76 gint n_components;
77 gint n_bins;
78 gdouble *values;
79 } CalculateContext;
80
81 typedef struct
82 {
83 GimpAsync *async;
84 CalculateContext *context;
85
86 const Babl *format;
87 GSList *values_list;
88 } CalculateData;
89
90
91 /* local function prototypes */
92
93 static void gimp_histogram_finalize (GObject *object);
94 static void gimp_histogram_set_property (GObject *object,
95 guint property_id,
96 const GValue *value,
97 GParamSpec *pspec);
98 static void gimp_histogram_get_property (GObject *object,
99 guint property_id,
100 GValue *value,
101 GParamSpec *pspec);
102
103 static gint64 gimp_histogram_get_memsize (GimpObject *object,
104 gint64 *gui_size);
105
106 static gboolean gimp_histogram_map_channel (GimpHistogram *histogram,
107 GimpHistogramChannel *channel);
108
109 static void gimp_histogram_set_values (GimpHistogram *histogram,
110 gint n_components,
111 gint n_bins,
112 gdouble *values);
113
114 static void gimp_histogram_calculate_internal (GimpAsync *async,
115 CalculateContext *context);
116 static void gimp_histogram_calculate_area (const GeglRectangle *area,
117 CalculateData *data);
118 static void gimp_histogram_calculate_async_callback (GimpAsync *async,
119 CalculateContext *context);
120
121
G_DEFINE_TYPE_WITH_PRIVATE(GimpHistogram,gimp_histogram,GIMP_TYPE_OBJECT)122 G_DEFINE_TYPE_WITH_PRIVATE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT)
123
124 #define parent_class gimp_histogram_parent_class
125
126
127 static void
128 gimp_histogram_class_init (GimpHistogramClass *klass)
129 {
130 GObjectClass *object_class = G_OBJECT_CLASS (klass);
131 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
132
133 object_class->finalize = gimp_histogram_finalize;
134 object_class->set_property = gimp_histogram_set_property;
135 object_class->get_property = gimp_histogram_get_property;
136
137 gimp_object_class->get_memsize = gimp_histogram_get_memsize;
138
139 g_object_class_install_property (object_class, PROP_N_COMPONENTS,
140 g_param_spec_int ("n-components", NULL, NULL,
141 0, MAX_N_COMPONENTS, 0,
142 GIMP_PARAM_READABLE));
143
144 g_object_class_install_property (object_class, PROP_N_BINS,
145 g_param_spec_int ("n-bins", NULL, NULL,
146 256, 1024, 1024,
147 GIMP_PARAM_READABLE));
148
149 /* this is just for notifications */
150 g_object_class_install_property (object_class, PROP_VALUES,
151 g_param_spec_boolean ("values", NULL, NULL,
152 FALSE,
153 G_PARAM_READABLE));
154 }
155
156 static void
gimp_histogram_init(GimpHistogram * histogram)157 gimp_histogram_init (GimpHistogram *histogram)
158 {
159 histogram->priv = gimp_histogram_get_instance_private (histogram);
160 }
161
162 static void
gimp_histogram_finalize(GObject * object)163 gimp_histogram_finalize (GObject *object)
164 {
165 GimpHistogram *histogram = GIMP_HISTOGRAM (object);
166
167 gimp_histogram_clear_values (histogram, 0);
168
169 G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171
172 static void
gimp_histogram_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)173 gimp_histogram_set_property (GObject *object,
174 guint property_id,
175 const GValue *value,
176 GParamSpec *pspec)
177 {
178 switch (property_id)
179 {
180 default:
181 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
182 break;
183 }
184 }
185
186 static void
gimp_histogram_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)187 gimp_histogram_get_property (GObject *object,
188 guint property_id,
189 GValue *value,
190 GParamSpec *pspec)
191 {
192 GimpHistogram *histogram = GIMP_HISTOGRAM (object);
193
194 switch (property_id)
195 {
196 case PROP_N_COMPONENTS:
197 g_value_set_int (value, gimp_histogram_n_components (histogram));
198 break;
199
200 case PROP_N_BINS:
201 g_value_set_int (value, histogram->priv->n_bins);
202 break;
203
204 case PROP_VALUES:
205 /* return a silly boolean */
206 g_value_set_boolean (value, histogram->priv->values != NULL);
207 break;
208
209 default:
210 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
211 break;
212 }
213 }
214
215 static gint64
gimp_histogram_get_memsize(GimpObject * object,gint64 * gui_size)216 gimp_histogram_get_memsize (GimpObject *object,
217 gint64 *gui_size)
218 {
219 GimpHistogram *histogram = GIMP_HISTOGRAM (object);
220 gint64 memsize = 0;
221
222 if (histogram->priv->values)
223 memsize += (histogram->priv->n_channels *
224 histogram->priv->n_bins * sizeof (gdouble));
225
226 return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
227 gui_size);
228 }
229
230 /* public functions */
231
232 GimpHistogram *
gimp_histogram_new(gboolean linear)233 gimp_histogram_new (gboolean linear)
234 {
235 GimpHistogram *histogram = g_object_new (GIMP_TYPE_HISTOGRAM, NULL);
236
237 histogram->priv->linear = linear;
238
239 return histogram;
240 }
241
242 /**
243 * gimp_histogram_duplicate:
244 * @histogram: a %GimpHistogram
245 *
246 * Creates a duplicate of @histogram. The duplicate has a reference
247 * count of 1 and contains the values from @histogram.
248 *
249 * Return value: a newly allocated %GimpHistogram
250 **/
251 GimpHistogram *
gimp_histogram_duplicate(GimpHistogram * histogram)252 gimp_histogram_duplicate (GimpHistogram *histogram)
253 {
254 GimpHistogram *dup;
255
256 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL);
257
258 if (histogram->priv->calculate_async)
259 gimp_waitable_wait (GIMP_WAITABLE (histogram->priv->calculate_async));
260
261 dup = gimp_histogram_new (histogram->priv->linear);
262
263 dup->priv->n_channels = histogram->priv->n_channels;
264 dup->priv->n_bins = histogram->priv->n_bins;
265 dup->priv->values = g_memdup (histogram->priv->values,
266 sizeof (gdouble) *
267 dup->priv->n_channels *
268 dup->priv->n_bins);
269
270 return dup;
271 }
272
273 void
gimp_histogram_calculate(GimpHistogram * histogram,GeglBuffer * buffer,const GeglRectangle * buffer_rect,GeglBuffer * mask,const GeglRectangle * mask_rect)274 gimp_histogram_calculate (GimpHistogram *histogram,
275 GeglBuffer *buffer,
276 const GeglRectangle *buffer_rect,
277 GeglBuffer *mask,
278 const GeglRectangle *mask_rect)
279 {
280 CalculateContext context = {};
281
282 g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
283 g_return_if_fail (GEGL_IS_BUFFER (buffer));
284 g_return_if_fail (buffer_rect != NULL);
285
286 if (histogram->priv->calculate_async)
287 gimp_async_cancel_and_wait (histogram->priv->calculate_async);
288
289 context.histogram = histogram;
290 context.buffer = buffer;
291 context.buffer_rect = *buffer_rect;
292
293 if (mask)
294 {
295 context.mask = mask;
296
297 if (mask_rect)
298 context.mask_rect = *mask_rect;
299 else
300 context.mask_rect = *gegl_buffer_get_extent (mask);
301 }
302
303 gimp_histogram_calculate_internal (NULL, &context);
304
305 gimp_histogram_set_values (histogram,
306 context.n_components, context.n_bins,
307 context.values);
308 }
309
310 GimpAsync *
gimp_histogram_calculate_async(GimpHistogram * histogram,GeglBuffer * buffer,const GeglRectangle * buffer_rect,GeglBuffer * mask,const GeglRectangle * mask_rect)311 gimp_histogram_calculate_async (GimpHistogram *histogram,
312 GeglBuffer *buffer,
313 const GeglRectangle *buffer_rect,
314 GeglBuffer *mask,
315 const GeglRectangle *mask_rect)
316 {
317 CalculateContext *context;
318 GeglRectangle rect;
319
320 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL);
321 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
322 g_return_val_if_fail (buffer_rect != NULL, NULL);
323
324 if (histogram->priv->calculate_async)
325 gimp_async_cancel_and_wait (histogram->priv->calculate_async);
326
327 gegl_rectangle_align_to_buffer (&rect, buffer_rect, buffer,
328 GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
329
330 context = g_slice_new0 (CalculateContext);
331
332 context->histogram = histogram;
333 context->buffer = gegl_buffer_new (&rect,
334 gegl_buffer_get_format (buffer));
335 context->buffer_rect = *buffer_rect;
336
337 gimp_gegl_buffer_copy (buffer, &rect, GEGL_ABYSS_NONE,
338 context->buffer, NULL);
339
340 if (mask)
341 {
342 if (mask_rect)
343 context->mask_rect = *mask_rect;
344 else
345 context->mask_rect = *gegl_buffer_get_extent (mask);
346
347 gegl_rectangle_align_to_buffer (&rect, &context->mask_rect, mask,
348 GEGL_RECTANGLE_ALIGNMENT_SUPERSET);
349
350 context->mask = gegl_buffer_new (&rect, gegl_buffer_get_format (mask));
351
352 gimp_gegl_buffer_copy (mask, &rect, GEGL_ABYSS_NONE,
353 context->mask, NULL);
354 }
355
356 histogram->priv->calculate_async = gimp_parallel_run_async (
357 (GimpRunAsyncFunc) gimp_histogram_calculate_internal,
358 context);
359
360 gimp_async_add_callback (
361 histogram->priv->calculate_async,
362 (GimpAsyncCallback) gimp_histogram_calculate_async_callback,
363 context);
364
365 return histogram->priv->calculate_async;
366 }
367
368 void
gimp_histogram_clear_values(GimpHistogram * histogram,gint n_components)369 gimp_histogram_clear_values (GimpHistogram *histogram,
370 gint n_components)
371 {
372 g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
373
374 if (histogram->priv->calculate_async)
375 gimp_async_cancel_and_wait (histogram->priv->calculate_async);
376
377 gimp_histogram_set_values (histogram, n_components, 0, NULL);
378 }
379
380
381 #define HISTOGRAM_VALUE(c,i) (priv->values[(c) * priv->n_bins + (i)])
382
383
384 gdouble
gimp_histogram_get_maximum(GimpHistogram * histogram,GimpHistogramChannel channel)385 gimp_histogram_get_maximum (GimpHistogram *histogram,
386 GimpHistogramChannel channel)
387 {
388 GimpHistogramPrivate *priv;
389 gdouble max = 0.0;
390 gint x;
391
392 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
393
394 priv = histogram->priv;
395
396 if (! priv->values ||
397 ! gimp_histogram_map_channel (histogram, &channel))
398 {
399 return 0.0;
400 }
401
402 if (channel == GIMP_HISTOGRAM_RGB)
403 {
404 for (x = 0; x < priv->n_bins; x++)
405 {
406 max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, x));
407 max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, x));
408 max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, x));
409 }
410 }
411 else
412 {
413 for (x = 0; x < priv->n_bins; x++)
414 {
415 max = MAX (max, HISTOGRAM_VALUE (channel, x));
416 }
417 }
418
419 return max;
420 }
421
422 gdouble
gimp_histogram_get_value(GimpHistogram * histogram,GimpHistogramChannel channel,gint bin)423 gimp_histogram_get_value (GimpHistogram *histogram,
424 GimpHistogramChannel channel,
425 gint bin)
426 {
427 GimpHistogramPrivate *priv;
428
429 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
430
431 priv = histogram->priv;
432
433 if (! priv->values ||
434 (bin < 0 || bin >= priv->n_bins) ||
435 ! gimp_histogram_map_channel (histogram, &channel))
436 {
437 return 0.0;
438 }
439
440 if (channel == GIMP_HISTOGRAM_RGB)
441 {
442 gdouble min = HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, bin);
443
444 min = MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, bin));
445
446 return MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, bin));
447 }
448 else
449 {
450 return HISTOGRAM_VALUE (channel, bin);
451 }
452 }
453
454 gdouble
gimp_histogram_get_component(GimpHistogram * histogram,gint component,gint bin)455 gimp_histogram_get_component (GimpHistogram *histogram,
456 gint component,
457 gint bin)
458 {
459 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
460
461 if (gimp_histogram_n_components (histogram) > 2)
462 component++;
463
464 return gimp_histogram_get_value (histogram, component, bin);
465 }
466
467 gint
gimp_histogram_n_components(GimpHistogram * histogram)468 gimp_histogram_n_components (GimpHistogram *histogram)
469 {
470 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
471
472 if (histogram->priv->n_channels > 0)
473 return histogram->priv->n_channels - N_DERIVED_CHANNELS;
474 else
475 return 0;
476 }
477
478 gint
gimp_histogram_n_bins(GimpHistogram * histogram)479 gimp_histogram_n_bins (GimpHistogram *histogram)
480 {
481 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
482
483 return histogram->priv->n_bins;
484 }
485
486 gboolean
gimp_histogram_has_channel(GimpHistogram * histogram,GimpHistogramChannel channel)487 gimp_histogram_has_channel (GimpHistogram *histogram,
488 GimpHistogramChannel channel)
489 {
490 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), FALSE);
491
492 switch (channel)
493 {
494 case GIMP_HISTOGRAM_VALUE:
495 return TRUE;
496
497 case GIMP_HISTOGRAM_RED:
498 case GIMP_HISTOGRAM_GREEN:
499 case GIMP_HISTOGRAM_BLUE:
500 case GIMP_HISTOGRAM_LUMINANCE:
501 case GIMP_HISTOGRAM_RGB:
502 return gimp_histogram_n_components (histogram) >= 3;
503
504 case GIMP_HISTOGRAM_ALPHA:
505 return gimp_histogram_n_components (histogram) == 2 ||
506 gimp_histogram_n_components (histogram) == 4;
507 }
508
509 g_return_val_if_reached (FALSE);
510 }
511
512 gdouble
gimp_histogram_get_count(GimpHistogram * histogram,GimpHistogramChannel channel,gint start,gint end)513 gimp_histogram_get_count (GimpHistogram *histogram,
514 GimpHistogramChannel channel,
515 gint start,
516 gint end)
517 {
518 GimpHistogramPrivate *priv;
519 gint i;
520 gdouble count = 0.0;
521
522 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
523
524 priv = histogram->priv;
525
526 if (! priv->values ||
527 start > end ||
528 ! gimp_histogram_map_channel (histogram, &channel))
529 {
530 return 0.0;
531 }
532
533 if (channel == GIMP_HISTOGRAM_RGB)
534 return (gimp_histogram_get_count (histogram,
535 GIMP_HISTOGRAM_RED, start, end) +
536 gimp_histogram_get_count (histogram,
537 GIMP_HISTOGRAM_GREEN, start, end) +
538 gimp_histogram_get_count (histogram,
539 GIMP_HISTOGRAM_BLUE, start, end));
540
541 start = CLAMP (start, 0, priv->n_bins - 1);
542 end = CLAMP (end, 0, priv->n_bins - 1);
543
544 for (i = start; i <= end; i++)
545 count += HISTOGRAM_VALUE (channel, i);
546
547 return count;
548 }
549
550 gdouble
gimp_histogram_get_mean(GimpHistogram * histogram,GimpHistogramChannel channel,gint start,gint end)551 gimp_histogram_get_mean (GimpHistogram *histogram,
552 GimpHistogramChannel channel,
553 gint start,
554 gint end)
555 {
556 GimpHistogramPrivate *priv;
557 gint i;
558 gdouble mean = 0.0;
559 gdouble count;
560
561 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
562
563 priv = histogram->priv;
564
565 if (! priv->values ||
566 start > end ||
567 ! gimp_histogram_map_channel (histogram, &channel))
568 {
569 return 0.0;
570 }
571
572 start = CLAMP (start, 0, priv->n_bins - 1);
573 end = CLAMP (end, 0, priv->n_bins - 1);
574
575 if (channel == GIMP_HISTOGRAM_RGB)
576 {
577 for (i = start; i <= end; i++)
578 {
579 gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
580
581 mean += (factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
582 factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
583 factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
584 }
585 }
586 else
587 {
588 for (i = start; i <= end; i++)
589 {
590 gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
591
592 mean += factor * HISTOGRAM_VALUE (channel, i);
593 }
594 }
595
596 count = gimp_histogram_get_count (histogram, channel, start, end);
597
598 if (count > 0.0)
599 return mean / count;
600
601 return mean;
602 }
603
604 gdouble
gimp_histogram_get_median(GimpHistogram * histogram,GimpHistogramChannel channel,gint start,gint end)605 gimp_histogram_get_median (GimpHistogram *histogram,
606 GimpHistogramChannel channel,
607 gint start,
608 gint end)
609 {
610 GimpHistogramPrivate *priv;
611 gint i;
612 gdouble sum = 0.0;
613 gdouble count;
614
615 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1.0);
616
617 priv = histogram->priv;
618
619 if (! priv->values ||
620 start > end ||
621 ! gimp_histogram_map_channel (histogram, &channel))
622 {
623 return 0.0;
624 }
625
626 start = CLAMP (start, 0, priv->n_bins - 1);
627 end = CLAMP (end, 0, priv->n_bins - 1);
628
629 count = gimp_histogram_get_count (histogram, channel, start, end);
630
631 if (channel == GIMP_HISTOGRAM_RGB)
632 {
633 for (i = start; i <= end; i++)
634 {
635 sum += (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
636 HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
637 HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
638
639 if (sum * 2 > count)
640 return ((gdouble) i / (gdouble) (priv->n_bins - 1));
641 }
642 }
643 else
644 {
645 for (i = start; i <= end; i++)
646 {
647 sum += HISTOGRAM_VALUE (channel, i);
648
649 if (sum * 2 > count)
650 return ((gdouble) i / (gdouble) (priv->n_bins - 1));
651 }
652 }
653
654 return -1.0;
655 }
656
657 /*
658 * adapted from GNU ocrad 0.14 : page_image_io.cc : otsu_th
659 *
660 * N. Otsu, "A threshold selection method from gray-level histograms,"
661 * IEEE Trans. Systems, Man, and Cybernetics, vol. 9, no. 1, pp. 62-66, 1979.
662 */
663 gdouble
gimp_histogram_get_threshold(GimpHistogram * histogram,GimpHistogramChannel channel,gint start,gint end)664 gimp_histogram_get_threshold (GimpHistogram *histogram,
665 GimpHistogramChannel channel,
666 gint start,
667 gint end)
668 {
669 GimpHistogramPrivate *priv;
670 gint i;
671 gint maxval;
672 gdouble *hist = NULL;
673 gdouble *chist = NULL;
674 gdouble *cmom = NULL;
675 gdouble hist_max = 0.0;
676 gdouble chist_max = 0.0;
677 gdouble cmom_max = 0.0;
678 gdouble bvar_max = 0.0;
679 gint threshold = 127;
680
681 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1);
682
683 priv = histogram->priv;
684
685 if (! priv->values ||
686 start > end ||
687 ! gimp_histogram_map_channel (histogram, &channel))
688 {
689 return 0;
690 }
691
692 start = CLAMP (start, 0, priv->n_bins - 1);
693 end = CLAMP (end, 0, priv->n_bins - 1);
694
695 maxval = end - start;
696
697 hist = g_newa (gdouble, maxval + 1);
698 chist = g_newa (gdouble, maxval + 1);
699 cmom = g_newa (gdouble, maxval + 1);
700
701 if (channel == GIMP_HISTOGRAM_RGB)
702 {
703 for (i = start; i <= end; i++)
704 hist[i - start] = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
705 HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
706 HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
707 }
708 else
709 {
710 for (i = start; i <= end; i++)
711 hist[i - start] = HISTOGRAM_VALUE (channel, i);
712 }
713
714 hist_max = hist[0];
715 chist[0] = hist[0];
716 cmom[0] = 0;
717
718 for (i = 1; i <= maxval; i++)
719 {
720 if (hist[i] > hist_max)
721 hist_max = hist[i];
722
723 chist[i] = chist[i-1] + hist[i];
724 cmom[i] = cmom[i-1] + i * hist[i];
725 }
726
727 chist_max = chist[maxval];
728 cmom_max = cmom[maxval];
729 bvar_max = 0;
730
731 for (i = 0; i < maxval; ++i)
732 {
733 if (chist[i] > 0 && chist[i] < chist_max)
734 {
735 gdouble bvar;
736
737 bvar = (gdouble) cmom[i] / chist[i];
738 bvar -= (cmom_max - cmom[i]) / (chist_max - chist[i]);
739 bvar *= bvar;
740 bvar *= chist[i];
741 bvar *= chist_max - chist[i];
742
743 if (bvar > bvar_max)
744 {
745 bvar_max = bvar;
746 threshold = start + i;
747 }
748 }
749 }
750
751 return threshold;
752 }
753
754 gdouble
gimp_histogram_get_std_dev(GimpHistogram * histogram,GimpHistogramChannel channel,gint start,gint end)755 gimp_histogram_get_std_dev (GimpHistogram *histogram,
756 GimpHistogramChannel channel,
757 gint start,
758 gint end)
759 {
760 GimpHistogramPrivate *priv;
761 gint i;
762 gdouble dev = 0.0;
763 gdouble count;
764 gdouble mean;
765
766 g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
767
768 priv = histogram->priv;
769
770 if (! priv->values ||
771 start > end ||
772 ! gimp_histogram_map_channel (histogram, &channel))
773 {
774 return 0.0;
775 }
776
777 mean = gimp_histogram_get_mean (histogram, channel, start, end);
778 count = gimp_histogram_get_count (histogram, channel, start, end);
779
780 if (count == 0.0)
781 count = 1.0;
782
783 for (i = start; i <= end; i++)
784 {
785 gdouble value;
786
787 if (channel == GIMP_HISTOGRAM_RGB)
788 {
789 value = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
790 HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
791 HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
792 }
793 else
794 {
795 value = gimp_histogram_get_value (histogram, channel, i);
796 }
797
798 dev += value * SQR (((gdouble) i / (gdouble) (priv->n_bins - 1)) - mean);
799 }
800
801 return sqrt (dev / count);
802 }
803
804
805 /* private functions */
806
807 static gboolean
gimp_histogram_map_channel(GimpHistogram * histogram,GimpHistogramChannel * channel)808 gimp_histogram_map_channel (GimpHistogram *histogram,
809 GimpHistogramChannel *channel)
810 {
811 GimpHistogramPrivate *priv = histogram->priv;
812
813 if (*channel == GIMP_HISTOGRAM_RGB)
814 return gimp_histogram_n_components (histogram) >= 3;
815
816 switch (*channel)
817 {
818 case GIMP_HISTOGRAM_ALPHA:
819 if (gimp_histogram_n_components (histogram) == 2)
820 *channel = 1;
821 break;
822
823 case GIMP_HISTOGRAM_LUMINANCE:
824 *channel = gimp_histogram_n_components (histogram) + 1;
825 break;
826
827 default:
828 break;
829 }
830
831 return *channel < priv->n_channels;
832 }
833
834 static void
gimp_histogram_set_values(GimpHistogram * histogram,gint n_components,gint n_bins,gdouble * values)835 gimp_histogram_set_values (GimpHistogram *histogram,
836 gint n_components,
837 gint n_bins,
838 gdouble *values)
839 {
840 GimpHistogramPrivate *priv = histogram->priv;
841 gint n_channels = n_components;
842 gboolean notify_n_components = FALSE;
843 gboolean notify_n_bins = FALSE;
844
845 if (n_channels > 0)
846 n_channels += N_DERIVED_CHANNELS;
847
848 if (n_channels != priv->n_channels)
849 {
850 priv->n_channels = n_channels;
851
852 notify_n_components = TRUE;
853 }
854
855 if (n_bins != priv->n_bins)
856 {
857 priv->n_bins = n_bins;
858
859 notify_n_bins = TRUE;
860 }
861
862 if (values != priv->values)
863 {
864 if (priv->values)
865 g_free (priv->values);
866
867 priv->values = values;
868 }
869
870 if (notify_n_components)
871 g_object_notify (G_OBJECT (histogram), "n-components");
872
873 if (notify_n_bins)
874 g_object_notify (G_OBJECT (histogram), "n-bins");
875
876 g_object_notify (G_OBJECT (histogram), "values");
877 }
878
879 static void
gimp_histogram_calculate_internal(GimpAsync * async,CalculateContext * context)880 gimp_histogram_calculate_internal (GimpAsync *async,
881 CalculateContext *context)
882 {
883 CalculateData data;
884 GimpHistogramPrivate *priv;
885 const Babl *format;
886
887 priv = context->histogram->priv;
888
889 format = gegl_buffer_get_format (context->buffer);
890
891 if (babl_format_get_type (format, 0) == babl_type ("u8"))
892 context->n_bins = 256;
893 else
894 context->n_bins = 1024;
895
896 if (babl_format_is_palette (format))
897 {
898 if (babl_format_has_alpha (format))
899 {
900 if (priv->linear)
901 format = babl_format ("RGB float");
902 else
903 format = babl_format ("R'G'B' float");
904 }
905 else
906 {
907 if (priv->linear)
908 format = babl_format ("RGBA float");
909 else
910 format = babl_format ("R'G'B'A float");
911 }
912 }
913 else
914 {
915 const Babl *model = babl_format_get_model (format);
916
917 if (model == babl_model ("Y") ||
918 model == babl_model ("Y'"))
919 {
920 if (priv->linear)
921 format = babl_format ("Y float");
922 else
923 format = babl_format ("Y' float");
924 }
925 else if (model == babl_model ("YA") ||
926 model == babl_model ("Y'A"))
927 {
928 if (priv->linear)
929 format = babl_format ("YA float");
930 else
931 format = babl_format ("Y'A float");
932 }
933 else if (model == babl_model ("RGB") ||
934 model == babl_model ("R'G'B'"))
935 {
936 if (priv->linear)
937 format = babl_format ("RGB float");
938 else
939 format = babl_format ("R'G'B' float");
940 }
941 else if (model == babl_model ("RGBA") ||
942 model == babl_model ("R'G'B'A"))
943 {
944 if (priv->linear)
945 format = babl_format ("RGBA float");
946 else
947 format = babl_format ("R'G'B'A float");
948 }
949 else
950 {
951 if (async)
952 gimp_async_abort (async);
953
954 g_return_if_reached ();
955 }
956 }
957
958 context->n_components = babl_format_get_n_components (format);
959
960 data.async = async;
961 data.context = context;
962 data.format = format;
963 data.values_list = NULL;
964
965 gegl_parallel_distribute_area (
966 &context->buffer_rect, PIXELS_PER_THREAD, GEGL_SPLIT_STRATEGY_AUTO,
967 (GeglParallelDistributeAreaFunc) gimp_histogram_calculate_area,
968 &data);
969
970 if (! async || ! gimp_async_is_canceled (async))
971 {
972 gdouble *total_values = NULL;
973 gint n_values = (context->n_components + N_DERIVED_CHANNELS) *
974 context->n_bins;
975 GSList *iter;
976
977 for (iter = data.values_list; iter; iter = g_slist_next (iter))
978 {
979 gdouble *values = iter->data;
980
981 if (! total_values)
982 {
983 total_values = values;
984 }
985 else
986 {
987 gint i;
988
989 for (i = 0; i < n_values; i++)
990 total_values[i] += values[i];
991
992 g_free (values);
993 }
994 }
995
996 g_slist_free (data.values_list);
997
998 context->values = total_values;
999
1000 if (async)
1001 gimp_async_finish (async, NULL);
1002 }
1003 else
1004 {
1005 g_slist_free_full (data.values_list, g_free);
1006
1007 if (async)
1008 gimp_async_abort (async);
1009 }
1010 }
1011
1012 static void
gimp_histogram_calculate_area(const GeglRectangle * area,CalculateData * data)1013 gimp_histogram_calculate_area (const GeglRectangle *area,
1014 CalculateData *data)
1015 {
1016 GimpAsync *async;
1017 CalculateContext *context;
1018 GeglBufferIterator *iter;
1019 gdouble *values;
1020 gint n_components;
1021 gint n_bins;
1022 gfloat n_bins_1f;
1023 gfloat temp;
1024
1025 async = data->async;
1026 context = data->context;
1027
1028 n_bins = context->n_bins;
1029 n_components = context->n_components;
1030
1031 values = g_new0 (gdouble, (n_components + N_DERIVED_CHANNELS) * n_bins);
1032 gimp_atomic_slist_push_head (&data->values_list, values);
1033
1034 iter = gegl_buffer_iterator_new (context->buffer, area, 0,
1035 data->format,
1036 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
1037
1038 if (context->mask)
1039 {
1040 GeglRectangle mask_area = *area;
1041
1042 mask_area.x += context->mask_rect.x - context->buffer_rect.x;
1043 mask_area.y += context->mask_rect.y - context->buffer_rect.y;
1044
1045 gegl_buffer_iterator_add (iter, context->mask, &mask_area, 0,
1046 babl_format ("Y float"),
1047 GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
1048 }
1049
1050 n_bins_1f = n_bins - 1;
1051
1052 #define VALUE(c,i) (*(temp = (i) * n_bins_1f, \
1053 &values[(c) * n_bins + \
1054 SIGNED_ROUND (SAFE_CLAMP (temp, \
1055 0.0f, \
1056 n_bins_1f))]))
1057
1058 #define CHECK_CANCELED(length) \
1059 G_STMT_START \
1060 { \
1061 if ((length) % 128 == 0 && async && gimp_async_is_canceled (async)) \
1062 { \
1063 gegl_buffer_iterator_stop (iter); \
1064 \
1065 return; \
1066 } \
1067 } \
1068 G_STMT_END
1069
1070 while (gegl_buffer_iterator_next (iter))
1071 {
1072 const gfloat *data = iter->items[0].data;
1073 gint length = iter->length;
1074 gfloat max;
1075 gfloat luminance;
1076
1077 CHECK_CANCELED (0);
1078
1079 if (context->mask)
1080 {
1081 const gfloat *mask_data = iter->items[1].data;
1082
1083 switch (n_components)
1084 {
1085 case 1:
1086 while (length--)
1087 {
1088 const gdouble masked = *mask_data;
1089
1090 VALUE (0, data[0]) += masked;
1091
1092 data += n_components;
1093 mask_data += 1;
1094
1095 CHECK_CANCELED (length);
1096 }
1097 break;
1098
1099 case 2:
1100 while (length--)
1101 {
1102 const gdouble masked = *mask_data;
1103 const gdouble weight = data[1];
1104
1105 VALUE (0, data[0]) += weight * masked;
1106 VALUE (1, data[1]) += masked;
1107
1108 data += n_components;
1109 mask_data += 1;
1110
1111 CHECK_CANCELED (length);
1112 }
1113 break;
1114
1115 case 3: /* calculate separate value values */
1116 while (length--)
1117 {
1118 const gdouble masked = *mask_data;
1119
1120 VALUE (1, data[0]) += masked;
1121 VALUE (2, data[1]) += masked;
1122 VALUE (3, data[2]) += masked;
1123
1124 max = MAX (data[0], data[1]);
1125 max = MAX (data[2], max);
1126 VALUE (0, max) += masked;
1127
1128 luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
1129 VALUE (4, luminance) += masked;
1130
1131 data += n_components;
1132 mask_data += 1;
1133
1134 CHECK_CANCELED (length);
1135 }
1136 break;
1137
1138 case 4: /* calculate separate value values */
1139 while (length--)
1140 {
1141 const gdouble masked = *mask_data;
1142 const gdouble weight = data[3];
1143
1144 VALUE (1, data[0]) += weight * masked;
1145 VALUE (2, data[1]) += weight * masked;
1146 VALUE (3, data[2]) += weight * masked;
1147 VALUE (4, data[3]) += masked;
1148
1149 max = MAX (data[0], data[1]);
1150 max = MAX (data[2], max);
1151 VALUE (0, max) += weight * masked;
1152
1153 luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
1154 VALUE (5, luminance) += weight * masked;
1155
1156 data += n_components;
1157 mask_data += 1;
1158
1159 CHECK_CANCELED (length);
1160 }
1161 break;
1162 }
1163 }
1164 else /* no mask */
1165 {
1166 switch (n_components)
1167 {
1168 case 1:
1169 while (length--)
1170 {
1171 VALUE (0, data[0]) += 1.0;
1172
1173 data += n_components;
1174
1175 CHECK_CANCELED (length);
1176 }
1177 break;
1178
1179 case 2:
1180 while (length--)
1181 {
1182 const gdouble weight = data[1];
1183
1184 VALUE (0, data[0]) += weight;
1185 VALUE (1, data[1]) += 1.0;
1186
1187 data += n_components;
1188
1189 CHECK_CANCELED (length);
1190 }
1191 break;
1192
1193 case 3: /* calculate separate value values */
1194 while (length--)
1195 {
1196 VALUE (1, data[0]) += 1.0;
1197 VALUE (2, data[1]) += 1.0;
1198 VALUE (3, data[2]) += 1.0;
1199
1200 max = MAX (data[0], data[1]);
1201 max = MAX (data[2], max);
1202 VALUE (0, max) += 1.0;
1203
1204 luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
1205 VALUE (4, luminance) += 1.0;
1206
1207 data += n_components;
1208
1209 CHECK_CANCELED (length);
1210 }
1211 break;
1212
1213 case 4: /* calculate separate value values */
1214 while (length--)
1215 {
1216 const gdouble weight = data[3];
1217
1218 VALUE (1, data[0]) += weight;
1219 VALUE (2, data[1]) += weight;
1220 VALUE (3, data[2]) += weight;
1221 VALUE (4, data[3]) += 1.0;
1222
1223 max = MAX (data[0], data[1]);
1224 max = MAX (data[2], max);
1225 VALUE (0, max) += weight;
1226
1227 luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
1228 VALUE (5, luminance) += weight;
1229
1230 data += n_components;
1231
1232 CHECK_CANCELED (length);
1233 }
1234 break;
1235 }
1236 }
1237 }
1238
1239 #undef VALUE
1240 #undef CHECK_CANCELED
1241 }
1242
1243 static void
gimp_histogram_calculate_async_callback(GimpAsync * async,CalculateContext * context)1244 gimp_histogram_calculate_async_callback (GimpAsync *async,
1245 CalculateContext *context)
1246 {
1247 context->histogram->priv->calculate_async = NULL;
1248
1249 if (gimp_async_is_finished (async))
1250 {
1251 gimp_histogram_set_values (context->histogram,
1252 context->n_components, context->n_bins,
1253 context->values);
1254 }
1255
1256 g_object_unref (context->buffer);
1257 if (context->mask)
1258 g_object_unref (context->mask);
1259
1260 g_slice_free (CalculateContext, context);
1261 }
1262