diff --git a/ChangeLog b/ChangeLog index 1a6022cc..b09b8420 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,8 @@ INTERPRETATION_MATRIX etc. - rewrite im_buildlut(), im_identity*(), im_maplut(), im_falsecolour(), im_gammacorrect(), im_histgr(), im_histcum(), im_histnorm(), im_heq(), - im_histnD(), im_histindexed(), im_histspec(), im_invertlut() as classes + im_histnD(), im_histindexed(), im_histspec(), im_invertlut(), im_lhisteq() + as classes - thin vips8 wrapper for im_histplot() - added vips_error_freeze() / vips_error_thaw() - used freeze() / thaw() to stop file format sniffers logging spurious errors diff --git a/TODO b/TODO index 9a6fc9c5..c97bbd5c 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,9 @@ +- is vips_hist_local() correct? it seems to leave white dots everywhere + +- use VipsInterpolate to generate intermediate values in buildlut etc. + + need to pull out columns of input matrix into separate image and expand each + one - use g_log() instead of vips_info() @@ -13,10 +19,6 @@ effect what vips does for open-via-disc, and therefore see >100mb ram use even in open-via-disc mode -- why doesn't this work sequentially? - - vips flip Chicago.png x.png horizontal --vips-progress - - support multiscan jpeg write? see https://github.com/jcupitt/ruby-vips/issues/41 diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index f77c0040..9c3b9d82 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -3445,6 +3445,23 @@ im_histgr( IMAGE *in, IMAGE *out, int bandno ) return( 0 ); } +int +im_lhisteq( VipsImage *in, VipsImage *out, int width, int height ) +{ + VipsImage *x; + + if( vips_hist_local( in, &x, width, height, NULL ) ) + return( -1 ); + + if( im_copy( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} + int im_histnD( VipsImage *in, VipsImage *out, int bins ) { diff --git a/libvips/histogram/Makefile.am b/libvips/histogram/Makefile.am index 184f8b1b..a9c456b4 100644 --- a/libvips/histogram/Makefile.am +++ b/libvips/histogram/Makefile.am @@ -11,10 +11,10 @@ libhistogram_la_SOURCES = \ hist_equal.c \ hist_plot.c \ hist_match.c \ + hist_local.c \ \ hist_dispatch.c \ im_mpercent.c \ - im_lhisteq.c \ im_project.c \ im_stdif.c \ tone.c diff --git a/libvips/histogram/hist_local.c b/libvips/histogram/hist_local.c new file mode 100644 index 00000000..29e95b8a --- /dev/null +++ b/libvips/histogram/hist_local.c @@ -0,0 +1,295 @@ +/* local histogram equalisation + * + * Copyright: 1991, N. Dessipris + * + * Author: N. Dessipris + * Written on: 24/10/1991 + * Modified on : + * 25/1/96 JC + * - rewritten, adapting im_spcor() + * - correct result, 2x faster, partial, simpler, better arg checking + * 8/7/04 + * - expand input rather than output with new im_embed() mode + * - _raw() output is one pixel larger + * - sets Xoffset/Yoffset + * 23/6/08 + * - check for window too small as well + * 25/3/10 + * - gtkdoc + * - small cleanups + * 5/9/13 + * - redo as a class + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +typedef struct _VipsHistLocal { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + + int width; + int height; + +} VipsHistLocal; + +typedef VipsOperationClass VipsHistLocalClass; + +G_DEFINE_TYPE( VipsHistLocal, vips_hist_local, VIPS_TYPE_OPERATION ); + +static int +vips_hist_local_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + const VipsHistLocal *local = (VipsHistLocal *) b; + VipsRect *r = &or->valid; + + VipsRect irect; + int y; + int lsk; + int centre; /* Offset to move to centre of window */ + + /* What part of ir do we need? + */ + irect.left = r->left; + irect.top = r->top; + irect.width = r->width + local->width; + irect.height = r->height + local->height; + if( vips_region_prepare( ir, &irect ) ) + return( -1 ); + + lsk = VIPS_REGION_LSKIP( ir ); + centre = lsk * (local->height / 2) + local->width / 2; + + for( y = 0; y < r->height; y++ ) { + /* Get input and output pointers for this line. + */ + VipsPel *p = VIPS_REGION_ADDR( ir, r->left, r->top + y ); + VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + + VipsPel *p1; + int hist[256]; + int x, i, j; + + /* Find histogram for start of this line. + */ + memset( hist, 0, 256 * sizeof( int ) ); + p1 = p; + for( j = 0; j < local->height; j++ ) { + for( i = 0; i < local->width; i++ ) + hist[p1[i]] += 1; + + p1 += lsk; + } + + /* Loop for output pels. + */ + for( x = 0; x < r->width; x++ ) { + /* Sum histogram up to current pel. + */ + int target = p[centre]; + int sum; + + sum = 0; + for( i = 0; i < target; i++ ) + sum += hist[i]; + + q[x] = 256 * sum / (local->width * local->height); + + /* Adapt histogram --- remove the pels from the left + * hand column, add in pels for a new right-hand + * column. + */ + p1 = p; + for( j = 0; j < local->height; j++ ) { + hist[p1[0]] -= 1; + hist[p1[local->width]] += 1; + + p1 += lsk; + } + + p += 1; + } + } + + return( 0 ); +} + +static int +vips_hist_local_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsHistLocal *local = (VipsHistLocal *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 3 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_hist_local_parent_class )->build( object ) ) + return( -1 ); + + in = local->in; + + if( vips_check_mono( class->nickname, in ) || + vips_check_uncoded( class->nickname, in ) || + vips_check_format( class->nickname, in, VIPS_FORMAT_UCHAR ) ) + return( -1 ); + + if( local->width > in->Xsize || + local->height > in->Ysize ) { + vips_error( class->nickname, "%s", _( "window too large" ) ); + return( -1 ); + } + if( local->width <= 0 || + local->height <= 0 ) { + vips_error( class->nickname, "%s", _( "window too small" ) ); + return( -1 ); + } + + /* Expand the input. + */ + if( vips_embed( in, &t[0], + local->width / 2, local->height / 2, + in->Xsize + local->width - 1, in->Ysize + local->height - 1, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[0]; + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( vips_image_copy_fields( local->out, in ) ) + return( -1 ); + local->out->Xsize -= local->width - 1; + local->out->Ysize -= local->height - 1; + + /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause + * too many recalculations on overlaps. + */ + vips_demand_hint( local->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + + if( vips_image_generate( local->out, + vips_start_one, vips_hist_local_gen, vips_stop_one, + in, local ) ) + return( -1 ); + + local->out->Xoffset = 0; + local->out->Yoffset = 0; + + return( 0 ); +} + +static void +vips_hist_local_class_init( VipsHistLocalClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "hist_local"; + object_class->description = _( "local histogram equalisation" ); + object_class->build = vips_hist_local_build; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistLocal, in ) ); + + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsHistLocal, out ) ); + + VIPS_ARG_INT( class, "width", 4, + _( "Width" ), + _( "Window width in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistLocal, width ), + 1, 1000000, 1 ); + + VIPS_ARG_INT( class, "height", 5, + _( "Height" ), + _( "Window height in pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistLocal, height ), + 1, 1000000, 1 ); +} + +static void +vips_hist_local_init( VipsHistLocal *local ) +{ +} + +/** + * vips_hist_local: + * @in: input image + * @out: output image + * @width: width of region + * @height: height of region + * @...: %NULL-terminated list of optional named arguments + * + * Performs local histogram equalisation on @in using a + * window of size @xwin by @ywin centered on the input pixel. Works only on + * monochrome images. + * + * The output image is the same size as the input image. The edge pixels are + * created by copy edge pixels of the input image outwards. + * + * See also: vips_hist_equal(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_hist_local( VipsImage *in, VipsImage **out, int width, int height, ... ) +{ + va_list ap; + int result; + + va_start( ap, height ); + result = vips_call_split( "hist_local", ap, in, out, width, height ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index 1bb8ad81..f6c395ec 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -238,6 +238,7 @@ vips_histogram_operation_init( void ) extern GType vips_hist_equal_get_type( void ); extern GType vips_hist_plot_get_type( void ); extern GType vips_hist_match_get_type( void ); + extern GType vips_hist_local_get_type( void ); vips_maplut_get_type(); vips_hist_cum_get_type(); @@ -245,4 +246,5 @@ vips_histogram_operation_init( void ) vips_hist_equal_get_type(); vips_hist_plot_get_type(); vips_hist_match_get_type(); + vips_hist_local_get_type(); } diff --git a/libvips/histogram/im_lhisteq.c b/libvips/histogram/im_lhisteq.c deleted file mode 100644 index ebd6ef79..00000000 --- a/libvips/histogram/im_lhisteq.c +++ /dev/null @@ -1,232 +0,0 @@ -/* local histogram equalisation - * - * Copyright: 1991, N. Dessipris - * - * Author: N. Dessipris - * Written on: 24/10/1991 - * Modified on : - * 25/1/96 JC - * - rewritten, adapting im_spcor() - * - correct result, 2x faster, partial, simpler, better arg checking - * 8/7/04 - * - expand input rather than output with new im_embed() mode - * - _raw() output is one pixel larger - * - sets Xoffset/Yoffset - * 23/6/08 - * - check for window too small as well - * 25/3/10 - * - gtkdoc - * - small cleanups - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include - -#include - -/* Hold global stuff here. - */ -typedef struct { - int xwin, ywin; /* Parameters */ - - int npels; /* Pels in window */ -} LhistInfo; - -/* lhist generate function. - */ -static int -lhist_gen( REGION *or, void *seq, void *a, void *b ) -{ - REGION *ir = (REGION *) seq; - LhistInfo *inf = (LhistInfo *) b; - Rect *r = &or->valid; - - Rect irect; - int x, y, i, j; - int lsk; - int centre; /* Offset to move to centre of window */ - - /* What part of ir do we need? - */ - irect.left = or->valid.left; - irect.top = or->valid.top; - irect.width = or->valid.width + inf->xwin; - irect.height = or->valid.height + inf->ywin; - if( im_prepare( ir, &irect ) ) - return( -1 ); - - lsk = IM_REGION_LSKIP( ir ); - centre = lsk * (inf->ywin / 2) + inf->xwin / 2; - - for( y = 0; y < r->height; y++ ) { - /* Get input and output pointers for this line. - */ - VipsPel *p = IM_REGION_ADDR( ir, r->left, r->top + y ); - VipsPel *q = IM_REGION_ADDR( or, r->left, r->top + y ); - - VipsPel *p1; - int hist[256]; - - /* Find histogram for start of this line. - */ - memset( hist, 0, 256 * sizeof( int ) ); - p1 = p; - for( j = 0; j < inf->ywin; j++ ) { - for( i = 0; i < inf->xwin; i++ ) - hist[p1[i]] += 1; - - p1 += lsk; - } - - /* Loop for output pels. - */ - for( x = 0; x < r->width; x++ ) { - /* Sum histogram up to current pel. - */ - int target = p[centre]; - int sum; - - sum = 0; - for( i = 0; i < target; i++ ) - sum += hist[i]; - - /* Transform. - */ - q[x] = sum * 256 / inf->npels; - - /* Adapt histogram - remove the pels from the left hand - * column, add in pels for a new right-hand column. - */ - p1 = p; - for( j = 0; j < inf->ywin; j++ ) { - hist[p1[0]] -= 1; - hist[p1[inf->xwin]] += 1; - - p1 += lsk; - } - - p += 1; - } - } - - return( 0 ); -} - -int -im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ) -{ - LhistInfo *inf; - - if( im_check_mono( "im_lhisteq", in ) || - im_check_uncoded( "im_lhisteq", in ) || - im_check_format( "im_lhisteq", in, IM_BANDFMT_UCHAR ) || - im_piocheck( in, out ) ) - return( -1 ); - if( xwin > in->Xsize || - ywin > in->Ysize ) { - im_error( "im_lhisteq", "%s", _( "window too large" ) ); - return( -1 ); - } - if( xwin <= 0 || - ywin <= 0 ) { - im_error( "im_lhisteq", "%s", _( "window too small" ) ); - return( -1 ); - } - - if( im_cp_desc( out, in ) ) - return( -1 ); - out->Xsize -= xwin - 1; - out->Ysize -= ywin - 1; - - /* Save parameters. - */ - if( !(inf = IM_NEW( out, LhistInfo )) ) - return( -1 ); - inf->xwin = xwin; - inf->ywin = ywin; - inf->npels = xwin * ywin; - - /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause - * too many recalculations on overlaps. - */ - if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) - return( -1 ); - - if( im_generate( out, - im_start_one, lhist_gen, im_stop_one, in, inf ) ) - return( -1 ); - - out->Xoffset = -xwin / 2; - out->Yoffset = -xwin / 2; - - return( 0 ); -} - -/** - * im_lhisteq: - * @in: input image - * @out: output image - * @xwin: width of region - * @ywin: height of region - * - * Performs local histogram equalisation on @in using a - * window of size @xwin by @ywin centered on the input pixel. Works only on - * monochrome images. - * - * The output image is the same size as the input image. The edge pixels are - * created by copy edge pixels of the input image outwards. - * - * See also: im_stdif(), im_heq(). - * - * Returns: 0 on success, -1 on error - */ -int -im_lhisteq( IMAGE *in, IMAGE *out, int xwin, int ywin ) -{ - IMAGE *t1; - - if( !(t1 = im_open_local( out, "im_lhisteq:1", "p" )) || - im_embed( in, t1, 1, - xwin / 2, ywin / 2, - in->Xsize + xwin - 1, in->Ysize + ywin - 1 ) || - im_lhisteq_raw( t1, out, xwin, ywin ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index f4fe3d87..69a50384 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -50,6 +50,9 @@ int vips_hist_plot( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_hist_match( VipsImage *in, VipsImage *ref, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_hist_local( VipsImage *in, VipsImage **out, + int width, int height, ... ) + __attribute__((sentinel)); int im_project( VipsImage *in, VipsImage *hout, VipsImage *vout ); @@ -59,7 +62,6 @@ int im_ismonotonic( VipsImage *lut, int *out ); int im_mpercent( VipsImage *in, double percent, int *out ); int im_mpercent_hist( VipsImage *hist, double percent, int *out ); -int im_lhisteq( VipsImage *in, VipsImage *out, int xwin, int ywin ); int im_stdif( VipsImage *in, VipsImage *out, double a, double m0, double b, double s0, int xwin, int ywin ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 9eca94e3..ba466162 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -863,6 +863,11 @@ int im_hist_indexed( VipsImage *index, VipsImage *value, VipsImage *out ); int im_histplot( VipsImage *in, VipsImage *out ); int im_hsp( VipsImage *in, VipsImage *ref, VipsImage *out ); int im_histspec( VipsImage *in, VipsImage *ref, VipsImage *out ); +int im_lhisteq( VipsImage *in, VipsImage *out, int xwin, int ywin ); + +/* Not really correct, but who uses this. + */ +#define im_lhisteq_raw im_lhisteq /* ruby-vips uses this */