From e489d2e0990a2e64371c8910fab078d45613c5e7 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 14 Jan 2014 19:31:19 +0000 Subject: [PATCH] add --uchar option to vips_linear() --- ChangeLog | 1 + TODO | 6 +- libvips/arithmetic/arithmetic.c | 8 +- libvips/arithmetic/linear.c | 152 +++++++++++++++++++++++++++---- libvips/arithmetic/parithmetic.h | 4 + libvips/conversion/scale.c | 16 ++-- libvips/create/point.c | 9 +- 7 files changed, 162 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index 76d7857a..0f887e98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -35,6 +35,7 @@ - redone im_fwfft(), im_invfft(), im_freqflt(), im_disp_ps(), im_fractsurf(), im_phasecor() as classes - vips_colourspace() allows B_W, GREY16, RGB16 as source / target +- vips_linear() has a uchar output mode 9/1/14 started 7.36.6 - fix some clang compiler warnings diff --git a/TODO b/TODO index 7d84baa9..495765cd 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,9 @@ +- now vips_linear() has uchar output, can we do something with orc? - do restrict on some more packages, we've just done arithmetic so far also resample, colour, some of conversion, create, -- orc is poor for float stuff ... if vips_linear() had a "preserve type" - option we could do something - - maybe a "uchar ouput" option? this seems to be a very common case - - maybe avg? but avg doesn't subclass arithmetic, so we can't diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index 76f51584..bf631dea 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -554,8 +554,11 @@ vips_arithmetic_build( VipsObject *object ) return( -1 ); arithmetic->out->Bands = arithmetic->ready[0]->Bands; - arithmetic->out->BandFmt = - aclass->format_table[arithmetic->ready[0]->BandFmt]; + if( arithmetic->format != VIPS_FORMAT_NOTSET ) + arithmetic->out->BandFmt = arithmetic->format; + else + arithmetic->out->BandFmt = + aclass->format_table[arithmetic->ready[0]->BandFmt]; if( vips_image_generate( arithmetic->out, vips_start_many, vips_arithmetic_gen, vips_stop_many, @@ -592,6 +595,7 @@ static void vips_arithmetic_init( VipsArithmetic *arithmetic ) { arithmetic->base_bands = 1; + arithmetic->format = VIPS_FORMAT_NOTSET; } void diff --git a/libvips/arithmetic/linear.c b/libvips/arithmetic/linear.c index 2f9538e2..26ee9d48 100644 --- a/libvips/arithmetic/linear.c +++ b/libvips/arithmetic/linear.c @@ -42,6 +42,8 @@ * - 1ary is back, faster with gcc 4.8 * 3/12/13 * - try an ORC path with the band loop unrolled + * 14/1/14 + * - add uchar output option */ /* @@ -96,6 +98,10 @@ typedef struct _VipsLinear { VipsArea *a; VipsArea *b; + /* uchar output. + */ + gboolean uchar; + /* Our constants expanded to match arith->ready in size. */ int n; @@ -159,6 +165,9 @@ vips_linear_build( VipsObject *object ) } } + if( linear->uchar ) + arithmetic->format = VIPS_FORMAT_UCHAR; + if( VIPS_OBJECT_CLASS( vips_linear_parent_class )->build( object ) ) return( -1 ); @@ -198,7 +207,6 @@ vips_linear_build( VipsObject *object ) } \ } - /* Complex input, complex output. */ #define LOOPCMPLXN( IN, OUT ) { \ @@ -214,6 +222,61 @@ vips_linear_build( VipsObject *object ) } \ } +/* Non-complex input, any output, all bands of the constant equal, uchar + * output. + */ +#define LOOP1uc( IN ) { \ + IN * restrict p = (IN *) in[0]; \ + VipsPel * restrict q = (VipsPel *) out; \ + float a1 = a[0]; \ + float b1 = b[0]; \ + int sz = width * nb; \ + \ + for( x = 0; x < sz; x++ ) { \ + float t = a1 * p[x] + b1; \ + \ + q[x] = VIPS_CLIP( 0, t, 255 ); \ + } \ +} + +/* Non-complex input, uchar output. + */ +#define LOOPNuc( IN ) { \ + IN * restrict p = (IN *) in[0]; \ + VipsPel * restrict q = (VipsPel *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( k = 0; k < nb; k++, i++ ) { \ + double t = a[k] * p[i] + b[k]; \ + \ + q[i] = VIPS_CLIP( 0, t, 255 ); \ + } \ +} + +#define LOOPuc( IN ) { \ + if( linear->a->n == 1 && linear->b->n == 1 ) { \ + LOOP1uc( IN ); \ + } \ + else { \ + LOOPNuc( IN ); \ + } \ +} + +/* Complex input, uchar output. + */ +#define LOOPCMPLXNuc( IN ) { \ + IN * restrict p = (IN *) in[0]; \ + VipsPel * restrict q = (VipsPel *) out; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( k = 0; k < nb; k++, i++ ) { \ + double t = a[k] * p[0] + b[k]; \ + \ + q[i] = VIPS_CLIP( 0, t, 255 ); \ + p += 2; \ + } \ +} + /* Lintra a buffer, n set of scale/offset. */ static void @@ -228,21 +291,59 @@ vips_linear_buffer( VipsArithmetic *arithmetic, int i, x, k; - switch( vips_image_get_format( im ) ) { - case VIPS_FORMAT_UCHAR: LOOP( unsigned char, float ); break; - case VIPS_FORMAT_CHAR: LOOP( signed char, float ); break; - case VIPS_FORMAT_USHORT: LOOP( unsigned short, float ); break; - case VIPS_FORMAT_SHORT: LOOP( signed short, float ); break; - case VIPS_FORMAT_UINT: LOOP( unsigned int, float ); break; - case VIPS_FORMAT_INT: LOOP( signed int, float ); break; - case VIPS_FORMAT_FLOAT: LOOP( float, float ); break; - case VIPS_FORMAT_DOUBLE: LOOP( double, double ); break; - case VIPS_FORMAT_COMPLEX: LOOPCMPLXN( float, float ); break; - case VIPS_FORMAT_DPCOMPLEX: LOOPCMPLXN( double, double ); break; + if( linear->uchar ) + switch( vips_image_get_format( im ) ) { + case VIPS_FORMAT_UCHAR: + LOOPuc( unsigned char ); break; + case VIPS_FORMAT_CHAR: + LOOPuc( signed char ); break; + case VIPS_FORMAT_USHORT: + LOOPuc( unsigned short ); break; + case VIPS_FORMAT_SHORT: + LOOPuc( signed short ); break; + case VIPS_FORMAT_UINT: + LOOPuc( unsigned int ); break; + case VIPS_FORMAT_INT: + LOOPuc( signed int ); break; + case VIPS_FORMAT_FLOAT: + LOOPuc( float ); break; + case VIPS_FORMAT_DOUBLE: + LOOPuc( double ); break; + case VIPS_FORMAT_COMPLEX: + LOOPCMPLXNuc( float ); break; + case VIPS_FORMAT_DPCOMPLEX: + LOOPCMPLXNuc( double ); break; + + default: + g_assert( 0 ); + } + else + switch( vips_image_get_format( im ) ) { + case VIPS_FORMAT_UCHAR: + LOOP( unsigned char, float ); break; + case VIPS_FORMAT_CHAR: + LOOP( signed char, float ); break; + case VIPS_FORMAT_USHORT: + LOOP( unsigned short, float ); break; + case VIPS_FORMAT_SHORT: + LOOP( signed short, float ); break; + case VIPS_FORMAT_UINT: + LOOP( unsigned int, float ); break; + case VIPS_FORMAT_INT: + LOOP( signed int, float ); break; + case VIPS_FORMAT_FLOAT: + LOOP( float, float ); break; + case VIPS_FORMAT_DOUBLE: + LOOP( double, double ); break; + case VIPS_FORMAT_COMPLEX: + LOOPCMPLXN( float, float ); break; + case VIPS_FORMAT_DPCOMPLEX: + LOOPCMPLXN( double, double ); break; + + default: + g_assert( 0 ); + } - default: - g_assert( 0 ); - } } /* Save a bit of typing. @@ -296,6 +397,14 @@ vips_linear_class_init( VipsLinearClass *class ) VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsLinear, b ), VIPS_TYPE_ARRAY_DOUBLE ); + + VIPS_ARG_BOOL( class, "uchar", 112, + _( "uchar" ), + _( "Output should be uchar" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsLinear, uchar ), + FALSE ); + } static void @@ -331,9 +440,14 @@ vips_linearv( VipsImage *in, VipsImage **out, * @n: length of constant arrays * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @uchar: output uchar pixels + * * Pass an image through a linear transform, ie. (@out = @in * @a + @b). Output - * is always float for integer input, double for double input, complex for - * complex input and double complex for double complex input. + * is float for integer input, double for double input, complex for + * complex input and double complex for double complex input. Set @uchar to + * output uchar pixels. * * If the arrays of constants have just one element, that constant is used for * all image bands. If the arrays have more than one element and they have @@ -367,6 +481,10 @@ vips_linear( VipsImage *in, VipsImage **out, double *a, double *b, int n, ... ) * @b: constant for addition * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @uchar: output uchar pixels + * * Run vips_linear() with a single constant. * * See also: vips_linear(). diff --git a/libvips/arithmetic/parithmetic.h b/libvips/arithmetic/parithmetic.h index 4fe431cb..a3196e0f 100644 --- a/libvips/arithmetic/parithmetic.h +++ b/libvips/arithmetic/parithmetic.h @@ -77,6 +77,10 @@ typedef struct _VipsArithmetic { /* The input images, ready for the operation. */ VipsImage **ready; + + /* Set this to override class->format_table. + */ + VipsBandFormat format; } VipsArithmetic; typedef struct _VipsArithmeticClass { diff --git a/libvips/conversion/scale.c b/libvips/conversion/scale.c index c5dec95a..31cb7d7f 100644 --- a/libvips/conversion/scale.c +++ b/libvips/conversion/scale.c @@ -18,6 +18,8 @@ * 30/5/13 * - redo as a class * - add log scale and exponent as an option + * 14/1/14 + * - use linear uchar mode */ /* @@ -107,18 +109,20 @@ vips_scale_build( VipsObject *object ) if( vips_pow_const1( scale->in, &t[2], scale->exp, NULL ) || vips_linear1( t[2], &t[3], 1.0, 1.0, NULL ) || vips_log10( t[3], &t[4], NULL ) || - vips_linear1( t[4], &t[5], f, 0.0, NULL ) || - vips_cast( t[5], &t[6], VIPS_FORMAT_UCHAR, NULL ) || - vips_image_write( t[6], conversion->out ) ) + vips_linear1( t[4], &t[5], f, 0.0, + "uchar", TRUE, + NULL ) || + vips_image_write( t[5], conversion->out ) ) return( -1 ); } else { double f = 255.0 / (mx - mn); double a = -(mn * f); - if( vips_linear1( scale->in, &t[2], f, a, NULL ) || - vips_cast( t[2], &t[3], VIPS_FORMAT_UCHAR, NULL ) || - vips_image_write( t[3], conversion->out ) ) + if( vips_linear1( scale->in, &t[2], f, a, + "uchar", TRUE, + NULL ) || + vips_image_write( t[2], conversion->out ) ) return( -1 ); } diff --git a/libvips/create/point.c b/libvips/create/point.c index 0964e660..e818f5ac 100644 --- a/libvips/create/point.c +++ b/libvips/create/point.c @@ -115,15 +115,16 @@ vips_point_build( VipsObject *object ) float range = max - min; if( vips_linear1( in, &t[2], - 255.0 / range, -min * 255.0 / range, NULL ) || - vips_cast( t[2], &t[3], VIPS_FORMAT_UCHAR, NULL ) ) + 255.0 / range, -min * 255.0 / range, + "uchar", TRUE, + NULL ) ) return( -1 ); - in = t[3]; + in = t[2]; /* uchar mode always does B_W. We don't want FOURIER or * whatever in this case. */ - t[3]->Type = VIPS_INTERPRETATION_B_W; + in->Type = VIPS_INTERPRETATION_B_W; } if( vips_image_write( in, create->out ) )