redo im_rank_image() as a class
This commit is contained in:
parent
5475cabbf2
commit
2720026a61
|
@ -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()
|
||||
|
|
|
@ -17,6 +17,7 @@ libconversion_la_SOURCES = \
|
|||
replicate.c \
|
||||
cast.c \
|
||||
bandjoin.c \
|
||||
bandrank.c \
|
||||
recomb.c \
|
||||
bandmean.c \
|
||||
bandbool.c \
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
Loading…
Reference in New Issue