libvips/libvips/histogram/maplut.c

775 lines
19 KiB
C

/* map though a LUT
*
* Modified:
* 18/6/93 JC
* - oops! im_incheck() added for LUT image
* - some ANSIfication
* 15/7/93 JC
* - adapted for partial v2
* - ANSIfied
* - now does complex LUTs too
* 10/3/94 JC
* - more helpful error messages, slight reformatting
* 24/8/94 JC
* - now allows non-uchar image input
* 7/10/94 JC
* - uses im_malloc(), IM_NEW() etc.
* 13/3/95 JC
* - now takes a private copy of LUT, so user can im_close() LUT image
* after im_maplut() without fear of coredumps
* 23/6/95 JC
* - lut may now have many bands if image has just one band
* 3/3/01 JC
* - small speed ups
* 30/6/04
* - heh, 1 band image + 3 band lut + >8bit output has been broken for 9
* years :-)
* 7/11/07
* - new eval start/end system
* 25/3/10
* - gtkdoc
* - small cleanups
* 5/7/13
* - convert to a class
* 2/10/13
* - add --band arg, replacing im_tone_map()
*/
/*
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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib/gi18n-lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
typedef struct _VipsMaplut {
VipsOperation parent_instance;
VipsImage *in;
VipsImage *out;
VipsImage *lut;
int band;
int fmt; /* LUT image BandFmt */
int nb; /* Number of bands in lut */
int es; /* VIPS_IMAGE_SIZEOF_ELEMENT() for lut image */
int sz; /* Number of elements in minor dimension */
int clp; /* Value we clip against */
VipsPel **table; /* Lut converted to 2d array */
int overflow; /* Number of overflows for non-uchar lut */
} VipsMaplut;
typedef VipsOperationClass VipsMaplutClass;
G_DEFINE_TYPE( VipsMaplut, vips_maplut, VIPS_TYPE_OPERATION );
static void
vips_maplut_preeval( VipsImage *image, VipsProgress *progress,
VipsMaplut *maplut )
{
maplut->overflow = 0;
}
static void
vips_maplut_posteval( VipsImage *image, VipsProgress *progress,
VipsMaplut *maplut )
{
if( maplut->overflow )
g_warning( _( "%d overflows detected" ), maplut->overflow );
}
/* Our sequence value: the region this sequence is using, and local stats.
*/
typedef struct {
VipsRegion *ir; /* Input region */
int overflow; /* Number of overflows */
} VipsMaplutSequence;
/* Our start function.
*/
static void *
vips_maplut_start( VipsImage *out, void *a, void *b )
{
VipsImage *in = (VipsImage *) a;
VipsMaplutSequence *seq;
if( !(seq = VIPS_NEW( out, VipsMaplutSequence )) )
return( NULL );
/* Init!
*/
seq->ir = NULL;
seq->overflow = 0;
if( !(seq->ir = vips_region_new( in )) )
return( NULL );
return( seq );
}
/* Map through n non-complex luts.
*/
#define loop( OUT ) { \
int b = maplut->nb; \
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
OUT *tlut = (OUT *) maplut->table[z]; \
\
for( x = z; x < ne; x += b ) \
q[x] = tlut[p[x]]; \
} \
} \
}
/* Map through n complex luts.
*/
#define loopc( OUT ) { \
int b = in->Bands; \
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ) + z * 2; \
OUT *tlut = (OUT *) maplut->table[z]; \
\
for( x = 0; x < ne; x += b ) { \
int n = p[x] * 2; \
\
q[0] = tlut[n]; \
q[1] = tlut[n + 1]; \
q += b * 2; \
} \
} \
} \
}
#define loopg( IN, OUT ) { \
int b = maplut->nb; \
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
OUT *tlut = (OUT *) maplut->table[z]; \
\
for( x = z; x < ne; x += b ) { \
int index = p[x]; \
\
if( index > maplut->clp ) { \
index = maplut->clp; \
seq->overflow++; \
} \
\
q[x] = tlut[index]; \
} \
} \
} \
}
#define loopcg( IN, OUT ) { \
int b = in->Bands; \
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ) + z * 2; \
OUT *tlut = (OUT *) maplut->table[z]; \
\
for( x = 0; x < ne; x += b ) { \
int index = p[x]; \
\
if( index > maplut->clp ) { \
index = maplut->clp; \
seq->overflow++; \
} \
\
q[0] = tlut[index * 2]; \
q[1] = tlut[index * 2 + 1]; \
\
q += b * 2; \
} \
} \
} \
}
/* Map image through one non-complex lut.
*/
#define loop1( OUT ) { \
OUT *tlut = (OUT *) maplut->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
\
for( x = 0; x < ne; x++ ) \
q[x] = tlut[p[x]]; \
} \
}
/* Map image through one complex lut.
*/
#define loop1c( OUT ) { \
OUT *tlut = (OUT *) maplut->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
\
for( x = 0; x < ne; x++ ) { \
int n = p[x] * 2; \
\
q[0] = tlut[n]; \
q[1] = tlut[n + 1]; \
q += 2; \
} \
} \
}
/* As above, but the input image may be any unsigned integer type. We have to
* index the lut carefully, and record the number of overflows we detect.
*/
#define loop1g( IN, OUT ) { \
OUT *tlut = (OUT *) maplut->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
\
for( x = 0; x < ne; x++ ) { \
int index = p[x]; \
\
if( index > maplut->clp ) { \
index = maplut->clp; \
seq->overflow++; \
} \
\
q[x] = tlut[index]; \
} \
} \
}
#define loop1cg( IN, OUT ) { \
OUT *tlut = (OUT *) maplut->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
\
for( x = 0; x < ne; x++ ) { \
int index = p[x]; \
\
if( index > maplut->clp ) { \
index = maplut->clp; \
seq->overflow++; \
} \
\
q[0] = tlut[index * 2]; \
q[1] = tlut[index * 2 + 1]; \
q += 2; \
} \
} \
}
/* Map 1-band image through a many-band non-complex lut.
*/
#define loop1m( OUT ) { \
OUT **tlut = (OUT **) maplut->table; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
\
for( i = 0, x = 0; x < np; x++ ) { \
int n = p[x]; \
\
for( z = 0; z < maplut->nb; z++, i++ ) \
q[i] = tlut[z][n]; \
} \
} \
}
/* Map 1-band image through many-band complex lut.
*/
#define loop1cm( OUT ) { \
OUT **tlut = (OUT **) maplut->table; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
\
for( x = 0; x < np; x++ ) { \
int n = p[x] * 2; \
\
for( z = 0; z < maplut->nb; z++ ) { \
q[0] = tlut[z][n]; \
q[1] = tlut[z][n+1]; \
q += 2; \
} \
} \
} \
}
/* Map 1-band uint or ushort image through a many-band non-complex LUT.
*/
#define loop1gm( IN, OUT ) { \
OUT **tlut = (OUT **) maplut->table; \
\
for( y = to; y < bo; y++ ) { \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
\
for( i = 0, x = 0; x < np; x++ ) { \
int n = p[x]; \
\
if( n > maplut->clp ) { \
n = maplut->clp; \
seq->overflow++; \
} \
\
for( z = 0; z < maplut->nb; z++, i++ ) \
q[i] = tlut[z][n]; \
} \
} \
}
/* Map 1-band uint or ushort image through a many-band complex LUT.
*/
#define loop1cgm( IN, OUT ) { \
OUT **tlut = (OUT **) maplut->table; \
\
for( y = to; y < bo; y++ ) { \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
\
for( x = 0; x < np; x++ ) { \
int n = p[x]; \
\
if( n > maplut->clp ) { \
n = maplut->clp; \
seq->overflow++; \
} \
\
for( z = 0; z < maplut->nb; z++ ) { \
q[0] = tlut[z][n * 2]; \
q[1] = tlut[z][n * 2 + 1]; \
q += 2; \
} \
} \
} \
}
/* Switch for input types. Has to be uint type!
*/
#define inner_switch( UCHAR, GEN, OUT ) \
switch( ir->im->BandFmt ) { \
case VIPS_FORMAT_UCHAR: UCHAR( OUT ); break; \
case VIPS_FORMAT_USHORT: GEN( unsigned short, OUT ); break; \
case VIPS_FORMAT_UINT: GEN( unsigned int, OUT ); break; \
default: \
g_assert_not_reached(); \
}
/* Switch for LUT types. One function for non-complex images, a
* variant for complex ones. Another pair as well, in case the input is not
* uchar.
*/
#define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \
switch( maplut->fmt ) { \
case VIPS_FORMAT_UCHAR: \
inner_switch( UCHAR_F, GEN_F, unsigned char ); break; \
case VIPS_FORMAT_CHAR:\
inner_switch( UCHAR_F, GEN_F, char ); break; \
case VIPS_FORMAT_USHORT: \
inner_switch( UCHAR_F, GEN_F, unsigned short ); break; \
case VIPS_FORMAT_SHORT: \
inner_switch( UCHAR_F, GEN_F, short ); break; \
case VIPS_FORMAT_UINT: \
inner_switch( UCHAR_F, GEN_F, unsigned int ); break; \
case VIPS_FORMAT_INT: \
inner_switch( UCHAR_F, GEN_F, int ); break; \
case VIPS_FORMAT_FLOAT: \
inner_switch( UCHAR_F, GEN_F, float ); break; \
case VIPS_FORMAT_DOUBLE: \
inner_switch( UCHAR_F, GEN_F, double ); break; \
case VIPS_FORMAT_COMPLEX: \
inner_switch( UCHAR_FC, GEN_FC, float ); break; \
case VIPS_FORMAT_DPCOMPLEX: \
inner_switch( UCHAR_FC, GEN_FC, double ); break; \
default: \
g_assert_not_reached(); \
}
/* Do a map.
*/
static int
vips_maplut_gen( VipsRegion *or, void *vseq, void *a, void *b,
gboolean *stop )
{
VipsMaplutSequence *seq = (VipsMaplutSequence *) vseq;
VipsImage *in = (VipsImage *) a;
VipsMaplut *maplut = (VipsMaplut *) b;
VipsRegion *ir = seq->ir;
VipsRect *r = &or->valid;
int le = r->left;
int to = r->top;
int bo = VIPS_RECT_BOTTOM( r );
int np = r->width; /* Pels across region */
int ne = VIPS_REGION_N_ELEMENTS( or ); /* Number of elements */
int x, y, z, i;
if( vips_region_prepare( ir, r ) )
return( -1 );
if( maplut->nb == 1 )
/* One band lut.
*/
outer_switch( loop1, loop1c, loop1g, loop1cg )
else
/* Many band lut.
*/
if( in->Bands == 1 )
/* ... but 1 band input.
*/
outer_switch( loop1m, loop1cm, loop1gm, loop1cgm )
else
outer_switch( loop, loopc, loopg, loopcg )
return( 0 );
}
/* Destroy a sequence value.
*/
static int
vips_maplut_stop( void *vseq, void *a, void *b )
{
VipsMaplutSequence *seq = (VipsMaplutSequence *) vseq;
VipsMaplut *maplut = (VipsMaplut *) b;
/* Add to global stats.
*/
maplut->overflow += seq->overflow;
VIPS_UNREF( seq->ir );
return( 0 );
}
/* Save a bit of typing.
*/
#define UC VIPS_FORMAT_UCHAR
#define US VIPS_FORMAT_USHORT
#define UI VIPS_FORMAT_UINT
/* Type mapping: go to uchar / ushort / uint to make an index.
*/
static const VipsBandFormat bandfmt_maplut[10] = {
/* Band format: UC C US S UI I F X D DX */
/* Promotion: */ UC, UC, US, US, UI, UI, UI, UI, UI, UI
};
/* Repack lut into a set of band arrays. If we're just passing one band of the
* image through the lut, put the identity function in the other bands.
*/
#define PACK_TABLE( TYPE ) { \
TYPE *data = (TYPE *) lut->data; \
int x, b; \
\
for( x = 0; x < maplut->sz; x++ ) \
for( b = 0; b < maplut->nb; b++ ) { \
TYPE *q = (TYPE *) maplut->table[b]; \
\
if( maplut->band >= 0 && \
lut->Bands == 1 ) { \
if( b == maplut->band ) \
q[x] = data[x]; \
else \
q[x] = x; \
} \
else \
q[x] = data[x * lut->Bands + b]; \
} \
}
#define PACK_TABLEC( TYPE ) { \
TYPE *data = (TYPE *) lut->data; \
int x, b; \
\
for( x = 0; x < maplut->sz; x++ ) \
for( b = 0; b < maplut->nb; b++ ) { \
TYPE *q = (TYPE *) maplut->table[b]; \
\
if( maplut->band >= 0 && \
lut->Bands == 1 ) { \
if( b == maplut->band ) { \
q[2 * x] = data[2 * x]; \
q[2 * x + 1] = data[2 * x + 1]; \
} \
else { \
q[2 * x] = x; \
q[2 * x + 1] = 0; \
} \
} \
else { \
q[2 * x] = data[2 * (x * lut->Bands + b)]; \
q[2 * x + 1] = \
data[2 * (x * lut->Bands + b) + 1]; \
} \
} \
}
static int
vips_maplut_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsMaplut *maplut = (VipsMaplut *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage *in;
VipsImage *lut;
int i;
g_object_set( object, "out", vips_image_new(), NULL );
if( VIPS_OBJECT_CLASS( vips_maplut_parent_class )->build( object ) )
return( -1 );
in = maplut->in;
lut = maplut->lut;
if( vips_check_hist( class->nickname, lut ) ||
vips_check_uncoded( class->nickname, lut ) )
return( -1 );
/* Cast @in to u8/u16/u32 to make the index image.
*/
if( vips_cast( in, &t[0], bandfmt_maplut[in->BandFmt], NULL ) )
return( -1 );
in = t[0];
if( vips_check_uncoded( class->nickname, in ) ||
vips_check_bands_1orn( class->nickname, in, lut ) ||
vips_image_pio_input( in ) )
return( -1 );
if( vips_image_pipelinev( maplut->out,
VIPS_DEMAND_STYLE_THINSTRIP, in, lut, NULL ) )
return( -1 );
maplut->out->BandFmt = lut->BandFmt;
/* Output has same number of bands as LUT, unless LUT has 1 band, in
* which case output has same number of bands as input.
*/
if( lut->Bands != 1 )
maplut->out->Bands = lut->Bands;
/* The Type comes from the image with many bands. A B_W index image,
* for example, needs to become an RGB image when it goes through a
* three-band LUT.
*/
if( lut->Bands != 1 )
maplut->out->Type = lut->Type;
g_signal_connect( in, "preeval",
G_CALLBACK( vips_maplut_preeval ), maplut );
g_signal_connect( in, "posteval",
G_CALLBACK( vips_maplut_posteval ), maplut );
/* Make luts. We unpack the LUT image into a 2D C array to speed
* processing.
*/
if( !(t[1] = vips_image_copy_memory( lut )) )
return( -1 );
lut = t[1];
maplut->fmt = lut->BandFmt;
maplut->es = VIPS_IMAGE_SIZEOF_ELEMENT( lut );
maplut->sz = lut->Xsize * lut->Ysize;
maplut->clp = maplut->sz - 1;
/* If @bands is >= 0, we need to expand the lut to the number of bands
* in the input image.
*/
if( maplut->band >= 0 &&
lut->Bands == 1 )
maplut->nb = in->Bands;
else
maplut->nb = lut->Bands;
/* Attach tables.
*/
if( !(maplut->table = VIPS_ARRAY( maplut, maplut->nb, VipsPel * )) )
return( -1 );
for( i = 0; i < maplut->nb; i++ )
if( !(maplut->table[i] = VIPS_ARRAY( maplut,
maplut->sz * maplut->es, VipsPel )) )
return( -1 );
/* Scan LUT and fill table.
*/
switch( lut->BandFmt ) {
case VIPS_FORMAT_UCHAR:
PACK_TABLE( unsigned char ); break;
case VIPS_FORMAT_CHAR:
PACK_TABLE( char ); break;
case VIPS_FORMAT_USHORT:
PACK_TABLE( unsigned short ); break;
case VIPS_FORMAT_SHORT:
PACK_TABLE( short ); break;
case VIPS_FORMAT_UINT:
PACK_TABLE( unsigned int ); break;
case VIPS_FORMAT_INT:
PACK_TABLE( int ); break;
case VIPS_FORMAT_FLOAT:
PACK_TABLE( float ); break;
case VIPS_FORMAT_DOUBLE:
PACK_TABLE( double ); break;
case VIPS_FORMAT_COMPLEX:
PACK_TABLEC( float ); break;
case VIPS_FORMAT_DPCOMPLEX:
PACK_TABLEC( double ); break;
default:
g_assert_not_reached();
}
if( vips_image_generate( maplut->out,
vips_maplut_start, vips_maplut_gen, vips_maplut_stop,
in, maplut ) )
return( -1 );
return( 0 );
}
static void
vips_maplut_class_init( VipsMaplutClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "maplut";
object_class->description = _( "map an image though a lut" );
object_class->build = vips_maplut_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_IMAGE( class, "in", 1,
_( "Input" ),
_( "Input image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMaplut, in ) );
VIPS_ARG_IMAGE( class, "out", 2,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsMaplut, out ) );
VIPS_ARG_IMAGE( class, "lut", 3,
_( "LUT" ),
_( "Look-up table image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMaplut, lut ) );
VIPS_ARG_INT( class, "band", 4,
_( "Band" ),
_( "Apply one-band lut to this band of in" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsMaplut, band ),
-1, 10000, -1 );
}
static void
vips_maplut_init( VipsMaplut *maplut )
{
maplut->band = -1;
}
/**
* vips_maplut: (method)
* @in: input image
* @out: (out): output image
* @lut: look-up table
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
*
* * @band: apply one-band @lut to this band of @in
*
* Map an image through another image acting as a LUT (Look Up Table).
* The lut may have any type and the output image will be that type.
*
* The input image will be cast to one of the unsigned integer types, that is,
* VIPS_FORMAT_UCHAR, VIPS_FORMAT_USHORT or VIPS_FORMAT_UINT.
*
* If @lut is too small for the input type (for example, if @in is
* VIPS_FORMAT_UCHAR but @lut only has 100 elements), the lut is padded out
* by copying the last element. Overflows are reported at the end of
* computation.
* If @lut is too large, extra values are ignored.
*
* If @lut has one band and @band is -1 (the default), then all bands of @in
* pass through @lut. If @band is >= 0, then just that band of @in passes
* through @lut and other bands are just copied.
*
* If @lut
* has same number of bands as @in, then each band is mapped
* separately. If @in has one band, then @lut may have many bands and
* the output will have the same number of bands as @lut.
*
* See also: vips_hist_find(), vips_identity().
*
* Returns: 0 on success, -1 on error
*/
int
vips_maplut( VipsImage *in, VipsImage **out, VipsImage *lut, ... )
{
va_list ap;
int result;
va_start( ap, lut );
result = vips_call_split( "maplut", ap, in, out, lut );
va_end( ap );
return( result );
}