Change GimpHistogram to take a "gboolean linear" parameter and always honor that parameter, so both kinds of histograms can now be created for all drawables. Add a horrible "Linear" toggle to the histogram dockable which always defaults to the active layer's actual pixel format, but can be switched at any time. This UI is ugly and needs to change. On the PDB, default to gamma-corrected if the plug-in is unaware of higher precision, and to the drawable's native pixel format otherwise. Other places using histograms (e.g. levels, curves) are unchanged.
981 lines
28 KiB
C
981 lines
28 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* gimphistogram module Copyright (C) 1999 Jay Cox <jaycox@gimp.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <cairo.h>
|
|
#include <gegl.h>
|
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
|
|
#include "libgimpmath/gimpmath.h"
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "gegl/gimp-babl.h"
|
|
|
|
#include "gimphistogram.h"
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_N_CHANNELS,
|
|
PROP_N_BINS,
|
|
PROP_VALUES
|
|
};
|
|
|
|
struct _GimpHistogramPrivate
|
|
{
|
|
gboolean linear;
|
|
gint n_channels;
|
|
gint n_bins;
|
|
gdouble *values;
|
|
};
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static void gimp_histogram_finalize (GObject *object);
|
|
static void gimp_histogram_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gimp_histogram_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static gint64 gimp_histogram_get_memsize (GimpObject *object,
|
|
gint64 *gui_size);
|
|
|
|
static void gimp_histogram_alloc_values (GimpHistogram *histogram,
|
|
gint n_components,
|
|
gint n_bins);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT)
|
|
|
|
#define parent_class gimp_histogram_parent_class
|
|
|
|
|
|
static void
|
|
gimp_histogram_class_init (GimpHistogramClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = gimp_histogram_finalize;
|
|
object_class->set_property = gimp_histogram_set_property;
|
|
object_class->get_property = gimp_histogram_get_property;
|
|
|
|
gimp_object_class->get_memsize = gimp_histogram_get_memsize;
|
|
|
|
g_object_class_install_property (object_class, PROP_N_CHANNELS,
|
|
g_param_spec_int ("n-channels", NULL, NULL,
|
|
0, 6, 0,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (object_class, PROP_N_BINS,
|
|
g_param_spec_int ("n-bins", NULL, NULL,
|
|
256, 1024, 1024,
|
|
GIMP_PARAM_READABLE));
|
|
|
|
/* this is just for notifications */
|
|
g_object_class_install_property (object_class, PROP_VALUES,
|
|
g_param_spec_boolean ("values", NULL, NULL,
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
|
|
g_type_class_add_private (klass, sizeof (GimpHistogramPrivate));
|
|
}
|
|
|
|
static void
|
|
gimp_histogram_init (GimpHistogram *histogram)
|
|
{
|
|
histogram->priv = G_TYPE_INSTANCE_GET_PRIVATE (histogram,
|
|
GIMP_TYPE_HISTOGRAM,
|
|
GimpHistogramPrivate);
|
|
|
|
histogram->priv->n_bins = 256;
|
|
}
|
|
|
|
static void
|
|
gimp_histogram_finalize (GObject *object)
|
|
{
|
|
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
|
|
|
|
gimp_histogram_clear_values (histogram);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gimp_histogram_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id)
|
|
{
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gimp_histogram_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_N_CHANNELS:
|
|
g_value_set_int (value, histogram->priv->n_channels);
|
|
break;
|
|
|
|
case PROP_N_BINS:
|
|
g_value_set_int (value, histogram->priv->n_bins);
|
|
break;
|
|
|
|
case PROP_VALUES:
|
|
/* return a silly boolean */
|
|
g_value_set_boolean (value, histogram->priv->values != NULL);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gint64
|
|
gimp_histogram_get_memsize (GimpObject *object,
|
|
gint64 *gui_size)
|
|
{
|
|
GimpHistogram *histogram = GIMP_HISTOGRAM (object);
|
|
gint64 memsize = 0;
|
|
|
|
if (histogram->priv->values)
|
|
memsize += (histogram->priv->n_channels *
|
|
histogram->priv->n_bins * sizeof (gdouble));
|
|
|
|
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
|
|
gui_size);
|
|
}
|
|
|
|
/* public functions */
|
|
|
|
GimpHistogram *
|
|
gimp_histogram_new (gboolean linear)
|
|
{
|
|
GimpHistogram *histogram = g_object_new (GIMP_TYPE_HISTOGRAM, NULL);
|
|
|
|
histogram->priv->linear = linear;
|
|
|
|
return histogram;
|
|
}
|
|
|
|
/**
|
|
* gimp_histogram_duplicate:
|
|
* @histogram: a %GimpHistogram
|
|
*
|
|
* Creates a duplicate of @histogram. The duplicate has a reference
|
|
* count of 1 and contains the values from @histogram.
|
|
*
|
|
* Return value: a newly allocated %GimpHistogram
|
|
**/
|
|
GimpHistogram *
|
|
gimp_histogram_duplicate (GimpHistogram *histogram)
|
|
{
|
|
GimpHistogram *dup;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), NULL);
|
|
|
|
dup = gimp_histogram_new (histogram->priv->linear);
|
|
|
|
dup->priv->n_channels = histogram->priv->n_channels;
|
|
dup->priv->n_bins = histogram->priv->n_bins;
|
|
dup->priv->values = g_memdup (histogram->priv->values,
|
|
sizeof (gdouble) *
|
|
dup->priv->n_channels *
|
|
dup->priv->n_bins);
|
|
|
|
return dup;
|
|
}
|
|
|
|
void
|
|
gimp_histogram_calculate (GimpHistogram *histogram,
|
|
GeglBuffer *buffer,
|
|
const GeglRectangle *buffer_rect,
|
|
GeglBuffer *mask,
|
|
const GeglRectangle *mask_rect)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
GeglBufferIterator *iter;
|
|
const Babl *format;
|
|
gint n_components;
|
|
gint n_bins;
|
|
|
|
g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
|
|
g_return_if_fail (GEGL_IS_BUFFER (buffer));
|
|
g_return_if_fail (buffer_rect != NULL);
|
|
|
|
priv = histogram->priv;
|
|
|
|
format = gegl_buffer_get_format (buffer);
|
|
|
|
if (babl_format_get_type (format, 0) == babl_type ("u8"))
|
|
n_bins = 256;
|
|
else
|
|
n_bins = 1024;
|
|
|
|
if (babl_format_is_palette (format))
|
|
{
|
|
if (babl_format_has_alpha (format))
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("RGB float");
|
|
else
|
|
format = babl_format ("R'G'B' float");
|
|
}
|
|
else
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("RGBA float");
|
|
else
|
|
format = babl_format ("R'G'B'A float");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const Babl *model = babl_format_get_model (format);
|
|
|
|
if (model == babl_model ("Y") ||
|
|
model == babl_model ("Y'"))
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("Y float");
|
|
else
|
|
format = babl_format ("Y' float");
|
|
}
|
|
else if (model == babl_model ("YA") ||
|
|
model == babl_model ("Y'A"))
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("YA float");
|
|
else
|
|
format = babl_format ("Y'A float");
|
|
}
|
|
else if (model == babl_model ("RGB") ||
|
|
model == babl_model ("R'G'B'"))
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("RGB float");
|
|
else
|
|
format = babl_format ("R'G'B' float");
|
|
}
|
|
else if (model == babl_model ("RGBA") ||
|
|
model == babl_model ("R'G'B'A"))
|
|
{
|
|
if (priv->linear)
|
|
format = babl_format ("RGBA float");
|
|
else
|
|
format = babl_format ("R'G'B'A float");
|
|
}
|
|
else
|
|
{
|
|
g_return_if_reached ();
|
|
}
|
|
}
|
|
|
|
n_components = babl_format_get_n_components (format);
|
|
|
|
g_object_freeze_notify (G_OBJECT (histogram));
|
|
|
|
gimp_histogram_alloc_values (histogram, n_components, n_bins);
|
|
|
|
iter = gegl_buffer_iterator_new (buffer, buffer_rect, 0, format,
|
|
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
|
|
|
|
if (mask)
|
|
gegl_buffer_iterator_add (iter, mask, mask_rect, 0,
|
|
babl_format ("Y float"),
|
|
GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
|
|
|
|
#define VALUE(c,i) (priv->values[(c) * priv->n_bins + \
|
|
(gint) (CLAMP ((i), 0.0, 1.0) * \
|
|
(priv->n_bins - 0.0001))])
|
|
|
|
while (gegl_buffer_iterator_next (iter))
|
|
{
|
|
const gfloat *data = iter->data[0];
|
|
gint length = iter->length;
|
|
gfloat max;
|
|
gfloat luminance;
|
|
|
|
if (mask)
|
|
{
|
|
const gfloat *mask_data = iter->data[1];
|
|
|
|
switch (n_components)
|
|
{
|
|
case 1:
|
|
while (length--)
|
|
{
|
|
const gdouble masked = *mask_data;
|
|
|
|
VALUE (0, data[0]) += masked;
|
|
|
|
data += n_components;
|
|
mask_data += 1;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
while (length--)
|
|
{
|
|
const gdouble masked = *mask_data;
|
|
const gdouble weight = data[1];
|
|
|
|
VALUE (0, data[0]) += weight * masked;
|
|
VALUE (1, data[1]) += masked;
|
|
|
|
data += n_components;
|
|
mask_data += 1;
|
|
}
|
|
break;
|
|
|
|
case 3: /* calculate separate value values */
|
|
while (length--)
|
|
{
|
|
const gdouble masked = *mask_data;
|
|
|
|
VALUE (1, data[0]) += masked;
|
|
VALUE (2, data[1]) += masked;
|
|
VALUE (3, data[2]) += masked;
|
|
|
|
max = MAX (data[0], data[1]);
|
|
max = MAX (data[2], max);
|
|
VALUE (0, max) += masked;
|
|
|
|
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
|
|
VALUE (4, luminance) += masked;
|
|
|
|
data += n_components;
|
|
mask_data += 1;
|
|
}
|
|
break;
|
|
|
|
case 4: /* calculate separate value values */
|
|
while (length--)
|
|
{
|
|
const gdouble masked = *mask_data;
|
|
const gdouble weight = data[3];
|
|
|
|
VALUE (1, data[0]) += weight * masked;
|
|
VALUE (2, data[1]) += weight * masked;
|
|
VALUE (3, data[2]) += weight * masked;
|
|
VALUE (4, data[3]) += masked;
|
|
|
|
max = MAX (data[0], data[1]);
|
|
max = MAX (data[2], max);
|
|
VALUE (0, max) += weight * masked;
|
|
|
|
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
|
|
VALUE (5, luminance) += weight * masked;
|
|
|
|
data += n_components;
|
|
mask_data += 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else /* no mask */
|
|
{
|
|
switch (n_components)
|
|
{
|
|
case 1:
|
|
while (length--)
|
|
{
|
|
VALUE (0, data[0]) += 1.0;
|
|
|
|
data += n_components;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
while (length--)
|
|
{
|
|
const gdouble weight = data[1];
|
|
|
|
VALUE (0, data[0]) += weight;
|
|
VALUE (1, data[1]) += 1.0;
|
|
|
|
data += n_components;
|
|
}
|
|
break;
|
|
|
|
case 3: /* calculate separate value values */
|
|
while (length--)
|
|
{
|
|
VALUE (1, data[0]) += 1.0;
|
|
VALUE (2, data[1]) += 1.0;
|
|
VALUE (3, data[2]) += 1.0;
|
|
|
|
max = MAX (data[0], data[1]);
|
|
max = MAX (data[2], max);
|
|
VALUE (0, max) += 1.0;
|
|
|
|
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
|
|
VALUE (4, luminance) += 1.0;
|
|
|
|
data += n_components;
|
|
}
|
|
break;
|
|
|
|
case 4: /* calculate separate value values */
|
|
while (length--)
|
|
{
|
|
const gdouble weight = data[3];
|
|
|
|
VALUE (1, data[0]) += weight;
|
|
VALUE (2, data[1]) += weight;
|
|
VALUE (3, data[2]) += weight;
|
|
VALUE (4, data[3]) += 1.0;
|
|
|
|
max = MAX (data[0], data[1]);
|
|
max = MAX (data[2], max);
|
|
VALUE (0, max) += weight;
|
|
|
|
luminance = GIMP_RGB_LUMINANCE (data[0], data[1], data[2]);
|
|
VALUE (5, luminance) += weight;
|
|
|
|
data += n_components;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_object_notify (G_OBJECT (histogram), "values");
|
|
|
|
g_object_thaw_notify (G_OBJECT (histogram));
|
|
|
|
#undef VALUE
|
|
}
|
|
|
|
void
|
|
gimp_histogram_clear_values (GimpHistogram *histogram)
|
|
{
|
|
g_return_if_fail (GIMP_IS_HISTOGRAM (histogram));
|
|
|
|
if (histogram->priv->values)
|
|
{
|
|
g_free (histogram->priv->values);
|
|
histogram->priv->values = NULL;
|
|
|
|
g_object_notify (G_OBJECT (histogram), "values");
|
|
}
|
|
|
|
if (histogram->priv->n_channels)
|
|
{
|
|
histogram->priv->n_channels = 0;
|
|
|
|
g_object_notify (G_OBJECT (histogram), "n-channels");
|
|
}
|
|
}
|
|
|
|
|
|
#define HISTOGRAM_VALUE(c,i) (priv->values[(c) * priv->n_bins + (i)])
|
|
|
|
|
|
gdouble
|
|
gimp_histogram_get_maximum (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gdouble max = 0.0;
|
|
gint x;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0.0;
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
for (x = 0; x < priv->n_bins; x++)
|
|
{
|
|
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, x));
|
|
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, x));
|
|
max = MAX (max, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, x));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (x = 0; x < priv->n_bins; x++)
|
|
{
|
|
max = MAX (max, HISTOGRAM_VALUE (channel, x));
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_value (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint bin)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
bin < 0 || bin >= priv->n_bins ||
|
|
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 4) ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0.0;
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
gdouble min = HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, bin);
|
|
|
|
min = MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, bin));
|
|
|
|
return MIN (min, HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, bin));
|
|
}
|
|
else
|
|
{
|
|
return HISTOGRAM_VALUE (channel, bin);
|
|
}
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_component (GimpHistogram *histogram,
|
|
gint component,
|
|
gint bin)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
if (histogram->priv->n_channels > 4)
|
|
component++;
|
|
|
|
return gimp_histogram_get_value (histogram, component, bin);
|
|
}
|
|
|
|
gint
|
|
gimp_histogram_n_channels (GimpHistogram *histogram)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
|
|
|
|
return histogram->priv->n_channels - 2;
|
|
}
|
|
|
|
gint
|
|
gimp_histogram_n_bins (GimpHistogram *histogram)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0);
|
|
|
|
return histogram->priv->n_bins;
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_count (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint start,
|
|
gint end)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gint i;
|
|
gdouble count = 0.0;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
return (gimp_histogram_get_count (histogram,
|
|
GIMP_HISTOGRAM_RED, start, end) +
|
|
gimp_histogram_get_count (histogram,
|
|
GIMP_HISTOGRAM_GREEN, start, end) +
|
|
gimp_histogram_get_count (histogram,
|
|
GIMP_HISTOGRAM_BLUE, start, end));
|
|
|
|
if (! priv->values ||
|
|
start > end ||
|
|
channel >= priv->n_channels)
|
|
return 0.0;
|
|
|
|
start = CLAMP (start, 0, priv->n_bins - 1);
|
|
end = CLAMP (end, 0, priv->n_bins - 1);
|
|
|
|
for (i = start; i <= end; i++)
|
|
count += HISTOGRAM_VALUE (channel, i);
|
|
|
|
return count;
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_mean (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint start,
|
|
gint end)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gint i;
|
|
gdouble mean = 0.0;
|
|
gdouble count;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
start > end ||
|
|
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0.0;
|
|
|
|
start = CLAMP (start, 0, priv->n_bins - 1);
|
|
end = CLAMP (end, 0, priv->n_bins - 1);
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
{
|
|
gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
|
|
|
|
mean += (factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
|
|
factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
|
|
factor * HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
{
|
|
gdouble factor = (gdouble) i / (gdouble) (priv->n_bins - 1);
|
|
|
|
mean += factor * HISTOGRAM_VALUE (channel, i);
|
|
}
|
|
}
|
|
|
|
count = gimp_histogram_get_count (histogram, channel, start, end);
|
|
|
|
if (count > 0.0)
|
|
return mean / count;
|
|
|
|
return mean;
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_median (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint start,
|
|
gint end)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gint i;
|
|
gdouble sum = 0.0;
|
|
gdouble count;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
start > end ||
|
|
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0.0;
|
|
|
|
start = CLAMP (start, 0, priv->n_bins - 1);
|
|
end = CLAMP (end, 0, priv->n_bins - 1);
|
|
|
|
count = gimp_histogram_get_count (histogram, channel, start, end);
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
{
|
|
sum += (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
|
|
|
|
if (sum * 2 > count)
|
|
return ((gdouble) i / (gdouble) (priv->n_bins - 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
{
|
|
sum += HISTOGRAM_VALUE (channel, i);
|
|
|
|
if (sum * 2 > count)
|
|
return ((gdouble) i / (gdouble) (priv->n_bins - 1));
|
|
}
|
|
}
|
|
|
|
return -1.0;
|
|
}
|
|
|
|
/*
|
|
* adapted from GNU ocrad 0.14 : page_image_io.cc : otsu_th
|
|
*
|
|
* N. Otsu, "A threshold selection method from gray-level histograms,"
|
|
* IEEE Trans. Systems, Man, and Cybernetics, vol. 9, no. 1, pp. 62-66, 1979.
|
|
*/
|
|
gdouble
|
|
gimp_histogram_get_threshold (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint start,
|
|
gint end)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gint i;
|
|
gint maxval;
|
|
gdouble *hist = NULL;
|
|
gdouble *chist = NULL;
|
|
gdouble *cmom = NULL;
|
|
gdouble hist_max = 0.0;
|
|
gdouble chist_max = 0.0;
|
|
gdouble cmom_max = 0.0;
|
|
gdouble bvar_max = 0.0;
|
|
gint threshold = 127;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), -1);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
start > end ||
|
|
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0;
|
|
|
|
start = CLAMP (start, 0, priv->n_bins - 1);
|
|
end = CLAMP (end, 0, priv->n_bins - 1);
|
|
|
|
maxval = end - start;
|
|
|
|
hist = g_newa (gdouble, maxval + 1);
|
|
chist = g_newa (gdouble, maxval + 1);
|
|
cmom = g_newa (gdouble, maxval + 1);
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
hist[i - start] = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
|
|
}
|
|
else
|
|
{
|
|
for (i = start; i <= end; i++)
|
|
hist[i - start] = HISTOGRAM_VALUE (channel, i);
|
|
}
|
|
|
|
hist_max = hist[0];
|
|
chist[0] = hist[0];
|
|
cmom[0] = 0;
|
|
|
|
for (i = 1; i <= maxval; i++)
|
|
{
|
|
if (hist[i] > hist_max)
|
|
hist_max = hist[i];
|
|
|
|
chist[i] = chist[i-1] + hist[i];
|
|
cmom[i] = cmom[i-1] + i * hist[i];
|
|
}
|
|
|
|
chist_max = chist[maxval];
|
|
cmom_max = cmom[maxval];
|
|
bvar_max = 0;
|
|
|
|
for (i = 0; i < maxval; ++i)
|
|
{
|
|
if (chist[i] > 0 && chist[i] < chist_max)
|
|
{
|
|
gdouble bvar;
|
|
|
|
bvar = (gdouble) cmom[i] / chist[i];
|
|
bvar -= (cmom_max - cmom[i]) / (chist_max - chist[i]);
|
|
bvar *= bvar;
|
|
bvar *= chist[i];
|
|
bvar *= chist_max - chist[i];
|
|
|
|
if (bvar > bvar_max)
|
|
{
|
|
bvar_max = bvar;
|
|
threshold = start + i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return threshold;
|
|
}
|
|
|
|
gdouble
|
|
gimp_histogram_get_std_dev (GimpHistogram *histogram,
|
|
GimpHistogramChannel channel,
|
|
gint start,
|
|
gint end)
|
|
{
|
|
GimpHistogramPrivate *priv;
|
|
gint i;
|
|
gdouble dev = 0.0;
|
|
gdouble count;
|
|
gdouble mean;
|
|
|
|
g_return_val_if_fail (GIMP_IS_HISTOGRAM (histogram), 0.0);
|
|
|
|
priv = histogram->priv;
|
|
|
|
/* the gray alpha channel is in slot 1 */
|
|
if (priv->n_channels == 4 && channel == GIMP_HISTOGRAM_ALPHA)
|
|
channel = 1;
|
|
/* the luminance channel is in slot 4 */
|
|
else if (priv->n_channels == 5 && channel == GIMP_HISTOGRAM_LUMINANCE)
|
|
channel = 4;
|
|
|
|
if (! priv->values ||
|
|
start > end ||
|
|
(channel == GIMP_HISTOGRAM_RGB && priv->n_channels < 5) ||
|
|
(channel != GIMP_HISTOGRAM_RGB && channel >= priv->n_channels))
|
|
return 0.0;
|
|
|
|
mean = gimp_histogram_get_mean (histogram, channel, start, end);
|
|
count = gimp_histogram_get_count (histogram, channel, start, end);
|
|
|
|
if (count == 0.0)
|
|
count = 1.0;
|
|
|
|
for (i = start; i <= end; i++)
|
|
{
|
|
gdouble value;
|
|
|
|
if (channel == GIMP_HISTOGRAM_RGB)
|
|
{
|
|
value = (HISTOGRAM_VALUE (GIMP_HISTOGRAM_RED, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_GREEN, i) +
|
|
HISTOGRAM_VALUE (GIMP_HISTOGRAM_BLUE, i));
|
|
}
|
|
else
|
|
{
|
|
value = gimp_histogram_get_value (histogram, channel, i);
|
|
}
|
|
|
|
dev += value * SQR (((gdouble) i / (gdouble) (priv->n_bins - 1)) - mean);
|
|
}
|
|
|
|
return sqrt (dev / count);
|
|
}
|
|
|
|
|
|
/* private functions */
|
|
|
|
static void
|
|
gimp_histogram_alloc_values (GimpHistogram *histogram,
|
|
gint n_components,
|
|
gint n_bins)
|
|
{
|
|
GimpHistogramPrivate *priv = histogram->priv;
|
|
|
|
if (n_components + 2 != priv->n_channels ||
|
|
n_bins != priv->n_bins)
|
|
{
|
|
gimp_histogram_clear_values (histogram);
|
|
|
|
if (n_components + 2 != priv->n_channels)
|
|
{
|
|
priv->n_channels = n_components + 2;
|
|
|
|
g_object_notify (G_OBJECT (histogram), "n-channels");
|
|
}
|
|
|
|
if (n_bins != priv->n_bins)
|
|
{
|
|
priv->n_bins = n_bins;
|
|
|
|
g_object_notify (G_OBJECT (histogram), "n-bins");
|
|
}
|
|
|
|
priv->values = g_new0 (gdouble, priv->n_channels * priv->n_bins);
|
|
}
|
|
else
|
|
{
|
|
memset (priv->values, 0,
|
|
priv->n_channels * priv->n_bins * sizeof (gdouble));
|
|
}
|
|
}
|