From 7490da07a67cd8753a4ff5d4f9ffdb53c31fe332 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 2 Jul 2013 22:09:50 +0100 Subject: [PATCH] rename image array as matrix we had vips_image_new_array(), this was confusing with VIPS_TYPE_ARRAY, something completely different ... rename image array as matrix --- ChangeLog | 2 + libvips/arithmetic/measure.c | 2 +- libvips/arithmetic/stats.c | 4 +- libvips/create/buildlut.c | 353 +++++++++++++++++++++++++++++++++++ libvips/foreign/csv.c | 2 +- libvips/include/vips/image.h | 6 +- libvips/iofuncs/enumtypes.c | 2 +- libvips/iofuncs/header.c | 4 +- libvips/iofuncs/image.c | 10 +- 9 files changed, 370 insertions(+), 15 deletions(-) create mode 100644 libvips/create/buildlut.c diff --git a/ChangeLog b/ChangeLog index d784be3e..9c2c81d8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 1/7/13 started 7.35.0 - added vips_matrixload() and vips_matrixsave(), load and save vips mat format +- rename image arrays as image matrices ... INTERPRETATION_ARRAY -> + INTERPRETAtioN_MATRIX etc. 28/6/13 started 7.34.1 - fix morphological operators on non-uchar images diff --git a/libvips/arithmetic/measure.c b/libvips/arithmetic/measure.c index b9b475f4..28f32412 100644 --- a/libvips/arithmetic/measure.c +++ b/libvips/arithmetic/measure.c @@ -108,7 +108,7 @@ vips_measure_build( VipsObject *object ) bands = vips_image_get_bands( measure->in ); g_object_set( object, - "out", vips_image_new_array( bands, measure->h * measure->v ), + "out", vips_image_new_matrix( bands, measure->h * measure->v ), NULL ); /* left/top/width/height default to the size of the image. diff --git a/libvips/arithmetic/stats.c b/libvips/arithmetic/stats.c index 5b6d2c74..c580eb77 100644 --- a/libvips/arithmetic/stats.c +++ b/libvips/arithmetic/stats.c @@ -124,7 +124,7 @@ vips_stats_build( VipsObject *object ) return( -1 ); g_object_set( object, - "out", vips_image_new_array( COL_LAST, bands + 1 ), + "out", vips_image_new_matrix( COL_LAST, bands + 1 ), NULL ); } @@ -236,7 +236,7 @@ vips_stats_start( VipsStatistic *statistic ) VipsStats *stats; stats = g_new( VipsStats, 1 ); - if( !(stats->out = vips_image_new_array( COL_LAST, bands + 1 )) ) { + if( !(stats->out = vips_image_new_matrix( COL_LAST, bands + 1 )) ) { g_free( stats ); return( NULL ); } diff --git a/libvips/create/buildlut.c b/libvips/create/buildlut.c new file mode 100644 index 00000000..7eb3f355 --- /dev/null +++ b/libvips/create/buildlut.c @@ -0,0 +1,353 @@ +/* Build a LUT from a set of x/y points. + * + * Written on: 26/9/06 + * - from im_invertlut() + * 9/10/06 + * - don't output x values + * 18/3/09 + * - saner limit and rounding behaviour + * 30/3/09 + * - argh, fixed again + * 22/6/09 + * - more fixes for tables that don't start at zero (thanks Jack) + * 23/3/10 + * - gtkdoc + * 2/7/13 + * - convert to 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 DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +/* Our state. + */ +typedef struct _VipsBuildlut { + VipsCreate parent_instance; + + VipsImage *input; /* Input matrix */ + + int xlow; /* Index 0 in output is this x */ + int lut_size; /* Number of output elements to generate */ + double **data; /* Rows of unpacked matrix */ + double *buf; /* Ouput buffer */ +} VipsBuildlut; + +typedef VipsCreateClass VipsBuildlutClass; + +G_DEFINE_TYPE( VipsBuildlut, vips_buildlut, VIPS_TYPE_CREATE ); + +static void +vips_buildlut_dispose( GObject *gobject ) +{ + VipsBuildlut *lut = (VipsBuildlut *) gobject; + + if( lut->data ) { + int i; + + for( i = 0; i < lut->input->Ysize; i++ ) + VIPS_FREE( lut->data[i] ); + } + + VIPS_FREE( lut->data ); + VIPS_FREE( lut->buf ); + + G_OBJECT_CLASS( vips_buildlut_parent_class )->dispose( gobject ); +} + +/* Use this to sort our input rows by the first column. + */ +static int +vips_buildlut_compare( const void *a, const void *b ) +{ + double **r1 = (double **) a; + double **r2 = (double **) b; + + double diff = r1[0][0] - r2[0][0]; + + if( diff > 0 ) + return( 1 ); + else if( diff == 0 ) + return( 0 ); + else + return( -1 ); +} + +static int +vips_buildlut_build_init( VipsBuildlut *lut ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( lut ); + VipsCreate *create = VIPS_CREATE( lut ); + + int x, y, i; + int xlow, xhigh; + + /* Need xlow and xhigh to get the size of the LUT we build. + */ + xlow = xhigh = input->coeff[0]; + for( y = 0; y < input->ysize; y++ ) { + double v = input->coeff[y * input->xsize]; + + if( floor( v ) != v ) { + im_error( "im_buildlut", + "%s", _( "x value not an int" ) ); + return( -1 ); + } + + if( v < xlow ) + xlow = v; + if( v > xhigh ) + xhigh = v; + } + state->xlow = xlow; + state->lut_size = xhigh - xlow + 1; + + if( state->lut_size < 1 ) { + im_error( "im_buildlut", "%s", _( "x range too small" ) ); + return( -1 ); + } + + if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) + return( -1 ); + for( y = 0; y < input->ysize; y++ ) + state->data[y] = NULL; + for( y = 0; y < input->ysize; y++ ) + if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) + return( -1 ); + + for( i = 0, y = 0; y < input->ysize; y++ ) + for( x = 0; x < input->xsize; x++, i++ ) + state->data[y][x] = input->coeff[i]; + + if( !(state->buf = IM_ARRAY( NULL, + state->lut_size * (input->xsize - 1), double )) ) + return( -1 ); + + /* Sort by 1st column in input. + */ + qsort( state->data, input->ysize, sizeof( double * ), compare ); + +#ifdef DEBUG + printf( "Input table, sorted by 1st column\n" ); + for( y = 0; y < input->ysize; y++ ) { + printf( "%.4d ", y ); + + for( x = 0; x < input->xsize; x++ ) + printf( "%.9f ", state->data[y][x] ); + + printf( "\n" ); + } +#endif /*DEBUG*/ + + return( 0 ); +} + +/* Fill our state. + */ +static int +vips_buildlut_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsBuildlut *lut = (VipsBuildlut *) object; + + if( VIPS_OBJECT_CLASS( vips_buildlut_parent_class )->build( object ) ) + return( -1 ); + + if( vips_buildlut_build_init( lut ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_buildlut_class_init( VipsTextClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->dispose = vips_buildlut_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "buildlut"; + vobject_class->description = _( "build a look-up table" ); + vobject_class->build = vips_buildlut_build; + + VIPS_ARG_IMAGE( class, "in", 4, + _( "Input" ), + _( "Matrix of XY coordinates" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsImage, input ), + NULL ); + +} + +static int +buildlut( State *state ) +{ + const int xlow = state->xlow; + const DOUBLEMASK *input = state->input; + const int ysize = input->ysize; + const int xsize = input->xsize; + const int bands = xsize - 1; + const int xlast = state->data[ysize - 1][0]; + + int b, i, x; + + /* Do each output channel separately. + */ + for( b = 0; b < bands; b++ ) { + for( i = 0; i < ysize - 1; i++ ) { + const int x1 = state->data[i][0]; + const int x2 = state->data[i + 1][0]; + const int dx = x2 - x1; + const double y1 = state->data[i][b + 1]; + const double y2 = state->data[i + 1][b + 1]; + const double dy = y2 - y1; + + for( x = 0; x < dx; x++ ) + state->buf[b + (x + x1 - xlow) * bands] = + y1 + x * dy / dx; + } + + /* We are inclusive: pop the final value in by hand. + */ + state->buf[b + (xlast - xlow) * bands] = + state->data[ysize - 1][b + 1]; + } + + return( 0 ); +} + +/** + * im_buildlut: + * @input: input mask + * @output: output image + * + * This operation builds a lookup table from a set of points. Intermediate + * values are generated by piecewise linear interpolation. + * + * For example, consider this 2 x 2 matrix of (x, y) coordinates: + * + * + * + * + * 0 + * 0 + * + * + * 255 + * 100 + * + * + * + * + * We then generate: + * + * + * + * + * Index + * Value + * + * + * + * + * 0 + * 0 + * + * + * 1 + * 0.4 + * + * + * ... + * etc. by linear interpolation + * + * + * 255 + * 100 + * + * + * + * + * This is then written as the output image, with the left column giving the + * index in the image to place the value. + * + * The (x, y) points don't need to be sorted: we do that. You can have + * several Ys, each becomes a band in the output LUT. You don't need to + * start at zero, any integer will do, including negatives. + * + * See also: im_identity(), im_invertlut(). + * + * Returns: 0 on success, -1 on error + */ +int +im_buildlut( DOUBLEMASK *input, IMAGE *output ) +{ + State state; + + if( !input || input->xsize < 2 || input->ysize < 1 ) { + im_error( "im_buildlut", "%s", _( "bad input matrix size" ) ); + return( -1 ); + } + + if( build_state( &state, input ) || + buildlut( &state ) ) { + free_state( &state ); + return( -1 ); + } + + im_initdesc( output, + state.lut_size, 1, input->xsize - 1, + IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, + IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); + if( im_setupout( output ) || + im_writeline( 0, output, (VipsPel *) state.buf ) ) { + free_state( &state ); + return( -1 ); + } + + free_state( &state ); + + return( 0 ); +} diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index 930b0059..561376cf 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -650,7 +650,7 @@ vips__array_read( const char *filename ) return( NULL ); } - if( !(out = vips_image_new_array( width, height )) ) + if( !(out = vips_image_new_matrix( width, height )) ) return( NULL ); vips_image_set_double( out, "scale", scale ); vips_image_set_double( out, "offset", offset ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index bb85fd66..e3dc28b6 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -127,7 +127,7 @@ typedef enum { * @VIPS_INTERPRETATION_YXY: pixels are CIE Yxy * @VIPS_INTERPRETATION_RGB16: generic 16-bit RGB * @VIPS_INTERPRETATION_GREY16: generic 16-bit mono - * @VIPS_INTERPRETATION_ARRAY: an array + * @VIPS_INTERPRETATION_MATRIX: a matrix * * How the values in an image should be interpreted. For example, a * three-band float image of type #VIPS_INTERPRETATION_LAB should have its @@ -158,7 +158,7 @@ typedef enum { VIPS_INTERPRETATION_FOURIER = 24, VIPS_INTERPRETATION_RGB16 = 25, VIPS_INTERPRETATION_GREY16 = 26, - VIPS_INTERPRETATION_ARRAY = 27, + VIPS_INTERPRETATION_MATRIX = 27, VIPS_INTERPRETATION_scRGB = 28 } VipsInterpretation; @@ -506,7 +506,7 @@ VipsImage *vips_image_new_from_file_raw( const char *filename, int xsize, int ysize, int bands, guint64 offset ); VipsImage *vips_image_new_from_memory( void *buffer, int xsize, int ysize, int bands, VipsBandFormat bandfmt ); -VipsImage *vips_image_new_array( int xsize, int ysize ); +VipsImage *vips_image_new_matrix( int xsize, int ysize ); void vips_image_set_delete_on_close( VipsImage *image, gboolean delete_on_close ); VipsImage *vips_image_new_temp_file( const char *format ); diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index d961c0d4..53c1ca03 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -492,7 +492,7 @@ vips_interpretation_get_type( void ) {VIPS_INTERPRETATION_FOURIER, "VIPS_INTERPRETATION_FOURIER", "fourier"}, {VIPS_INTERPRETATION_RGB16, "VIPS_INTERPRETATION_RGB16", "rgb16"}, {VIPS_INTERPRETATION_GREY16, "VIPS_INTERPRETATION_GREY16", "grey16"}, - {VIPS_INTERPRETATION_ARRAY, "VIPS_INTERPRETATION_ARRAY", "array"}, + {VIPS_INTERPRETATION_MATRIX, "VIPS_INTERPRETATION_MATRIX", "matrix"}, {VIPS_INTERPRETATION_scRGB, "VIPS_INTERPRETATION_scRGB", "scrgb"}, {0, NULL, NULL} }; diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index 13b4c172..a4187fe5 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -493,11 +493,11 @@ vips_image_guess_interpretation( const VipsImage *image ) sane = FALSE; break; - case VIPS_INTERPRETATION_ARRAY: + case VIPS_INTERPRETATION_MATRIX: if( image->Bands != 1 ) sane = FALSE; break; - + default: g_assert( 0 ); sane = FALSE; diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 13bc4ae0..f0991a2a 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -1616,11 +1616,11 @@ vips_image_new_from_memory( void *buffer, } /** - * vips_image_new_array: + * vips_image_new_matrix: * @xsize: image width * @ysize: image height * - * This convenience function makes an image which is an array: a one-band + * This convenience function makes an image which is a matrix: a one-band * VIPS_FORMAT_DOUBLE image held in memory. * * Use VIPS_IMAGE_ADDR() to address pixels in the image. @@ -1628,7 +1628,7 @@ vips_image_new_from_memory( void *buffer, * Returns: the new #VipsImage, or %NULL on error. */ VipsImage * -vips_image_new_array( int xsize, int ysize ) +vips_image_new_matrix( int xsize, int ysize ) { VipsImage *image; @@ -1636,13 +1636,13 @@ vips_image_new_array( int xsize, int ysize ) image = VIPS_IMAGE( g_object_new( VIPS_TYPE_IMAGE, NULL ) ); g_object_set( image, - "filename", "vips_image_new_array", + "filename", "vips_image_new_matrix", "mode", "t", "width", xsize, "height", ysize, "bands", 1, "format", VIPS_FORMAT_DOUBLE, - "interpretation", VIPS_INTERPRETATION_ARRAY, + "interpretation", VIPS_INTERPRETATION_MATRIX, NULL ); if( vips_object_build( VIPS_OBJECT( image ) ) ) { VIPS_UNREF( image );