| /* im_maxpos_vec.c |
| * |
| * Copyright: 2006, The Nottingham Trent University |
| * Copyright: 2006, Tom Vajzovic |
| * |
| * Author: Tom Vajzovic |
| * |
| * Written on: 2006-09-01 |
| * |
| * 9/9/09 |
| * - gtkdoc comments |
| */ |
| |
| /* |
| |
| 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 |
| |
| */ |
| |
| /** HEADERS **/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif /* HAVE_CONFIG_H */ |
| #include <vips/intl.h> |
| |
| #include <stdlib.h> |
| #include <float.h> |
| |
| #include <vips/vips.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC */ |
| |
| |
| /** TYPE DEFINITIONS **/ |
| |
| typedef struct { |
| |
| int *xs; |
| int *ys; |
| double *vals; |
| int *ptrs; |
| int start; |
| |
| } maxpos_list; |
| |
| |
| /** LOCAL FUNCTIONS DECLARATIONS **/ |
| |
| static maxpos_list *maxpos_list_alloc( int n ); |
| static void maxpos_list_free( maxpos_list *list ); |
| |
| static void maxpos_list_init( maxpos_list *list, int n ); |
| static void *maxpos_vec_start( IMAGE *unrequired, void *, void * ); |
| static int maxpos_vec_scan( REGION *reg, void *seq, void *, void * ); |
| static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ); |
| static int maxpos_vec_stop( void *seq, void *, void * ); |
| |
| static void minpos_list_init( maxpos_list *list, int n ); |
| static void *minpos_vec_start( IMAGE *unrequired, void *, void * ); |
| static int minpos_vec_scan( REGION *reg, void *seq, void *, void * ); |
| static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ); |
| static int minpos_vec_stop( void *seq, void *, void * ); |
| |
| |
| /** EXPORTED FUNCTIONS **/ |
| |
| /** |
| * im_maxpos_vec: |
| * @im: image to scan |
| * @xpos: array to return x positions |
| * @ypos: array to return y positions |
| * @maxima: array to return values |
| * @n: number of maxima to search for |
| * |
| * Find the coordinates and values of the n maxima of an image. |
| * |
| * For 8 and 16-bit images, it's much faster to find the histogram and then |
| * calculate a threshold from that. See im_mpercent(). |
| * |
| * See also: im_minpos(), im_min(), im_stats(), im_maxpos_avg(). |
| * |
| * Returns: 0 on success, -1 on error |
| */ |
| |
| int im_maxpos_vec( IMAGE *im, int *xpos, int *ypos, double *maxima, int n ){ |
| #define FUNCTION_NAME "im_maxpos_vec" |
| /* number of sequences used is beyond my control at this level, but I note that */ |
| /* efficiency decreases as more sequences are used - speed may still increase */ |
| |
| int result; |
| int *pointers= im_malloc( NULL, n * sizeof( int* ) ); |
| maxpos_list master_list= { xpos, ypos, maxima, pointers, 0 }; |
| |
| if( im_pincheck( im ) ) |
| return -1; |
| |
| if( !pointers ) |
| return -1; |
| |
| if( ! ( vips_bandfmt_isint( im->BandFmt ) || |
| vips_bandfmt_isfloat( im->BandFmt ) ) ){ |
| im_error( FUNCTION_NAME, "%s", _( "scalar images only" ) ); |
| return -1; |
| } |
| |
| if( 1 != im-> Bands ){ |
| im_error( FUNCTION_NAME, "%s", _( "single band images only" ) ); |
| return -1; |
| } |
| |
| if( IM_CODING_NONE != im-> Coding ){ |
| im_error( FUNCTION_NAME, "%s", _( "uncoded images only" ) ); |
| return -1; |
| } |
| |
| if( ! xpos || ! ypos || ! maxima || n < 1 ){ |
| im_error( FUNCTION_NAME, "%s", _( "invalid argument" ) ); |
| return -1; |
| } |
| |
| maxpos_list_init( &master_list, n ); |
| |
| result= vips_sink( im, maxpos_vec_start, maxpos_vec_scan, maxpos_vec_stop, &n, &master_list ); |
| |
| im_free( pointers ); |
| |
| return result; |
| #undef FUNCTION_NAME |
| } |
| |
| |
| /** |
| * im_minpos_vec: |
| * @im: image to scan |
| * @xpos: array to return x positions |
| * @ypos: array to return y positions |
| * @maxima: array to return values |
| * @n: number of minima to search for |
| * |
| * Find the coordinates and values of the n minima of an image. |
| * |
| * For 8 and 16-bit images, it's much faster to find the histogram and then |
| * calculate a threshold from that. See im_mpercent(). |
| * |
| * See also: im_minpos(), im_min(), im_stats(), im_maxpos_avg(). |
| * |
| * Returns: 0 on success, -1 on error |
| */ |
| |
| int im_minpos_vec( IMAGE *im, int *xpos, int *ypos, double *minima, int n ){ |
| #define FUNCTION_NAME "im_minpos_vec" |
| /* number of sequences used is beyond my control at this level, but I note that */ |
| /* effeciency decreases as more sequences are used - speed may still increase */ |
| |
| int result; |
| int *pointers= im_malloc( NULL, n * sizeof( int* ) ); |
| maxpos_list master_list= { xpos, ypos, minima, pointers, 0 }; |
| |
| if( im_pincheck( im ) ) |
| return -1; |
| |
| if( !pointers ) |
| return -1; |
| |
| if( ! ( vips_bandfmt_isint( im->BandFmt ) || |
| vips_bandfmt_isfloat( im->BandFmt ) ) ){ |
| im_error( FUNCTION_NAME, "%s", _( "scalar images only" ) ); |
| return -1; |
| } |
| |
| if( 1 != im-> Bands ){ |
| im_error( FUNCTION_NAME, "%s", _( "single band images only" ) ); |
| return -1; |
| } |
| |
| if( IM_CODING_NONE != im-> Coding ){ |
| im_error( FUNCTION_NAME, "%s", _( "uncoded images only" ) ); |
| return -1; |
| } |
| |
| if( ! xpos || ! ypos || ! minima || n < 1 ){ |
| im_error( FUNCTION_NAME, "%s", _( "invalid argument" ) ); |
| return -1; |
| } |
| |
| minpos_list_init( &master_list, n ); |
| |
| result= vips_sink( im, minpos_vec_start, minpos_vec_scan, minpos_vec_stop, &n, &master_list ); |
| |
| im_free( pointers ); |
| |
| return result; |
| #undef FUNCTION_NAME |
| } |
| |
| |
| /** LOCAL FUNCTION DEFINITIONS **/ |
| |
| static maxpos_list *maxpos_list_alloc( int n ){ |
| |
| maxpos_list *list= im_malloc( NULL, sizeof( maxpos_list ) ); |
| |
| if( ! list ) |
| return NULL; |
| |
| list-> xs= im_malloc( NULL, 3 * n * sizeof( int ) ); |
| list-> vals= im_malloc( NULL, n * sizeof( double ) ); |
| |
| if( ! list-> xs || ! list-> vals ){ |
| im_free( list-> xs ); |
| im_free( list-> vals ); |
| im_free( list ); |
| return NULL; |
| } |
| list-> ys= list-> xs + n; |
| list-> ptrs= list-> ys + n; |
| |
| return list; |
| } |
| |
| static void maxpos_list_free( maxpos_list *list ){ |
| im_free( list-> xs ); |
| im_free( list-> vals ); |
| im_free( list ); |
| } |
| |
| |
| static void maxpos_list_init( maxpos_list *list, int n ){ |
| int i; |
| |
| for( i= 0; i < n; ++i ){ |
| list-> xs[ i ]= 0; |
| list-> ys[ i ]= 0; |
| list-> vals[ i ]= 0; |
| list-> ptrs[ i ]= i + 1; |
| } |
| |
| list-> ptrs[ n - 1 ]= -1; |
| list-> start= 0; |
| } |
| |
| static void *maxpos_vec_start( IMAGE *unrequired, void *a, void *b ){ |
| |
| int *n = (int *) a; |
| maxpos_list *list= maxpos_list_alloc( *n ); |
| |
| if( ! list ) |
| return NULL; |
| |
| maxpos_list_init( list, *n ); |
| |
| return list; |
| } |
| |
| static int maxpos_vec_scan( REGION *reg, void *seq, void *a, void *b ){ |
| |
| maxpos_list *list = (maxpos_list *) seq; |
| |
| #define MAXPOS_VEC_SCAN( type ){ \ |
| \ |
| int y= reg-> valid. top; \ |
| int x; \ |
| int ymax= y + reg-> valid. height; \ |
| int xmax= reg-> valid. left + reg-> valid. width; \ |
| \ |
| type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ |
| size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ |
| \ |
| for( ; y < ymax; ++y, row+= skip ) \ |
| for( x= reg-> valid. left; x < xmax; ++x ) \ |
| if( row[ x ] > list-> vals[ list-> start ] ) \ |
| add_to_maxpos_list( list, x, y, row[ x ] ); \ |
| } |
| |
| switch( reg-> im-> BandFmt ){ |
| case IM_BANDFMT_UCHAR: MAXPOS_VEC_SCAN( guint8 ) break; |
| case IM_BANDFMT_CHAR: MAXPOS_VEC_SCAN( gint8 ) break; |
| case IM_BANDFMT_USHORT: MAXPOS_VEC_SCAN( guint16 ) break; |
| case IM_BANDFMT_SHORT: MAXPOS_VEC_SCAN( gint16 ) break; |
| case IM_BANDFMT_UINT: MAXPOS_VEC_SCAN( guint32 ) break; |
| case IM_BANDFMT_INT: MAXPOS_VEC_SCAN( gint32 ) break; |
| case IM_BANDFMT_FLOAT: MAXPOS_VEC_SCAN( float ) break; |
| case IM_BANDFMT_DOUBLE: MAXPOS_VEC_SCAN( double ) break; |
| default: |
| g_assert( 0 ); |
| } |
| |
| #undef MAXPOS_VEC_SCAN |
| |
| return 0; |
| } |
| |
| static void add_to_maxpos_list( maxpos_list *list, int x, int y, double val ){ |
| |
| int pointer= list-> start; |
| |
| while( -1 != list-> ptrs[ pointer ] && val > list-> vals[ list-> ptrs[ pointer ] ] ) |
| pointer= list-> ptrs[ pointer ]; |
| |
| list-> xs[ list-> start ]= x; |
| list-> ys[ list-> start ]= y; |
| list-> vals[ list-> start ]= val; |
| |
| if( list-> start != pointer ){ |
| /* we are adding mid-chain not at the very bottom */ |
| int second= list-> ptrs[ list-> start ]; |
| |
| list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; |
| list-> ptrs[ pointer ]= list-> start; |
| list-> start= second; |
| } |
| } |
| |
| static int maxpos_vec_stop( void *seq, void *a, void *b ){ |
| |
| /* reverse list */ |
| |
| maxpos_list *list = (maxpos_list *) seq; |
| maxpos_list *master_list = (maxpos_list *) b; |
| |
| int prev= -1; |
| int pointer= list-> start; |
| |
| while( -1 != list-> ptrs[ pointer ] ){ |
| |
| int next= list-> ptrs[ pointer ]; |
| |
| list-> ptrs[ pointer ]= prev; |
| prev= pointer; |
| pointer= next; |
| } |
| list-> ptrs[ pointer ]= prev; |
| list-> start= pointer; |
| |
| /* add to main list */ |
| |
| for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) |
| /* loop over all the ones found in this sequence */ |
| |
| if( list-> vals[ pointer ] > master_list-> vals[ master_list-> start ] ) |
| add_to_maxpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); |
| else |
| break; |
| /* since we are now high->low, if this one isn't big enough, none of the rest are */ |
| |
| maxpos_list_free( list ); |
| |
| return 0; |
| } |
| |
| |
| static void minpos_list_init( maxpos_list *list, int n ){ |
| int i; |
| |
| for( i= 0; i < n; ++i ){ |
| list-> xs[ i ]= 0; |
| list-> ys[ i ]= 0; |
| list-> vals[ i ]= DBL_MAX; |
| list-> ptrs[ i ]= i + 1; |
| } |
| |
| list-> ptrs[ n - 1 ]= -1; |
| list-> start= 0; |
| } |
| |
| static void *minpos_vec_start( IMAGE *unrequired, void *a, void *b ){ |
| |
| int *n = (int *) a; |
| maxpos_list *list= maxpos_list_alloc( *n ); |
| |
| if( ! list ) |
| return NULL; |
| |
| minpos_list_init( list, *n ); |
| |
| return list; |
| } |
| |
| static int minpos_vec_scan( REGION *reg, void *seq, void *a, void *b ){ |
| |
| maxpos_list *list = (maxpos_list *) seq; |
| |
| #define MINPOS_VEC_SCAN( type ){ \ |
| \ |
| int y= reg-> valid. top; \ |
| int x; \ |
| int ymax= y + reg-> valid. height; \ |
| int xmax= reg-> valid. left + reg-> valid. width; \ |
| \ |
| type *row= (type*)IM_REGION_ADDR( reg, reg-> valid. left, y ) - reg-> valid. left; \ |
| size_t skip= IM_REGION_LSKIP( reg ) / sizeof( type ); \ |
| \ |
| for( ; y < ymax; ++y, row+= skip ) \ |
| for( x= reg-> valid. left; x < xmax; ++x ) \ |
| if( row[ x ] < list-> vals[ list-> start ] ) \ |
| add_to_minpos_list( list, x, y, row[ x ] ); \ |
| } |
| |
| switch( reg-> im-> BandFmt ){ |
| case IM_BANDFMT_UCHAR: MINPOS_VEC_SCAN( guint8 ) break; |
| case IM_BANDFMT_CHAR: MINPOS_VEC_SCAN( gint8 ) break; |
| case IM_BANDFMT_USHORT: MINPOS_VEC_SCAN( guint16 ) break; |
| case IM_BANDFMT_SHORT: MINPOS_VEC_SCAN( gint16 ) break; |
| case IM_BANDFMT_UINT: MINPOS_VEC_SCAN( guint32 ) break; |
| case IM_BANDFMT_INT: MINPOS_VEC_SCAN( gint32 ) break; |
| case IM_BANDFMT_FLOAT: MINPOS_VEC_SCAN( float ) break; |
| case IM_BANDFMT_DOUBLE: MINPOS_VEC_SCAN( double ) break; |
| default: |
| g_assert( 0 ); |
| } |
| |
| #undef MINPOS_VEC_SCAN |
| |
| return 0; |
| } |
| |
| static void add_to_minpos_list( maxpos_list *list, int x, int y, double val ){ |
| |
| int pointer= list-> start; |
| |
| while( -1 != list-> ptrs[ pointer ] && val < list-> vals[ list-> ptrs[ pointer ] ] ) |
| pointer= list-> ptrs[ pointer ]; |
| |
| list-> xs[ list-> start ]= x; |
| list-> ys[ list-> start ]= y; |
| list-> vals[ list-> start ]= val; |
| |
| if( list-> start != pointer ){ |
| /* we are adding mid-chain not at the very bottom */ |
| int second= list-> ptrs[ list-> start ]; |
| |
| list-> ptrs[ list-> start ]= list-> ptrs[ pointer ]; |
| list-> ptrs[ pointer ]= list-> start; |
| list-> start= second; |
| } |
| } |
| |
| static int minpos_vec_stop( void *seq, void *a, void *b ){ |
| |
| /* reverse list */ |
| |
| maxpos_list *list = (maxpos_list *) seq; |
| maxpos_list *master_list = (maxpos_list *) b; |
| int prev= -1; |
| int pointer= list-> start; |
| |
| while( -1 != list-> ptrs[ pointer ] ){ |
| |
| int next= list-> ptrs[ pointer ]; |
| |
| list-> ptrs[ pointer ]= prev; |
| prev= pointer; |
| pointer= next; |
| } |
| list-> ptrs[ pointer ]= prev; |
| list-> start= pointer; |
| |
| /* add to main list */ |
| |
| for( ; -1 != pointer; pointer= list-> ptrs[ pointer ] ) |
| /* loop over all the ones found in this sequence */ |
| |
| if( list-> vals[ pointer ] < master_list-> vals[ master_list-> start ] ) |
| add_to_minpos_list( master_list, list-> xs[ pointer ], list-> ys[ pointer ], list-> vals[ pointer ] ); |
| else |
| break; |
| /* since we are now high->low, if this one isn't big enough, none of the rest are */ |
| |
| maxpos_list_free( list ); |
| |
| return 0; |
| } |
| |