redo im_rank_image() as a class

This commit is contained in:
John Cupitt 2013-10-23 14:54:22 +01:00
parent 5475cabbf2
commit 2720026a61
11 changed files with 356 additions and 362 deletions

View File

@ -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()

View File

@ -17,6 +17,7 @@ libconversion_la_SOURCES = \
replicate.c \
cast.c \
bandjoin.c \
bandrank.c \
recomb.c \
bandmean.c \
bandbool.c \

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>
#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 );
}

View File

@ -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();

View File

@ -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 )
{

View File

@ -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));

View File

@ -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 );

View File

@ -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 );

View File

@ -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

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <vips/vips.h>
#include <vips/internal.h>
/* 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 ) );
}