more maplut hacking

This commit is contained in:
John Cupitt 2013-07-15 14:57:01 +01:00
parent 440b3a3362
commit 6b3dc259d1
3 changed files with 218 additions and 133 deletions

6
TODO
View File

@ -1,3 +1,9 @@
- 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
- need a new uncached mode for open via disc
currently we have a huge linecache for "vips copy x.png x.v", in

View File

@ -76,9 +76,13 @@
typedef struct _VipsMaplut {
VipsHistogram parent_instance;
/* Process this image (@in is the LUT).
*/
VipsImage *process;
int fmt; /* LUT image BandFmt */
int nb; /* Number of bands in lut */
int es; /* IM_IMAGE_SIZEOF_ELEMENT() for lut image */
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 */
@ -90,99 +94,40 @@ typedef VipsHistogramClass VipsMaplutClass;
G_DEFINE_TYPE( VipsMaplut, vips_maplut, VIPS_TYPE_HISTOGRAM );
/* Print overflows, if any.
*/
static int
lut_end( LutInfo *st )
static void
vips_maplut_preeval( VipsImage *image, VipsProgress *progress,
VipsMaplut *maplut )
{
if( st->overflow )
im_warn( "im_maplut", _( "%d overflows detected" ),
st->overflow );
return( 0 );
maplut->overflow = 0;
}
/* Build a lut table.
*/
static LutInfo *
build_luts( IMAGE *out, IMAGE *lut )
static void
vips_maplut_posteval( VipsImage *image, VipsProgress *progress,
VipsMaplut *maplut )
{
LutInfo *st;
int i, x;
VipsPel *q;
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( maplut );
if( !(st = IM_NEW( out, LutInfo )) )
return( NULL );
/* Make luts. We unpack the LUT image into a C 2D array to speed
* processing.
*/
st->fmt = lut->BandFmt;
st->es = IM_IMAGE_SIZEOF_ELEMENT( lut );
st->nb = lut->Bands;
st->sz = lut->Xsize * lut->Ysize;
st->clp = st->sz - 1;
st->overflow = 0;
st->table = NULL;
if( im_add_evalstart_callback( out,
(im_callback_fn) lut_start, st, NULL ) ||
im_add_evalend_callback( out,
(im_callback_fn) lut_end, st, NULL ) )
return( NULL );
/* Attach tables.
*/
if( !(st->table = IM_ARRAY( out, lut->Bands, VipsPel * )) )
return( NULL );
for( i = 0; i < lut->Bands; i++ )
if( !(st->table[i] = IM_ARRAY( out, st->sz * st->es, VipsPel )) )
return( NULL );
/* Scan LUT and fill table.
*/
q = (VipsPel *) lut->data;
for( x = 0; x < st->sz; x++ )
for( i = 0; i < st->nb; i++ ) {
memcpy( st->table[i] + x * st->es, q, st->es );
q += st->es;
}
return( st );
if( maplut->overflow )
vips_warn( class->nickname,
_( "%d overflows detected" ), maplut->overflow );
}
/* Our sequence value: the region this sequence is using, and local stats.
*/
typedef struct {
REGION *ir; /* Input region */
VipsRegion *ir; /* Input region */
int overflow; /* Number of overflows */
} Seq;
/* Destroy a sequence value.
*/
static int
maplut_stop( void *vseq, void *a, void *b )
{
Seq *seq = (Seq *) vseq;
LutInfo *st = (LutInfo *) b;
/* Add to global stats.
*/
st->overflow += seq->overflow;
IM_FREEF( im_region_free, seq->ir );
return( 0 );
}
} VipsMaplutSequence;
/* Our start function.
*/
static void *
maplut_start( IMAGE *out, void *a, void *b )
vips_maplut_start( VipsImage *out, void *a, void *b )
{
IMAGE *in = (IMAGE *) a;
Seq *seq;
VipsImage *in = (VipsImage *) a;
VipsMaplutSequence *seq;
if( !(seq = IM_NEW( out, Seq )) )
if( !(seq = VIPS_NEW( out, VipsMaplutSequence )) )
return( NULL );
/* Init!
@ -190,7 +135,7 @@ maplut_start( IMAGE *out, void *a, void *b )
seq->ir = NULL;
seq->overflow = 0;
if( !(seq->ir = im_region_create( in )) )
if( !(seq->ir = vips_region_new( in )) )
return( NULL );
return( seq );
@ -203,8 +148,8 @@ maplut_start( IMAGE *out, void *a, void *b )
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
VipsPel *p = IM_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
OUT *tlut = (OUT *) st->table[z]; \
\
for( x = z; x < ne; x += b ) \
@ -220,8 +165,8 @@ maplut_start( IMAGE *out, void *a, void *b )
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
VipsPel *p = IM_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z * 2; \
VipsPel *p = VIPS_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ) + z * 2; \
OUT *tlut = (OUT *) st->table[z]; \
\
for( x = 0; x < ne; x += b ) { \
@ -240,8 +185,8 @@ maplut_start( IMAGE *out, void *a, void *b )
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ); \
OUT *tlut = (OUT *) st->table[z]; \
\
for( x = z; x < ne; x += b ) { \
@ -263,8 +208,8 @@ maplut_start( IMAGE *out, void *a, void *b )
\
for( y = to; y < bo; y++ ) { \
for( z = 0; z < b; z++ ) { \
IN *p = (IN *) IM_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ) + z * 2; \
IN *p = (IN *) VIPS_REGION_ADDR( ir, le, y ) + z; \
OUT *q = (OUT *) VIPS_REGION_ADDR( or, le, y ) + z * 2; \
OUT *tlut = (OUT *) st->table[z]; \
\
for( x = 0; x < ne; x += b ) { \
@ -290,8 +235,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT *tlut = (OUT *) st->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
VipsPel *p = IM_REGION_ADDR( ir, le, 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]]; \
@ -304,8 +249,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT *tlut = (OUT *) st->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
VipsPel *p = IM_REGION_ADDR( ir, le, 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; \
@ -324,8 +269,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT *tlut = (OUT *) st->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
IN *p = (IN *) IM_REGION_ADDR( ir, le, 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]; \
@ -344,8 +289,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT *tlut = (OUT *) st->table[0]; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
IN *p = (IN *) IM_REGION_ADDR( ir, le, 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]; \
@ -368,8 +313,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT **tlut = (OUT **) st->table; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
VipsPel *p = IM_REGION_ADDR( ir, le, 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]; \
@ -386,8 +331,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT **tlut = (OUT **) st->table; \
\
for( y = to; y < bo; y++ ) { \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, y ); \
VipsPel *p = IM_REGION_ADDR( ir, le, 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; \
@ -407,8 +352,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT **tlut = (OUT **) st->table; \
\
for( y = to; y < bo; y++ ) { \
IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, 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]; \
@ -430,8 +375,8 @@ maplut_start( IMAGE *out, void *a, void *b )
OUT **tlut = (OUT **) st->table; \
\
for( y = to; y < bo; y++ ) { \
IN *p = (IN *) IM_REGION_ADDR( ir, le, y ); \
OUT *q = (OUT *) IM_REGION_ADDR( or, le, 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]; \
@ -454,9 +399,9 @@ maplut_start( IMAGE *out, void *a, void *b )
*/
#define inner_switch( UCHAR, GEN, OUT ) \
switch( ir->im->BandFmt ) { \
case IM_BANDFMT_UCHAR: UCHAR( OUT ); break; \
case IM_BANDFMT_USHORT: GEN( unsigned short, OUT ); break; \
case IM_BANDFMT_UINT: GEN( unsigned int, OUT ); break; \
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( 0 ); \
}
@ -467,25 +412,25 @@ maplut_start( IMAGE *out, void *a, void *b )
*/
#define outer_switch( UCHAR_F, UCHAR_FC, GEN_F, GEN_FC ) \
switch( st->fmt ) { \
case IM_BANDFMT_UCHAR: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_UCHAR: inner_switch( UCHAR_F, GEN_F, \
unsigned char ); break; \
case IM_BANDFMT_CHAR: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_CHAR: inner_switch( UCHAR_F, GEN_F, \
char ); break; \
case IM_BANDFMT_USHORT: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_USHORT: inner_switch( UCHAR_F, GEN_F, \
unsigned short ); break; \
case IM_BANDFMT_SHORT: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_SHORT: inner_switch( UCHAR_F, GEN_F, \
short ); break; \
case IM_BANDFMT_UINT: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_UINT: inner_switch( UCHAR_F, GEN_F, \
unsigned int ); break; \
case IM_BANDFMT_INT: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_INT: inner_switch( UCHAR_F, GEN_F, \
int ); break; \
case IM_BANDFMT_FLOAT: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_FLOAT: inner_switch( UCHAR_F, GEN_F, \
float ); break; \
case IM_BANDFMT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \
case VIPS_FORMAT_DOUBLE: inner_switch( UCHAR_F, GEN_F, \
double ); break; \
case IM_BANDFMT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \
case VIPS_FORMAT_COMPLEX: inner_switch( UCHAR_FC, GEN_FC, \
float ); break; \
case IM_BANDFMT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \
case VIPS_FORMAT_DPCOMPLEX: inner_switch( UCHAR_FC, GEN_FC, \
double ); break; \
default: \
g_assert( 0 ); \
@ -494,18 +439,19 @@ maplut_start( IMAGE *out, void *a, void *b )
/* Do a map.
*/
static int
maplut_gen( REGION *or, void *vseq, void *a, void *b )
vips_maplut_gen( VipsRegion *or, void *vseq, void *a, void *b,
gboolean *stop )
{
Seq *seq = (Seq *) vseq;
IMAGE *in = (IMAGE *) a;
LutInfo *st = (LutInfo *) b;
REGION *ir = seq->ir;
VipsMaplutSequence *seq = (VipsMaplutSequence *) vseq;
VipsImage *in = (VipsImage *) a;
VipsMaplut *st = (VipsMaplut *) b;
VipsRegion *ir = seq->ir;
Rect *r = &or->valid;
int le = r->left;
int to = r->top;
int bo = IM_RECT_BOTTOM(r);
int bo = VIPS_RECT_BOTTOM(r);
int np = r->width; /* Pels across region */
int ne = IM_REGION_N_ELEMENTS( or ); /* Number of elements */
int ne = VIPS_REGION_N_ELEMENTS( or ); /* Number of elements */
int x, y, z, i;
/* Get input ready.
@ -532,19 +478,152 @@ maplut_gen( REGION *or, void *vseq, void *a, void *b )
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 IM_BANDFMT_UCHAR
#define US IM_BANDFMT_USHORT
#define UI IM_BANDFMT_UINT
#define UC VIPS_FORMAT_UCHAR
#define US VIPS_FORMAT_USHORT
#define UI VIPS_FORMAT_UINT
/* Type mapping: go to uchar or ushort.
/* Type mapping: go to uchar / ushort / uint to make an index.
*/
static int bandfmt_maplut[10] = {
/* UC C US S UI I F X D DX */
UC, UC, US, US, UI, UI, UI, UI, UI, UI
};
static int
vips_maplut_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsHistogram *histogram = VIPS_HISTOGRAM( object );
VipsMaplut *maplut = (VipsMaplut *) object;
int i, x;
VipsPel *q;
if( VIPS_OBJECT_CLASS( vips_maplut_parent_class )->build( object ) )
return( -1 );
/* @in is the LUT.
*/
if( vips_check_uncoded( class->nickname, histogram->in ) ||
vips_image_wio_input( histogram->in ) )
return( -1 );
/* Cast @process to u8/u16/u32.
*/
if( !(t = im_open_local( out, "im_maplut", "p" )) ||
im_clip2fmt( in, t, bandfmt_maplut[in->BandFmt] ) )
return( -1 );
if( vips_check_uncoded( class->nickname, maplut->process ) ||
vips_check_bands_1orn( class->nickname,
maplut->process, histogram->in ) ||
vips_image_pio_input( maplut->process ) )
return( -1 );
if( vips_image_copy_fieldsv( histogram->out,
maplut->process, histogram->in, NULL ) )
return( -1 );
vips_demand_hint( histogram->out, VIPS_DEMAND_STYLE_THINSTRIP,
maplut->process, histogram->in, NULL );
histogram->out->BandFmt = histogram->in->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( histogram->in->Bands != 1 )
histogram->out->Bands = histogram->in->Bands;
g_signal_connect( maplut->in, "preeval",
G_CALLBACK( vips_maplut_preeval ), maplut );
g_signal_connect( maplut->in, "posteval",
G_CALLBACK( vips_maplut_posteval ), maplut );
/* Make luts. We unpack the LUT image into a C 2D array to speed
* processing.
*/
maplut->fmt = histogram->in->BandFmt;
maplut->es = VIPS_IMAGE_SIZEOF_ELEMENT( histogram->in );
maplut->nb = histogram->in->Bands;
maplut->sz = histogram->in->Xsize * histogram->in->Ysize;
maplut->clp = maplut->sz - 1;
/* Attach tables.
*/
if( !(maplut->table = VIPS_ARRAY( maplut,
histogram->in->Bands, VipsPel * )) )
return( NULL );
for( i = 0; i < histogram->in->Bands; i++ )
if( !(maplut->table[i] =
VIPS_ARRAY( maplut, maplut->sz * maplut->es, VipsPel )) )
return( NULL );
/* Scan LUT and fill table.
*/
q = (VipsPel *) histogram->in->data;
for( x = 0; x < maplut->sz; x++ )
for( i = 0; i < maplut->nb; i++ ) {
memcpy( maplut->table[i] + x * maplut->es, q, maplut->es );
q += maplut->es;
}
if( vips_image_generate( histogram->out,
vips_maplut_start, vips_maplut_gen, vips_maplut_stop,
maplut->process, maplut ) )
return( -1 );
return( 0 );
}
static void
vips_maplut_class_init( VipsCastClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
VIPS_DEBUG_MSG( "vips_maplut_class_init\n" );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "maplut";
vobject_class->description = _( "map an image though a lut" );
vobject_class->build = vips_maplut_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_IMAGE( class, "process", 2,
_( "Process" ),
_( "Image to pass through LUT" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsMaplut, process ) );
}
static void
vips_maplut_init( VipsCast *maplut )
{
}
/**
* im_maplut:
* @in: input image
@ -555,10 +634,10 @@ static int bandfmt_maplut[10] = {
* 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,
* IM_BANDFMT_UCHAR, IM_BANDFMT_USHORT or IM_BANDFMT_UINT.
* VIPS_FORMAT_UCHAR, VIPS_FORMAT_USHORT or VIPS_FORMAT_UINT.
*
* If @lut is too small for the input type (for example, if @in is
* IM_BANDFMT_UCHAR but @lut only has 100 elements), the lut is padded out
* 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.
@ -573,10 +652,10 @@ static int bandfmt_maplut[10] = {
* Returns: 0 on success, -1 on error
*/
int
im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut )
im_maplut( VipsImage *in, VipsImage *out, VipsImage *lut )
{
IMAGE *t;
LutInfo *st;
VipsImage *t;
VipsMaplut *st;
/* Check input output. Old-style IO from lut, for simplicity.
*/
@ -616,7 +695,7 @@ im_maplut( IMAGE *in, IMAGE *out, IMAGE *lut )
/* Set demand hints.
*/
if( im_demand_hint( out, IM_THINSTRIP, t, NULL ) )
if( im_demand_hint( out, VIPS_THINSTRIP, t, NULL ) )
return( -1 );
/* Process!

View File

@ -688,7 +688,7 @@ meta_cp( VipsImage *dst, const VipsImage *src )
* on @out.
*
* Image history is completely copied from all @in. @out will have the history
* of all the intput images.
* of all the input images.
*
* See also: vips_image_copy_fieldsv(), vips_image_copy_fields().
*