blob: f2953efd7295887bc1dbe57c680f46be3ea15d69 [file] [log] [blame]
/* @(#) Sort a set of images, pixelwise, and pick out the index at each point.
* @(#)
* @(#) int im_rank_image( imarray, imout, no, index )
* @(#) IMAGE *imarray[], *imout;
* @(#) int no, index;
* @(#)
* @(#) All functions return 0 on success and -1 on error
* @(#)
*
* 19/8/03
* - from im_maxvalue(), via im_gbandjoin()
*/
/*
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
/* 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 )
{
int i;
Rank *rank;
if( !(rank = IM_NEW( out, Rank )) )
return( NULL );
rank->n = n;
rank->index = index;
rank->out = out;
if( !(rank->in = IM_ARRAY( out, n + 1, IMAGE * )) )
return( NULL );
for( i = 0; i < n; i++ )
rank->in[i] = in[i];
rank->in[n] = NULL;
return( rank );
}
/* Our sequence value.
*/
typedef struct {
Rank *rank;
REGION **ir; /* Input regions */
PEL **pts; /* Per-input region data pointer */
PEL *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;
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, PEL * );
seq->sort = IM_ARRAY( out,
rank->n * IM_IMAGE_SIZEOF_ELEMENT( in[0] ), PEL );
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++ ) {
PEL *q = (PEL *) IM_REGION_ADDR( or, le, y );
for( i = 0; i < rank->n; i++ )
seq->pts[i] = (PEL *)
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 );
}
/* pair-wise rank of a vector of image descriptors.
*/
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] ) )
return( -1 );
if( in[i]->Coding != IM_CODING_NONE ||
vips_bandfmt_iscomplex( in[i]->BandFmt ) ) {
im_error( "im_rank_image", "%s",
_( "uncoded non-complex only" ) );
return( -1 );
}
if( in[0]->BandFmt != in[i]->BandFmt ) {
im_error( "im_rank_image", "%s",
_( "input images differ in format" ) );
return( -1 );
}
if( in[0]->Xsize != in[i]->Xsize ||
in[0]->Ysize != in[i]->Ysize ) {
im_error( "im_rank_image", "%s",
_( "input images differ in size" ) );
return( -1 );
}
if( in[0]->Bands != in[i]->Bands ) {
im_error( "im_rank_image", "%s",
_( "input images differ in number of bands" ) );
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 );
}
int
im_maxvalue( IMAGE **in, IMAGE *out, int n )
{
return( im_rank_image( in, out, n, n - 1 ) );
}