From ae37c8eb6f2165c383d0cf130f83b6cf48324cf9 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 15 Oct 2013 21:21:35 +0100 Subject: [PATCH 01/26] start rot45 --- libvips/conversion/rot.c | 2 +- libvips/conversion/rot45.c | 268 +++++++++++++++++++++++++++++++++++ libvips/include/vips/error.h | 1 + libvips/iofuncs/error.c | 26 ++++ 4 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 libvips/conversion/rot45.c diff --git a/libvips/conversion/rot.c b/libvips/conversion/rot.c index 2288e563..19de6b85 100644 --- a/libvips/conversion/rot.c +++ b/libvips/conversion/rot.c @@ -1,4 +1,4 @@ -/* im_rot90 +/* rotate by 0/90/180/270 degrees * * Copyright: 1991, N. Dessipris * Written on: 28/10/91 diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c new file mode 100644 index 00000000..e75420a5 --- /dev/null +++ b/libvips/conversion/rot45.c @@ -0,0 +1,268 @@ +/* 'lossless' 45 degree rot45ate ... odd-sized square images only + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: 28/05/1991 + * 12/10/95 JC + * - small revisions, needs rewriting really + * 7/8/96 JC + * - absolutely foul desp code revised + * - many bugs and mem leaks fixed + * 1/3/99 JC + * - oops, fns were not preserving scale and offset + * 1/12/10 + * - allow any size mask for the 90 degree rot45ates by using im_rot4590(). + * 12/10/13 + * - redone as a class from im_offsets45() + */ + +/* + + 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "pconversion.h" + +typedef struct _VipsRot45 { + VipsConversion parent_instance; + + /* The input image. + */ + VipsImage *in; + + /* Rotate by ... + */ + VipsAngle45 angle; + + /* Output memory buffer ... copy this to ->out. + */ + VipsImage *outbuf; + +} VipsRot45; + +typedef VipsConversionClass VipsRot45Class; + +G_DEFINE_TYPE( VipsRot45, vips_rot45, VIPS_TYPE_CONVERSION ); + + +/* Creates the offsets to rotate by 45 degrees an odd size square mask + */ +int * +im_offsets45( int size ) +{ + int temp; + int x, y; + int size2 = size * size; + int size_2 = size / 2; + int *pnt, *cpnt1, *cpnt2; + + if( size%2 == 0 ) { + im_error( "im_offsets45", "%s", _( "size not odd" ) ); + return( NULL ); + } + if( !(pnt = IM_ARRAY( NULL, size2, int )) ) + return( NULL ); + + /* point at the beginning and end of the buffer + */ + cpnt1 = pnt; cpnt2 = pnt + size2 - 1; + + for( y = 0; y < size_2; y++ ) { + temp = (size_2 + y) * size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + + for( x = 0; x < y; x++ ) { + temp -= (size-1); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp -= size; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < size_2 - y; x++ ) { + temp++; + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + + for( x = 0; x < y; x++ ) { + temp -= ( size - 1 ); + *cpnt1++ = temp; + *cpnt2-- = size2 - 1 - temp; + } + } + + /* the diagonal now + */ + temp = size * (size - 1); + cpnt1 = pnt + size_2 * size; + for( x = 0; x < size; x++ ) { + *cpnt1++ = temp; + temp -= (size-1); + } + +#ifdef PIM_RINT + temp = 0; + for( y = 0; y < size; y++ ) { + for( x = 0; x < size; x++ ) { + fprintf( stderr, "%4d", *(pnt+temp) ); + temp++; + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); +#endif + + return( pnt ); +} + +static void +vips_rot45_45( VipsRot45 *rot45 ) +{ + int x, y, i; + + +} + +static int +vips_rot45_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsRot45 *rot45 = (VipsRot *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); + + if( VIPS_OBJECT_CLASS( vips_rot45_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_oddsquare( class->nickname, rot45->in ) ) + return( -1 ); + + if( rot45->angle == VIPS_ANGLE45_0 ) + return( vips_image_write( rot45->in, conversion->out ) ); + + if( vips_image_wio_input( rot45->in ) ) + return( -1 ); + + if( vips_image_copy_fields( conversion->out, rot45->in ) ) + return( -1 ); + + switch( rot45->angle ) { + case VIPS_ANGLE45_45: + break; + + default: + g_assert( 0 ); + + /* Keep -Wall happy. + */ + return( 0 ); + } + + return( 0 ); +} + +static void +vips_rot45_class_init( VipsRot45Class *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_rot45_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "rot45"; + vobject_class->description = _( "rotate an image" ); + vobject_class->build = vips_rot45_build; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRot45, in ) ); + + VIPS_ARG_ENUM( class, "angle", 6, + _( "Angle" ), + _( "Angle to rotate image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRot45, angle ), + VIPS_TYPE_ANGLE45, VIPS_ANGLE_45 ); +} + +static void +vips_rot45_init( VipsRot45 *rot45 ) +{ +} + +/** + * vips_rot45: + * @in: input image + * @out: output image + * @angle: rotation angle + * @...: %NULL-terminated list of optional named arguments + * + * Rotate @in by a multiple of 45 degrees. Odd-length sides and square images + * only. + * + * See also: vips_rot(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_rot45( VipsImage *in, VipsImage **out, VipsAngle angle, ... ) +{ + va_list ap; + int result; + + va_start( ap, angle ); + result = vips_call_split( "rot45", ap, in, out, angle ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/error.h b/libvips/include/vips/error.h index 6873547f..cba8dca6 100644 --- a/libvips/include/vips/error.h +++ b/libvips/include/vips/error.h @@ -83,6 +83,7 @@ int vips_check_8or16( const char *domain, VipsImage *im ); int vips_check_u8or16orf( const char *domain, VipsImage *im ); int vips_check_format_same( const char *domain, VipsImage *im1, VipsImage *im2 ); int vips_check_size_same( const char *domain, VipsImage *im1, VipsImage *im2 ); +int vips_check_oddsquare( const char *domain, VipsImage *im ); int vips_check_vector_length( const char *domain, int n, int len ); int vips_check_vector( const char *domain, int n, VipsImage *im ); int vips_check_hist( const char *domain, VipsImage *im ); diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index cdfebe3b..ab272f59 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -1012,6 +1012,32 @@ vips_check_size_same( const char *domain, VipsImage *im1, VipsImage *im2 ) return( 0 ); } +/** + * vips_check_oddsquare: + * @domain: the originating domain for the error message + * @im: image to check + * + * Check that the image is square and that the sides are odd. + * If not, set an error message + * and return non-zero. + * + * See also: vips_error(). + * + * Returns: 0 if OK, -1 otherwise. + */ +int +vips_check_oddsquare( const char *domain, VipsImage *im ) +{ + if( im->Xsize != im->Ysize || + im->Xsize % 2 != 0 ) { + vips_error( domain, + "%s", _( "images must be odd and square" ) ); + return( -1 ); + } + + return( 0 ); +} + /** * vips_check_bands_same: * @domain: the originating domain for the error message From 67dff74b2cd82f181d996e2788a9216eb82033f2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 19 Oct 2013 18:26:46 +0100 Subject: [PATCH 02/26] rot45 works --- configure.ac | 12 +- libvips/conversion/Makefile.am | 1 + libvips/conversion/conversion.c | 2 + libvips/conversion/rot45.c | 226 ++++++++++++++++++------------ libvips/include/vips/conversion.h | 14 ++ libvips/include/vips/enumtypes.h | 2 + libvips/iofuncs/enumtypes.c | 24 ++++ libvips/iofuncs/error.c | 2 +- libvips/iofuncs/image.c | 6 +- tools/Makefile.am | 4 +- tools/{vips-7.36 => vips-7.37} | 0 11 files changed, 194 insertions(+), 99 deletions(-) rename tools/{vips-7.36 => vips-7.37} (100%) diff --git a/configure.ac b/configure.ac index 3cb0e2c3..e559dcb3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [7.36.3], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [7.37.0], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -16,8 +16,8 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [7]) -m4_define([vips_minor_version], [36]) -m4_define([vips_micro_version], [3]) +m4_define([vips_minor_version], [37]) +m4_define([vips_micro_version], [0]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -36,9 +36,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes backwards compatible?: increment age # binary interface changes not backwards compatible?: reset age to 0 -LIBRARY_CURRENT=35 -LIBRARY_REVISION=3 -LIBRARY_AGE=0 +LIBRARY_CURRENT=36 +LIBRARY_REVISION=0 +LIBRARY_AGE=1 # patched into include/vips/version.h AC_SUBST(VIPS_VERSION) diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index f0ab2221..e7240f46 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -23,6 +23,7 @@ libconversion_la_SOURCES = \ bandary.h \ bandary.c \ rot.c \ + rot45.c \ ifthenelse.c \ falsecolour.c \ msb.c \ diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 4b00a8eb..af1c07b3 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -212,6 +212,7 @@ vips_conversion_operation_init( void ) extern GType vips_bandjoin_get_type( void ); extern GType vips_black_get_type( void ); extern GType vips_rot_get_type( void ); + extern GType vips_rot45_get_type( void ); extern GType vips_ifthenelse_get_type( void ); extern GType vips_recomb_get_type( void ); extern GType vips_bandmean_get_type( void ); @@ -247,6 +248,7 @@ vips_conversion_operation_init( void ) vips_bandjoin_get_type(); vips_black_get_type(); vips_rot_get_type(); + vips_rot45_get_type(); vips_ifthenelse_get_type(); vips_recomb_get_type(); vips_bandmean_get_type(); diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index e75420a5..bc47750a 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -1,4 +1,4 @@ -/* 'lossless' 45 degree rot45ate ... odd-sized square images only +/* 'lossless' 45 degree rotate ... odd-sized square images only * * Author: N. Dessipris (Copyright, N. Dessipris 1991) * Written on: 08/05/1991 @@ -13,7 +13,7 @@ * 1/12/10 * - allow any size mask for the 90 degree rot45ates by using im_rot4590(). * 12/10/13 - * - redone as a class from im_offsets45() + * - rewritten as a class */ /* @@ -73,99 +73,115 @@ typedef struct _VipsRot45 { */ VipsAngle45 angle; - /* Output memory buffer ... copy this to ->out. - */ - VipsImage *outbuf; - } VipsRot45; typedef VipsConversionClass VipsRot45Class; G_DEFINE_TYPE( VipsRot45, vips_rot45, VIPS_TYPE_CONVERSION ); - -/* Creates the offsets to rotate by 45 degrees an odd size square mask - */ -int * -im_offsets45( int size ) -{ - int temp; - int x, y; - int size2 = size * size; - int size_2 = size / 2; - int *pnt, *cpnt1, *cpnt2; - - if( size%2 == 0 ) { - im_error( "im_offsets45", "%s", _( "size not odd" ) ); - return( NULL ); - } - if( !(pnt = IM_ARRAY( NULL, size2, int )) ) - return( NULL ); - - /* point at the beginning and end of the buffer - */ - cpnt1 = pnt; cpnt2 = pnt + size2 - 1; - - for( y = 0; y < size_2; y++ ) { - temp = (size_2 + y) * size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - - for( x = 0; x < y; x++ ) { - temp -= (size-1); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp -= size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp++; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < y; x++ ) { - temp -= ( size - 1 ); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - } - - /* the diagonal now - */ - temp = size * (size - 1); - cpnt1 = pnt + size_2 * size; - for( x = 0; x < size; x++ ) { - *cpnt1++ = temp; - temp -= (size-1); - } - -#ifdef PIM_RINT - temp = 0; - for( y = 0; y < size; y++ ) { - for( x = 0; x < size; x++ ) { - fprintf( stderr, "%4d", *(pnt+temp) ); - temp++; - } - fprintf(stderr, "\n"); - } - fprintf(stderr, "\n"); -#endif - - return( pnt ); +#define ASSIGN( Xout, Yout, Xin, Yin ) { \ + VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ + VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ + int b;\ + \ + for( b = 0; b < ps; b++ )\ + q[b] = p[b];\ } +#define POINT_TO_TEMP( q, Xin, Yin ) { \ + VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ + int b;\ + \ + for( b = 0; b < ps; b++ )\ + q[b] = p[b];\ +} + +#define TEMP_TO_POINT( Xout, Yout, p ) { \ + VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ + int b;\ + \ + for( b = 0; b < ps; b++ )\ + q[b] = p[b];\ +} + +/* This can work inplace, ie. in == out is allowed. + */ static void -vips_rot45_45( VipsRot45 *rot45 ) +vips_rot45_rot45( VipsImage *out, VipsImage *in ) { - int x, y, i; + size_t ps = VIPS_IMAGE_SIZEOF_PEL( in ); + VipsPel *temp = VIPS_ARRAY( in, ps, VipsPel ); + int size = in->Xsize; + int size_2 = size / 2; + int x, y; + g_assert( in->Xsize == in->Ysize ); + g_assert( out->Xsize == out->Ysize ); + g_assert( in->Xsize == out->Ssize ); + g_assert( in->Xsize % 2 == 0 ); + + /* Split the square into 8 triangles. Loop over the top-left one, + * reflect into the others. + * + * 1 1 2 2 3 + * 8 1 2 3 3 + * 8 8 x 4 4 + * 7 7 6 5 4 + * 7 6 6 5 5 + * + * do the centre separately. + */ + + for( y = 0; y < size_2; y++ ) + for( x = y; x < size_2; x++ ) { + /* Save 1, it goes into 8 at the end. + */ + POINT_TO_TEMP( temp, x, y ); + + /* Fill 1 from 2. + */ + ASSIGN( x, y, + (x - y) + size_2, y ); + + /* 2 from 3. + */ + ASSIGN( (x - y) + size_2, y, + (size - 1) - y, x ); + + /* 3 from 4. + */ + ASSIGN( (size - 1) - y, x, + (size - 1) - y, (x - y) + size_2 ); + + /* 4 from 5. + */ + ASSIGN( (size - 1) - y, (x - y) + size_2, + (size - 1) - x, (size - 1) - y ); + + /* 5 from 6. + */ + ASSIGN( (size - 1) - x, (size - 1) - y, + size_2 - (x - y), (size - 1) - y ); + + /* 6 from 7. + */ + ASSIGN( size_2 - (x - y), (size - 1) - y, + y, (size - 1) - x ); + + /* 7 from 8. + */ + ASSIGN( y, (size - 1) - x, + y, size_2 - (x - y) ); + + /* 8 from saved 1. + */ + TEMP_TO_POINT( y, size_2 - (x - y), temp ); + } + + /* Centre. + */ + ASSIGN( size_2, size_2, size_2, size_2 ); } static int @@ -173,9 +189,11 @@ vips_rot45_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsConversion *conversion = VIPS_CONVERSION( object ); - VipsRot45 *rot45 = (VipsRot *) object; + VipsRot45 *rot45 = (VipsRot45 *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); + VipsImage *from; + if( VIPS_OBJECT_CLASS( vips_rot45_parent_class )->build( object ) ) return( -1 ); @@ -188,11 +206,41 @@ vips_rot45_build( VipsObject *object ) if( vips_image_wio_input( rot45->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, rot45->in ) ) + t[0] = vips_image_new_buffer(); + if( vips_image_copy_fields( t[0], rot45->in ) || + vips_image_write_prepare( t[0] ) ) return( -1 ); + from = rot45->in; + switch( rot45->angle ) { + case VIPS_ANGLE45_315: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_270: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_225: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_180: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_135: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_90: + vips_rot45_rot45( t[0], from ); + from = t[0]; + case VIPS_ANGLE45_45: + vips_rot45_rot45( t[0], from ); + from = t[0]; break; default: @@ -203,6 +251,9 @@ vips_rot45_build( VipsObject *object ) return( 0 ); } + if( vips_image_write( t[0], conversion->out ) ) + return( -1 ); + return( 0 ); } @@ -232,12 +283,13 @@ vips_rot45_class_init( VipsRot45Class *class ) _( "Angle to rotate image" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsRot45, angle ), - VIPS_TYPE_ANGLE45, VIPS_ANGLE_45 ); + VIPS_TYPE_ANGLE45, VIPS_ANGLE45_45 ); } static void vips_rot45_init( VipsRot45 *rot45 ) { + rot45->angle = VIPS_ANGLE45_45; } /** @@ -255,7 +307,7 @@ vips_rot45_init( VipsRot45 *rot45 ) * Returns: 0 on success, -1 on error */ int -vips_rot45( VipsImage *in, VipsImage **out, VipsAngle angle, ... ) +vips_rot45( VipsImage *in, VipsImage **out, VipsAngle45 angle, ... ) { va_list ap; int result; diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index f2c891f6..8164ac4c 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -69,6 +69,18 @@ typedef enum { VIPS_ANGLE_LAST } VipsAngle; +typedef enum { + VIPS_ANGLE45_0, + VIPS_ANGLE45_45, + VIPS_ANGLE45_90, + VIPS_ANGLE45_135, + VIPS_ANGLE45_180, + VIPS_ANGLE45_225, + VIPS_ANGLE45_270, + VIPS_ANGLE45_315, + VIPS_ANGLE45_LAST +} VipsAngle45; + int vips_copy( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_tilecache( VipsImage *in, VipsImage **out, ... ) @@ -107,6 +119,8 @@ int vips_wrap( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_rot( VipsImage *in, VipsImage **out, VipsAngle angle, ... ) __attribute__((sentinel)); +int vips_rot45( VipsImage *in, VipsImage **out, VipsAngle45 angle, ... ) + __attribute__((sentinel)); int vips_zoom( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) __attribute__((sentinel)); int vips_subsample( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 0655a2ae..fa619dfb 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -47,6 +47,8 @@ GType vips_align_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ALIGN (vips_align_get_type()) GType vips_angle_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ANGLE (vips_angle_get_type()) +GType vips_angle45_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_ANGLE45 (vips_angle45_get_type()) /* enumerations from "../../../libvips/include/vips/util.h" */ GType vips_token_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_TOKEN (vips_token_get_type()) diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 1ec1cca0..2f7a0873 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -223,6 +223,30 @@ vips_angle_get_type( void ) return( etype ); } +GType +vips_angle45_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_ANGLE45_0, "VIPS_ANGLE45_0", "0"}, + {VIPS_ANGLE45_45, "VIPS_ANGLE45_45", "45"}, + {VIPS_ANGLE45_90, "VIPS_ANGLE45_90", "90"}, + {VIPS_ANGLE45_135, "VIPS_ANGLE45_135", "135"}, + {VIPS_ANGLE45_180, "VIPS_ANGLE45_180", "180"}, + {VIPS_ANGLE45_225, "VIPS_ANGLE45_225", "225"}, + {VIPS_ANGLE45_270, "VIPS_ANGLE45_270", "270"}, + {VIPS_ANGLE45_315, "VIPS_ANGLE45_315", "315"}, + {VIPS_ANGLE45_LAST, "VIPS_ANGLE45_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsAngle45", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/arithmetic.h" */ GType vips_operation_math_get_type( void ) diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index ab272f59..e9806801 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -1029,7 +1029,7 @@ int vips_check_oddsquare( const char *domain, VipsImage *im ) { if( im->Xsize != im->Ysize || - im->Xsize % 2 != 0 ) { + im->Xsize % 2 == 0 ) { vips_error( domain, "%s", _( "images must be odd and square" ) ); return( -1 ); diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 386415b1..4775d554 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -2358,7 +2358,7 @@ int vips__image_wio_output( VipsImage *image ) { #ifdef DEBUG_IO - printf( "vips_image_wio_output: WIO output for %s\n", + printf( "vips__image_wio_output: WIO output for %s\n", image->filename ); #endif/*DEBUG_IO*/ @@ -2367,7 +2367,7 @@ vips__image_wio_output( VipsImage *image ) /* Make sure nothing is attached. */ if( image->generate_fn ) { - vips_error( "vips_image_wio_output", + vips_error( "vips__image_wio_output", "%s", _( "image already written" ) ); return( -1 ); } @@ -2391,7 +2391,7 @@ vips__image_wio_output( VipsImage *image ) break; default: - vips_error( "vips_image_wio_output", + vips_error( "vips__image_wio_output", "%s", _( "image not writeable" ) ); return( -1 ); } diff --git a/tools/Makefile.am b/tools/Makefile.am index 5031b47f..90ae9a41 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -26,10 +26,10 @@ bin_SCRIPTS = \ batch_image_convert \ batch_rubber_sheet \ batch_crop \ - vips-7.36 + vips-7.37 EXTRA_DIST = \ - vips-7.36 \ + vips-7.37 \ light_correct.in \ shrink_width.in \ batch_image_convert.in \ diff --git a/tools/vips-7.36 b/tools/vips-7.37 similarity index 100% rename from tools/vips-7.36 rename to tools/vips-7.37 From 4e06d0a2b47b2ade90d3ac781a88468e18dfc2da Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 12:17:29 +0100 Subject: [PATCH 03/26] redo im_gauss*mask*() as classes --- ChangeLog | 4 + libvips/conversion/rot45.c | 30 ++-- libvips/create/Makefile.am | 1 + libvips/create/create.c | 2 + libvips/create/gauss.c | 244 +++++++++++++++++++++++++ libvips/deprecated/Makefile.am | 1 - libvips/deprecated/im_gaussmasks.c | 280 ----------------------------- libvips/deprecated/vips7compat.c | 76 ++++++++ libvips/include/vips/create.h | 2 + libvips/include/vips/mask.h | 3 + 10 files changed, 344 insertions(+), 299 deletions(-) create mode 100644 libvips/create/gauss.c delete mode 100644 libvips/deprecated/im_gaussmasks.c diff --git a/ChangeLog b/ChangeLog index 23dfb8c6..3e2a4739 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +19/10/13 started 7.37.0 +- added vips_rot45() +- redone im_gauss_*mask*() as classes + 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index bc47750a..4d2ef978 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -79,30 +79,24 @@ typedef VipsConversionClass VipsRot45Class; G_DEFINE_TYPE( VipsRot45, vips_rot45, VIPS_TYPE_CONVERSION ); -#define ASSIGN( Xout, Yout, Xin, Yin ) { \ - VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ - VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ +#define COPY( Q, P ) { \ + VipsPel *q = (Q); \ + VipsPel *p = (P); \ int b;\ \ for( b = 0; b < ps; b++ )\ q[b] = p[b];\ } -#define POINT_TO_TEMP( q, Xin, Yin ) { \ - VipsPel *p = VIPS_IMAGE_ADDR( in, Xin, Yin ); \ - int b;\ - \ - for( b = 0; b < ps; b++ )\ - q[b] = p[b];\ -} +#define ASSIGN( Xout, Yout, Xin, Yin ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), \ + VIPS_IMAGE_ADDR( in, Xin, Yin ) ) -#define TEMP_TO_POINT( Xout, Yout, p ) { \ - VipsPel *q = VIPS_IMAGE_ADDR( out, Xout, Yout ); \ - int b;\ - \ - for( b = 0; b < ps; b++ )\ - q[b] = p[b];\ -} +#define POINT_TO_TEMP( q, Xin, Yin ) \ + COPY( q, VIPS_IMAGE_ADDR( in, Xin, Yin ) ) + +#define TEMP_TO_POINT( Xout, Yout, p ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), p ) /* This can work inplace, ie. in == out is allowed. */ @@ -118,7 +112,7 @@ vips_rot45_rot45( VipsImage *out, VipsImage *in ) g_assert( in->Xsize == in->Ysize ); g_assert( out->Xsize == out->Ysize ); - g_assert( in->Xsize == out->Ssize ); + g_assert( in->Xsize == out->Xsize ); g_assert( in->Xsize % 2 == 0 ); /* Split the square into 8 triangles. Loop over the top-left one, diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index 60812e38..44752d97 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ create.c \ pcreate.h \ + gauss.c \ buildlut.c \ invertlut.c \ tonelut.c \ diff --git a/libvips/create/create.c b/libvips/create/create.c index 61b6e234..4f75dea1 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -111,6 +111,7 @@ void vips_create_operation_init( void ) { extern GType vips_black_get_type( void ); + extern GType vips_gauss_get_type( void ); extern GType vips_gaussnoise_get_type( void ); #ifdef HAVE_PANGOFT2 extern GType vips_text_get_type( void ); @@ -126,6 +127,7 @@ vips_create_operation_init( void ) extern GType vips_identity_get_type( void ); vips_black_get_type(); + vips_gauss_get_type(); vips_gaussnoise_get_type(); #ifdef HAVE_PANGOFT2 vips_text_get_type(); diff --git a/libvips/create/gauss.c b/libvips/create/gauss.c new file mode 100644 index 00000000..47a64cca --- /dev/null +++ b/libvips/create/gauss.c @@ -0,0 +1,244 @@ +/* generate gaussian images + * + * Written on: 30/11/1989 by Nicos + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 18/3/09 + * - bumped max mask size *40 + * - added _sep variant + * 30/3/09 + * - set scale in _sep variant, why not + * 21/10/10 + * - gtkdoc + * 20/10/10 + * - redone 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsGauss { + VipsCreate parent_instance; + + double sigma; + double min_ampl; + + gboolean separable; + gboolean integer; + +} VipsGauss; + +typedef struct _VipsGaussClass { + VipsCreateClass parent_class; + +} VipsGaussClass; + +G_DEFINE_TYPE( VipsGauss, vips_gauss, VIPS_TYPE_CREATE ); + +static int +vips_gauss_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsGauss *gauss = (VipsGauss *) object; + + int x, y; + int max_x; + int width, height; + double sig2; /* sig2 = 2. * sigma * sigma */ + double sum; + + if( VIPS_OBJECT_CLASS( vips_gauss_parent_class )->build( object ) ) + return( -1 ); + + /* Find the size of the mask. Limit the mask size to 10k x 10k for + * sanity. + */ + sig2 = 2. * gauss->sigma * gauss->sigma; + max_x = 8 * gauss->sigma > 5000 ? 5000 : 8 * gauss->sigma ; + for( x = 0; x < max_x; x++ ) { + double v = exp( - ((double)(x * x)) / sig2 ); + + if( v < gauss->min_ampl ) + break; + } + if( x == max_x ) { + vips_error( class->nickname, "%s", _( "mask too large" ) ); + return( -1 ); + } + width = x * 2 + 1; + height = gauss->separable ? 1 : width; + + vips_image_init_fields( create->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_demand_hint( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_write_prepare( create->out ) ) + return( -1 ); + + sum = 0.0; + for( y = 0; y < height; y++ ) { + for( x = 0; x < width; x++ ) { + int xo = x - width / 2; + int yo = y - height / 2; + double distance = xo * xo + yo * yo; + double v = exp( -distance / sig2 ); + + if( gauss->integer ) + v = VIPS_RINT( 20 * v ); + + *VIPS_MATRIX( create->out, x, y ) = v; + sum += v; + } + } + + vips_image_set_double( create->out, "scale", sum ); + vips_image_set_double( create->out, "offset", 0.0 ); + + return( 0 ); +} + +static void +vips_gauss_class_init( VipsGaussClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "gauss"; + vobject_class->description = _( "make a gaussian image" ); + vobject_class->build = vips_gauss_build; + + VIPS_ARG_DOUBLE( class, "sigma", 2, + _( "Radius" ), + _( "Radius of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGauss, sigma ), + 0.000001, 10000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "min_ampl", 3, + _( "Width" ), + _( "Minimum amplitude of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGauss, min_ampl ), + 0.000001, 10000.0, 0.1 ); + + VIPS_ARG_BOOL( class, "separable", 4, + _( "Separable" ), + _( "Generate separable Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGauss, separable ), + FALSE ); + + VIPS_ARG_BOOL( class, "integer", 5, + _( "Integer" ), + _( "Generate integer Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGauss, integer ), + FALSE ); + +} + +static void +vips_gauss_init( VipsGauss *gauss ) +{ + gauss->sigma = 1; + gauss->min_ampl = 0.1; +} + +/** + * vips_gauss: + * @sigma: standard deviation of mask + * @min_ampl: minimum amplitude + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @separable: generate a separable gaussian + * @integer: generate an integer gaussian + * + * Creates a circularly symmetric Gaussian image of radius + * @sigma. The size of the mask is determined by the variable @min_ampl; + * if for instance the value .1 is entered this means that the produced mask + * is clipped at values less than 10 percent of the maximum amplitude. + * + * The program uses the following equation: + * + * H(r) = exp( -(r * r) / (2 * @sigma * @sigma) ) + * + * The generated image has odd size and its maximum value is normalised to + * 1.0, unless @integer is set. + * + * If @separable is set, only the centre horizontal is generated. This is + * useful for separable convolutions. + * + * If @integer is set, an integer gaussian is generated. This is useful for + * integer convolutions. + * + * "scale" is set to the sum of all the mask elements. + * + * See also: im_log_dmask(), vips_conv(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) +{ + va_list ap; + int result; + + va_start( ap, min_ampl ); + result = vips_call_split( "gauss", ap, out, sigma, min_ampl ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/deprecated/Makefile.am b/libvips/deprecated/Makefile.am index c61a6f82..cbd17ab9 100644 --- a/libvips/deprecated/Makefile.am +++ b/libvips/deprecated/Makefile.am @@ -45,7 +45,6 @@ libdeprecated_la_SOURCES = \ im_mask2vips.c \ im_vips2mask.c \ rotmask.c \ - im_gaussmasks.c \ im_logmasks.c \ rw_mask.c \ im_matcat.c \ diff --git a/libvips/deprecated/im_gaussmasks.c b/libvips/deprecated/im_gaussmasks.c deleted file mode 100644 index 0d1dc13d..00000000 --- a/libvips/deprecated/im_gaussmasks.c +++ /dev/null @@ -1,280 +0,0 @@ -/* generate gaussian masks - * - * Written on: 30/11/1989 by Nicos - * Updated on: 6/12/1991 - * 7/8/96 JC - * - ansified, mem leaks plugged - * 20/11/98 JC - * - mask too large check added - * 18/3/09 - * - bumped max mask size *40 - * - added _sep variant - * 30/3/09 - * - set scale in _sep variant, why not - * 21/10/10 - * - gtkdoc - */ - -/* - - 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 - -#define IM_MAXMASK 5000 - -/** - * im_gauss_dmask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask() creates a circularly symmetric Gaussian mask of radius - * @sigma. The size of the mask is determined by the variable @min_ampl; - * if for instance the value .1 is entered this means that the produced mask - * is clipped at values less than 10 percent of the maximum amplitude. - * - * The program uses the following equation: - * - * H(r) = exp( -(r * r) / (2 * sigma * sigma) ) - * - * The generated mask has odd size and its maximum value is normalised to - * 1.0. - * - * See also: im_gauss_imask(), im_gauss_imask_sep(), im_log_dmask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask( const char *filename, double sigma, double min_ampl ) -{ - int x, y, k; - double distance; - double temp; - double *pt1, *pt2, *pt3, *pt4; - int max_x; - int xm, ym; - int xm2, ym2; /* xm2 = xm/2 */ - int offset; - double *cf, *cfs, *mc; - DOUBLEMASK *m; - double sig2, sum; /* sig2 = 2. * sigma * sigma */ - - /* Find the size of the mask depending on the entered data - */ - sig2 = 2. * sigma * sigma; - max_x = 8 * sigma > IM_MAXMASK ? IM_MAXMASK : 8 * sigma ; - for( x = 0; x < max_x; x++ ) { - temp = exp( - ((double)(x * x))/sig2 ); - if( temp < min_ampl ) - break; - } - if( x == max_x ) { - im_error( "im_gauss_dmask", "%s", _( "mask too large" ) ); - return( NULL ); - } - - xm2 = x; - ym2 = x; - xm = xm2 * 2 + 1; - ym = ym2 * 2 + 1; - - if( !(cfs = IM_ARRAY( NULL, (xm2+1)*(ym2+1), double )) ) - return( NULL ); - - for( k = 0, y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++, k++ ) { - distance = x*x + y*y; - cfs[k] = exp( -distance / sig2 ); - } - } - -#ifdef PIM_RINT - for( k = 0, y = 0; y <= ymask_2; y++ ) { - for( x = 0; x <= xmask_2; x++, k++ ) - fprintf(stderr, "%3.2f ", cfs[k] ); - fprintf(stderr, "\n"); - } -#endif - - if( !(m = im_create_dmask( filename, xm, ym )) ) { - im_free( cfs ); - return( NULL ); - } - - /* copy the 1/4 cfs into the m - */ - cf = cfs; - offset = xm2 * (xm + 1); - mc = m->coeff + offset; - for( y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++ ) { - pt1 = mc + (y * xm) + x; - pt2 = mc - (y * xm) + x; - pt3 = mc + (y * xm) - x; - pt4 = mc - (y * xm) - x; - - *pt1 = cf[x]; - *pt2 = cf[x]; - *pt3 = cf[x]; - *pt4 = cf[x]; - } - - cf += (xm2 + 1); - } - im_free( cfs ); - - sum = 0.0; - for( k = 0, y = 0; y < m->ysize; y++ ) - for( x = 0; x < m->xsize; x++, k++ ) - sum += m->coeff[k]; - m->scale = sum; - m->offset = 0.0; - -#ifdef PIM_RINT - im_print_dmask( m ); -#endif - return( m ); -} - -/** - * im_gauss_dmask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask_sep() works exactly as im_gauss_dmask(), but returns only - * the central line of the mask. This is useful with im_convsepf(). - * - * See also: im_gauss_dmask(), im_convsepf(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *im; - DOUBLEMASK *im2; - int i; - double sum; - - if( !(im = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_dmask( filename, im->xsize, 1 )) ) { - im_free_dmask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_dmask( im ); - - return( im2 ); -} - -/** - * im_gauss_imask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask() works exactly as im_gauss_dmask(), but the returned mask - * is scaled so that it's maximum value it set to 100. - * - * See also: im_gauss_dmask(), im_gauss_imask_sep(), im_conv(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *dm; - INTMASK *im; - - if( !(dm = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - - if( !(im = im_scale_dmask( dm, dm->filename )) ) { - im_free_dmask( dm ); - return( NULL ); - } - im_free_dmask( dm ); - - return( im ) ; -} - -/** - * im_gauss_imask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask_sep() works exactly as im_gauss_imask(), but returns only - * the central line of the mask. This is useful with im_convsep(). - * - * See also: im_gauss_dmask(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) -{ - INTMASK *im; - INTMASK *im2; - int i; - int sum; - - if( !(im = im_gauss_imask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_imask( filename, im->xsize, 1 )) ) { - im_free_imask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_imask( im ); - - return( im2 ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 046ae5ef..783554c5 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1894,6 +1894,82 @@ im_stats( VipsImage *in ) return( msk ); } +DOUBLEMASK * +im_gauss_dmask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +DOUBLEMASK * +im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "seperable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "integer", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gauss( &t, sigma, min_ampl, + "integer", TRUE, + "seperable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + int im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) { diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index abaa7167..4c68abe8 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -47,6 +47,8 @@ int vips_xyz( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); int vips_grey( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); +int vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) + __attribute__((sentinel)); int vips_text( VipsImage **out, const char *text, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/mask.h b/libvips/include/vips/mask.h index 8ce14e19..624dc7c0 100644 --- a/libvips/include/vips/mask.h +++ b/libvips/include/vips/mask.h @@ -4,6 +4,9 @@ * - from proto.h */ +/* All deprecated. + */ + /* This file is part of VIPS. From aa107b1bf76aa715172d4a548931078c2a38277d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 12:30:54 +0100 Subject: [PATCH 04/26] remove old 45 degree mask rotate code just a wrapper now --- ChangeLog | 3 +- TODO | 4 +- libvips/conversion/rot45.c | 13 +- libvips/deprecated/rotmask.c | 212 ++++-------------------------- libvips/include/vips/conversion.h | 2 +- 5 files changed, 39 insertions(+), 195 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3e2a4739..d575da57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,5 @@ 19/10/13 started 7.37.0 -- added vips_rot45() -- redone im_gauss_*mask*() as classes +- redone im_rotate_*mask45(), im_gauss_*mask*() as classes 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/TODO b/TODO index b1a8e71f..ff93a325 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,4 @@ - -Next version -============ +- do log mask generation, common base class with the gauss mask builder? - make vips_init() into a macro, check sizes of important structs against size at library compile time diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index 4d2ef978..46514608 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -275,7 +275,7 @@ vips_rot45_class_init( VipsRot45Class *class ) VIPS_ARG_ENUM( class, "angle", 6, _( "Angle" ), _( "Angle to rotate image" ), - VIPS_ARGUMENT_REQUIRED_INPUT, + VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsRot45, angle ), VIPS_TYPE_ANGLE45, VIPS_ANGLE45_45 ); } @@ -290,9 +290,12 @@ vips_rot45_init( VipsRot45 *rot45 ) * vips_rot45: * @in: input image * @out: output image - * @angle: rotation angle * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @angle: rotation angle + * * Rotate @in by a multiple of 45 degrees. Odd-length sides and square images * only. * @@ -301,13 +304,13 @@ vips_rot45_init( VipsRot45 *rot45 ) * Returns: 0 on success, -1 on error */ int -vips_rot45( VipsImage *in, VipsImage **out, VipsAngle45 angle, ... ) +vips_rot45( VipsImage *in, VipsImage **out, ... ) { va_list ap; int result; - va_start( ap, angle ); - result = vips_call_split( "rot45", ap, in, out, angle ); + va_start( ap, out ); + result = vips_call_split( "rot45", ap, in, out ); va_end( ap ); return( result ); diff --git a/libvips/deprecated/rotmask.c b/libvips/deprecated/rotmask.c index 4cdfc6ae..3863eebd 100644 --- a/libvips/deprecated/rotmask.c +++ b/libvips/deprecated/rotmask.c @@ -56,166 +56,6 @@ #include -/* Creates the offsets to rotate by 45 degrees an odd size square mask - */ -int * -im_offsets45( int size ) -{ - int temp; - int x, y; - int size2 = size * size; - int size_2 = size / 2; - int *pnt, *cpnt1, *cpnt2; - - if( size%2 == 0 ) { - im_error( "im_offsets45", "%s", _( "size not odd" ) ); - return( NULL ); - } - if( !(pnt = IM_ARRAY( NULL, size2, int )) ) - return( NULL ); - - /* point at the beginning and end of the buffer - */ - cpnt1 = pnt; cpnt2 = pnt + size2 - 1; - - for( y = 0; y < size_2; y++ ) { - temp = (size_2 + y) * size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - - for( x = 0; x < y; x++ ) { - temp -= (size-1); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp -= size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp++; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < y; x++ ) { - temp -= ( size - 1 ); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - } - - /* the diagonal now - */ - temp = size * (size - 1); - cpnt1 = pnt + size_2 * size; - for( x = 0; x < size; x++ ) { - *cpnt1++ = temp; - temp -= (size-1); - } - -#ifdef PIM_RINT - temp = 0; - for( y = 0; y < size; y++ ) { - for( x = 0; x < size; x++ ) { - fprintf( stderr, "%4d", *(pnt+temp) ); - temp++; - } - fprintf(stderr, "\n"); - } - fprintf(stderr, "\n"); -#endif - - return( pnt ); -} - -/** - * im_rotate_dmask45: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 45 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_dmask90(). - * - * Returns: the result matrix on success, or %NULL on error. - */ -DOUBLEMASK * -im_rotate_dmask45( DOUBLEMASK *in, const char *filename ) -{ - DOUBLEMASK *out; - int size = in->xsize * in->ysize; - int *offsets; - int i; - - if( in->xsize != in->ysize || (in->xsize % 2) == 0 ) { - im_error( "im_rotate_dmask45", "%s", - _( "mask should be square of odd size" ) ); - return( NULL ); - } - if( !(offsets = im_offsets45( in->xsize )) ) - return( NULL ); - if( !(out = im_create_dmask( filename, in->xsize, in->ysize )) ) { - im_free( offsets ); - return( NULL ); - } - out->scale = in->scale; - out->offset = in->offset; - - for( i = 0; i < size; i++ ) - out->coeff[i] = in->coeff[offsets[i]]; - - im_free( offsets ); - - return( out ); -} - -/** - * im_rotate_imask45: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 45 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_imask90(). - * - * Returns: the result matrix on success, or %NULL on error. - */ -INTMASK * -im_rotate_imask45( INTMASK *in, const char *filename ) -{ - INTMASK *out; - int size = in->xsize * in->ysize; - int *offsets; - int i; - - if( in->xsize != in->ysize || (in->xsize % 2) == 0 ) { - im_error( "im_rotate_imask45", "%s", - _( "mask should be square of odd size" ) ); - return( NULL ); - } - if( !(offsets = im_offsets45( in->xsize )) ) - return( NULL ); - if( !(out = im_create_imask( filename, in->xsize, in->ysize )) ) { - im_free( offsets ); - return( NULL ); - } - out->scale = in->scale; - out->offset = in->offset; - - for( i = 0; i < size; i++ ) - out->coeff[i] = in->coeff[offsets[i]]; - - im_free( offsets ); - - return( out ); -} - /* The type of the vips operations we support. */ typedef int (*vips_fn)( IMAGE *in, IMAGE *out ); @@ -273,38 +113,42 @@ vapplydmask( DOUBLEMASK *in, const char *name, vips_fn fn ) return( out ); } -/** - * im_rotate_imask90: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 90 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_imask45(). - * - * Returns: the result matrix on success, or %NULL on error. - */ INTMASK * im_rotate_imask90( INTMASK *in, const char *filename ) { return( vapplyimask( in, filename, im_rot90 ) ); } -/** - * im_rotate_dmask90: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 90 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_dmask45(). - * - * Returns: the result matrix on success, or %NULL on error. - */ DOUBLEMASK * im_rotate_dmask90( DOUBLEMASK *in, const char *filename ) { return( vapplydmask( in, filename, im_rot90 ) ); } + +static int +im_rot45( IMAGE *in, IMAGE *out ) +{ + VipsImage *t; + + if( vips_rot45( in, &t, NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +INTMASK * +im_rotate_imask45( INTMASK *in, const char *filename ) +{ + return( vapplyimask( in, filename, im_rot45 ) ); +} + +DOUBLEMASK * +im_rotate_dmask45( DOUBLEMASK *in, const char *filename ) +{ + return( vapplydmask( in, filename, im_rot45 ) ); +} diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 8164ac4c..6855d428 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -119,7 +119,7 @@ int vips_wrap( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_rot( VipsImage *in, VipsImage **out, VipsAngle angle, ... ) __attribute__((sentinel)); -int vips_rot45( VipsImage *in, VipsImage **out, VipsAngle45 angle, ... ) +int vips_rot45( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_zoom( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) __attribute__((sentinel)); From 7ef4573f1893e943374ff3f7a7962ed5c49b6c69 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 16:10:22 +0100 Subject: [PATCH 05/26] redo im_log_*mask() as classes --- ChangeLog | 2 +- libvips/create/Makefile.am | 3 +- libvips/create/create.c | 6 +- libvips/create/{gauss.c => gaussmat.c} | 56 +++-- libvips/create/logmat.c | 273 +++++++++++++++++++++++++ libvips/deprecated/Makefile.am | 1 - libvips/deprecated/im_logmasks.c | 238 --------------------- libvips/deprecated/vips7compat.c | 45 +++- libvips/include/vips/create.h | 4 +- 9 files changed, 351 insertions(+), 277 deletions(-) rename libvips/create/{gauss.c => gaussmat.c} (81%) create mode 100644 libvips/create/logmat.c delete mode 100644 libvips/deprecated/im_logmasks.c diff --git a/ChangeLog b/ChangeLog index d575da57..1aba8933 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ 19/10/13 started 7.37.0 -- redone im_rotate_*mask45(), im_gauss_*mask*() as classes +- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask*() as classes 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index 44752d97..59c2cf1b 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -3,7 +3,8 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ create.c \ pcreate.h \ - gauss.c \ + gaussmat.c \ + logmat.c \ buildlut.c \ invertlut.c \ tonelut.c \ diff --git a/libvips/create/create.c b/libvips/create/create.c index 4f75dea1..5130e92a 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -111,7 +111,8 @@ void vips_create_operation_init( void ) { extern GType vips_black_get_type( void ); - extern GType vips_gauss_get_type( void ); + extern GType vips_gaussmat_get_type( void ); + extern GType vips_logmat_get_type( void ); extern GType vips_gaussnoise_get_type( void ); #ifdef HAVE_PANGOFT2 extern GType vips_text_get_type( void ); @@ -127,7 +128,8 @@ vips_create_operation_init( void ) extern GType vips_identity_get_type( void ); vips_black_get_type(); - vips_gauss_get_type(); + vips_gaussmat_get_type(); + vips_logmat_get_type(); vips_gaussnoise_get_type(); #ifdef HAVE_PANGOFT2 vips_text_get_type(); diff --git a/libvips/create/gauss.c b/libvips/create/gaussmat.c similarity index 81% rename from libvips/create/gauss.c rename to libvips/create/gaussmat.c index 47a64cca..e7ec4705 100644 --- a/libvips/create/gauss.c +++ b/libvips/create/gaussmat.c @@ -13,7 +13,7 @@ * - set scale in _sep variant, why not * 21/10/10 * - gtkdoc - * 20/10/10 + * 20/10/13 * - redone as a class */ @@ -62,7 +62,7 @@ #include "pcreate.h" -typedef struct _VipsGauss { +typedef struct _VipsGaussmat { VipsCreate parent_instance; double sigma; @@ -71,40 +71,38 @@ typedef struct _VipsGauss { gboolean separable; gboolean integer; -} VipsGauss; +} VipsGaussmat; -typedef struct _VipsGaussClass { +typedef struct _VipsGaussmatClass { VipsCreateClass parent_class; -} VipsGaussClass; +} VipsGaussmatClass; -G_DEFINE_TYPE( VipsGauss, vips_gauss, VIPS_TYPE_CREATE ); +G_DEFINE_TYPE( VipsGaussmat, vips_gaussmat, VIPS_TYPE_CREATE ); static int -vips_gauss_build( VipsObject *object ) +vips_gaussmat_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsCreate *create = VIPS_CREATE( object ); - VipsGauss *gauss = (VipsGauss *) object; + VipsGaussmat *gaussmat = (VipsGaussmat *) object; + double sig2 = 2. * gaussmat->sigma * gaussmat->sigma; + int max_x = 8 * gaussmat->sigma > 5000 ? 5000 : 8 * gaussmat->sigma ; int x, y; - int max_x; int width, height; - double sig2; /* sig2 = 2. * sigma * sigma */ double sum; - if( VIPS_OBJECT_CLASS( vips_gauss_parent_class )->build( object ) ) + if( VIPS_OBJECT_CLASS( vips_gaussmat_parent_class )->build( object ) ) return( -1 ); /* Find the size of the mask. Limit the mask size to 10k x 10k for * sanity. */ - sig2 = 2. * gauss->sigma * gauss->sigma; - max_x = 8 * gauss->sigma > 5000 ? 5000 : 8 * gauss->sigma ; for( x = 0; x < max_x; x++ ) { double v = exp( - ((double)(x * x)) / sig2 ); - if( v < gauss->min_ampl ) + if( v < gaussmat->min_ampl ) break; } if( x == max_x ) { @@ -112,7 +110,7 @@ vips_gauss_build( VipsObject *object ) return( -1 ); } width = x * 2 + 1; - height = gauss->separable ? 1 : width; + height = gaussmat->separable ? 1 : width; vips_image_init_fields( create->out, width, height, 1, @@ -131,7 +129,7 @@ vips_gauss_build( VipsObject *object ) double distance = xo * xo + yo * yo; double v = exp( -distance / sig2 ); - if( gauss->integer ) + if( gaussmat->integer ) v = VIPS_RINT( 20 * v ); *VIPS_MATRIX( create->out, x, y ) = v; @@ -146,7 +144,7 @@ vips_gauss_build( VipsObject *object ) } static void -vips_gauss_class_init( VipsGaussClass *class ) +vips_gaussmat_class_init( VipsGaussmatClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); @@ -154,49 +152,49 @@ vips_gauss_class_init( VipsGaussClass *class ) gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - vobject_class->nickname = "gauss"; + vobject_class->nickname = "gaussmat"; vobject_class->description = _( "make a gaussian image" ); - vobject_class->build = vips_gauss_build; + vobject_class->build = vips_gaussmat_build; VIPS_ARG_DOUBLE( class, "sigma", 2, _( "Radius" ), _( "Radius of Gaussian" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsGauss, sigma ), + G_STRUCT_OFFSET( VipsGaussmat, sigma ), 0.000001, 10000.0, 1.0 ); VIPS_ARG_DOUBLE( class, "min_ampl", 3, _( "Width" ), _( "Minimum amplitude of Gaussian" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsGauss, min_ampl ), + G_STRUCT_OFFSET( VipsGaussmat, min_ampl ), 0.000001, 10000.0, 0.1 ); VIPS_ARG_BOOL( class, "separable", 4, _( "Separable" ), _( "Generate separable Gaussian" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsGauss, separable ), + G_STRUCT_OFFSET( VipsGaussmat, separable ), FALSE ); VIPS_ARG_BOOL( class, "integer", 5, _( "Integer" ), _( "Generate integer Gaussian" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsGauss, integer ), + G_STRUCT_OFFSET( VipsGaussmat, integer ), FALSE ); } static void -vips_gauss_init( VipsGauss *gauss ) +vips_gaussmat_init( VipsGaussmat *gaussmat ) { - gauss->sigma = 1; - gauss->min_ampl = 0.1; + gaussmat->sigma = 1; + gaussmat->min_ampl = 0.1; } /** - * vips_gauss: + * vips_gaussmat: * @sigma: standard deviation of mask * @min_ampl: minimum amplitude * @...: %NULL-terminated list of optional named arguments @@ -231,13 +229,13 @@ vips_gauss_init( VipsGauss *gauss ) * Returns: 0 on success, -1 on error */ int -vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) +vips_gaussmat( VipsImage **out, double sigma, double min_ampl, ... ) { va_list ap; int result; va_start( ap, min_ampl ); - result = vips_call_split( "gauss", ap, out, sigma, min_ampl ); + result = vips_call_split( "gaussmat", ap, out, sigma, min_ampl ); va_end( ap ); return( result ); diff --git a/libvips/create/logmat.c b/libvips/create/logmat.c new file mode 100644 index 00000000..e5af0ee4 --- /dev/null +++ b/libvips/create/logmat.c @@ -0,0 +1,273 @@ +/* laplacian of logmatian + * + * Written on: 30/11/1989 + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 26/3/02 JC + * - ahem, was broken since '96, thanks matt + * 16/7/03 JC + * - makes mask out to zero, not out to minimum, thanks again matt + * 22/10/10 + * - gtkdoc + * 20/10/13 + * - redone as a class from logmat.c + */ + +/* + + 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsLogmat { + VipsCreate parent_instance; + + double sigma; + double min_ampl; + + gboolean separable; + gboolean integer; + +} VipsLogmat; + +typedef struct _VipsLogmatClass { + VipsCreateClass parent_class; + +} VipsLogmatClass; + +G_DEFINE_TYPE( VipsLogmat, vips_logmat, VIPS_TYPE_CREATE ); + +static int +vips_logmat_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsLogmat *logmat = (VipsLogmat *) object; + double sig2 = logmat->sigma * logmat->sigma; + + double last; + int x, y; + int width, height; + double sum; + + if( VIPS_OBJECT_CLASS( vips_logmat_parent_class )->build( object ) ) + return( -1 ); + + /* Find the size of the mask. We want to eval the mask out to the + * flat zero part, ie. beyond the minimum and to the point where it + * comes back up towards zero. + */ + last = 0.0; + for( x = 0; x < 5000; x++ ) { + const double distance = x * x; + double val; + + /* Handbook of Pattern Recognition and image processing + * by Young and Fu AP 1986 pp 220-221 + * temp = (1.0 / (2.0 * IM_PI * sig4)) * + (2.0 - (distance / sig2)) * + exp( (-1.0) * distance / (2.0 * sig2) ) + + .. use 0.5 to normalise + */ + val = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + /* Stop when change in value (ie. difference from the last + * point) is positive (ie. we are going up) and absolute value + * is less than the min. + */ + if( val - last >= 0 && + fabs( val ) < logmat->min_ampl ) + break; + + last = val; + } + if( x == 5000 ) { + vips_error( class->nickname, "%s", _( "mask too large" ) ); + return( -1 ); + } + + width = x * 2 + 1; + height = logmat->separable ? 1 : width; + + vips_image_init_fields( create->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_demand_hint( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_write_prepare( create->out ) ) + return( -1 ); + + sum = 0.0; + for( y = 0; y < height; y++ ) { + for( x = 0; x < width; x++ ) { + int xo = x - width / 2; + int yo = y - height / 2; + double distance = xo * xo + yo * yo; + double v = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + if( logmat->integer ) + v = VIPS_RINT( 20 * v ); + + *VIPS_MATRIX( create->out, x, y ) = v; + sum += v; + } + } + + vips_image_set_double( create->out, "scale", sum ); + vips_image_set_double( create->out, "offset", 0.0 ); + + return( 0 ); +} + +static void +vips_logmat_class_init( VipsLogmatClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "logmat"; + vobject_class->description = _( "make a laplacian of gaussian image" ); + vobject_class->build = vips_logmat_build; + + VIPS_ARG_DOUBLE( class, "sigma", 2, + _( "Radius" ), + _( "Radius of Logmatian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsLogmat, sigma ), + 0.000001, 10000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "min_ampl", 3, + _( "Width" ), + _( "Minimum amplitude of Logmatian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsLogmat, min_ampl ), + 0.000001, 10000.0, 0.1 ); + + VIPS_ARG_BOOL( class, "separable", 4, + _( "Separable" ), + _( "Generate separable Logmatian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsLogmat, separable ), + FALSE ); + + VIPS_ARG_BOOL( class, "integer", 5, + _( "Integer" ), + _( "Generate integer Logmatian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsLogmat, integer ), + FALSE ); + +} + +static void +vips_logmat_init( VipsLogmat *logmat ) +{ + logmat->sigma = 1; + logmat->min_ampl = 0.1; +} + +/** + * vips_logmat: + * @sigma: standard deviation of mask + * @min_ampl: minimum amplitude + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @separable: generate a separable logmatian + * @integer: generate an integer logmatian + * + * Creates a circularly symmetric Laplacian of Gaussian mask + * of radius + * @sigma. The size of the mask is determined by the variable @min_ampl; + * if for instance the value .1 is entered this means that the produced mask + * is clipped at values within 10 persent of zero, and where the change + * between mask elements is less than 10%. + * + * The program uses the following equation: (from Handbook of Pattern + * Recognition and image processing by Young and Fu, AP 1986 pages 220-221): + * + * H(r) = (1 / (2 * M_PI * s4)) * + * (2 - (r2 / s2)) * + * exp(-r2 / (2 * s2)) + * + * where s2 = @sigma * @sigma, s4 = s2 * s2, r2 = r * r. + * + * The generated mask has odd size and its maximum value is normalised to + * 1.0, unless @integer is set. + * + * If @separable is set, only the centre horizontal is generated. This is + * useful for separable convolutions. + * + * If @integer is set, an integer logmatian is generated. This is useful for + * integer convolutions. + * + * "scale" is set to the sum of all the mask elements. + * + * See also: vips_gauss(), vips_conv(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_logmat( VipsImage **out, double sigma, double min_ampl, ... ) +{ + va_list ap; + int result; + + va_start( ap, min_ampl ); + result = vips_call_split( "logmat", ap, out, sigma, min_ampl ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/deprecated/Makefile.am b/libvips/deprecated/Makefile.am index cbd17ab9..d0110ab6 100644 --- a/libvips/deprecated/Makefile.am +++ b/libvips/deprecated/Makefile.am @@ -45,7 +45,6 @@ libdeprecated_la_SOURCES = \ im_mask2vips.c \ im_vips2mask.c \ rotmask.c \ - im_logmasks.c \ rw_mask.c \ im_matcat.c \ im_matinv.c \ diff --git a/libvips/deprecated/im_logmasks.c b/libvips/deprecated/im_logmasks.c deleted file mode 100644 index a044d43c..00000000 --- a/libvips/deprecated/im_logmasks.c +++ /dev/null @@ -1,238 +0,0 @@ -/* laplacian of gaussian - * - * Written on: 30/11/1989 - * Updated on: 6/12/1991 - * 7/8/96 JC - * - ansified, mem leaks plugged - * 20/11/98 JC - * - mask too large check added - * 26/3/02 JC - * - ahem, was broken since '96, thanks matt - * 16/7/03 JC - * - makes mask out to zero, not out to minimum, thanks again matt - * 22/10/10 - * - gtkdoc - */ - -/* - - 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 - - */ - -/* -#define PIM_RINT 1 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include -#include - -#define IM_MAXMASK 256 - -/** - * im_log_dmask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_log_dmask() creates a circularly symmetric Laplacian of Gaussian mask - * of radius - * @sigma. The size of the mask is determined by the variable @min_ampl; - * if for instance the value .1 is entered this means that the produced mask - * is clipped at values within 10 persent of zero, and where the change - * between mask elements is less than 10%. - * - * The program uses the following equation: (from Handbook of Pattern - * Recognition and image processing by Young and Fu, AP 1986 pages 220-221): - * - * H(r) = (1 / (2 * M_PI * s4)) * - * (2 - (r2 / s2)) * - * exp(-r2 / (2 * s2)) - * - * where s2 = sigma * sigma, s4 = s2 * s2, r2 = r * r. - * - * The generated mask has odd size and its maximum value is normalised to 1.0. - * - * See also: im_log_imask(), im_gauss_dmask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_log_dmask( const char *filename, double sigma, double min_ampl ) -{ - const double sig2 = sigma * sigma; - - double last; - int x, y, k; - - double *pt1, *pt2, *pt3, *pt4; - int xm, ym; - int xm2, ym2; /* xm2 = xm/2 */ - int offset; - double *cf, *cfs, *mc; - DOUBLEMASK *m; - double sum; - - /* Find the size of the mask depending on the entered data. We want to - * eval the mask out to the flat zero part, ie. beyond the minimum and - * to the point where it comes back up towards zero. - */ - last = 0.0; - for( x = 0; x < IM_MAXMASK; x++ ) { - const double distance = x * x; - double val; - - /* Handbook of Pattern Recognition and image processing - * by Young and Fu AP 1986 pp 220-221 - * temp = (1.0 / (2.0 * IM_PI * sig4)) * - (2.0 - (distance / sig2)) * - exp( (-1.0) * distance / (2.0 * sig2) ) - - .. use 0.5 to normalise - */ - val = 0.5 * - (2.0 - (distance / sig2)) * - exp( -distance / (2.0 * sig2) ); - - /* Stop when change in value (ie. difference from the last - * point) is positive (ie. we are going up) and absolute value - * is less than the min. - */ - if( val - last >= 0 && - fabs( val ) < min_ampl ) - break; - - last = val; - } - if( x == IM_MAXMASK ) { - im_error( "im_log_dmask", "%s", _( "mask too large" ) ); - return( NULL ); - } - - xm2 = x; - ym2 = x; - xm = xm2 * 2 + 1; - ym = ym2 * 2 + 1; - - if( !(cfs = IM_ARRAY( NULL, (xm2 + 1) * (ym2 + 1), double )) ) - return( NULL ); - - /* Make 1/4 of the mask. - */ - for( k = 0, y = 0; y <= ym2; y++ ) - for( x = 0; x <= xm2; x++, k++ ) { - const double distance = x * x + y * y; - - cfs[k] = 0.5 * - (2.0 - (distance / sig2)) * - exp( -distance / (2.0 * sig2) ); - } - -#ifdef PIM_RINT - for( k = 0, y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++, k++ ) - fprintf( stderr, "%3.2f ", cfs[k] ); - fprintf( stderr, "\n" ); - } -#endif - - if( !(m = im_create_dmask( filename, xm, ym )) ) { - im_free( cfs ); - return( NULL ); - } - - /* Copy the 1/4 cfs into the m - */ - cf = cfs; - offset = xm2 * (xm + 1); - mc = m->coeff + offset; - for( y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++ ) { - pt1 = mc + (y * xm) + x; - pt2 = mc - (y * xm) + x; - pt3 = mc + (y * xm) - x; - pt4 = mc - (y * xm) - x; - - *pt1 = cf[x]; - *pt2 = cf[x]; - *pt3 = cf[x]; - *pt4 = cf[x]; - } - - cf += (xm2 + 1); - } - im_free( cfs ); - - sum = 0.0; - for( k = 0, y = 0; y < m->ysize; y++ ) - for( x = 0; x < m->xsize; x++, k++ ) - sum += m->coeff[k]; - m->scale = sum; - m->offset = 0.0; - -#ifdef PIM_RINT - im_print_dmask( m ); -#endif - - return( m ); -} - -/** - * im_log_imask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_log_imask() works exactly as im_log_dmask(), but the returned mask - * is scaled so that it's maximum value it set to 100. - * - * See also: im_log_dmask(), im_gauss_imask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_log_imask( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *dm; - INTMASK *im; - - if( !(dm = im_log_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - - if( !(im = im_scale_dmask( dm, dm->filename )) ) { - im_free_dmask( dm ); - return( NULL ); - } - im_free_dmask( dm ); - - return( im ) ; -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 783554c5..3f46f2a6 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1900,7 +1900,7 @@ im_gauss_dmask( const char *filename, double sigma, double min_ampl ) VipsImage *t; DOUBLEMASK *msk; - if( vips_gauss( &t, sigma, min_ampl, + if( vips_gaussmat( &t, sigma, min_ampl, NULL ) ) return( NULL ); if( !(msk = im_vips2mask( t, filename )) ) { @@ -1918,7 +1918,7 @@ im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) VipsImage *t; DOUBLEMASK *msk; - if( vips_gauss( &t, sigma, min_ampl, + if( vips_gaussmat( &t, sigma, min_ampl, "seperable", TRUE, NULL ) ) return( NULL ); @@ -1937,7 +1937,7 @@ im_gauss_imask( const char *filename, double sigma, double min_ampl ) VipsImage *t; INTMASK *msk; - if( vips_gauss( &t, sigma, min_ampl, + if( vips_gaussmat( &t, sigma, min_ampl, "integer", TRUE, NULL ) ) return( NULL ); @@ -1956,7 +1956,7 @@ im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) VipsImage *t; INTMASK *msk; - if( vips_gauss( &t, sigma, min_ampl, + if( vips_gaussmat( &t, sigma, min_ampl, "integer", TRUE, "seperable", TRUE, NULL ) ) @@ -1970,6 +1970,43 @@ im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) return( msk ); } +INTMASK * +im_log_imask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_logmat( &t, sigma, min_ampl, + "integer", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +DOUBLEMASK * +im_log_dmask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_logmat( &t, sigma, min_ampl, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + int im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) { diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index 4c68abe8..ddcf9c66 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -47,7 +47,9 @@ int vips_xyz( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); int vips_grey( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); -int vips_gauss( VipsImage **out, double sigma, double min_ampl, ... ) +int vips_gaussmat( VipsImage **out, double sigma, double min_ampl, ... ) + __attribute__((sentinel)); +int vips_logmat( VipsImage **out, double sigma, double min_ampl, ... ) __attribute__((sentinel)); int vips_text( VipsImage **out, const char *text, ... ) From f7f061d265621fd1443120dfdd33ba3c7274f19d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 16:46:55 +0100 Subject: [PATCH 06/26] vips_init() does ABI checking just checks sizeof(VipsObject) for now --- ChangeLog | 4 +++- TODO | 12 ------------ libvips/deprecated/vips7compat.c | 6 ++++++ libvips/include/vips/private.h | 5 +++++ libvips/include/vips/vips.h | 14 +++++++++++++- libvips/include/vips/vips7compat.h | 3 ++- libvips/iofuncs/init.c | 23 +++++++++++++++++++++-- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1aba8933..5c7cc703 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 19/10/13 started 7.37.0 -- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask*() as classes +- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask() as classes +- vips_init() now does some ABI compat checking, though this change requires + an ABI break 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/TODO b/TODO index ff93a325..b4aed4ea 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,3 @@ -- do log mask generation, common base class with the gauss mask builder? - -- make vips_init() into a macro, check sizes of important structs against size - at library compile time - - add a new API call like vips__get_sizeof_image() which returns - sizeof(VipsImage) from library compile time, compare that to the application - compile-time value - - vips_init() macro then passes control on to vips__init() for real startup - - breaks binary API sadly - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 3f46f2a6..3c31ff14 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -50,6 +50,12 @@ #include #include +int +im_init_world( const char *argv0 ) +{ + return( vips_init( argv0 ) ); +} + VipsImage * im_open( const char *filename, const char *mode ) { diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index 03479055..123dda67 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -56,6 +56,11 @@ extern "C" { */ #define VIPS_SIZEOF_HEADER (64) +/* Startup plus ABI check. + */ +int vips__init( const char *argv0 ); +size_t vips__get_sizeof_vipsobject( void ); + /* What we track for each mmap window. Have a list of these on an openin * VipsImage. */ diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 85078776..ca777e0f 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -149,8 +149,20 @@ extern "C" { #include #include +#define vips_init( ARGV0 ) \ + (sizeof( VipsObject ) != vips__get_sizeof_vipsobject() ? ( \ + vips_info( "vips_init", "%s", _( "ABI mismatch" ) ), \ + vips_info( "vips_init", \ + _( "library has sizeof(VipsObject) == %zd" ), \ + vips__get_sizeof_vipsobject() ), \ + vips_info( "vips_init", \ + _( "application has sizeof(VipsObject) == %zd" ), \ + sizeof( VipsObject() ) ), \ + vips_error( "vips_init", "%s", _( "ABI mismatch" ) ), \ + -1 ) : \ + vips__init( ARGV0 )) + const char *vips_get_argv0( void ); -int vips_init( const char *argv0 ); void vips_check_init( void ); void vips_shutdown( void ); GOptionGroup *vips_get_option_group( void ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 32c3d88d..04a0e151 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -203,7 +203,6 @@ extern "C" { #define im_get_argv0 vips_get_argv0 #define im_version_string vips_version_string #define im_version vips_version -#define im_init_world vips_init #define im_get_option_group vips_get_option_group #define im_guess_prefix vips_guess_prefix #define im_guess_libdir vips_guess_libdir @@ -586,6 +585,8 @@ size_t im_ref_string_get_length( const GValue *value ); #define im_concurrency_set vips_concurrency_set #define im_concurrency_get vips_concurrency_get +int im_init_world( const char *argv0 ); + int im_add( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_subtract( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_multiply( VipsImage *in1, VipsImage *in2, VipsImage *out ); diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index eac7745e..74390550 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -140,7 +140,7 @@ vips_get_argv0( void ) * * * - * loads any plugins from $libdir/vips-x.y, where x and y are the + * loads any plugins from $libdir/vips-x.y/, where x and y are the * major and minor version numbers for this VIPS. * * @@ -165,8 +165,13 @@ vips_get_argv0( void ) * * Returns: 0 on success, -1 otherwise */ + +/* vips_init() is actually a macro which checks library and application + * compatibility before calling vips__init(). + */ + int -vips_init( const char *argv0 ) +vips__init( const char *argv0 ) { extern GType vips_system_get_type( void ); @@ -299,6 +304,20 @@ vips_init( const char *argv0 ) return( 0 ); } +/* Return the sizeof() various important data structures. These are checked + * against the headers used to build our caller by vips_init(). + * + * We allow direct access to members of VipsImage and VipsRegion (mostly for + * reasons of history), so any change to a superclass of either of these + * objects will break our ABI. + */ + +size_t +vips__get_sizeof_vipsobject( void ) +{ + return( sizeof( VipsObject ) ); +} + /* Call this before vips stuff that uses stuff we need to have inited. */ void From 30e6c27fd53e6b543cad6422074c52be43078dd8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 20 Oct 2013 16:56:42 +0100 Subject: [PATCH 07/26] toto update --- TODO | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TODO b/TODO index b4aed4ea..a117ce6d 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,10 @@ +- get rid of vips_image_copy_fields(out, in) ... very confusing + + just have + + vips_image_copy_fieldsv( out, in1, in2, .. NULL ) + + makes arg order clearer - do conv and morph quickly as simple wrappers over the vips7 operations @@ -15,6 +22,8 @@ could we add something to check that the two calls agree on the image lists? I think they should + combine the two into one call? + - support multiscan jpeg write? see https://github.com/jcupitt/ruby-vips/issues/41 From 0bfb23eca86e221dc8d5cfd881c3dfba2d5d380b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 21 Oct 2013 12:16:33 +0100 Subject: [PATCH 08/26] add "interlace" option to vips_jpegsave() see https://github.com/jcupitt/libvips/issues/77 --- ChangeLog | 1 + libvips/foreign/foreign.c | 9 +++++++++ libvips/foreign/jpegsave.c | 20 +++++++++++++++++--- libvips/foreign/vips2jpeg.c | 15 ++++++++++----- libvips/foreign/vipsjpeg.h | 4 ++-- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5c7cc703..62edaf02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ - redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask() as classes - vips_init() now does some ABI compat checking, though this change requires an ABI break +- add "interlace" option to vips_jpegsave() 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 10be5040..b539aa25 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1988,6 +1988,7 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) * @Q: quality factor * @profile: attach this ICC profile * @optimize_coding: compute optimal Huffman coding tables + * @interlace: write an interlaced (progressive) jpeg * * Write a VIPS image to a file as JPEG. * @@ -2012,6 +2013,13 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) * IPCT as @VIPS_META_IPCT_NAME ("ipct-data") and XMP as VIPS_META_XMP_NAME * ("xmp-data") are coded and attached. * + * If @optimize_coding is set, the Huffman tables are optimised. This is + * sllightly slower and produces slightly smaller files. + * + * If @interlace is set, the jpeg files will be interlaced (progressive jpeg, + * in jpg parlance). These files may be better for display over a slow network + * conection, but need much more memory to encode and decode. + * * See also: vips_jpegsave_buffer(), vips_image_write_file(). * * Returns: 0 on success, -1 on error. @@ -2041,6 +2049,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... ) * @Q: JPEG quality factor * @profile: attach this ICC profile * @optimize_coding: compute optimal Huffman coding tables + * @interlace: write an interlaced (progressive) jpeg * * As vips_jpegsave(), but save to a memory buffer. * diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index e14c6086..ec21af6d 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -83,6 +83,10 @@ typedef struct _VipsForeignSaveJpeg { */ gboolean optimize_coding; + /* Generate an interlaced (progressive, in jpg terminology) file. + */ + gboolean interlace; + } VipsForeignSaveJpeg; typedef VipsForeignSaveClass VipsForeignSaveJpegClass; @@ -135,6 +139,13 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_coding ), FALSE ); + + VIPS_ARG_BOOL( class, "interlace", 13, + _( "interlace" ), + _( "Generate an interlaced (progressive) jpeg" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, interlace ), + FALSE ); } static void @@ -169,7 +180,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_file( save->ready, file->filename, - jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); return( 0 ); @@ -234,7 +246,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_buffer( save->ready, - &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); area = vips_area_new_blob( (VipsCallbackFn) vips_free, obuf, olen ); @@ -295,7 +308,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_buffer( save->ready, - &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); printf( "Content-length: %zd\r\n", olen ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 104f1079..fc47385b 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -841,7 +841,7 @@ write_jpeg_block( REGION *region, Rect *area, void *a ) */ static int write_vips( Write *write, int qfac, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { VipsImage *in; J_COLOR_SPACE space; @@ -898,6 +898,11 @@ write_vips( Write *write, int qfac, const char *profile, */ write->cinfo.optimize_coding = optimize_coding; + /* Enable progressive write. + */ + if( progressive ) + jpeg_simple_progression( &write->cinfo ); + /* Build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); @@ -941,7 +946,7 @@ write_vips( Write *write, int qfac, const char *profile, int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { Write *write; @@ -971,7 +976,7 @@ vips__jpeg_write_file( VipsImage *in, /* Convert! */ - if( write_vips( write, Q, profile, optimize_coding ) ) { + if( write_vips( write, Q, profile, optimize_coding, progressive ) ) { write_destroy( write ); return( -1 ); } @@ -1218,7 +1223,7 @@ buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen ) int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { Write *write; @@ -1247,7 +1252,7 @@ vips__jpeg_write_buffer( VipsImage *in, /* Convert! */ - if( write_vips( write, Q, profile, optimize_coding ) ) { + if( write_vips( write, Q, profile, optimize_coding, progressive ) ) { write_destroy( write ); return( -1 ); diff --git a/libvips/foreign/vipsjpeg.h b/libvips/foreign/vipsjpeg.h index b4337812..246b51ad 100644 --- a/libvips/foreign/vipsjpeg.h +++ b/libvips/foreign/vipsjpeg.h @@ -39,10 +39,10 @@ extern const char *vips__jpeg_suffs[]; int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, - gboolean optimize_coding ); + gboolean optimize_coding, gboolean progressive ); int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, - gboolean optimize_coding ); + gboolean optimize_coding, gboolean progressive ); int vips__isjpeg( const char *filename ); int vips__jpeg_read_file( const char *name, VipsImage *out, From 8299bea9845d6f7df4917046c5c5b78f67b18ddd Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 21 Oct 2013 13:13:34 +0100 Subject: [PATCH 09/26] todo updates --- TODO | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TODO b/TODO index a117ce6d..657cd48f 100644 --- a/TODO +++ b/TODO @@ -24,10 +24,6 @@ combine the two into one call? -- support multiscan jpeg write? see - - https://github.com/jcupitt/ruby-vips/issues/41 - - object construction is threadsafe, but class construction is not https://github.com/jcupitt/libvips/issues/64 From 12cf71a6a97d6f068e11b14b569e324b64b3b324 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 22 Oct 2013 09:29:40 +0100 Subject: [PATCH 10/26] combin copy_fields and demand_hint We had two API calls, vips_image_copy_fields() and vips_demand_hint(). They are now combined in the single vips_image_pipeline() call. All operations are now slightly smaller and simpler. --- ChangeLog | 2 + TODO | 15 ++--- libvips/arithmetic/arithmetic.c | 6 +- libvips/arithmetic/hist_find.c | 3 +- libvips/arithmetic/hist_find_indexed.c | 3 +- libvips/arithmetic/hist_find_ndim.c | 3 +- libvips/arithmetic/profile.c | 6 +- libvips/arithmetic/project.c | 6 +- libvips/colour/colour.c | 5 +- libvips/conversion/bandary.c | 5 +- libvips/conversion/cast.c | 5 +- libvips/conversion/copy.c | 5 +- libvips/conversion/embed.c | 8 +-- libvips/conversion/extract.c | 5 +- libvips/conversion/flatten.c | 5 +- libvips/conversion/flip.c | 5 +- libvips/conversion/grid.c | 7 +- libvips/conversion/ifthenelse.c | 5 +- libvips/conversion/insert.c | 5 +- libvips/conversion/msb.c | 5 +- libvips/conversion/recomb.c | 5 +- libvips/conversion/replicate.c | 5 +- libvips/conversion/rot.c | 11 ++-- libvips/conversion/rot45.c | 8 ++- libvips/conversion/sequential.c | 5 +- libvips/conversion/subsample.c | 14 ++-- libvips/conversion/tilecache.c | 10 ++- libvips/conversion/zoom.c | 7 +- libvips/create/black.c | 2 +- libvips/create/gaussmat.c | 2 +- libvips/create/gaussnoise.c | 2 +- libvips/create/identity.c | 2 +- libvips/create/logmat.c | 2 +- libvips/create/point.c | 2 +- libvips/create/text.c | 2 +- libvips/create/xyz.c | 2 +- libvips/deprecated/lazy.c | 2 +- libvips/deprecated/vips7compat.c | 36 ++++++++-- libvips/foreign/csv.c | 2 +- libvips/foreign/dzsave.c | 3 +- libvips/foreign/fits.c | 2 +- libvips/foreign/foreign.c | 4 +- libvips/foreign/jpeg2vips.c | 2 +- libvips/foreign/magick2vips.c | 2 +- libvips/foreign/openexr2vips.c | 4 +- libvips/foreign/openslide2vips.c | 4 +- libvips/foreign/tiff2vips.c | 4 +- libvips/foreign/vipspng.c | 2 +- libvips/foreign/webp2vips.c | 2 +- libvips/histogram/hist_local.c | 12 ++-- libvips/histogram/histogram.c | 5 +- libvips/histogram/maplut.c | 5 +- libvips/histogram/stdif.c | 12 ++-- libvips/include/vips/generate.h | 4 +- libvips/include/vips/header.h | 5 -- libvips/include/vips/private.h | 5 ++ libvips/include/vips/vips7compat.h | 12 ++-- libvips/iofuncs/generate.c | 89 +++++++++++++++++-------- libvips/iofuncs/header.c | 91 ++------------------------ libvips/iofuncs/image.c | 9 ++- libvips/iofuncs/sinkscreen.c | 8 +-- libvips/resample/affine.c | 19 +++--- libvips/resample/quadratic.c | 12 ++-- libvips/resample/shrink.c | 8 +-- 64 files changed, 256 insertions(+), 299 deletions(-) diff --git a/ChangeLog b/ChangeLog index 62edaf02..ba178acf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,8 @@ - vips_init() now does some ABI compat checking, though this change requires an ABI break - add "interlace" option to vips_jpegsave() +- remove vips_image_copy_fields() and vips_demand_hint() and add + vips_image_pipeline() to do both jobs 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/TODO b/TODO index 657cd48f..0f325b8b 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,3 @@ -- get rid of vips_image_copy_fields(out, in) ... very confusing - - just have - - vips_image_copy_fieldsv( out, in1, in2, .. NULL ) - - makes arg order clearer - - do conv and morph quickly as simple wrappers over the vips7 operations - do much fancier profiling with timing on all locks saved in memory and @@ -24,6 +16,13 @@ combine the two into one call? + hist_find at least does not call demand_hint, same for all writeline output + operators I guess + + how about + + vips_image_pipelinev( out, hint, in1, ... ) + - object construction is threadsafe, but class construction is not https://github.com/jcupitt/libvips/issues/64 diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index 59fe05aa..c4fb82e5 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -545,11 +545,9 @@ vips_arithmetic_build( VipsObject *object ) */ arithmetic->ready = size; - if( vips_image_copy_fields_array( arithmetic->out, - arithmetic->ready ) ) + if( vips_image_pipeline_array( arithmetic->out, + VIPS_DEMAND_STYLE_THINSTRIP, arithmetic->ready ) ) return( -1 ); - vips_demand_hint_array( arithmetic->out, - VIPS_DEMAND_STYLE_THINSTRIP, arithmetic->ready ); arithmetic->out->Bands = arithmetic->ready[0]->Bands; arithmetic->out->BandFmt = diff --git a/libvips/arithmetic/hist_find.c b/libvips/arithmetic/hist_find.c index 175adcb5..5c98ee26 100644 --- a/libvips/arithmetic/hist_find.c +++ b/libvips/arithmetic/hist_find.c @@ -150,7 +150,8 @@ vips_hist_find_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( hist_find->out, statistic->ready ) ) + if( vips_image_pipelinev( hist_find->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); vips_image_init_fields( hist_find->out, hist_find->hist->mx + 1, 1, hist_find->hist->bands, diff --git a/libvips/arithmetic/hist_find_indexed.c b/libvips/arithmetic/hist_find_indexed.c index 7ce56fea..e2632a42 100644 --- a/libvips/arithmetic/hist_find_indexed.c +++ b/libvips/arithmetic/hist_find_indexed.c @@ -163,7 +163,8 @@ vips_hist_find_indexed_build( VipsObject *object ) VIPS_UNREF( indexed->hist->reg ); - if( vips_image_copy_fieldsv( indexed->out, + if( vips_image_pipelinev( indexed->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, indexed->index_ready, NULL ) ) return( -1 ); vips_image_init_fields( indexed->out, diff --git a/libvips/arithmetic/hist_find_ndim.c b/libvips/arithmetic/hist_find_ndim.c index f170599d..85db4a98 100644 --- a/libvips/arithmetic/hist_find_ndim.c +++ b/libvips/arithmetic/hist_find_ndim.c @@ -154,7 +154,8 @@ vips_hist_find_ndim_build( VipsObject *object ) build( object ) ) return( -1 ); - if( vips_image_copy_fields( ndim->out, statistic->ready ) ) + if( vips_image_pipelinev( ndim->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); vips_image_init_fields( ndim->out, ndim->bins, diff --git a/libvips/arithmetic/profile.c b/libvips/arithmetic/profile.c index 11cc7f53..7777b60c 100644 --- a/libvips/arithmetic/profile.c +++ b/libvips/arithmetic/profile.c @@ -136,8 +136,10 @@ vips_profile_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( profile->columns, statistic->ready ) || - vips_image_copy_fields( profile->rows, statistic->ready ) ) + if( vips_image_pipelinev( profile->columns, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) || + vips_image_pipelinev( profile->rows, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); profile->columns->Ysize = 1; profile->columns->BandFmt = VIPS_FORMAT_INT; diff --git a/libvips/arithmetic/project.c b/libvips/arithmetic/project.c index c2159179..6b61686a 100644 --- a/libvips/arithmetic/project.c +++ b/libvips/arithmetic/project.c @@ -141,8 +141,10 @@ vips_project_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( project->columns, statistic->ready ) || - vips_image_copy_fields( project->rows, statistic->ready ) ) + if( vips_image_pipelinev( project->columns, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) || + vips_image_pipelinev( project->rows, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); project->columns->Ysize = 1; project->columns->BandFmt = diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index 3d31e2bb..eb70d8e1 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -320,10 +320,9 @@ vips_colour_build( VipsObject *object ) */ g_assert( !colour->in[colour->n] ); - if( vips_image_copy_fields_array( colour->out, colour->in ) ) + if( vips_image_pipeline_array( colour->out, + VIPS_DEMAND_STYLE_THINSTRIP, colour->in ) ) return( -1 ); - vips_demand_hint_array( colour->out, - VIPS_DEMAND_STYLE_THINSTRIP, colour->in ); colour->out->Coding = colour->coding; colour->out->Type = colour->interpretation; colour->out->BandFmt = colour->format; diff --git a/libvips/conversion/bandary.c b/libvips/conversion/bandary.c index 5e4e4f0b..99594aa0 100644 --- a/libvips/conversion/bandary.c +++ b/libvips/conversion/bandary.c @@ -147,10 +147,9 @@ vips_bandary_build( VipsObject *object ) return( -1 ); bandary->ready = size; - if( vips_image_copy_fields_array( conversion->out, bandary->ready ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, bandary->ready ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, bandary->ready ); conversion->out->Bands = bandary->out_bands; diff --git a/libvips/conversion/cast.c b/libvips/conversion/cast.c index 38df919a..be048d0c 100644 --- a/libvips/conversion/cast.c +++ b/libvips/conversion/cast.c @@ -441,10 +441,9 @@ vips_cast_build( VipsObject *object ) vips_image_pio_input( cast->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, cast->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, cast->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, cast->in, NULL ); conversion->out->BandFmt = cast->format; diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index 2b3981a9..e2753a2b 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -250,10 +250,9 @@ vips_copy_build( VipsObject *object ) if( vips_image_pio_input( copy->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, copy->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ); /* Use props to adjust header fields. */ diff --git a/libvips/conversion/embed.c b/libvips/conversion/embed.c index 8f33e248..46a2d557 100644 --- a/libvips/conversion/embed.c +++ b/libvips/conversion/embed.c @@ -426,14 +426,12 @@ vips_embed_build( VipsObject *object ) case VIPS_EXTEND_WHITE: case VIPS_EXTEND_BACKGROUND: case VIPS_EXTEND_COPY: - if( vips_image_copy_fields( conversion->out, embed->in ) ) - return( -1 ); - /* embed is used in many places. We don't really care about * geometry, so use ANY to avoid disturbing all pipelines. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_ANY, embed->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_ANY, embed->in, NULL ) ) + return( -1 ); conversion->out->Xsize = embed->width; conversion->out->Ysize = embed->height; diff --git a/libvips/conversion/extract.c b/libvips/conversion/extract.c index 5f3a00bf..8f674170 100644 --- a/libvips/conversion/extract.c +++ b/libvips/conversion/extract.c @@ -155,10 +155,9 @@ vips_extract_area_build( VipsObject *object ) vips_check_coding_known( class->nickname, extract->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, extract->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, extract->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, extract->in, NULL ); conversion->out->Xsize = extract->width; conversion->out->Ysize = extract->height; diff --git a/libvips/conversion/flatten.c b/libvips/conversion/flatten.c index 352e3734..889844a9 100644 --- a/libvips/conversion/flatten.c +++ b/libvips/conversion/flatten.c @@ -316,10 +316,9 @@ vips_flatten_build( VipsObject *object ) vips_image_pio_input( flatten->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, flatten->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, flatten->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, flatten->in, NULL ); conversion->out->Bands -= 1; diff --git a/libvips/conversion/flip.c b/libvips/conversion/flip.c index 824e1f8f..ed70a3c6 100644 --- a/libvips/conversion/flip.c +++ b/libvips/conversion/flip.c @@ -198,10 +198,9 @@ vips_flip_build( VipsObject *object ) if( vips_image_pio_input( flip->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, flip->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ); if( flip->direction == VIPS_DIRECTION_HORIZONTAL ) { generate_fn = vips_flip_horizontal_gen; diff --git a/libvips/conversion/grid.c b/libvips/conversion/grid.c index de5d45ef..be39dfd5 100644 --- a/libvips/conversion/grid.c +++ b/libvips/conversion/grid.c @@ -166,12 +166,11 @@ vips_grid_build( VipsObject *object ) return( -1 ); } - if( vips_image_copy_fields( conversion->out, grid->in ) ) - return( -1 ); /* We can render small tiles with pointer copies. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, grid->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, grid->in, NULL ) ) + return( -1 ); conversion->out->Xsize = grid->in->Xsize * grid->across; conversion->out->Ysize = grid->tile_height * grid->down; diff --git a/libvips/conversion/ifthenelse.c b/libvips/conversion/ifthenelse.c index 450cfd3a..7c088e77 100644 --- a/libvips/conversion/ifthenelse.c +++ b/libvips/conversion/ifthenelse.c @@ -442,10 +442,9 @@ vips_ifthenelse_build( VipsObject *object ) if( vips__formatalike_vec( size, format, 2 ) ) return( -1 ); - if( vips_image_copy_fields_array( conversion->out, format ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, format ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, format ); if( vips_image_generate( conversion->out, vips_start_many, generate_fn, vips_stop_many, diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index 4b1fbbf9..f0c3a15a 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -284,10 +284,9 @@ vips_insert_build( VipsObject *object ) insert->main_processed, insert->sub_processed, NULL )) ) return( -1 ); - if( vips_image_copy_fields_array( conversion->out, arry ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_ANY, arry ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_ANY, arry ); /* Calculate geometry. */ diff --git a/libvips/conversion/msb.c b/libvips/conversion/msb.c index cefb366b..9a526e6f 100644 --- a/libvips/conversion/msb.c +++ b/libvips/conversion/msb.c @@ -201,10 +201,9 @@ vips_msb_build( VipsObject *object ) msb->in->BandFmt == VIPS_FORMAT_UCHAR ) return( vips_image_write( msb->in, conversion->out ) ); - if( vips_image_copy_fields( conversion->out, msb->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, msb->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, msb->in, NULL ); if( msb->band != -1 ) conversion->out->Bands = 1; diff --git a/libvips/conversion/recomb.c b/libvips/conversion/recomb.c index 5339b080..38bc2867 100644 --- a/libvips/conversion/recomb.c +++ b/libvips/conversion/recomb.c @@ -164,10 +164,9 @@ vips_recomb_build( VipsObject *object ) return( -1 ); recomb->coeff = t[0]; - if( vips_image_copy_fields( conversion->out, recomb->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, recomb->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, recomb->in, NULL ); conversion->out->Bands = recomb->m->Ysize; if( vips_bandfmt_isint( recomb->in->BandFmt ) ) diff --git a/libvips/conversion/replicate.c b/libvips/conversion/replicate.c index 8d0ec6ff..ee8e9d27 100644 --- a/libvips/conversion/replicate.c +++ b/libvips/conversion/replicate.c @@ -162,10 +162,9 @@ vips_replicate_build( VipsObject *object ) if( vips_image_pio_input( replicate->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, replicate->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, replicate->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, replicate->in, NULL ); conversion->out->Xsize *= replicate->across; conversion->out->Ysize *= replicate->down; diff --git a/libvips/conversion/rot.c b/libvips/conversion/rot.c index 19de6b85..8c9c9e73 100644 --- a/libvips/conversion/rot.c +++ b/libvips/conversion/rot.c @@ -294,13 +294,16 @@ vips_rot_build( VipsObject *object ) if( vips_image_pio_input( rot->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, rot->in ) ) + hint = rot->angle == VIPS_ANGLE_180 ? + VIPS_DEMAND_STYLE_THINSTRIP : + VIPS_DEMAND_STYLE_SMALLTILE; + + if( vips_image_pipelinev( conversion->out, hint, rot->in, NULL ) ) return( -1 ); switch( rot->angle ) { case VIPS_ANGLE_90: generate_fn = vips_rot90_gen; - hint = VIPS_DEMAND_STYLE_SMALLTILE; conversion->out->Xsize = rot->in->Ysize; conversion->out->Ysize = rot->in->Xsize; conversion->out->Xoffset = rot->in->Ysize; @@ -309,14 +312,12 @@ vips_rot_build( VipsObject *object ) case VIPS_ANGLE_180: generate_fn = vips_rot180_gen; - hint = VIPS_DEMAND_STYLE_THINSTRIP; conversion->out->Xoffset = rot->in->Xsize; conversion->out->Yoffset = rot->in->Ysize; break; case VIPS_ANGLE_270: generate_fn = vips_rot270_gen; - hint = VIPS_DEMAND_STYLE_SMALLTILE; conversion->out->Xsize = rot->in->Ysize; conversion->out->Ysize = rot->in->Xsize; conversion->out->Xoffset = 0; @@ -331,8 +332,6 @@ vips_rot_build( VipsObject *object ) return( 0 ); } - vips_demand_hint( conversion->out, hint, rot->in, NULL ); - if( vips_image_generate( conversion->out, vips_start_one, generate_fn, vips_stop_one, rot->in, rot ) ) diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index 46514608..99712ab6 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -113,7 +113,7 @@ vips_rot45_rot45( VipsImage *out, VipsImage *in ) g_assert( in->Xsize == in->Ysize ); g_assert( out->Xsize == out->Ysize ); g_assert( in->Xsize == out->Xsize ); - g_assert( in->Xsize % 2 == 0 ); + g_assert( in->Xsize % 2 == 1 ); /* Split the square into 8 triangles. Loop over the top-left one, * reflect into the others. @@ -201,8 +201,10 @@ vips_rot45_build( VipsObject *object ) return( -1 ); t[0] = vips_image_new_buffer(); - if( vips_image_copy_fields( t[0], rot45->in ) || - vips_image_write_prepare( t[0] ) ) + if( vips_image_pipelinev( t[0], + VIPS_DEMAND_STYLE_ANY, rot45->in, NULL ) ) + return( -1 ); + if( vips_image_write_prepare( t[0] ) ) return( -1 ); from = rot45->in; diff --git a/libvips/conversion/sequential.c b/libvips/conversion/sequential.c index d5b473f5..f64c031e 100644 --- a/libvips/conversion/sequential.c +++ b/libvips/conversion/sequential.c @@ -273,10 +273,9 @@ vips_sequential_build( VipsObject *object ) vips_object_local( object, t ); - if( vips_image_copy_fields( conversion->out, t ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_sequential_generate, vips_stop_one, t, sequential ) ) diff --git a/libvips/conversion/subsample.c b/libvips/conversion/subsample.c index 45f58643..d4ace719 100644 --- a/libvips/conversion/subsample.c +++ b/libvips/conversion/subsample.c @@ -209,10 +209,14 @@ vips_subsample_build( VipsObject *object ) vips_check_coding_known( class->nickname, subsample->in ) ) return( -1 ); + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, subsample->in, NULL ) ) + return( -1 ); /* Prepare output. Note: we round the output width down! */ - if( vips_image_copy_fields( conversion->out, subsample->in ) ) - return( -1 ); conversion->out->Xsize = subsample->in->Xsize / subsample->xfac; conversion->out->Ysize = subsample->in->Ysize / subsample->yfac; conversion->out->Xres = subsample->in->Xres / subsample->xfac; @@ -224,12 +228,6 @@ vips_subsample_build( VipsObject *object ) return( -1 ); } - /* Set demand hints. We want THINSTRIP, as we will be demanding a - * large area of input for each output line. - */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, subsample->in, NULL ); - /* Generate! If this is a very large shrink, then it's * probably faster to do it a pixel at a time. */ diff --git a/libvips/conversion/tilecache.c b/libvips/conversion/tilecache.c index 52c63c2b..058d94dc 100644 --- a/libvips/conversion/tilecache.c +++ b/libvips/conversion/tilecache.c @@ -740,10 +740,9 @@ vips_tile_cache_build( VipsObject *object ) if( vips_image_pio_input( block_cache->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, block_cache->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, block_cache->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, block_cache->in, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_tile_cache_gen, vips_stop_one, @@ -926,10 +925,9 @@ vips_line_cache_build( VipsObject *object ) if( vips_image_pio_input( block_cache->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, block_cache->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, block_cache->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, block_cache->in, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_line_cache_gen, vips_stop_one, diff --git a/libvips/conversion/zoom.c b/libvips/conversion/zoom.c index 0e7553f5..c41d4e05 100644 --- a/libvips/conversion/zoom.c +++ b/libvips/conversion/zoom.c @@ -349,13 +349,12 @@ vips_zoom_build( VipsObject *object ) vips_check_coding_known( class->nickname, zoom->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, zoom->in ) ) - return( -1 ); /* Set demand hints. THINSTRIP will prevent us from using * vips_zoom_paint_whole() much ... so go for FATSTRIP. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_FATSTRIP, zoom->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_FATSTRIP, zoom->in, NULL ) ) + return( -1 ); conversion->out->Xsize = zoom->in->Xsize * zoom->xfac; conversion->out->Ysize = zoom->in->Ysize * zoom->yfac; diff --git a/libvips/create/black.c b/libvips/create/black.c index 0081a2cf..9c4ad4a1 100644 --- a/libvips/create/black.c +++ b/libvips/create/black.c @@ -100,7 +100,7 @@ vips_black_build( VipsObject *object ) black->bands == 1 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/gaussmat.c b/libvips/create/gaussmat.c index e7ec4705..e1f624cf 100644 --- a/libvips/create/gaussmat.c +++ b/libvips/create/gaussmat.c @@ -116,7 +116,7 @@ vips_gaussmat_build( VipsObject *object ) width, height, 1, VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_write_prepare( create->out ) ) return( -1 ); diff --git a/libvips/create/gaussnoise.c b/libvips/create/gaussnoise.c index 035063a9..ccbae21c 100644 --- a/libvips/create/gaussnoise.c +++ b/libvips/create/gaussnoise.c @@ -131,7 +131,7 @@ vips_gaussnoise_build( VipsObject *object ) gaussnoise->width, gaussnoise->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/identity.c b/libvips/create/identity.c index d30d72b5..d596bd12 100644 --- a/libvips/create/identity.c +++ b/libvips/create/identity.c @@ -116,7 +116,7 @@ vips_identity_build( VipsObject *object ) VIPS_CODING_NONE, VIPS_INTERPRETATION_HISTOGRAM, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/logmat.c b/libvips/create/logmat.c index e5af0ee4..536599bb 100644 --- a/libvips/create/logmat.c +++ b/libvips/create/logmat.c @@ -138,7 +138,7 @@ vips_logmat_build( VipsObject *object ) width, height, 1, VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_write_prepare( create->out ) ) return( -1 ); diff --git a/libvips/create/point.c b/libvips/create/point.c index 0e03e515..b26c6cb4 100644 --- a/libvips/create/point.c +++ b/libvips/create/point.c @@ -103,7 +103,7 @@ vips_point_build( VipsObject *object ) point->width, point->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( t[0], + vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( t[0], NULL, vips_point_gen, NULL, point, NULL ) ) diff --git a/libvips/create/text.c b/libvips/create/text.c index 6677fdda..bb53fb91 100644 --- a/libvips/create/text.c +++ b/libvips/create/text.c @@ -233,7 +233,7 @@ vips_text_build( VipsObject *object ) text->bitmap.width, text->bitmap.rows, 1, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); for( y = 0; y < text->bitmap.rows; y++ ) diff --git a/libvips/create/xyz.c b/libvips/create/xyz.c index b50fad65..d4a9940e 100644 --- a/libvips/create/xyz.c +++ b/libvips/create/xyz.c @@ -163,7 +163,7 @@ vips_xyz_build( VipsObject *object ) VIPS_FORMAT_UINT, VIPS_CODING_NONE, VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/deprecated/lazy.c b/libvips/deprecated/lazy.c index 0980d169..0985e527 100644 --- a/libvips/deprecated/lazy.c +++ b/libvips/deprecated/lazy.c @@ -221,7 +221,7 @@ vips_image_open_lazy( VipsImage *image, /* Then 'start' creates the real image and 'gen' paints 'image' * with pixels from the real image on demand. */ - vips_demand_hint( image, image->dhint, NULL ); + vips_image_pipelinev( image, image->dhint, NULL ); if( vips_image_generate( image, open_lazy_start, open_lazy_generate, vips_stop_one, lazy, NULL ) ) diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 3c31ff14..4200847f 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -486,7 +486,7 @@ im_wrapmany( IMAGE **in, IMAGE *out, im_wrapmany_fn fn, void *a, void *b ) if( vips_image_pio_input( in[i] ) ) return( -1 ); } - vips_demand_hint_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ); + vips_image_pipeline_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ); /* Generate! */ @@ -959,7 +959,35 @@ im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...) return (-1); } - return (im_demand_hint_array (im, hint, ar)); + vips__demand_hint_array (im, hint, ar); + + return (0); +} + +int +im_cp_descv (IMAGE * im, ...) +{ + va_list ap; + int i; + IMAGE *ar[MAX_IMAGES]; + + va_start (ap, im); + for (i = 0; i < MAX_IMAGES && (ar[i] = va_arg (ap, IMAGE *)); i++) + ; + va_end (ap); + if (i == MAX_IMAGES) + { + im_error ("im_cp_descv", "%s", _("too many images")); + return (-1); + } + + return (vips__image_copy_fields_array (im, ar)); +} + +int +im_cp_desc(IMAGE *out, IMAGE *in ) +{ + return( im_cp_descv( out, in, NULL)); } int @@ -1925,7 +1953,7 @@ im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) DOUBLEMASK *msk; if( vips_gaussmat( &t, sigma, min_ampl, - "seperable", TRUE, + "separable", TRUE, NULL ) ) return( NULL ); if( !(msk = im_vips2mask( t, filename )) ) { @@ -1964,7 +1992,7 @@ im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) if( vips_gaussmat( &t, sigma, min_ampl, "integer", TRUE, - "seperable", TRUE, + "separable", TRUE, NULL ) ) return( NULL ); if( !(msk = im_vips2imask( t, filename )) ) { diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index 78689740..eed5d08c 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -280,11 +280,11 @@ read_csv( FILE *fp, VipsImage *out, fsetpos( fp, &pos ); } + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); vips_image_init_fields( out, columns, lines, 1, VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Just reading the header? We are done. */ diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index f070527c..877846c8 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -263,7 +263,8 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, * easy. */ layer->image = vips_image_new(); - if( vips_image_copy_fields( layer->image, save->ready ) ) { + if( vips_image_pipelinev( layer->image, + VIPS_DEMAND_STYLE_ANY, save->ready, NULL ) ) { layer_free( layer ); return( NULL ); } diff --git a/libvips/foreign/fits.c b/libvips/foreign/fits.c index db0ebdef..ac0e4a9c 100644 --- a/libvips/foreign/fits.c +++ b/libvips/foreign/fits.c @@ -296,11 +296,11 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out ) else type = VIPS_INTERPRETATION_MULTIBAND; + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); vips_image_init_fields( out, width, height, bands, format, VIPS_CODING_NONE, type, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Read all keys into meta. */ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index b539aa25..e9bb5048 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -776,7 +776,7 @@ vips_foreign_load_start( VipsImage *out, void *a, void *b ) /* We have to tell vips that out depends on real. We've set * the demand hint below, but not given an input there. */ - vips_demand_hint( load->out, load->out->dhint, + vips_image_pipelinev( load->out, load->out->dhint, load->real, NULL ); } @@ -874,7 +874,7 @@ vips_foreign_load_build( VipsObject *object ) /* ->header() should set the dhint. It'll default to the safe * SMALLTILE if header() did not set it. */ - vips_demand_hint( load->out, load->out->dhint, NULL ); + vips_image_pipelinev( load->out, load->out->dhint, NULL ); /* Then 'start' creates the real image and 'gen' fetches * pixels for @out from @real on demand. diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 90ef0c5f..a7e2a234 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -764,7 +764,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) interpretation, xres, yres ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c index 75e0e27a..f64d35f8 100644 --- a/libvips/foreign/magick2vips.c +++ b/libvips/foreign/magick2vips.c @@ -337,7 +337,7 @@ parse_header( Read *read ) */ im->Coding = VIPS_CODING_NONE; - vips_demand_hint( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Three ways to loop over attributes / properties :-( */ diff --git a/libvips/foreign/openexr2vips.c b/libvips/foreign/openexr2vips.c index bef7f1e3..c6a13f53 100644 --- a/libvips/foreign/openexr2vips.c +++ b/libvips/foreign/openexr2vips.c @@ -212,9 +212,9 @@ read_header( Read *read, VipsImage *out ) VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); if( read->tiles ) - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); else - vips_demand_hint( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); } int diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index a2a7760b..59ccd931 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -220,7 +220,7 @@ readslide_new( const char *filename, VipsImage *out, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { char buf[256]; @@ -231,7 +231,7 @@ readslide_new( const char *filename, VipsImage *out, rslide->downsample = openslide_get_level_downsample( rslide->osr, level ); vips_image_set_int( out, "slide-level", level ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Try to get tile width/height. An undocumented, experimental * feature. diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 8367fc25..bd139538 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1236,7 +1236,7 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out ) * the cache we are quite happy serving that if anything downstream * would like it. */ - vips_demand_hint( raw, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( raw, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); if( vips_image_generate( raw, tiff_seq_start, tiff_fill_region, tiff_seq_stop, @@ -1413,7 +1413,7 @@ read_stripwise( ReadTiff *rtiff, VipsImage *out ) if( parse_header( rtiff, t[0] ) ) return( -1 ); - vips_demand_hint( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); if( !tfget32( rtiff->tiff, TIFFTAG_ROWSPERSTRIP, &rtiff->rows_per_strip ) ) diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 02a87e38..5062741a 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -351,7 +351,7 @@ png2vips_header( Read *read, VipsImage *out ) /* Sequential mode needs thinstrip to work with things like * vips_shrink(). */ - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Fetch the ICC profile. @name is useless, something like "icc" or * "ICC Profile" etc. Ignore it. diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 4996e5c2..031ebad5 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -153,7 +153,7 @@ read_header( Read *read, VipsImage *out ) VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); return( 0 ); } diff --git a/libvips/histogram/hist_local.c b/libvips/histogram/hist_local.c index 70456091..41365738 100644 --- a/libvips/histogram/hist_local.c +++ b/libvips/histogram/hist_local.c @@ -256,16 +256,14 @@ vips_hist_local_build( VipsObject *object ) 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_pipelinev( local->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) + return( -1 ); + local->out->Xsize -= local->width - 1; + local->out->Ysize -= local->height - 1; if( vips_image_generate( local->out, vips_hist_local_start, diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index a2c024ef..48c1c6b3 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -157,10 +157,9 @@ vips_histogram_build( VipsObject *object ) */ histogram->ready = size; - if( vips_image_copy_fields_array( histogram->out, histogram->ready ) ) + if( vips_image_pipeline_array( histogram->out, + VIPS_DEMAND_STYLE_THINSTRIP, histogram->ready ) ) return( -1 ); - vips_demand_hint_array( histogram->out, - VIPS_DEMAND_STYLE_THINSTRIP, histogram->ready ); histogram->out->Xsize = VIPS_IMAGE_N_PELS( histogram->ready[0] ); histogram->out->Ysize = 1; diff --git a/libvips/histogram/maplut.c b/libvips/histogram/maplut.c index d6df93fb..f3366c9e 100644 --- a/libvips/histogram/maplut.c +++ b/libvips/histogram/maplut.c @@ -564,10 +564,9 @@ vips_maplut_build( VipsObject *object ) vips_image_pio_input( in ) ) return( -1 ); - if( vips_image_copy_fieldsv( maplut->out, in, lut, NULL ) ) + if( vips_image_pipelinev( maplut->out, + VIPS_DEMAND_STYLE_THINSTRIP, in, lut, NULL ) ) return( -1 ); - vips_demand_hint( maplut->out, VIPS_DEMAND_STYLE_THINSTRIP, - in, lut, NULL ); maplut->out->BandFmt = lut->BandFmt; /* Output has same number of bands as LUT, unless LUT has 1 band, in diff --git a/libvips/histogram/stdif.c b/libvips/histogram/stdif.c index b64052c8..59e0fe95 100644 --- a/libvips/histogram/stdif.c +++ b/libvips/histogram/stdif.c @@ -249,16 +249,14 @@ vips_stdif_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( vips_image_copy_fields( stdif->out, in ) ) - return( -1 ); - stdif->out->Xsize -= stdif->width - 1; - stdif->out->Ysize -= stdif->height - 1; - /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause * too many recalculations on overlaps. */ - vips_demand_hint( stdif->out, - VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + if( vips_image_pipelinev( stdif->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) + return( -1 ); + stdif->out->Xsize -= stdif->width - 1; + stdif->out->Ysize -= stdif->height - 1; if( vips_image_generate( stdif->out, vips_start_one, diff --git a/libvips/include/vips/generate.h b/libvips/include/vips/generate.h index 4e26d833..f7af6884 100644 --- a/libvips/include/vips/generate.h +++ b/libvips/include/vips/generate.h @@ -73,9 +73,9 @@ int vips_image_generate( VipsImage *im, void *a, void *b ); -void vips_demand_hint_array( VipsImage *image, +int vips_image_pipeline_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ); -void vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) +int vips_image_pipelinev( VipsImage *image, VipsDemandStyle hint, ... ) __attribute__((sentinel)); #ifdef __cplusplus diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 95da4126..f27f205d 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -118,11 +118,6 @@ void vips_image_init_fields( VipsImage *image, VipsInterpretation interpretation, double xres, double yres ); -int vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ); -int vips_image_copy_fieldsv( VipsImage *out, VipsImage *in1, ... ) - __attribute__((sentinel)); -int vips_image_copy_fields( VipsImage *out, VipsImage *in ); - void vips_image_set( VipsImage *image, const char *field, GValue *value ); int vips_image_get( const VipsImage *image, const char *field, GValue *value_copy ); diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index 123dda67..7b270002 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -170,6 +170,11 @@ VipsArgumentInstance *vips__argument_get_instance( VipsArgument *vips__argument_table_lookup( VipsArgumentTable *table, GParamSpec *pspec); +void vips__demand_hint_array( struct _VipsImage *image, + int hint, struct _VipsImage **in ); +int vips__image_copy_fields_array( struct _VipsImage *out, + struct _VipsImage *in[] ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 04a0e151..7324c653 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -208,9 +208,12 @@ extern "C" { #define im_guess_libdir vips_guess_libdir #define im__global_lock vips__global_lock -#define im_cp_desc vips_image_copy_fields -#define im_cp_descv vips_image_copy_fieldsv -#define im_cp_desc_array vips_image_copy_fields_array +int im_cp_desc(IMAGE *out, IMAGE *in ); +int im_cp_descv (IMAGE * im, ...); +#define im_cp_desc_array(I, A) vips__image_copy_fields_array(I, A) +int im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...); +#define im_demand_hint_array( A, B, C ) (vips__demand_hint_array( A, B, C ), 0) + #define im_image vips_image_new_from_memory #define im_binfile vips_image_new_from_file_raw #define im__open_temp vips_image_new_temp_file @@ -324,9 +327,6 @@ VipsDemandStyle im_char2dhint( const char *str ); #define im_rect_dup vips_rect_dup #define im_rect_normalise vips_rect_normalise -int im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...); -#define im_demand_hint_array( A, B, C ) (vips_demand_hint_array( A, B, C ), 0) - #define im_start_one vips_start_one #define im_stop_one vips_stop_one #define im_start_many vips_start_many diff --git a/libvips/iofuncs/generate.c b/libvips/iofuncs/generate.c index 174671fe..f23dccc1 100644 --- a/libvips/iofuncs/generate.c +++ b/libvips/iofuncs/generate.c @@ -274,26 +274,12 @@ vips__link_map( VipsImage *image, gboolean upstream, return( result ); } -/** - * vips_demand_hint_array: - * @image: image to set hint for - * @hint: hint for this image - * @in: array of input images to this operation - * - * Operations can set demand hints, that is, hints to the VIPS IO system about - * the type of region geometry this operation works best with. For example, - * operations which transform coordinates will usually work best with - * %VIPS_DEMAND_STYLE_SMALLTILE, operations which work on local windows of - * pixels will like %VIPS_DEMAND_STYLE_FATSTRIP. - * - * VIPS uses the list of input images to build the tree of operations it needs - * for the cache invalidation system. You have to call this function, or its - * varargs friend vips_demand_hint(). - * - * See also: vips_demand_hint(), vips_image_generate(). +/* We have to have this as a separate entry point so we can support the old + * vips7 API. */ void -vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) +vips__demand_hint_array( VipsImage *image, + VipsDemandStyle hint, VipsImage **in ) { int i, len, nany; VipsDemandStyle set_hint; @@ -327,7 +313,7 @@ vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) image->dhint = set_hint; #ifdef DEBUG - printf( "vips_demand_hint_array: set dhint for \"%s\" to %s\n", + printf( "vips_image_pipeline_array: set dhint for \"%s\" to %s\n", image->filename, vips_enum_nick( VIPS_TYPE_DEMAND_STYLE, image->dhint ) ); printf( "\toperation requested %s\n", @@ -354,17 +340,62 @@ vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) } /** - * vips_demand_hint: - * @image: image to set hint for - * @hint: hint for this image - * @Varargs: %NULL-terminated list of input images to this operation + * vips_image_pipeline_array: + * @image: output image + * @hint: demand hint for @image + * @in: %NULL-terminated array of input images * - * Build an array and call vips_demand_hint_array(). + * Add an image to a pipeline. @image depends on all of the images in @in, + * @image prefers to supply pixels according to @hint. * - * See also: vips_demand_hint(), vips_image_generate(). + * Operations can set demand hints, that is, hints to the VIPS IO system about + * the type of region geometry this operation works best with. For example, + * operations which transform coordinates will usually work best with + * %VIPS_DEMAND_STYLE_SMALLTILE, operations which work on local windows of + * pixels will like %VIPS_DEMAND_STYLE_FATSTRIP. + * + * Header fields in @image are set from the fields in @in, with lower-numbered + * images in @in taking priority. + * For example, if @in[0] and @in[1] both have an item + * called "icc-profile", it's the profile attached to @in[0] that will end up + * on @image. + * Image history is completely copied from all @in. @image will have the history + * of all the input images. + * The array of input images can be empty, meaning @image is at the start of a + * pipeline. + * + * VIPS uses the list of input images to build the tree of operations it needs + * for the cache invalidation system. + * + * See also: vips_image_pipelinev(), vips_image_generate(). + * + * Returns: 0 on success, -1 on error. */ -void -vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) +int +vips_image_pipeline_array( VipsImage *image, + VipsDemandStyle hint, VipsImage **in ) +{ + vips__demand_hint_array( image, hint, in ); + + if( in[0] && + vips__image_copy_fields_array( image, in ) ) + return( -1 ); + + return( 0 ); +} + +/** + * vips_image_pipelinev: + * @image: output image of pipeline + * @hint: hint for this image + * @...: %NULL-terminated list of input images + * + * Build an array and call vips_image_pipeline_array(). + * + * See also: vips_image_generate(). + */ +int +vips_image_pipelinev( VipsImage *image, VipsDemandStyle hint, ... ) { va_list ap; int i; @@ -376,14 +407,14 @@ vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) ; va_end( ap ); if( i == MAX_IMAGES ) { - vips_warn( "vips_demand_hint", "%s", _( "too many images" ) ); + vips_warn( "vips_image_pipeline", "%s", _( "too many images" ) ); /* Make sure we have a sentinel there. */ ar[i - 1] = NULL; } - vips_demand_hint_array( image, hint, ar ); + return( vips_image_pipeline_array( image, hint, ar ) ); } /** diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index f430bd53..d3ebbb4c 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -604,13 +604,13 @@ vips_image_get_data( VipsImage *image ) * @yres: vertical resolution, pixels per millimetre * * A convenience function to set the header fields after creating an image. - * Normally you copy the fields from one of your input images with - * vips_image_copy_fields() and then make + * Normally you copy the fields from your input images with + * vips_image_pipelinev() and then make * any adjustments you need, but if you are creating an image from scratch, * for example im_black() or im_jpeg2vips(), you do need to set all the * fields yourself. * - * See also: vips_image_copy_fields(). + * See also: vips_image_pipelinev(). */ void vips_image_init_fields( VipsImage *image, @@ -655,7 +655,7 @@ meta_cp_field( VipsMeta *meta, VipsImage *dst ) return( NULL ); } -/* Copy meta on to dst. Called from vips_cp_desc(). +/* Copy meta on to dst. */ static int meta_cp( VipsImage *dst, const VipsImage *src ) @@ -671,31 +671,11 @@ meta_cp( VipsImage *dst, const VipsImage *src ) return( 0 ); } -/** - * vips_image_copy_fields_array: - * @out: image to copy to - * @in: %NULL-terminated array of images to copy from - * - * Copy fields from all the input images to the output image. There must be at - * least one input image. - * - * The first input image is used to set the main fields of @out (@width, - * @coding and so on). - * - * Metadata from all the images is merged on to @out, with lower-numbered items - * overriding higher. So for example, if @in[0] and @in[1] both have an item - * called "icc-profile", it's the profile attached to @in[0] that will end up - * on @out. - * - * Image history is completely copied from all @in. @out will have the history - * of all the input images. - * - * See also: vips_image_copy_fieldsv(), vips_image_copy_fields(). - * - * Returns: 0 on success, -1 on error. +/* We have to have this as a separate entry point so we can support the old + * vips7 API. */ int -vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ) +vips__image_copy_fields_array( VipsImage *out, VipsImage *in[] ) { int i; int ni; @@ -740,63 +720,6 @@ vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ) return( 0 ); } -/* Max number of images we can handle. - */ -#define MAX_IMAGES (1000) - -/** - * vips_image_copy_fieldsv: - * @out: image to copy to - * @in1: first image to copy from - * @Varargs: %NULL-terminated list of images to copy from - * - * Copy fields from all the input images to the output image. A convenience - * function over vips_image_copy_fields_array(). - * - * See also: vips_image_copy_fields_array(), vips_image_copy_fields(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_image_copy_fieldsv( VipsImage *out, VipsImage *in1, ... ) -{ - va_list ap; - int i; - VipsImage *in[MAX_IMAGES]; - - in[0] = in1; - va_start( ap, in1 ); - for( i = 1; i < MAX_IMAGES && - (in[i] = va_arg( ap, VipsImage * )); i++ ) - ; - va_end( ap ); - if( i == MAX_IMAGES ) { - vips_error( "vips_image_copy_fieldsv", - "%s", _( "too many images" ) ); - return( -1 ); - } - - return( vips_image_copy_fields_array( out, in ) ); -} - -/** - * vips_image_copy_fields: - * @out: image to copy to - * @in: image to copy from - * - * Copy fields from @in to @out. A convenience - * function over vips_image_copy_fields_array(). - * - * See also: vips_image_copy_fields_array(), vips_image_copy_fieldsv(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_image_copy_fields( VipsImage *out, VipsImage *in ) -{ - return( vips_image_copy_fieldsv( out, in, NULL ) ); -} - /** * vips_image_set: * @image: image to set the metadata on diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 4775d554..22ac7927 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -105,7 +105,7 @@ * @VIPS_DEMAND_STYLE_THINSTRIP: demand in thin (typically 1 pixel high) strips * @VIPS_DEMAND_STYLE_ANY: demand geometry does not matter * - * See vips_demand_hint(). Operations can hint to the VIPS image IO system about + * See vips_image_pipelinev(). Operations can hint to the VIPS image IO system about * the kind of demand geometry they prefer. * * These demand styles are given below in order of increasing @@ -134,7 +134,7 @@ * file (even indirectly) so any demand style is OK. It's used for things like * im_black() where the pixels are calculated. * - * See also: vips_demand_hint(). + * See also: vips_image_pipelinev(). */ /** @@ -1939,10 +1939,9 @@ int vips_image_write( VipsImage *image, VipsImage *out ) { if( vips_image_pio_input( image ) || - vips_image_copy_fields( out, image ) ) + vips_image_pipelinev( out, + VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ) ) return( -1 ); - vips_demand_hint( out, - VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ); /* We generate from @image partially, so we need to keep it about as * long as @out is about. diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index 4e91f9ef..358fbadd 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -1067,14 +1067,14 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask, } if( vips_image_pio_input( in ) || - vips_image_copy_fields( out, in ) ) + vips_image_pipelinev( out, + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); if( mask ) { - if( vips_image_copy_fields( mask, in ) ) + if( vips_image_pipelinev( mask, + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); - vips_demand_hint( mask, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); mask->Bands = 1; mask->BandFmt = VIPS_FORMAT_UCHAR; diff --git a/libvips/resample/affine.c b/libvips/resample/affine.c index 76789ba6..6d07d1d2 100644 --- a/libvips/resample/affine.c +++ b/libvips/resample/affine.c @@ -381,6 +381,7 @@ vips_affine_build( VipsObject *object ) VipsImage *in; gboolean repack; + VipsDemandStyle hint; int window_size; int window_offset; double edge; @@ -479,21 +480,19 @@ vips_affine_build( VipsObject *object ) return( -1 ); in = t[1]; - if( vips_image_copy_fields( resample->out, in ) ) - return( -1 ); - - resample->out->Xsize = affine->trn.oarea.width; - resample->out->Ysize = affine->trn.oarea.height; - /* Normally SMALLTILE ... except if this is a size up/down affine. */ if( affine->trn.b == 0.0 && affine->trn.c == 0.0 ) - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + hint = VIPS_DEMAND_STYLE_FATSTRIP; else - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); + hint = VIPS_DEMAND_STYLE_SMALLTILE; + + if( vips_image_pipelinev( resample->out, hint, in, NULL ) ) + return( -1 ); + + resample->out->Xsize = affine->trn.oarea.width; + resample->out->Ysize = affine->trn.oarea.height; /* Generate! */ diff --git a/libvips/resample/quadratic.c b/libvips/resample/quadratic.c index 5ab0158b..5fedd545 100644 --- a/libvips/resample/quadratic.c +++ b/libvips/resample/quadratic.c @@ -251,7 +251,11 @@ vips_quadratic_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_quadratic_parent_class )->build( object ) ) return( -1 ); - if( vips_image_copy_fields( resample->out, resample->in ) ) + /* We have the whole of the input in memory, so we can generate any + * output. + */ + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_ANY, resample->in, NULL ) ) return( -1 ); in = resample->in; @@ -315,12 +319,6 @@ vips_quadratic_build( VipsObject *object ) if( vips_image_wio_input( in ) ) return( -1 ); - /* We have the whole of the input in memory, so we can generate any - * output. - */ - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_ANY, resample->in, NULL ); - if( vips_image_generate( resample->out, vips_start_one, vips_quadratic_gen, vips_stop_one, in, quadratic ) ) diff --git a/libvips/resample/shrink.c b/libvips/resample/shrink.c index cff50c7c..14ee61a1 100644 --- a/libvips/resample/shrink.c +++ b/libvips/resample/shrink.c @@ -336,15 +336,13 @@ vips_shrink_build( VipsObject *object ) shrink->yshrink == 1.0 ) return( vips_image_write( resample->in, resample->out ) ); - if( vips_image_copy_fields( resample->out, resample->in ) ) - return( -1 ); - /* THINSTRIP will work, anything else will break seq mode. If you * combine shrink with conv you'll need to use a line cache to maintain * sequentiality. */ - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_THINSTRIP, resample->in, NULL ); + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_THINSTRIP, resample->in, NULL ) ) + return( -1 ); /* Size output. Note: we round the output width down! * From dd4a8435bf46bfc7d1ed89b3c099abd09aeb0c2e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 22 Oct 2013 14:43:27 +0100 Subject: [PATCH 11/26] sync --- TODO | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/TODO b/TODO index 0f325b8b..04fe80a9 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,8 @@ - do conv and morph quickly as simple wrappers over the vips7 operations +- add vips_gaussian_blur() with approx / int / float precision, maybe + vips_resize() as well? + - do much fancier profiling with timing on all locks saved in memory and dumped on exit @@ -8,21 +11,6 @@ - use g_log() instead of vips_info() -- do we always call copy_fields and demand_hint with ALL input images? what - about the operators in conversion? - - could we add something to check that the two calls agree on the image lists? - I think they should - - combine the two into one call? - - hist_find at least does not call demand_hint, same for all writeline output - operators I guess - - how about - - vips_image_pipelinev( out, hint, in1, ... ) - - object construction is threadsafe, but class construction is not https://github.com/jcupitt/libvips/issues/64 From 767a11a5e80e0f99f5717e3c1871ade1541f767e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 22 Oct 2013 17:12:50 +0100 Subject: [PATCH 12/26] man page fixes --- man/header.1 | 6 ++++++ man/vips.1 | 16 ++++++++-------- man/vipsthumbnail.1 | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/man/header.1 b/man/header.1 index 254dfc3e..4c8404e7 100644 --- a/man/header.1 +++ b/man/header.1 @@ -17,6 +17,12 @@ the VIPS extension block: the XML defining the image metadata. You can alter this, then reattach with .B edvips(1). +.TP +.B -a, --all +Show all fields. Normally +.B header +just shows a one-line summary. + .SH EXAMPLES $ header -f Xsize ~/pics/*.v 1024 diff --git a/man/vips.1 b/man/vips.1 index 7f393174..4186470b 100644 --- a/man/vips.1 +++ b/man/vips.1 @@ -57,15 +57,15 @@ Run a vips8 operation. Operation options must follow the operation name. Get a "usage" message for an operation $ vips add - VipsAdd (add), add two images - add left right out + usage: + add left right out + add two images where: - left :: VipsImage (input) - right :: VipsImage (input) - out :: VipsImage (output) - optional arguments: - imtest :: VipsImage (input) - booltest :: gboolean (input) + left - Left-hand image argument, input VipsImage + right - Right-hand image argument, input VipsImage + out - Output image, output VipsImage + operation flags: sequential-unbuffered + add: too few arguments .SH RETURN VALUE returns 0 on success and non-zero on error. diff --git a/man/vipsthumbnail.1 b/man/vipsthumbnail.1 index abf67d1c..91f997fc 100644 --- a/man/vipsthumbnail.1 +++ b/man/vipsthumbnail.1 @@ -55,7 +55,7 @@ The default value is meaning JPEG output, with .B tn_ prepended. You can add format options too, for example -.B tn_%s.jpg:20 +.B tn_%s.jpg[Q=20] will write JPEG images with Q set to 20. .TP From 29a0c398aaf8bc9654f10fbf7858f6b046c9015e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 23 Oct 2013 08:49:44 +0100 Subject: [PATCH 13/26] todo --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 04fe80a9..f63987f6 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,7 @@ +- vipsthumbnail needs non-square BBs, perhaps + + --size 200x300 + - do conv and morph quickly as simple wrappers over the vips7 operations - add vips_gaussian_blur() with approx / int / float precision, maybe From 8d270d49c46d2390eb4d0365efed7c85d129b037 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 23 Oct 2013 09:37:45 +0100 Subject: [PATCH 14/26] allow non-square bounding boxes in vipsthumbnail thanks seth --- ChangeLog | 1 + man/vipsthumbnail.1 | 3 ++- tools/vipsthumbnail.c | 43 ++++++++++++++++++++++++++++--------------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index ba178acf..e8646834 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ - add "interlace" option to vips_jpegsave() - remove vips_image_copy_fields() and vips_demand_hint() and add vips_image_pipeline() to do both jobs +- vipsthumbnail allows non-square bounding boxes, thanks seth 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/man/vipsthumbnail.1 b/man/vipsthumbnail.1 index 91f997fc..4dfee8bd 100644 --- a/man/vipsthumbnail.1 +++ b/man/vipsthumbnail.1 @@ -40,7 +40,8 @@ Set the output thumbnail size to .B N x .B N -pixels. The image is shrunk so that it just fits within this area, Images +pixels. You can use MxN to specify a rectangular bounding box. +The image is shrunk so that it just fits within this area, images which are smaller than this are expanded. .TP diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 4c618705..9b589558 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -53,7 +53,9 @@ #include -static int thumbnail_size = 128; +static char *thumbnail_size = "128"; +static int thumbnail_width = 128; +static int thumbnail_height = 128; static char *output_format = "tn_%s.jpg"; static char *interpolator = "bilinear"; static char *export_profile = NULL; @@ -68,8 +70,8 @@ static gboolean nodelete_profile = FALSE; static GOptionEntry options[] = { { "size", 's', 0, - G_OPTION_ARG_INT, &thumbnail_size, - N_( "set thumbnail size to SIZE" ), + G_OPTION_ARG_STRING, &thumbnail_size, + N_( "shrink to SIZE or to WIDTHxHEIGHT" ), N_( "SIZE" ) }, { "output", 'o', 0, G_OPTION_ARG_STRING, &output_format, @@ -115,13 +117,14 @@ static GOptionEntry options[] = { static int calculate_shrink( int width, int height, double *residual ) { - /* We shrink to make the largest dimension equal to size. + /* Calculate the horizontal and vertical shrink we'd need to fit the + * image to the bounding box, and pick the biggest. */ - int dimension = IM_MAX( width, height ); + double horizontal = (double) width / thumbnail_width; + double vertical = (double) height / thumbnail_height; + double factor = VIPS_MAX( horizontal, vertical ); - double factor = dimension / (double) thumbnail_size; - - /* If the shrink factor is <=1.0, we need to zoom rather than shrink. + /* If the shrink factor is <= 1.0, we need to zoom rather than shrink. * Just set the factor to 1 in this case. */ double factor2 = factor < 1.0 ? 1.0 : factor; @@ -130,14 +133,15 @@ calculate_shrink( int width, int height, double *residual ) */ int shrink = floor( factor2 ); - /* Size after int shrink. - */ - int isize = floor( dimension / shrink ); + if( residual ) { + /* Width after int shrink. + */ + int iwidth = width / shrink; - /* Therefore residual scale factor is. - */ - if( residual ) - *residual = thumbnail_size / (double) isize; + /* Therefore residual scale factor is. + */ + *residual = (width / factor) / iwidth; + } return( shrink ); } @@ -555,6 +559,15 @@ main( int argc, char **argv ) g_option_context_free( context ); + if( sscanf( thumbnail_size, "%d x %d", + &thumbnail_width, &thumbnail_height ) != 2 ) { + if( sscanf( thumbnail_size, "%d", &thumbnail_width ) != 1 ) + vips_error_exit( "unable to parse size \"%s\" -- " + "use eg. 128 or 200x300", thumbnail_size ); + + thumbnail_height = thumbnail_width; + } + for( i = 1; i < argc; i++ ) { /* Hang resources for this processing off this. */ From 5475cabbf2340b0c720346e702261afb687f3091 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 23 Oct 2013 13:15:31 +0100 Subject: [PATCH 15/26] redo im_dilate()/erode() as classes just a thin wrapper over the vips7 operations for now --- ChangeLog | 3 +- TODO | 3 - libvips/convolution/Makefile.am | 1 + libvips/convolution/convolution.c | 2 + libvips/convolution/morph.c | 179 ++++++++++++++++++++++++++++++ libvips/include/vips/Makefile.am | 1 + libvips/include/vips/enumtypes.h | 3 + libvips/include/vips/morphology.h | 30 ++++- libvips/iofuncs/Makefile.am | 1 + libvips/iofuncs/enumtypes.c | 19 ++++ libvips/morphology/morphology.c | 66 ----------- 11 files changed, 235 insertions(+), 73 deletions(-) create mode 100644 libvips/convolution/morph.c diff --git a/ChangeLog b/ChangeLog index e8646834..3f5bae5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 19/10/13 started 7.37.0 -- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask() as classes +- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(), + im_erode() as classes - vips_init() now does some ABI compat checking, though this change requires an ABI break - add "interlace" option to vips_jpegsave() diff --git a/TODO b/TODO index f63987f6..d3c45e78 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,3 @@ -- vipsthumbnail needs non-square BBs, perhaps - - --size 200x300 - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index 30f185c5..b089c7fa 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -4,6 +4,7 @@ libconvolution_la_SOURCES = \ convolution.c \ pconvolution.h \ conv.c \ + morph.c \ convol_dispatch.c \ im_addgnoise.c \ im_compass.c \ diff --git a/libvips/convolution/convolution.c b/libvips/convolution/convolution.c index 824e9b80..d726762f 100644 --- a/libvips/convolution/convolution.c +++ b/libvips/convolution/convolution.c @@ -147,6 +147,8 @@ void vips_convolution_operation_init( void ) { extern int vips_conv_get_type( void ); + extern int vips_morph_get_type( void ); vips_conv_get_type(); + vips_morph_get_type(); } diff --git a/libvips/convolution/morph.c b/libvips/convolution/morph.c new file mode 100644 index 00000000..6b699ff0 --- /dev/null +++ b/libvips/convolution/morph.c @@ -0,0 +1,179 @@ +/* morphology + * + * 23/10/13 + * - from vips_conv() + */ + +/* + + 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 + + */ + +/* This is a simple wrapper over the old vips7 functions. At some point we + * should rewrite this as a pure vips8 class and redo the vips7 functions as + * wrappers over this. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#include "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + VipsOperationMorphology morph; + +} VipsMorph; + +typedef VipsConvolutionClass VipsMorphClass; + +G_DEFINE_TYPE( VipsMorph, vips_morph, VIPS_TYPE_CONVOLUTION ); + +static int +vips_morph_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = (VipsConvolution *) object; + VipsMorph *morph = (VipsMorph *) object; + + INTMASK *imsk; + + g_object_set( morph, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_morph_parent_class )->build( object ) ) + return( -1 ); + + if( !(imsk = im_vips2imask( convolution->M, class->nickname )) || + !im_local_imask( convolution->out, imsk ) ) + return( -1 ); + + switch( morph->morph ) { + case VIPS_OPERATION_MORPHOLOGY_DILATE: + if( im_dilate( convolution->in, convolution->out, imsk ) ) + return( -1 ); + break; + + case VIPS_OPERATION_MORPHOLOGY_ERODE: + if( im_erode( convolution->in, convolution->out, imsk ) ) + return( -1 ); + break; + + default: + g_assert( 0 ); + } + + return( 0 ); +} + +static void +vips_morph_class_init( VipsMorphClass *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 = "morph"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_morph_build; + + VIPS_ARG_ENUM( class, "morph", 103, + _( "Morphology" ), + _( "Morphological operation to perform" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMorph, morph ), + VIPS_TYPE_OPERATION_MORPHOLOGY, + VIPS_OPERATION_MORPHOLOGY_ERODE ); + +} + +static void +vips_morph_init( VipsMorph *morph ) +{ + morph->morph = VIPS_OPERATION_MORPHOLOGY_ERODE; +} + +/** + * vips_morph: + * @in: input image + * @out: output image + * @mask: morphology with this mask + * @morph: operation to perform + * @...: %NULL-terminated list of optional named arguments + * + * Performs a morphological operation on @in using @mask as a + * structuring element. + * + * The image should have 0 (black) for no object and 255 + * (non-zero) for an object. Note that this is the reverse of the usual + * convention for these operations, but more convenient when combined with the + * boolean operators. The output image is the same + * size as the input image: edge pxels are made by expanding the input image + * as necessary. + * + * Mask coefficients can be either 0 (for object) or 255 (for background) + * or 128 (for do not care). The origin of the mask is at location + * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been + * based on the book "Fundamentals of Digital Image Processing" by A. Jain, + * pp 384-388, Prentice-Hall, 1989. + * + * For #VIPS_OPERATION_MOPHOLOGY_ERODE, + * the whole mask must match for the output pixel to be + * set, that is, the result is the logical AND of the selected input pixels. + * + * For #VIPS_OPERATION_MOPHOLOGY_DILATE, + * the output pixel is set if any part of the mask + * matches, that is, the result is the logical OR of the selected input pixels. + * + * See the boolean operations vips_andimage(), vips_orimage() and + * vips_eorimage() + * for analogues of the usual set difference and set union operations. + * + * Operations are performed using the processor's vector unit, + * if possible. Disable this with --vips-novector or IM_NOVECTOR. + * + * Returns: 0 on success, -1 on error + */ +int +vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, + VipsOperationMorphology morph, ... ) +{ + va_list ap; + int result; + + va_start( ap, morph ); + result = vips_call_split( "morph", ap, in, out, mask, morph ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 415f0cb4..17e219f7 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -65,6 +65,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/colour.h \ ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/convolution.h \ + ${top_srcdir}/libvips/include/vips/morphology.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.h: $(vips_scan_headers) Makefile diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index fa619dfb..a5c6df37 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -74,6 +74,9 @@ GType vips_operation_flags_get_type (void) G_GNUC_CONST; /* enumerations from "../../../libvips/include/vips/convolution.h" */ GType vips_precision_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_PRECISION (vips_precision_get_type()) +/* enumerations from "../../../libvips/include/vips/morphology.h" */ +GType vips_operation_morphology_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_OPERATION_MORPHOLOGY (vips_operation_morphology_get_type()) /* enumerations from "../../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ARGUMENT_FLAGS (vips_argument_flags_get_type()) diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index 1e383097..7014ceb2 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -31,13 +31,37 @@ */ -#ifndef IM_MORPHOLOGY_H -#define IM_MORPHOLOGY_H +#ifndef VIPS_MORPHOLOGY_H +#define VIPS_MORPHOLOGY_H #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ +/** + * VipsOperationMorphology: + * @VIPS_OPERATION_MORPHOLOGY_ERODE: true if all set + * @VIPS_OPERATION_MORPHOLOGY_DILATE: true if one set + * + * More like hit-miss, really. + * + * See also: vips_morph(). + */ + +typedef enum { + VIPS_OPERATION_MORPHOLOGY_ERODE, + VIPS_OPERATION_MORPHOLOGY_DILATE, + VIPS_OPERATION_MORPHOLOGY_LAST +} VipsOperationMorphology; + +int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, + VipsOperationMorphology morph, ... ) + __attribute__((sentinel)); + + + + + int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); @@ -53,4 +77,4 @@ int im_label_regions( VipsImage *test, VipsImage *mask, int *segments ); } #endif /*__cplusplus*/ -#endif /*IM_MORPHOLOGY_H*/ +#endif /*VIPS_MORPHOLOGY_H*/ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index d4f4ff12..62dfc644 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -50,6 +50,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/colour.h \ ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/convolution.h \ + ${top_srcdir}/libvips/include/vips/morphology.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.c: $(vips_scan_headers) Makefile diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 2f7a0873..9016b906 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -633,6 +633,25 @@ vips_precision_get_type( void ) return( etype ); } +/* enumerations from "../../libvips/include/vips/morphology.h" */ +GType +vips_operation_morphology_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_OPERATION_MORPHOLOGY_ERODE, "VIPS_OPERATION_MORPHOLOGY_ERODE", "erode"}, + {VIPS_OPERATION_MORPHOLOGY_DILATE, "VIPS_OPERATION_MORPHOLOGY_DILATE", "dilate"}, + {VIPS_OPERATION_MORPHOLOGY_LAST, "VIPS_OPERATION_MORPHOLOGY_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsOperationMorphology", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type( void ) diff --git a/libvips/morphology/morphology.c b/libvips/morphology/morphology.c index 9882e6fc..0abbe60e 100644 --- a/libvips/morphology/morphology.c +++ b/libvips/morphology/morphology.c @@ -766,39 +766,6 @@ im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) return( morphology( in, out, mask, ERODE ) ); } -/** - * im_dilate: - * @in: input image - * @out: output image - * @mask: mask - * - * im_dilate() performs a morphological dilate operation on @in using @mask as a - * structuring element. The output pixel is set if any part of the mask - * matches, that is, the result is the logical OR of the selected input pixels. - * - * The image should have 0 (black) for no object and 255 - * (non-zero) for an object. Note that this is the reverse of the usual - * convention for these operations, but more convenient when combined with the - * boolean operators im_andimage() and friends. The output image is the same - * size as the input image: edge pxels are made by expanding the input image - * as necessary in the manner of im_conv(). - * - * Mask coefficients can be either 0 (for object) or 255 (for background) - * or 128 (for do not care). The origin of the mask is at location - * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been - * based on the book "Fundamentals of Digital Image Processing" by A. Jain, - * pp 384-388, Prentice-Hall, 1989. - * - * See the boolean operations im_andimage(), im_orimage() and im_eorimage() - * for analogues of the usual set difference and set union operations. - * - * Operations are performed using the processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_erode(). - * - * Returns: 0 on success, -1 on error - */ int im_dilate( IMAGE *in, IMAGE *out, INTMASK *mask ) { @@ -817,39 +784,6 @@ im_dilate( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } -/** - * im_erode: - * @in: input image - * @out: output image - * @mask: mask - * - * im_erode() performs a morphological erode operation on @in using @mask as a - * structuring element. The whole mask must match for the output pixel to be - * set, that is, the result is the logical AND of the selected input pixels. - * - * The image should have 0 (black) for no object and 255 - * (non-zero) for an object. Note that this is the reverse of the usual - * convention for these operations, but more convenient when combined with the - * boolean operators im_andimage() and friends. The output image is the same - * size as the input image: edge pxels are made by expanding the input image - * as necessary in the manner of im_conv(). - * - * Mask coefficients can be either 0 (for object) or 255 (for background) - * or 128 (for do not care). The origin of the mask is at location - * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been - * based on the book "Fundamentals of Digital Image Processing" by A. Jain, - * pp 384-388, Prentice-Hall, 1989. - * - * See the boolean operations im_andimage(), im_orimage() and im_eorimage() - * for analogues of the usual set difference and set union operations. - * - * Operations are performed using the processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_dilate(). - * - * Returns: 0 on success, -1 on error - */ int im_erode( IMAGE *in, IMAGE *out, INTMASK *mask ) { From 2720026a61437d5b73656f132c38af53d4a0c6ca Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 23 Oct 2013 14:54:22 +0100 Subject: [PATCH 16/26] redo im_rank_image() as a class --- ChangeLog | 2 +- libvips/conversion/Makefile.am | 1 + libvips/conversion/bandrank.c | 322 ++++++++++++++++ libvips/conversion/conversion.c | 2 + libvips/deprecated/vips7compat.c | 24 ++ libvips/include/vips/conversion.h | 4 +- libvips/include/vips/morphology.h | 2 - libvips/include/vips/vips7compat.h | 2 + libvips/morphology/Makefile.am | 3 +- .../morphology/{morphology.c => hitmiss.c} | 0 libvips/morphology/im_rank_image.c | 356 ------------------ 11 files changed, 356 insertions(+), 362 deletions(-) create mode 100644 libvips/conversion/bandrank.c rename libvips/morphology/{morphology.c => hitmiss.c} (100%) delete mode 100644 libvips/morphology/im_rank_image.c diff --git a/ChangeLog b/ChangeLog index 3f5bae5f..0acacfca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ 19/10/13 started 7.37.0 - redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(), - im_erode() as classes + im_erode(), im_rank_image() as classes - vips_init() now does some ABI compat checking, though this change requires an ABI break - add "interlace" option to vips_jpegsave() diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index e7240f46..8e3cb0af 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -17,6 +17,7 @@ libconversion_la_SOURCES = \ replicate.c \ cast.c \ bandjoin.c \ + bandrank.c \ recomb.c \ bandmean.c \ bandbool.c \ diff --git a/libvips/conversion/bandrank.c b/libvips/conversion/bandrank.c new file mode 100644 index 00000000..14d55136 --- /dev/null +++ b/libvips/conversion/bandrank.c @@ -0,0 +1,322 @@ +/* Sort a set of images, pixelwise, and pick out the index at each point. + * + * 19/8/03 + * - from im_maxvalue(), via im_gbandrank() + * 10/11/10 + * - gtkdoc + * - cleanups + * - any mix of formats and bands + * 23/10/13 + * - redo as a class, from bandrank.c + */ + +/* + + 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "bandary.h" + +typedef struct _VipsBandrank { + VipsBandary parent_instance; + + /* The input images. + */ + VipsArea *in; + int index; /* Pick out this one */ +} VipsBandrank; + +typedef VipsBandaryClass VipsBandrankClass; + +G_DEFINE_TYPE( VipsBandrank, vips_bandrank, VIPS_TYPE_BANDARY ); + +/* Special-case max and min (rather common). + */ +#define FIND_MAX( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE top = ((TYPE *) p[0])[x]; \ + \ + for( i = 1; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + if( v > top ) \ + top = v; \ + } \ + \ + ((TYPE *) q)[x] = top; \ + } \ +} + +#define FIND_MIN( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE bot = ((TYPE *) p[0])[x]; \ + \ + for( i = 1; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + if( v < bot ) \ + bot = v; \ + } \ + \ + ((TYPE *) q)[x] = bot; \ + } \ +} + +#define FIND_RANK( TYPE ) { \ + TYPE *sort = (TYPE *) sort_buffer; \ + \ + for( x = 0; x < sz; x++ ) { \ + for( i = 0; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + /* Search for element >v. + */\ + for( j = 0; j < i; j++ ) \ + if( sort[j] > v ) \ + break; \ + \ + /* Move remaining elements down. + */ \ + for( k = i; k > j; k-- ) \ + sort[k] = sort[k - 1]; \ + \ + /* Insert this element. + */ \ + sort[j] = v; \ + } \ + \ + ((TYPE *) q)[x] = sort[bandrank->index]; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( in[0]->BandFmt ) { \ + case VIPS_FORMAT_UCHAR: OPERATION( unsigned char ); break; \ + case VIPS_FORMAT_CHAR: OPERATION( signed char ); break; \ + case VIPS_FORMAT_USHORT: OPERATION( unsigned short ); break; \ + case VIPS_FORMAT_SHORT: OPERATION( signed short ); break; \ + case VIPS_FORMAT_UINT: OPERATION( unsigned int ); break; \ + case VIPS_FORMAT_INT: OPERATION( signed int ); break; \ + case VIPS_FORMAT_FLOAT: OPERATION( float ); break; \ + case VIPS_FORMAT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + g_assert( 0 ); \ + } + +/* Sort input band elements in the stack. Needs to be big enough for + * sizeof(band-element) * number-of-images. + */ +#define SORT_BUFFER (1024) + +static void +vips_bandrank_buffer( VipsBandary *bandary, VipsPel *q, VipsPel **p, int width ) +{ + VipsBandrank *bandrank = (VipsBandrank *) bandary; + VipsImage **in = bandary->ready; + int sz = width * in[0]->Bands; + + int i, j, k; + int x; + VipsPel sort_buffer[SORT_BUFFER]; + + /* Special-case max and min. + */ + if( bandrank->index == 0 ) + SWITCH( FIND_MIN ) + else if( bandrank->index == bandary->n - 1 ) + SWITCH( FIND_MAX ) + else + SWITCH( FIND_RANK ) +} + +static int +vips_bandrank_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsBandary *bandary = (VipsBandary *) object; + VipsBandrank *bandrank = (VipsBandrank *) object; + + if( bandrank->in ) { + VipsImage **band = (VipsImage **) + vips_object_local_array( object, bandrank->in->n ); + + if( bandrank->in->n == 1 ) + return( vips_bandary_copy( bandary ) ); + + /* We need to keep one band element for every input image + * on the stack. + */ + if( sizeof( double ) * bandrank->in->n > SORT_BUFFER ) { + vips_error( class->nickname, + "%s", _( "too many input images" ) ); + return( -1 ); + } + + if( vips__bandalike_vec( class->nickname, + bandrank->in->data, band, bandrank->in->n, 0 ) ) + return( -1 ); + + bandary->in = band; + bandary->n = bandrank->in->n; + bandary->out_bands = band[0]->Bands; + + if( bandrank->index == -1 ) + bandrank->index = bandary->n / 2; + } + + if( VIPS_OBJECT_CLASS( vips_bandrank_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_bandrank_class_init( VipsBandrankClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_bandrank_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "bandrank"; + vobject_class->description = _( "band-wise rank of a set of images" ); + vobject_class->build = vips_bandrank_build; + + bandary_class->process_line = vips_bandrank_buffer; + + VIPS_ARG_BOXED( class, "in", 0, + _( "Input" ), + _( "Array of input images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsBandrank, in ), + VIPS_TYPE_ARRAY_IMAGE ); + + VIPS_ARG_INT( class, "index", 0, + _( "Index" ), + _( "Select this band element from sorted list" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsBandrank, index ), + -1, 1000000, -1 ); + +} + +static void +vips_bandrank_init( VipsBandrank *bandrank ) +{ + /* -1 means median. + */ + bandrank->index = -1; +} + +static int +vips_bandrankv( VipsImage **in, VipsImage **out, int n, va_list ap ) +{ + VipsArea *area; + VipsImage **array; + int i; + int result; + + area = vips_area_new_array_object( n ); + array = (VipsImage **) area->data; + for( i = 0; i < n; i++ ) { + array[i] = in[i]; + g_object_ref( array[i] ); + } + + result = vips_call_split( "bandrank", ap, area, out ); + + vips_area_unref( area ); + + return( result ); +} + +/** + * vips_bandrank: + * @in: array of input images + * @out: output image + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @index: pick this index from list of sorted values + * + * Sorts the images @in band-element-wise, then outputs an + * image in which each band element is selected from the sorted list by the + * @index parameter. For example, if @index + * is zero, then each output band element will be the minimum of all the + * corresponding input band element. + * + * By default, @index is -1, meaning pick the median value. + * + * It works for any uncoded, non-complex image type. Images are cast up to the + * smallest common-format. + * + * Any image can have either 1 band or n bands, where n is the same for all + * the non-1-band images. Single band images are then effectively copied to + * make n-band images. + * + * Smaller input images are expanded by adding black pixels. + * + * See also: vips_rank(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_bandrank( VipsImage **in, VipsImage **out, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_bandrankv( in, out, n, ap ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index af1c07b3..56582973 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -210,6 +210,7 @@ vips_conversion_operation_init( void ) extern GType vips_replicate_get_type( void ); extern GType vips_cast_get_type( void ); extern GType vips_bandjoin_get_type( void ); + extern GType vips_bandrank_get_type( void ); extern GType vips_black_get_type( void ); extern GType vips_rot_get_type( void ); extern GType vips_rot45_get_type( void ); @@ -246,6 +247,7 @@ vips_conversion_operation_init( void ) vips_replicate_get_type(); vips_cast_get_type(); vips_bandjoin_get_type(); + vips_bandrank_get_type(); vips_black_get_type(); vips_rot_get_type(); vips_rot45_get_type(); diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 4200847f..0123570a 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1390,6 +1390,30 @@ im_gbandjoin( VipsImage **in, VipsImage *out, int n ) return( 0 ); } +int +im_rank_image( VipsImage **in, VipsImage *out, int n, int index ) +{ + VipsImage *t; + + if( vips_bandrank( in, &t, n, + "index", index, + NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +int +im_maxvalue( IMAGE **in, IMAGE *out, int n ) +{ + return( im_rank_image( in, out, n, n - 1 ) ); +} + int im_invert( IMAGE *in, IMAGE *out ) { diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 6855d428..611f8846 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -157,7 +157,7 @@ int vips_bandjoin( VipsImage **in, VipsImage **out, int n, ... ) __attribute__((sentinel)); int vips_bandjoin2( VipsImage *in1, VipsImage *in2, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_bandmean( VipsImage *in, VipsImage **out, ... ) +int vips_bandrank( VipsImage **in, VipsImage **out, int n, ... ) __attribute__((sentinel)); int vips_bandbool( VipsImage *in, VipsImage **out, @@ -169,6 +169,8 @@ int vips_bandor( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_bandeor( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_bandmean( VipsImage *in, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index 7014ceb2..c997e0fb 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -66,8 +66,6 @@ int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_rank( VipsImage *in, VipsImage *out, int width, int height, int index ); -int im_rank_image( VipsImage **in, VipsImage *out, int n, int index ); -int im_maxvalue( VipsImage **in, VipsImage *out, int n ); int im_cntlines( VipsImage *im, double *nolines, int flag ); int im_zerox( VipsImage *in, VipsImage *out, int sign ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 7324c653..758880c3 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -712,6 +712,8 @@ int im_rotquad( VipsImage *in, VipsImage *out ); int im_clip2fmt( VipsImage *in, VipsImage *out, VipsBandFormat fmt ); int im_bandjoin( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_gbandjoin( VipsImage **in, VipsImage *out, int n ); +int im_rank_image( VipsImage **in, VipsImage *out, int n, int index ); +int im_maxvalue( VipsImage **in, VipsImage *out, int n ); int im_grid( VipsImage *in, VipsImage *out, int tile_height, int across, int down ); int im_scale( VipsImage *in, VipsImage *out ); int im_scaleps( VipsImage *in, VipsImage *out ); diff --git a/libvips/morphology/Makefile.am b/libvips/morphology/Makefile.am index e4b7e75b..eb75e9c5 100644 --- a/libvips/morphology/Makefile.am +++ b/libvips/morphology/Makefile.am @@ -2,9 +2,8 @@ noinst_LTLIBRARIES = libmorphology.la libmorphology_la_SOURCES = \ im_cntlines.c \ - morphology.c\ + hitmiss.c\ im_rank.c \ - im_rank_image.c \ im_zerox.c \ morph_dispatch.c \ im_label_regions.c diff --git a/libvips/morphology/morphology.c b/libvips/morphology/hitmiss.c similarity index 100% rename from libvips/morphology/morphology.c rename to libvips/morphology/hitmiss.c diff --git a/libvips/morphology/im_rank_image.c b/libvips/morphology/im_rank_image.c deleted file mode 100644 index 47f7b412..00000000 --- a/libvips/morphology/im_rank_image.c +++ /dev/null @@ -1,356 +0,0 @@ -/* Sort a set of images, pixelwise, and pick out the index at each point. - * - * 19/8/03 - * - from im_maxvalue(), via im_gbandjoin() - * 10/11/10 - * - gtkdoc - * - cleanups - * - any mix of formats and bands - */ - -/* - - 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 -#include - -/* Parameters. - */ -typedef struct Rank { - IMAGE **in; /* Array of input images, NULL-terminated */ - IMAGE *out; - int n; /* Number of input images */ - int index; /* Pick out this one */ -} Rank; - -/* Make a Rank struct. - */ -static Rank * -rank_new( IMAGE **in, IMAGE *out, int n, int index ) -{ - Rank *rank; - IMAGE **t; - - if( !(rank = IM_NEW( out, Rank )) ) - return( NULL ); - - rank->n = n; - rank->index = index; - rank->out = out; - if( !(t = IM_ARRAY( out, n, IMAGE * )) || - !(rank->in = IM_ARRAY( out, n + 1, IMAGE * )) ) - return( NULL ); - - /* Cast inputs up to a common format, common bands. - */ - if( im_open_local_array( out, t, n, "im_rank_image", "p" ) || - im_open_local_array( out, rank->in, n, "im_rank_image", "p" ) || - im__bandalike_vec( "im_rank_image", in, t, n ) || - im__formatalike_vec( t, rank->in, n ) ) - return( NULL ); - rank->in[n] = NULL; - - return( rank ); -} - -/* Our sequence value. - */ -typedef struct { - Rank *rank; - - REGION **ir; /* Input regions */ - VipsPel **pts; /* Per-input region data pointer */ - VipsPel *sort; /* Sort pixels here */ -} RankSequence; - -/* Free a sequence value. - */ -static int -rank_stop( void *vseq, void *a, void *b ) -{ - RankSequence *seq = (RankSequence *) vseq; - Rank *rank = (Rank *) b; - - if( seq->ir ) { - int i; - - for( i = 0; i < rank->n; i++ ) - IM_FREEF( im_region_free, seq->ir[i] ); - } - - return( 0 ); -} - -/* Make a sequence value. - */ -static void * -rank_start( IMAGE *out, void *a, void *b ) -{ - IMAGE **in = (IMAGE **) a; - Rank *rank = (Rank *) b; - - RankSequence *seq; - int i; - - if( !(seq = IM_NEW( out, RankSequence )) ) - return( NULL ); - - /* Init! - */ - seq->rank = rank; - seq->ir = NULL; - seq->pts = NULL; - - /* Attach regions and arrays. - */ - seq->ir = IM_ARRAY( out, rank->n + 1, REGION * ); - seq->pts = IM_ARRAY( out, rank->n + 1, VipsPel * ); - seq->sort = IM_ARRAY( out, - rank->n * IM_IMAGE_SIZEOF_ELEMENT( in[0] ), VipsPel ); - if( !seq->ir || !seq->pts || !seq->sort ) { - rank_stop( seq, in, rank ); - return( NULL ); - } - - for( i = 0; i < rank->n; i++ ) - if( !(seq->ir[i] = im_region_create( in[i] )) ) { - rank_stop( seq, in, rank ); - return( NULL ); - } - seq->ir[i] = NULL; - - return( (void *) seq ); -} - -/* Special-case max and min (rather common). - */ -#define FIND_IM_MAX( TYPE ) { \ - for( x = 0; x < sz; x++ ) { \ - TYPE top = ((TYPE *) seq->pts[0])[x]; \ - \ - for( i = 1; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - if( v > top ) \ - top = v; \ - } \ - \ - ((TYPE *) q)[x] = top; \ - } \ -} - -#define FIND_IM_MIN( TYPE ) { \ - for( x = 0; x < sz; x++ ) { \ - TYPE bot = ((TYPE *) seq->pts[0])[x]; \ - \ - for( i = 1; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - if( v < bot ) \ - bot = v; \ - } \ - \ - ((TYPE *) q)[x] = bot; \ - } \ -} - -/* Inner loop for sorting. - */ -#define FIND_IM_RANK( TYPE ) { \ - TYPE *sort = (TYPE *) seq->sort; \ - \ - for( x = 0; x < sz; x++ ) { \ - for( i = 0; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - /* Search for element >v. - */\ - for( j = 0; j < i; j++ ) \ - if( sort[j] > v ) \ - break; \ - \ - /* Move remaining elements down. - */ \ - for( k = i; k > j; k-- ) \ - sort[k] = sort[k - 1]; \ - \ - /* Insert this element. - */ \ - sort[j] = v; \ - } \ - \ - ((TYPE *) q)[x] = sort[rank->index]; \ - } \ -} - -#define SWITCH( OPERATION ) \ - switch( rank->out->BandFmt ) { \ - case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ - case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ - case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ - case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ - case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ - case IM_BANDFMT_INT: OPERATION( signed int ); break; \ - case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ - case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ - \ - default: \ - assert( 0 ); \ - } - -static int -rank_gen( REGION *or, void *vseq, void *a, void *b ) -{ - RankSequence *seq = (RankSequence *) vseq; - Rank *rank = (Rank *) b; - Rect *r = &or->valid; - int le = r->left; - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - int sz = IM_REGION_N_ELEMENTS( or ); - - int x, y, i, j, k; - - /* Prepare each input area. - */ - for( i = 0; i < rank->n; i++ ) - if( im_prepare( seq->ir[i], r ) ) - return( -1 ); - - /* Loop over output! - */ - for( y = to; y < bo; y++ ) { - VipsPel *q = IM_REGION_ADDR( or, le, y ); - - for( i = 0; i < rank->n; i++ ) - seq->pts[i] = IM_REGION_ADDR( seq->ir[i], le, y ); - - /* Special-case max and min. - */ - if( rank->index == 0 ) - SWITCH( FIND_IM_MIN ) - else if( rank->index == rank->n - 1 ) - SWITCH( FIND_IM_MAX ) - else - SWITCH( FIND_IM_RANK ) - } - - return( 0 ); -} - -/** - * im_rank_image: - * @in: input image array - * @out: output image - * @n: number of input images - * @index: select pixel - * - * im_rank_image() sorts the images @in pixel-wise, then outputs an - * image in which each pixel is selected from the sorted list by the - * @index parameter. For example, if @index - * is zero, then each output pixel will be the minimum of all the - * corresponding input pixels. - * - * It works for any uncoded, non-complex image type. Images are cast up to the - * smallest common-format. - * - * Any image can have either 1 band or n bands, where n is the same for all - * the non-1-band images. Single band images are then effectively copied to - * make n-band images. - * - * See also: im_rank(), im_maxvalue(). - * - * Returns: 0 on success, -1 on error - */ -int -im_rank_image( IMAGE **in, IMAGE *out, int n, int index ) -{ - int i; - Rank *rank; - - if( n < 1 ) { - im_error( "im_rank_image", "%s", _( "zero input images!" ) ); - return( -1 ); - } - if( index < 0 || index > n - 1 ) { - im_error( "im_rank_image", - _( "index should be in range 0 - %d" ), n - 1 ); - return( -1 ); - } - if( im_poutcheck( out ) ) - return( -1 ); - for( i = 0; i < n; i++ ) - if( im_pincheck( in[i] ) || - im_check_uncoded( "im_rank_image", in[i] ) || - im_check_noncomplex( "im_rank_image", in[i] ) || - im_check_size_same( "im_rank_image", in[i], in[0] ) ) - return( -1 ); - - if( !(rank = rank_new( in, out, n, index )) || - im_cp_desc_array( out, rank->in ) || - im_demand_hint_array( out, IM_THINSTRIP, rank->in ) || - im_generate( out, - rank_start, rank_gen, rank_stop, rank->in, rank ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_maxvalue: - * @in: input image array - * @out: output image - * @n: number of input images - * - * im_maxvalue() is a convenience function over im_rank_image(). It sorts the - * input images pixel-wise, then outputs an image - * in which each pixel is the maximum of all the corresponding input images. - * It works for any uncoded, non-complex image type. Images are cast up to the - * smallest common-format. - * - * Any image can have either 1 band or n bands, where n is the same for all - * the non-1-band images. Single band images are then effectively copied to - * make n-band images. - * - * See also: im_rank_image(). - * - * Returns: 0 on success, -1 on error - */ -int -im_maxvalue( IMAGE **in, IMAGE *out, int n ) -{ - return( im_rank_image( in, out, n, n - 1 ) ); -} From 4d724c00239c53bee80fbe5148df31719e096898 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 24 Oct 2013 08:33:59 +0100 Subject: [PATCH 17/26] started compass --- TODO | 3 + libvips/convolution/compass.c | 131 ++++++++++++++++++++++++++++++++++ libvips/convolution/conv.c | 21 +++--- 3 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 libvips/convolution/compass.c diff --git a/TODO b/TODO index d3c45e78..53cadebf 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,6 @@ +- started compass .. convolution with a rotating mask + + need to do sep - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/convolution/compass.c b/libvips/convolution/compass.c new file mode 100644 index 00000000..2553e5e8 --- /dev/null +++ b/libvips/convolution/compass.c @@ -0,0 +1,131 @@ +/* repeatedly convolve with a rotating mask + * + * 23/10/13 + * - from vips_conv() + */ + +/* + + 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 + + */ + +/* This is a simple wrapper over the old vips7 functions. At some point we + * should rewrite this as a pure vips8 class and redo the vips7 functions as + * wrappers over this. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#include "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + int times; + VipsRotate45 angle; + int join; +} VipsCompass; + +typedef VipsConvolutionClass VipsCompassClass; + +G_DEFINE_TYPE( VipsCompass, vips_compass, VIPS_TYPE_CONVOLUTION ); + +static int +vips_compass_build( VipsObject *object ) +{ + VipsConvolution *convolution = (VipsConvolution *) object; + VipsCompass *compass = (VipsCompass *) object; + + g_object_set( compass, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_compass_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_compass_class_init( VipsCompassClass *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 = "compass"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_compass_build; + + VIPS_ARG_ENUM( class, "precision", 103, + _( "Precision" ), + _( "Convolve with this precision" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, precision ), + VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER ); + + VIPS_ARG_INT( class, "layers", 104, + _( "Layers" ), + _( "Use this many layers in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, layers ), + 1, 1000, 5 ); + + VIPS_ARG_INT( class, "cluster", 105, + _( "Cluster" ), + _( "Cluster lines closer than this in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, cluster ), + 1, 100, 1 ); + +} + +static void +vips_compass_init( VipsCompass *compass ) +{ + compass->times = 1; + compass->angle = 5; + compass->join = 1; +} + +int +vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) +{ + va_list ap; + int result; + + va_start( ap, mask ); + result = vips_call_split( "compass", ap, in, out, mask ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/convolution/conv.c b/libvips/convolution/conv.c index 759b91b8..74d79a76 100644 --- a/libvips/convolution/conv.c +++ b/libvips/convolution/conv.c @@ -62,6 +62,7 @@ G_DEFINE_TYPE( VipsConv, vips_conv, VIPS_TYPE_CONVOLUTION ); static int vips_conv_build( VipsObject *object ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsConvolution *convolution = (VipsConvolution *) object; VipsConv *conv = (VipsConv *) object; @@ -73,27 +74,27 @@ vips_conv_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_conv_parent_class )->build( object ) ) return( -1 ); + if( !(imsk = im_vips2imask( convolution->M, class->nickname )) || + !im_local_imask( convolution->out, imsk ) ) + return( -1 ); + if( !(dmsk = im_vips2mask( convolution->M, class->nickname )) || + !im_local_dmask( convolution->out, dmsk ) ) + return( -1 ); switch( conv->precision ) { case VIPS_PRECISION_INTEGER: - if( !(imsk = im_vips2imask( convolution->M, "im_stats" )) || - !im_local_imask( convolution->out, imsk ) || - im_conv( convolution->in, convolution->out, imsk ) ) + if( im_conv( convolution->in, convolution->out, imsk ) ) return( -1 ); break; case VIPS_PRECISION_FLOAT: - if( !(dmsk = im_vips2mask( convolution->M, "im_stats" )) || - !im_local_dmask( convolution->out, dmsk ) || - im_conv_f( convolution->in, convolution->out, dmsk ) ) + if( im_conv_f( convolution->in, convolution->out, dmsk ) ) return( -1 ); break; case VIPS_PRECISION_APPROXIMATE: - if( !(dmsk = im_vips2mask( convolution->M, "im_stats" )) || - !im_local_dmask( convolution->out, dmsk ) || - im_aconv( convolution->in, convolution->out, dmsk, - conv->layers, conv->cluster ) ) + if( im_aconv( convolution->in, convolution->out, dmsk, + conv->layers, conv->cluster ) ) return( -1 ); break; From e74a0b71d9479c193ccb6c0882be24892cf4bc18 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 24 Oct 2013 14:46:08 +0100 Subject: [PATCH 18/26] almost done compass --- TODO | 7 ++ libvips/convolution/Makefile.am | 1 + libvips/convolution/compass.c | 109 +++++++++++++++++++++++++---- libvips/convolution/convolution.c | 4 +- libvips/deprecated/vips7compat.c | 76 ++++++++++++++++++++ libvips/include/vips/convolution.h | 31 +++++++- libvips/include/vips/enumtypes.h | 3 +- libvips/include/vips/morphology.h | 20 ------ libvips/iofuncs/enumtypes.c | 20 +++++- 9 files changed, 234 insertions(+), 37 deletions(-) diff --git a/TODO b/TODO index 53cadebf..5a0f66ce 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,12 @@ - started compass .. convolution with a rotating mask + vips im_gradient k2.jpg x.v line.mat + vips compass k2.jpg x2.v line.mat --times 2 --angle 90 --combine sum + + should give the same result, you'd think + + deprecated/vips7compat.c defs for compass/grad/lindet commented out for now + need to do sep - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index b089c7fa..a771122d 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -4,6 +4,7 @@ libconvolution_la_SOURCES = \ convolution.c \ pconvolution.h \ conv.c \ + compass.c \ morph.c \ convol_dispatch.c \ im_addgnoise.c \ diff --git a/libvips/convolution/compass.c b/libvips/convolution/compass.c index 2553e5e8..5ff9c522 100644 --- a/libvips/convolution/compass.c +++ b/libvips/convolution/compass.c @@ -31,11 +31,6 @@ */ -/* This is a simple wrapper over the old vips7 functions. At some point we - * should rewrite this as a pure vips8 class and redo the vips7 functions as - * wrappers over this. - */ - #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ @@ -51,8 +46,11 @@ typedef struct { VipsConvolution parent_instance; int times; - VipsRotate45 angle; - int join; + VipsAngle45 angle; + VipsCombine combine; + VipsPrecision precision; + int layers; + int cluster; } VipsCompass; typedef VipsConvolutionClass VipsCompassClass; @@ -64,12 +62,73 @@ vips_compass_build( VipsObject *object ) { VipsConvolution *convolution = (VipsConvolution *) object; VipsCompass *compass = (VipsCompass *) object; + VipsImage **masks; + VipsImage *mask; + VipsImage **images; + int i; + VipsImage **abs; + VipsImage **combine; + VipsImage *x; g_object_set( compass, "out", vips_image_new(), NULL ); if( VIPS_OBJECT_CLASS( vips_compass_parent_class )->build( object ) ) return( -1 ); + masks = (VipsImage **) + vips_object_local_array( object, compass->times ); + images = (VipsImage **) + vips_object_local_array( object, compass->times ); + abs = (VipsImage **) + vips_object_local_array( object, compass->times ); + combine = (VipsImage **) + vips_object_local_array( object, compass->times ); + + mask = convolution->M; + for( i = 0; i < compass->times; i++ ) { + if( vips_conv( convolution->in, &images[i], mask, + "precision", compass->precision, + "layers", compass->layers, + "cluster", compass->cluster, + NULL ) ) + return( -1 ); + if( vips_rot45( mask, &masks[i], + "angle", compass->angle, + NULL ) ) + return( -1 ); + + mask = masks[i]; + } + + for( i = 0; i < compass->times; i++ ) + if( vips_abs( images[i], &abs[i], NULL ) ) + return( -1 ); + + switch( compass->combine ) { + case VIPS_COMBINE_MAX: + if( vips_bandrank( abs, &combine[0], compass->times, + "index", compass->times - 1, + NULL ) ) + return( -1 ); + x = combine[0]; + break; + + case VIPS_COMBINE_SUM: + x = abs[0]; + for( i = 1; i < compass->times; i++ ) { + if( vips_add( x, abs[i], &combine[i], NULL ) ) + return( -1 ); + x = combine[i]; + } + break; + + default: + g_assert( 0 ); + } + + if( vips_image_write( x, convolution->out ) ) + return( -1 ); + return( 0 ); } @@ -86,21 +145,42 @@ vips_compass_class_init( VipsCompassClass *class ) object_class->description = _( "convolution operation" ); object_class->build = vips_compass_build; - VIPS_ARG_ENUM( class, "precision", 103, + VIPS_ARG_INT( class, "times", 101, + _( "Times" ), + _( "Rotate and convolve this many times" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, times ), + 1, 1000, 2 ); + + VIPS_ARG_ENUM( class, "angle", 103, + _( "Angle" ), + _( "Rotate mask by this much between convolutions" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, angle ), + VIPS_TYPE_ANGLE45, VIPS_ANGLE45_90 ); + + VIPS_ARG_ENUM( class, "combine", 104, + _( "Combine" ), + _( "Combine convolution results like this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, combine ), + VIPS_TYPE_COMBINE, VIPS_COMBINE_MAX ); + + VIPS_ARG_ENUM( class, "precision", 203, _( "Precision" ), _( "Convolve with this precision" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCompass, precision ), VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER ); - VIPS_ARG_INT( class, "layers", 104, + VIPS_ARG_INT( class, "layers", 204, _( "Layers" ), _( "Use this many layers in approximation" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsCompass, layers ), 1, 1000, 5 ); - VIPS_ARG_INT( class, "cluster", 105, + VIPS_ARG_INT( class, "cluster", 205, _( "Cluster" ), _( "Cluster lines closer than this in approximation" ), VIPS_ARGUMENT_OPTIONAL_INPUT, @@ -112,9 +192,12 @@ vips_compass_class_init( VipsCompassClass *class ) static void vips_compass_init( VipsCompass *compass ) { - compass->times = 1; - compass->angle = 5; - compass->join = 1; + compass->times = 2; + compass->angle = VIPS_ANGLE45_90; + compass->combine = VIPS_COMBINE_MAX; + compass->precision = VIPS_PRECISION_INTEGER; + compass->layers = 5; + compass->cluster = 1; } int diff --git a/libvips/convolution/convolution.c b/libvips/convolution/convolution.c index d726762f..dec22e52 100644 --- a/libvips/convolution/convolution.c +++ b/libvips/convolution/convolution.c @@ -127,7 +127,7 @@ vips_convolution_class_init( VipsConvolutionClass *class ) VIPS_ARGUMENT_REQUIRED_OUTPUT, G_STRUCT_OFFSET( VipsConvolution, out ) ); - VIPS_ARG_IMAGE( class, "mask", 102, + VIPS_ARG_IMAGE( class, "mask", 20, _( "Mask" ), _( "Input matrix image" ), VIPS_ARGUMENT_REQUIRED_INPUT, @@ -148,7 +148,9 @@ vips_convolution_operation_init( void ) { extern int vips_conv_get_type( void ); extern int vips_morph_get_type( void ); + extern int vips_compass_get_type( void ); vips_conv_get_type(); vips_morph_get_type(); + vips_compass_get_type(); } diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 0123570a..0719b858 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2088,6 +2088,82 @@ im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) return( 0 ); } +/* +int +im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 8, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 4, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 2, + "angle", VIPS_ANGLE45_90, + "combine", VIPS_COMBINE_SUM, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + */ + static int vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round ) { diff --git a/libvips/include/vips/convolution.h b/libvips/include/vips/convolution.h index 5e16909a..e8a6c1d0 100644 --- a/libvips/include/vips/convolution.h +++ b/libvips/include/vips/convolution.h @@ -41,11 +41,40 @@ extern "C" { typedef enum { VIPS_PRECISION_INTEGER, VIPS_PRECISION_FLOAT, - VIPS_PRECISION_APPROXIMATE + VIPS_PRECISION_APPROXIMATE, + VIPS_PRECISION_LAST } VipsPrecision; +typedef enum { + VIPS_COMBINE_MAX, + VIPS_COMBINE_SUM, + VIPS_COMBINE_LAST +} VipsCombine; + +/** + * VipsOperationMorphology: + * @VIPS_OPERATION_MORPHOLOGY_ERODE: true if all set + * @VIPS_OPERATION_MORPHOLOGY_DILATE: true if one set + * + * More like hit-miss, really. + * + * See also: vips_morph(). + */ + +typedef enum { + VIPS_OPERATION_MORPHOLOGY_ERODE, + VIPS_OPERATION_MORPHOLOGY_DILATE, + VIPS_OPERATION_MORPHOLOGY_LAST +} VipsOperationMorphology; + int vips_conv( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) __attribute__((sentinel)); +int vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) + __attribute__((sentinel)); + +int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, + VipsOperationMorphology morph, ... ) + __attribute__((sentinel)); void vips_convolution_operation_init( void ); diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index a5c6df37..4c1fa2f0 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -74,7 +74,8 @@ GType vips_operation_flags_get_type (void) G_GNUC_CONST; /* enumerations from "../../../libvips/include/vips/convolution.h" */ GType vips_precision_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_PRECISION (vips_precision_get_type()) -/* enumerations from "../../../libvips/include/vips/morphology.h" */ +GType vips_combine_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_COMBINE (vips_combine_get_type()) GType vips_operation_morphology_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_OPERATION_MORPHOLOGY (vips_operation_morphology_get_type()) /* enumerations from "../../../libvips/include/vips/object.h" */ diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index c997e0fb..ca182a39 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -38,26 +38,6 @@ extern "C" { #endif /*__cplusplus*/ -/** - * VipsOperationMorphology: - * @VIPS_OPERATION_MORPHOLOGY_ERODE: true if all set - * @VIPS_OPERATION_MORPHOLOGY_DILATE: true if one set - * - * More like hit-miss, really. - * - * See also: vips_morph(). - */ - -typedef enum { - VIPS_OPERATION_MORPHOLOGY_ERODE, - VIPS_OPERATION_MORPHOLOGY_DILATE, - VIPS_OPERATION_MORPHOLOGY_LAST -} VipsOperationMorphology; - -int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, - VipsOperationMorphology morph, ... ) - __attribute__((sentinel)); - diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 9016b906..75f8bdc8 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -625,6 +625,7 @@ vips_precision_get_type( void ) {VIPS_PRECISION_INTEGER, "VIPS_PRECISION_INTEGER", "integer"}, {VIPS_PRECISION_FLOAT, "VIPS_PRECISION_FLOAT", "float"}, {VIPS_PRECISION_APPROXIMATE, "VIPS_PRECISION_APPROXIMATE", "approximate"}, + {VIPS_PRECISION_LAST, "VIPS_PRECISION_LAST", "last"}, {0, NULL, NULL} }; @@ -633,7 +634,24 @@ vips_precision_get_type( void ) return( etype ); } -/* enumerations from "../../libvips/include/vips/morphology.h" */ +GType +vips_combine_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_COMBINE_MAX, "VIPS_COMBINE_MAX", "max"}, + {VIPS_COMBINE_SUM, "VIPS_COMBINE_SUM", "sum"}, + {VIPS_COMBINE_LAST, "VIPS_COMBINE_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsCombine", values ); + } + + return( etype ); +} GType vips_operation_morphology_get_type( void ) { From 33f978f0ca77d3fbf50d6272d654a52e7f5eb4d6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 12:02:02 +0100 Subject: [PATCH 19/26] add vips_matrixprint() --- ChangeLog | 1 + libvips/convolution/conv.c | 5 +++ libvips/convolution/im_conv.c | 2 +- libvips/foreign/csv.c | 47 ++++++++++++++-------- libvips/foreign/csv.h | 2 + libvips/foreign/foreign.c | 2 + libvips/foreign/matrixsave.c | 72 ++++++++++++++++++++++++++++++++++ libvips/include/vips/foreign.h | 2 + 8 files changed, 116 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0acacfca..d7cc19d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - remove vips_image_copy_fields() and vips_demand_hint() and add vips_image_pipeline() to do both jobs - vipsthumbnail allows non-square bounding boxes, thanks seth +- add vips_matrixprint() 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 diff --git a/libvips/convolution/conv.c b/libvips/convolution/conv.c index 74d79a76..441cb550 100644 --- a/libvips/convolution/conv.c +++ b/libvips/convolution/conv.c @@ -74,6 +74,11 @@ vips_conv_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_conv_parent_class )->build( object ) ) return( -1 ); + /* + printf( "vips_conv_build: convolving with:\n" ); + vips_matrixprint( convolution->M, NULL ); + */ + if( !(imsk = im_vips2imask( convolution->M, class->nickname )) || !im_local_imask( convolution->out, imsk ) ) return( -1 ); diff --git a/libvips/convolution/im_conv.c b/libvips/convolution/im_conv.c index efa8136e..7c0a0db2 100644 --- a/libvips/convolution/im_conv.c +++ b/libvips/convolution/im_conv.c @@ -1011,9 +1011,9 @@ im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) im_generate_fn generate; #ifdef DEBUG +#endif /*DEBUG*/ printf( "im_conv_raw: starting with matrix:\n" ); im_print_imask( mask ); -#endif /*DEBUG*/ /* Check parameters. */ diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index eed5d08c..75a85c6f 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -663,12 +663,11 @@ vips__matrix_body( char *whitemap, VipsImage *out, FILE *fp ) } VipsImage * -vips__matrix_read( const char *filename ) +vips__matrix_read_file( FILE *fp ) { char whitemap[256]; int i; char *p; - FILE *fp; int width; int height; double scale; @@ -680,13 +679,9 @@ vips__matrix_read( const char *filename ) for( p = WHITESPACE; *p; p++ ) whitemap[(int) *p] = 1; - if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) - return( NULL ); if( vips__matrix_header( whitemap, fp, - &width, &height, &scale, &offset ) ) { - fclose( fp ); + &width, &height, &scale, &offset ) ) return( NULL ); - } if( !(out = vips_image_new_matrix( width, height )) ) return( NULL ); @@ -695,28 +690,35 @@ vips__matrix_read( const char *filename ) if( vips__matrix_body( whitemap, out, fp ) ) { g_object_unref( out ); - fclose( fp ); return( NULL ); } - fclose( fp ); return( out ); } +VipsImage * +vips__matrix_read( const char *filename ) +{ + FILE *fp; + VipsImage *out; + + if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) + return( NULL ); + out = vips__matrix_read_file( fp ); + fclose( fp ); + + return( out ); +} + int -vips__matrix_write( VipsImage *in, const char *filename ) +vips__matrix_write_file( VipsImage *in, FILE *fp ) { VipsImage *mask; - FILE *fp; int x, y; if( vips_check_matrix( "vips2mask", in, &mask ) ) return( -1 ); - if( !(fp = vips__file_open_write( filename, TRUE )) ) { - g_object_unref( mask ); - return( -1 ); - } fprintf( fp, "%d %d ", mask->Xsize, mask->Ysize ); if( vips_image_get_typeof( mask, "scale" ) && vips_image_get_typeof( mask, "offset" ) ) @@ -733,10 +735,23 @@ vips__matrix_write( VipsImage *in, const char *filename ) } g_object_unref( mask ); - fclose( fp ); return( 0 ); } +int +vips__matrix_write( VipsImage *in, const char *filename ) +{ + FILE *fp; + int result; + + if( !(fp = vips__file_open_write( filename, TRUE )) ) + return( -1 ); + result = vips__matrix_write_file( in, fp ); + fclose( fp ); + + return( result ); +} + const char *vips__foreign_matrix_suffs[] = { ".mat", NULL }; diff --git a/libvips/foreign/csv.h b/libvips/foreign/csv.h index 7f868c3c..15d1e090 100644 --- a/libvips/foreign/csv.h +++ b/libvips/foreign/csv.h @@ -48,8 +48,10 @@ int vips__csv_write( VipsImage *in, const char *filename, int vips__matrix_read_header( const char *filename, int *width, int *height, double *scale, double *offset ); int vips__matrix_ismatrix( const char *filename ); +VipsImage *vips__matrix_read_file( FILE *fp ); VipsImage *vips__matrix_read( const char *filename ); int vips__matrix_write( VipsImage *in, const char *filename ); +int vips__matrix_write_file( VipsImage *in, FILE *fp ); extern const char *vips__foreign_matrix_suffs[]; diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index e9bb5048..830b6240 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1611,6 +1611,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_csv_get_type( void ); extern GType vips_foreign_load_matrix_get_type( void ); extern GType vips_foreign_save_matrix_get_type( void ); + extern GType vips_foreign_print_matrix_get_type( void ); extern GType vips_foreign_load_fits_get_type( void ); extern GType vips_foreign_save_fits_get_type( void ); extern GType vips_foreign_load_analyze_get_type( void ); @@ -1643,6 +1644,7 @@ vips_foreign_operation_init( void ) vips_foreign_save_csv_get_type(); vips_foreign_load_matrix_get_type(); vips_foreign_save_matrix_get_type(); + vips_foreign_print_matrix_get_type(); vips_foreign_load_analyze_get_type(); vips_foreign_load_raw_get_type(); vips_foreign_save_raw_get_type(); diff --git a/libvips/foreign/matrixsave.c b/libvips/foreign/matrixsave.c index dd36e045..d3e1934b 100644 --- a/libvips/foreign/matrixsave.c +++ b/libvips/foreign/matrixsave.c @@ -155,3 +155,75 @@ vips_matrixsave( VipsImage *in, const char *filename, ... ) return( result ); } + +typedef struct _VipsForeignPrintMatrix { + VipsForeignSave parent_object; + +} VipsForeignPrintMatrix; + +typedef VipsForeignSaveClass VipsForeignPrintMatrixClass; + +G_DEFINE_TYPE( VipsForeignPrintMatrix, vips_foreign_print_matrix, + VIPS_TYPE_FOREIGN_SAVE ); + +static int +vips_foreign_print_matrix_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_print_matrix_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__matrix_write_file( save->ready, stdout ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_print_matrix_class_init( VipsForeignPrintMatrixClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; + + object_class->nickname = "matrixprint"; + object_class->description = _( "print matrix" ); + object_class->build = vips_foreign_print_matrix_build; + + foreign_class->suffs = vips__foreign_matrix_suffs; + + save_class->saveable = VIPS_SAVEABLE_MONO; + save_class->format_table = bandfmt_matrix; +} + +static void +vips_foreign_print_matrix_init( VipsForeignPrintMatrix *matrix ) +{ +} + +/** + * vips_matrixprint: + * @in: image to print + * @...: %NULL-terminated list of optional named arguments + * + * Print @in to %stdout in matrix format. See vips_matrixload() for a + * description of the format. + * + * See also: vips_matrixload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_matrixprint( VipsImage *in, ... ) +{ + va_list ap; + int result; + + va_start( ap, in ); + result = vips_call_split( "matrixprint", ap, in ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index b3b1e79d..80dc7d32 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -421,6 +421,8 @@ int vips_matrixload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_matrixsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_matrixprint( VipsImage *in, ... ) + __attribute__((sentinel)); int vips_magickload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); From 25fd67bb84ba72a01e94591cbea735aa1cb2d6ee Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 13:24:48 +0100 Subject: [PATCH 20/26] reverse the direction of rot45 now matches the rest of vips --- libvips/conversion/rot45.c | 70 +++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c index 99712ab6..b6cfe2c4 100644 --- a/libvips/conversion/rot45.c +++ b/libvips/conversion/rot45.c @@ -116,7 +116,7 @@ vips_rot45_rot45( VipsImage *out, VipsImage *in ) g_assert( in->Xsize % 2 == 1 ); /* Split the square into 8 triangles. Loop over the top-left one, - * reflect into the others. + * reflect to index the others. * * 1 1 2 2 3 * 8 1 2 3 3 @@ -129,48 +129,48 @@ vips_rot45_rot45( VipsImage *out, VipsImage *in ) for( y = 0; y < size_2; y++ ) for( x = y; x < size_2; x++ ) { - /* Save 1, it goes into 8 at the end. + /* Save 1, it goes into 2 at the end. */ POINT_TO_TEMP( temp, x, y ); - /* Fill 1 from 2. + /* Fill 1 from 8. */ ASSIGN( x, y, - (x - y) + size_2, y ); - - /* 2 from 3. - */ - ASSIGN( (x - y) + size_2, y, - (size - 1) - y, x ); - - /* 3 from 4. - */ - ASSIGN( (size - 1) - y, x, - (size - 1) - y, (x - y) + size_2 ); - - /* 4 from 5. - */ - ASSIGN( (size - 1) - y, (x - y) + size_2, - (size - 1) - x, (size - 1) - y ); - - /* 5 from 6. - */ - ASSIGN( (size - 1) - x, (size - 1) - y, - size_2 - (x - y), (size - 1) - y ); - - /* 6 from 7. - */ - ASSIGN( size_2 - (x - y), (size - 1) - y, - y, (size - 1) - x ); - - /* 7 from 8. - */ - ASSIGN( y, (size - 1) - x, y, size_2 - (x - y) ); - /* 8 from saved 1. + /* 8 from 7. */ - TEMP_TO_POINT( y, size_2 - (x - y), temp ); + ASSIGN( y, size_2 - (x - y), + y, (size - 1) - x ); + + /* 7 from 6. + */ + ASSIGN( y, (size - 1) - x, + size_2 - (x - y), (size - 1) - y ); + + /* 6 from 5. + */ + ASSIGN( size_2 - (x - y), (size - 1) - y, + (size - 1) - x, (size - 1) - y ); + + /* 5 from 4. + */ + ASSIGN( (size - 1) - x, (size - 1) - y, + (size - 1) - y, (x - y) + size_2 ); + + /* 4 from 3. + */ + ASSIGN( (size - 1) - y, (x - y) + size_2, + (size - 1) - y, x ); + + /* 3 from 2. + */ + ASSIGN( (size - 1) - y, x, + (x - y) + size_2, y ); + + /* 2 from saved 1. + */ + TEMP_TO_POINT( (x - y) + size_2, y, temp ); } /* Centre. From 2e3d7db0e611839679c1743541e5e3d48817d121 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 13:46:17 +0100 Subject: [PATCH 21/26] switch to new im_compass() plus im_linedet() and im_gradient() --- ChangeLog | 3 +- TODO | 11 +- libvips/convolution/Makefile.am | 1 - libvips/convolution/im_compass.c | 158 ----------------------------- libvips/convolution/im_conv.c | 2 +- libvips/deprecated/vips7compat.c | 2 - libvips/include/vips/convolution.h | 10 +- libvips/include/vips/morphology.h | 3 - libvips/include/vips/vips7compat.h | 12 +++ 9 files changed, 17 insertions(+), 185 deletions(-) delete mode 100644 libvips/convolution/im_compass.c diff --git a/ChangeLog b/ChangeLog index f5fd0bb1..794ccffb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 19/10/13 started 7.37.0 - redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(), - im_erode(), im_rank_image() as classes + im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient() + as classes - vips_init() now does some ABI compat checking, though this change requires an ABI break - add "interlace" option to vips_jpegsave() diff --git a/TODO b/TODO index 5a0f66ce..fb737f4c 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,4 @@ -- started compass .. convolution with a rotating mask - - vips im_gradient k2.jpg x.v line.mat - vips compass k2.jpg x2.v line.mat --times 2 --angle 90 --combine sum - - should give the same result, you'd think - - deprecated/vips7compat.c defs for compass/grad/lindet commented out for now - - need to do sep +- need to do conv sep - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index a771122d..995e735b 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -8,7 +8,6 @@ libconvolution_la_SOURCES = \ morph.c \ convol_dispatch.c \ im_addgnoise.c \ - im_compass.c \ im_aconv.c \ im_aconvsep.c \ im_conv.c \ diff --git a/libvips/convolution/im_compass.c b/libvips/convolution/im_compass.c deleted file mode 100644 index 5963cfa7..00000000 --- a/libvips/convolution/im_compass.c +++ /dev/null @@ -1,158 +0,0 @@ -/* im_compass - * - * Author: N. Dessipris (Copyright, N. Dessipris 1991) - * Written on: 08/05/1991 - * Modified on: - * 11/3/01 JC - * - rewritten, calling im_conv() and im_maxvalue() - * 3/2/10 - * - gtkdoc - */ - -/* - - 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 - -/** - * im_compass: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved 8 times with @mask, each time @mask is rotated by 45 - * degrees. Each output pixel is the largest absolute value of the 8 - * convolutions. - * - * See also: im_lindetect(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_compass( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *filtered[8]; - IMAGE *absed[8]; - int i; - - if( im_open_local_array( out, filtered, 8, "im_compass:1", "p" ) || - im_open_local_array( out, absed, 8, "im_compass:2", "p" ) ) - return( -1 ); - - for( i = 0; i < 8; i++ ) { - if( im_conv( in, filtered[i], mask ) || - !(mask = im_local_imask( out, - im_rotate_imask45( mask, mask->filename ) )) ) - return( -1 ); - } - - for( i = 0; i < 8; i++ ) - if( im_abs( filtered[i], absed[i] ) ) - return( -1 ); - - return( im_maxvalue( absed, out, 8 ) ); -} - -/** - * im_lindetect: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved four times with @mask, each time @mask is rotated by 45 - * degrees. Each output pixel is the largest absolute value of the four - * convolutions. - * - * See also: im_compass(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *filtered[4]; - IMAGE *absed[4]; - int i; - - if( im_open_local_array( out, filtered, 4, "im_lindetect:1", "p" ) || - im_open_local_array( out, absed, 4, "im_lindetect:2", "p" ) ) - return( -1 ); - - for( i = 0; i < 4; i++ ) { - if( im_conv( in, filtered[i], mask ) || - !(mask = im_local_imask( out, - im_rotate_imask45( mask, mask->filename ) )) ) - return( -1 ); - } - - for( i = 0; i < 4; i++ ) - if( im_abs( filtered[i], absed[i] ) ) - return( -1 ); - - return( im_maxvalue( absed, out, 4 ) ); -} - -/** - * im_gradient: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved with @mask and with @mask after a 90 degree rotation. The - * result is the sum of the absolute value of the two convolutions. - * - * See also: im_lindetect(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t[4]; - INTMASK *rmask; - - if( im_open_local_array( out, t, 4, "im_gradient", "p" ) ) - return( -1 ); - - if( !(rmask = im_local_imask( out, - im_rotate_imask90( mask, mask->filename ) )) ) - return( -1 ); - - if( im_conv( in, t[0], mask ) || - im_conv( in, t[1], rmask ) || - im_abs( t[0], t[2] ) || - im_abs( t[1], t[3] ) || - im_add( t[2], t[3], out ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/convolution/im_conv.c b/libvips/convolution/im_conv.c index 7c0a0db2..efa8136e 100644 --- a/libvips/convolution/im_conv.c +++ b/libvips/convolution/im_conv.c @@ -1011,9 +1011,9 @@ im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) im_generate_fn generate; #ifdef DEBUG -#endif /*DEBUG*/ printf( "im_conv_raw: starting with matrix:\n" ); im_print_imask( mask ); +#endif /*DEBUG*/ /* Check parameters. */ diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 0719b858..5959b7d6 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2088,7 +2088,6 @@ im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) return( 0 ); } -/* int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ) { @@ -2162,7 +2161,6 @@ im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } - */ static int vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round ) diff --git a/libvips/include/vips/convolution.h b/libvips/include/vips/convolution.h index e8a6c1d0..940f6d86 100644 --- a/libvips/include/vips/convolution.h +++ b/libvips/include/vips/convolution.h @@ -81,20 +81,12 @@ void vips_convolution_operation_init( void ); - int im_aconvsep( VipsImage *in, VipsImage *out, DOUBLEMASK *mask, int n_layers ); -int im_aconv( VipsImage *in, VipsImage *out, - DOUBLEMASK *mask, int n_layers, int cluster ); -int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); + int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); -int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); - int im_sharpen( VipsImage *in, VipsImage *out, int mask_size, double x1, double y2, double y3, diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index ca182a39..b546bff0 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -42,9 +42,6 @@ extern "C" { -int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); - int im_rank( VipsImage *in, VipsImage *out, int width, int height, int index ); int im_cntlines( VipsImage *im, double *nolines, int flag ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 758880c3..58924752 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -897,6 +897,18 @@ int im_tone_map( VipsImage *in, VipsImage *out, VipsImage *lut ); */ #define vips_class_map_concrete_all vips_class_map_all +int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); + +int im_aconv( VipsImage *in, VipsImage *out, + DOUBLEMASK *mask, int n_layers, int cluster ); +int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); + +int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); + #ifdef __cplusplus } #endif /*__cplusplus*/ From edbbc5fe2b9cf059e79b22d2807fe399e90c425e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 13:56:13 +0100 Subject: [PATCH 22/26] add im_imask2vips() --- libvips/deprecated/im_mask2vips.c | 14 ++++++++++++++ libvips/include/vips/vips7compat.h | 1 + 2 files changed, 15 insertions(+) diff --git a/libvips/deprecated/im_mask2vips.c b/libvips/deprecated/im_mask2vips.c index f4c9e45d..1cf0142d 100644 --- a/libvips/deprecated/im_mask2vips.c +++ b/libvips/deprecated/im_mask2vips.c @@ -101,3 +101,17 @@ im_mask2vips( DOUBLEMASK *in, IMAGE *out ) return( 0 ); } +int +im_imask2vips( INTMASK *in, IMAGE *out ) +{ + DOUBLEMASK *d; + int result; + + if( !(d = im_imask2dmask( in, in->filename )) ) + return( -1 ); + result = im_mask2vips( d, out ); + im_free_dmask( d ); + + return( result ); +} + diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 58924752..534a7957 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -771,6 +771,7 @@ int im_blend( VipsImage *c, VipsImage *a, VipsImage *b, VipsImage *out ); DOUBLEMASK *im_vips2mask( VipsImage *in, const char *filename ); INTMASK *im_vips2imask( IMAGE *in, const char *filename ); int im_mask2vips( DOUBLEMASK *in, VipsImage *out ); +int im_imask2vips( INTMASK *in, VipsImage *out ); int im_bandmean( VipsImage *in, VipsImage *out ); int im_recomb( VipsImage *in, VipsImage *out, DOUBLEMASK *recomb ); From 68c5f1909a77f7606a546f3a900973c449c561a6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 14:37:43 +0100 Subject: [PATCH 23/26] redo im_convsep() as a class and im_convsep_f() --- ChangeLog | 4 +- TODO | 10 +- libvips/convolution/Makefile.am | 1 + libvips/convolution/convolution.c | 4 +- libvips/convolution/convsep.c | 173 +++++++++++++++++++++++++++++ libvips/convolution/im_conv.c | 91 --------------- libvips/convolution/im_conv_f.c | 74 ------------ libvips/deprecated/vips7compat.c | 59 ++++++++++ libvips/include/vips/convolution.h | 8 +- libvips/include/vips/error.h | 1 + libvips/include/vips/vips7compat.h | 6 + libvips/iofuncs/error.c | 27 +++++ 12 files changed, 283 insertions(+), 175 deletions(-) create mode 100644 libvips/convolution/convsep.c diff --git a/ChangeLog b/ChangeLog index 794ccffb..19902d8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 19/10/13 started 7.37.0 - redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(), - im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient() - as classes + im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient(), + im_convsep(), im_convsep_f() as classes - vips_init() now does some ABI compat checking, though this change requires an ABI break - add "interlace" option to vips_jpegsave() diff --git a/TODO b/TODO index fb737f4c..1fd4ef85 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,12 @@ -- need to do conv sep +- we have aconv and aconvsep + + test timing, make sure it;s worth having a separate aconvsep version + + if it is, make im_aconvsep an optimisation: call im_aconvsep_raw() from + vips_conv() if mask width or height == 1 and prec == APPROX + + now we can get rid of im_aconvsep() since it's just vips_convsep() with prec + set to approx - do conv and morph quickly as simple wrappers over the vips7 operations diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index 995e735b..4e2f0bb4 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -4,6 +4,7 @@ libconvolution_la_SOURCES = \ convolution.c \ pconvolution.h \ conv.c \ + convsep.c \ compass.c \ morph.c \ convol_dispatch.c \ diff --git a/libvips/convolution/convolution.c b/libvips/convolution/convolution.c index dec22e52..12abd08f 100644 --- a/libvips/convolution/convolution.c +++ b/libvips/convolution/convolution.c @@ -78,8 +78,8 @@ G_DEFINE_ABSTRACT_TYPE( VipsConvolution, vips_convolution, static int vips_convolution_build( VipsObject *object ) { - VipsConvolution *convolution = VIPS_CONVOLUTION( object ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = VIPS_CONVOLUTION( object ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); #ifdef DEBUG @@ -149,8 +149,10 @@ vips_convolution_operation_init( void ) extern int vips_conv_get_type( void ); extern int vips_morph_get_type( void ); extern int vips_compass_get_type( void ); + extern int vips_convsep_get_type( void ); vips_conv_get_type(); vips_morph_get_type(); vips_compass_get_type(); + vips_convsep_get_type(); } diff --git a/libvips/convolution/convsep.c b/libvips/convolution/convsep.c new file mode 100644 index 00000000..b942f67b --- /dev/null +++ b/libvips/convolution/convsep.c @@ -0,0 +1,173 @@ +/* convolve twice, rotating the mask + * + * 23/10/13 + * - from vips_convsep() + */ + +/* + + 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 "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + VipsPrecision precision; + int layers; + int cluster; +} VipsConvsep; + +typedef VipsConvolutionClass VipsConvsepClass; + +G_DEFINE_TYPE( VipsConvsep, vips_convsep, VIPS_TYPE_CONVOLUTION ); + +static int +vips_convsep_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = (VipsConvolution *) object; + VipsConvsep *convsep = (VipsConvsep *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 3 ); + + g_object_set( convsep, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_convsep_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_separable( class->nickname, convolution->M ) ) + return( -1 ); + + if( vips_rot( convolution->M, &t[0], VIPS_ANGLE_90, NULL ) || + vips_conv( convolution->in, &t[1], convolution->M, + "precision", convsep->precision, + "layers", convsep->layers, + "cluster", convsep->cluster, + NULL ) || + vips_conv( t[1], &t[2], t[0], + "precision", convsep->precision, + "layers", convsep->layers, + "cluster", convsep->cluster, + NULL ) ) + return( -1 ); + + if( vips_image_write( t[2], convolution->out ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_convsep_class_init( VipsConvsepClass *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 = "convsep"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_convsep_build; + + VIPS_ARG_ENUM( class, "precision", 203, + _( "Precision" ), + _( "Convolve with this precision" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, precision ), + VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER ); + + VIPS_ARG_INT( class, "layers", 204, + _( "Layers" ), + _( "Use this many layers in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, layers ), + 1, 1000, 5 ); + + VIPS_ARG_INT( class, "cluster", 205, + _( "Cluster" ), + _( "Cluster lines closer than this in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, cluster ), + 1, 100, 1 ); + +} + +static void +vips_convsep_init( VipsConvsep *convsep ) +{ + convsep->precision = VIPS_PRECISION_INTEGER; + convsep->layers = 5; + convsep->cluster = 1; +} + +/** + * vips_convsep: + * @in: input image + * @out: output image + * @mask: convolution mask + * + * Optional arguments: + * + * @precision: calculation accuracy + * @layers: number of layers for approximation + * @cluster: cluster lines closer than this distance + * + * Perform a separable convolution of @in with @mask. + * See vips_conv() for a detailed description. + * + * The mask must be 1xn or nx1 elements. + * + * The image is convolved twice: once with @mask and then again with @mask + * rotated by 90 degrees. This is much faster for certain types of mask + * (gaussian blur, for example) than doing a full 2D convolution. + * + * See also: vips_conv(), vips_gaussmat(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) +{ + va_list ap; + int result; + + va_start( ap, mask ); + result = vips_call_split( "convsep", ap, in, out, mask ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/convolution/im_conv.c b/libvips/convolution/im_conv.c index efa8136e..a2a8ea82 100644 --- a/libvips/convolution/im_conv.c +++ b/libvips/convolution/im_conv.c @@ -1075,28 +1075,6 @@ im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } -/** - * im_conv: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Convolve @in with @mask using integer arithmetic. The output image - * always has the same #VipsBandFmt as the input image. - * - * Each output pixel is - * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale - * and offset are part of @mask. For integer @in, the division by scale - * includes round-to-nearest. - * - * Convolutions on unsigned 8-bit images are calculated with the - * processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_conv_f(), im_convsep(), im_create_imaskv(). - * - * Returns: 0 on success, -1 on error - */ int im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) { @@ -1114,72 +1092,3 @@ im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } - -int -im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t; - INTMASK *rmask; - - if( mask->xsize != 1 && mask->ysize != 1 ) { - im_error( "im_convsep", - "%s", _( "expect 1xN or Nx1 input mask" ) ); - return( -1 ); - } - - if( !(t = im_open_local( out, "im_convsep", "p" )) || - !(rmask = (INTMASK *) im_local( out, - (im_construct_fn) im_dup_imask, - (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) - return( -1 ); - - rmask->xsize = mask->ysize; - rmask->ysize = mask->xsize; - rmask->offset = 0.; - - if( im_conv_raw( in, t, rmask ) || - im_conv_raw( t, out, mask ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_convsep: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Perform a separable convolution of @in with @mask using integer arithmetic. - * See im_conv() for a detailed description. - * - * The mask must be 1xn or nx1 elements. - * The output image - * always has the same #VipsBandFmt as the input image. - * - * The image is convolved twice: once with @mask and then again with @mask - * rotated by 90 degrees. This is much faster for certain types of mask - * (gaussian blur, for example) than doing a full 2D convolution. - * - * See also: im_convsep_f(), im_conv(), im_create_imaskv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); - int n_mask = mask->xsize * mask->ysize; - - if( !t1 || - im_embed( in, t1, 1, n_mask / 2, n_mask / 2, - in->Xsize + n_mask - 1, - in->Ysize + n_mask - 1 ) || - im_convsep_raw( t1, out, mask ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/convolution/im_conv_f.c b/libvips/convolution/im_conv_f.c index a4ee23ab..d4fefad2 100644 --- a/libvips/convolution/im_conv_f.c +++ b/libvips/convolution/im_conv_f.c @@ -392,77 +392,3 @@ im_conv_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) return( 0 ); } - -int -im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - IMAGE *t; - DOUBLEMASK *rmask; - - if( mask->xsize != 1 && mask->ysize != 1 ) { - im_error( "im_convsep_f", - "%s", _( "expect 1xN or Nx1 input mask" ) ); - return( -1 ); - } - - if( !(t = im_open_local( out, "im_convsep_f", "p" )) || - !(rmask = (DOUBLEMASK *) im_local( out, - (im_construct_fn) im_dup_dmask, - (im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) ) - return( -1 ); - - rmask->xsize = mask->ysize; - rmask->ysize = mask->xsize; - rmask->offset = 0.; - - if( im_conv_f_raw( in, t, rmask ) || - im_conv_f_raw( t, out, mask ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_convsep_f: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Perform a separable convolution of @in with @mask using floating-point - * arithmetic. - * - * The mask must be 1xn or nx1 elements. - * The output image - * is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case - * @out is also %IM_BANDFMT_DOUBLE. - * - * The image is convolved twice: once with @mask and then again with @mask - * rotated by 90 degrees. This is much faster for certain types of mask - * (gaussian blur, for example) than doing a full 2D convolution. - * - * Each output pixel is - * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale - * and offset are part of @mask. - * - * See also: im_convsep(), im_conv(), im_create_dmaskv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); - int size = mask->xsize * mask->ysize; - - if( !t1 || - im_embed( in, t1, 1, size / 2, size / 2, - in->Xsize + size - 1, - in->Ysize + size - 1 ) || - im_convsep_f_raw( t1, out, mask ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 5959b7d6..5a3ccba9 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2162,6 +2162,65 @@ im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } +int +im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + im_error( "im_convsep_raw", "no compat function" ); + return( -1 ); +} + +int +im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_convsep( in, &t2, t1, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ +} + +int +im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_convsep( in, &t2, t1, + "precision", VIPS_PRECISION_FLOAT, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + static int vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round ) { diff --git a/libvips/include/vips/convolution.h b/libvips/include/vips/convolution.h index 940f6d86..4d256b3f 100644 --- a/libvips/include/vips/convolution.h +++ b/libvips/include/vips/convolution.h @@ -71,6 +71,8 @@ int vips_conv( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) __attribute__((sentinel)); int vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) __attribute__((sentinel)); +int vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) + __attribute__((sentinel)); int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, VipsOperationMorphology morph, ... ) @@ -81,12 +83,6 @@ void vips_convolution_operation_init( void ); -int im_aconvsep( VipsImage *in, VipsImage *out, - DOUBLEMASK *mask, int n_layers ); - -int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); - int im_sharpen( VipsImage *in, VipsImage *out, int mask_size, double x1, double y2, double y3, diff --git a/libvips/include/vips/error.h b/libvips/include/vips/error.h index cba8dca6..9bfdf139 100644 --- a/libvips/include/vips/error.h +++ b/libvips/include/vips/error.h @@ -88,6 +88,7 @@ int vips_check_vector_length( const char *domain, int n, int len ); int vips_check_vector( const char *domain, int n, VipsImage *im ); int vips_check_hist( const char *domain, VipsImage *im ); int vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ); +int vips_check_separable( const char *domain, VipsImage *im ); int vips_check_imask( const char *domain, INTMASK *mask ); int vips_check_dmask( const char *domain, DOUBLEMASK *mask ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 534a7957..bf4830b8 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -906,6 +906,12 @@ int im_aconv( VipsImage *in, VipsImage *out, int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); +int im_aconvsep( VipsImage *in, VipsImage *out, + DOUBLEMASK *mask, int n_layers ); + +int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); + int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index e9806801..28cc8d99 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -1266,6 +1266,33 @@ vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ) return( 0 ); } +/** + * vips_check_separable: + * @domain: the originating domain for the error message + * @im: image to check + * + * Separable matrix images must have width or height 1. + * Return 0 if the image will pass, or -1 and + * set an error message otherwise. + * + * See also: vips_error(). + * + * Returns: 0 if OK, -1 otherwise. + */ +int +vips_check_separable( const char *domain, VipsImage *im ) +{ + if( im->Xsize != 1 && + im->Ysize != 1 ) { + vips_error( domain, + "%s", _( "separable matrix images must have " + "width or height 1" ) ); + return( -1 ); + } + + return( 0 ); +} + /** * vips_check_imask: (skip) * @domain: the originating domain for the error message From 4b0bcd311b6afad10843b08beb52bf97c9768321 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 25 Oct 2013 14:52:58 +0100 Subject: [PATCH 24/26] deprecate im_addgnoise() it's just noise + add now. --- TODO | 2 + libvips/convolution/Makefile.am | 1 - libvips/convolution/im_addgnoise.c | 83 ------------------------------ libvips/deprecated/vips7compat.c | 13 +++++ libvips/include/vips/convolution.h | 2 - libvips/include/vips/vips7compat.h | 2 + 6 files changed, 17 insertions(+), 86 deletions(-) delete mode 100644 libvips/convolution/im_addgnoise.c diff --git a/TODO b/TODO index 1fd4ef85..b5860f24 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,8 @@ now we can get rid of im_aconvsep() since it's just vips_convsep() with prec set to approx + aconv needs some more work, get it going at least with gaussian + - do conv and morph quickly as simple wrappers over the vips7 operations - add vips_gaussian_blur() with approx / int / float precision, maybe diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index 4e2f0bb4..a156a44f 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -8,7 +8,6 @@ libconvolution_la_SOURCES = \ compass.c \ morph.c \ convol_dispatch.c \ - im_addgnoise.c \ im_aconv.c \ im_aconvsep.c \ im_conv.c \ diff --git a/libvips/convolution/im_addgnoise.c b/libvips/convolution/im_addgnoise.c deleted file mode 100644 index 428eb2d9..00000000 --- a/libvips/convolution/im_addgnoise.c +++ /dev/null @@ -1,83 +0,0 @@ -/* im_addgnoise - * - * Copyright 1990, N. Dessipris. - * - * File written on 2/12/1986 - * Author : N. Dessipris - * Updated : 22/01/1991 - * 1/2/95 JC - * - junked! - * - now uses partial im_gaussnoise() and partial im_add() to do the - * same job - & 1/5/01 JC - * - oops, failed for not-1-band images - * - * 2008-01-28 tcv: - * - now works (was broken) - * - no limit on bands - * 4/2/10 - * - no need to bandup here now, im_add() does that - * - gtkdoc - */ - -/* - - 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 - -/** - * im_addgnoise: - * @in: input image - * @out: output image - * @sigma: standard deviation of noise - * - * Add gaussian noise with mean 0 and variance sigma to @in. - * The noise is generated by averaging 12 random numbers, - * see page 78, PIETGEN, 1989. - * - * See also: im_gaussnoise(). - * - * Returns: 0 on success, -1 on error - */ -int -im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) -{ - IMAGE *t; - - if( !(t = im_open_local( out, "im_addgnoise", "p" )) || - im_gaussnoise( t, in->Xsize, in->Ysize, 0, sigma ) || - im_add( in, t, out ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 5a3ccba9..a541845a 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2221,6 +2221,19 @@ im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) return( 0 ); } +int +im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) +{ + IMAGE *t; + + if( !(t = im_open_local( out, "im_addgnoise", "p" )) || + im_gaussnoise( t, in->Xsize, in->Ysize, 0, sigma ) || + im_add( in, t, out ) ) + return( -1 ); + + return( 0 ); +} + static int vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round ) { diff --git a/libvips/include/vips/convolution.h b/libvips/include/vips/convolution.h index 4d256b3f..3ce122f8 100644 --- a/libvips/include/vips/convolution.h +++ b/libvips/include/vips/convolution.h @@ -97,8 +97,6 @@ int im_gradcor( VipsImage *in, VipsImage *ref, VipsImage *out ); int im_contrast_surface( VipsImage *in, VipsImage *out, int half_win_size, int spacing ); -int im_addgnoise( VipsImage *in, VipsImage *out, double sigma ); - #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index bf4830b8..ba476e4c 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -916,6 +916,8 @@ int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_addgnoise( VipsImage *in, VipsImage *out, double sigma ); + #ifdef __cplusplus } #endif /*__cplusplus*/ From 9b72a57511c9047b8a2e6a6565ed01565be2f061 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 28 Oct 2013 15:59:25 +0000 Subject: [PATCH 25/26] sync --- TODO | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index b5860f24..0a1fb5d4 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,3 @@ -- we have aconv and aconvsep - - test timing, make sure it;s worth having a separate aconvsep version - - if it is, make im_aconvsep an optimisation: call im_aconvsep_raw() from - vips_conv() if mask width or height == 1 and prec == APPROX - - now we can get rid of im_aconvsep() since it's just vips_convsep() with prec - set to approx - - aconv needs some more work, get it going at least with gaussian - do conv and morph quickly as simple wrappers over the vips7 operations @@ -147,6 +136,18 @@ convolution wait for vipsobject for this +- we have aconv and aconvsep + + test timing, make sure it;s worth having a separate aconvsep version + + if it is, make im_aconvsep an optimisation: call im_aconvsep_raw() from + vips_conv() if mask width or height == 1 and prec == APPROX + + now we can get rid of im_aconvsep() since it's just vips_convsep() with prec + set to approx + + aconv needs some more work, get it going at least with gaussian + arithmetic ========== From 8f69e4ece2acd91d8853f9bc6ba74a44d55c15aa Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 28 Oct 2013 21:16:31 +0000 Subject: [PATCH 26/26] compat --- libvips/deprecated/vips7compat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index a541845a..dba66fab 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2195,6 +2195,8 @@ im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) int im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) { + im_error( "im_convsep_raw", "no compat function" ); + return( -1 ); } int @@ -2203,7 +2205,7 @@ im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) VipsImage *t1, *t2; if( !(t1 = vips_image_new()) || - im_imask2vips( mask, t1 ) ) + im_mask2vips( mask, t1 ) ) return( -1 ); if( vips_convsep( in, &t2, t1, "precision", VIPS_PRECISION_FLOAT,