added im_flood_other(), better check() functions

This commit is contained in:
John Cupitt 2009-09-28 17:03:52 +00:00
parent 02bbf2fd5a
commit ce4ed9d2bf
12 changed files with 405 additions and 21 deletions

View File

@ -47,6 +47,8 @@
- im_blend()/im_ifthenelse() allows many-band conditional, 1-band then/else
- im_blend()/im_ifthenelse() allows band and format to differ between then
and else parts
- better im_check() functions
- added im_flood_other() as start of simple segmentation operator
25/3/09 started 7.18.0
- revised version numbers

2
TODO
View File

@ -1,3 +1,5 @@
- im_flood*() should use inline rather than #define
- we have tools/ and contrib/ argh

View File

@ -141,11 +141,11 @@ int im_cross_phase( IMAGE *a, IMAGE *b, IMAGE *out ){
if( im_pincheck( a ) || im_pincheck( b ) || im_poutcheck( out ))
return -1;
if( im_check_size( FUNCTION_NAME, a, b ) ||
im_check_bands( FUNCTION_NAME, a, b ) ||
if( im_check_same_size( FUNCTION_NAME, a, b ) ||
im_check_same_bands( FUNCTION_NAME, a, b ) ||
im_check_same_format( FUNCTION_NAME, a, b ) ||
im_check_uncoded( FUNCTION_NAME, a ) ||
im_check_uncoded( FUNCTION_NAME, b ) ||
im_check_format( FUNCTION_NAME, a, b ) ||
im_check_complex( FUNCTION_NAME, a ) ||
im_check_complex( FUNCTION_NAME, b ) )
return -1;

View File

@ -123,7 +123,7 @@ im_invert( IMAGE *in, IMAGE *out )
/* Check args.
*/
if( im_check_uncoded( "im_invert", in ) ||
im_check_uchar( "im_invert", in ) ||
im_check_format( "im_invert", in, IM_BANDFMT_UCHAR ) ||
im_piocheck( in, out ) )
return( -1 );

View File

@ -140,12 +140,12 @@ int im_check_known_coded( const char *domain, IMAGE *im );
int im_check_bands_1orn( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_noncomplex( const char *domain, IMAGE *im );
int im_check_complex( const char *domain, IMAGE *im );
int im_check_uchar( const char *domain, IMAGE *im );
int im_check_format( const char *domain, IMAGE *im, VipsBandFmt fmt );
int im_check_mono( const char *domain, IMAGE *im );
int im_check_int( const char *domain, IMAGE *im );
int im_check_size( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_bands( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_format( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_same_size( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_same_bands( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_same_format( const char *domain, IMAGE *im1, IMAGE *im2 );
int im_check_vector( const char *domain, int n, IMAGE *im );
int im_ispoweroftwo( int );
@ -619,6 +619,9 @@ int im_readpoint( IMAGE *, int, int, PEL * );
int im_flood( IMAGE *, int, int, PEL *, Rect * );
int im_flood_blob( IMAGE *, int, int, PEL *, Rect * );
int im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink );
int im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial );
int im_flood_other_copy( IMAGE *mask, IMAGE *out,
IMAGE *test, int x, int y, int serial );
int im_lineset( IMAGE *in, IMAGE *out, IMAGE *mask, IMAGE *ink,
int n, int *x1v, int *y1v, int *x2v, int *y2v );

View File

@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libinplace.la
libinplace_la_SOURCES = \
im_circle.c \
im_flood.c \
im_flood_other.c \
im_insertplace.c \
im_line.c \
im_paintrect.c \

View File

@ -20,6 +20,8 @@
* - im_invalidate() after paint
* 24/3/09
* - added IM_CODING_RAD support
* 28/9/09
* - ooops, tiny memleak
*/
/*
@ -146,6 +148,7 @@ free_state( State *st )
/* Free our stuff.
*/
IM_FREE( st->ink );
IM_FREE( st->edge );
IM_FREEF( free_buffer, st->buf1 );
IM_FREEF( free_buffer, st->buf2 );
im_free( st );
@ -165,6 +168,7 @@ build_state( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
st->y = y;
st->ink = NULL;
st->dout = dout;
st->edge = NULL;
st->ps = IM_IMAGE_SIZEOF_PEL( im );
st->ls = IM_IMAGE_SIZEOF_LINE( im );
st->buf1 = NULL;
@ -410,7 +414,7 @@ im_flood_blob( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
}
/* A Flood blob we can call from nip. Grr! Should be a way to wrap these
* automatically. Maybe nip could do it if it seems a RW image argument?
* automatically. Maybe nip could do it if it sees a RW image argument?
*/
int
im_flood_blob_copy( IMAGE *in, IMAGE *out, int x, int y, PEL *ink )

View File

@ -0,0 +1,332 @@
/* int im_flood( IMAGE *im, int x, int y, PEL *ink, Rect *dout )
*
* Flood fill from point (x,y) with colour ink. Flood up to boundary == ink.
* Any type, any number of bands, IM_CODING_LABQ too. Returns the bounding box
* of the modified pixels in dout, whether it succeeds or not.
*
* Currently a rather inefficient pixel-based algorithm, should put something
* better in, really. Speed isn't likely to be a problem, except for very
* large images.
*
* JC 30/8/97
* - VIPSified, cleaned up, from "John Robinson's prog to fill
* enclosed areas"
* - something Kirk gave me, so thanks John
* JC 1/10/97
* - swapped inner memcmp/cpy for a loop ... faster for small pixels
* 13/7/02 JC
* - im_flood_blob() added
* 5/12/06
* - im_invalidate() after paint
* 24/3/09
* - added IM_CODING_RAD support
*/
/*
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 <string.h>
#include <vips/vips.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
/* Size of a point buffer. We allocate a list of these to hold points we need
* to visit.
*/
#define PBUFSIZE (1000)
/* An xy position.
*/
typedef struct {
int x, y;
} Point;
/* A buffer of points, and how many of them have been used. When full, alloc a
* new buffer, and link it on.
*/
typedef struct _Buffer {
struct _Buffer *next;
int n;
Point points[PBUFSIZE];
} Buffer;
/* Our state.
*/
typedef struct {
/* Parameters.
*/
IMAGE *mask;
IMAGE *test;
int x, y;
unsigned int serial;
/* Derived stuff.
*/
PEL *edge; /* Searching for these pixels */
int ps; /* sizeof( one pel ) */
int ls; /* sizeof( one line ) */
int left, right; /* Area will fill within */
int top, bottom;
/* Two buffers of points which we know need checking.
*/
Buffer *buf1;
Buffer *buf2;
} State;
/* Alloc a new buffer.
*/
static Buffer *
build_buffer( void )
{
Buffer *buf = IM_NEW( NULL, Buffer );
if( !buf )
return( NULL );
buf->next = NULL;
buf->n = 0;
return( buf );
}
/* Free a chain of buffers.
*/
static void
free_buffer( Buffer *buf )
{
IM_FREE( buf->next );
im_free( buf );
}
/* Free a state.
*/
static void
free_state( State *st )
{
IM_FREE( st->edge );
IM_FREEF( free_buffer, st->buf1 );
IM_FREEF( free_buffer, st->buf2 );
im_free( st );
}
/* Build a state.
*/
static State *
build_state( IMAGE *mask, IMAGE *test, int x, int y, int serial )
{
State *st = IM_NEW( NULL, State );
if( !st )
return( NULL );
st->mask = mask;
st->test = test;
st->x = x;
st->y = y;
st->serial = serial;
st->edge = NULL;
st->ps = IM_IMAGE_SIZEOF_PEL( test );
st->ls = IM_IMAGE_SIZEOF_LINE( test );
st->buf1 = NULL;
st->buf2 = NULL;
st->left = 0;
st->top = 0;
st->right = test->Xsize;
st->bottom = test->Ysize;
if( !(st->edge = (PEL *) im_malloc( NULL, st->ps )) ||
!(st->buf1 = build_buffer()) ||
!(st->buf2 = build_buffer()) ) {
free_state( st );
return( NULL );
}
return( st );
}
/* Add xy to buffer, move buffer on on overflow.
*/
#define ADD( BUF, X, Y ) { \
BUF->points[BUF->n].x = X; \
BUF->points[BUF->n].y = Y; \
BUF->n++; \
if( BUF->n == PBUFSIZE ) { \
if( !BUF->next ) { \
if( !(BUF->next = build_buffer()) ) \
return( -1 ); \
} \
BUF = BUF->next; \
BUF->n = 0; \
} \
}
/* If point == blob colour, add it to out.
*/
#define ADDIFEDGE( P, X, Y ) { \
const PEL *p1 = (P); \
\
for( j = 0; j < st->ps; j++ ) \
if( p1[j] != st->edge[j] ) \
break; \
if( j == st->ps ) \
ADD( out, X, Y ); \
}
/* Read points to fill from in, write new points to out.
*/
static int
dofill( State *st, Buffer *in, Buffer *out )
{
const int width = st->mask->Xsize;
int i, j;
/* Clear output buffer.
*/
out->n = 0;
/* Loop over chain of input buffers.
*/
for(;;) {
/* Loop for this buffer.
*/
for( i = 0; i < in->n; i++ ) {
/* Find this pixel.
*/
const int x = in->points[i].x;
const int y = in->points[i].y;
const PEL *p = (PEL *) st->test->data +
x * st->ps + y * st->ls;
int *m = (int *) st->mask->data + x + y * width;
/* Has it been marked already? Done.
*/
if( *m == st->serial )
continue;
*m = st->serial;
/* Propogate to neighbours.
*/
if( x < st->right - 1 && !m[1] )
ADDIFEDGE( p + st->ps, x + 1, y );
if( x > st->left && !m[-1] )
ADDIFEDGE( p - st->ps, x - 1, y );
if( y < st->bottom - 1 && !m[width] )
ADDIFEDGE( p + st->ls, x, y + 1 );
if( y > st->top && !m[-width] )
ADDIFEDGE( p - st->ls, x, y - 1 );
}
if( in->n == PBUFSIZE )
/* Buffer full ... must be another one.
*/
in = in->next;
else
break;
}
return( 0 );
}
int
im_flood_other( IMAGE *mask, IMAGE *test, int x, int y, int serial )
{
State *st;
Buffer *in, *out, *t;
PEL *p;
int *m;
if( im_rwcheck( mask ) ||
im_incheck( test ) )
return( -1 );
if( im_check_known_coded( "im_flood_other", test ) ||
im_check_uncoded( "im_flood_other", mask ) ||
im_check_mono( "im_flood_other", mask ) ||
im_check_int( "im_flood_other", mask ) ||
im_check_same_size( "im_flood_other", test, mask ) )
return( -1 );
/* Make sure the mask has zero at the start position. If it does, we
* must have filled with this serial already, so ... job done.
*/
m = (int *) mask->data + x + y * mask->Ysize;
if( *m == serial )
return( 0 );
if( !(st = build_state( mask, test, x, y, serial )) )
return( -1 );
/* Edge is set by colour of start pixel.
*/
p = (PEL *) test->data + x * st->ps + y * st->ls;
memcpy( st->edge, p, st->ps );
/* Add start pixel to the work buffer, and loop.
*/
ADD( st->buf1, x, y )
for( in = st->buf1, out = st->buf2;
in->n > 0; t = in, in = out, out = t )
if( dofill( st, in, out ) ) {
free_state( st );
return( -1 );
}
free_state( st );
im_invalidate( mask );
return( 0 );
}
/* A Flood blob we can call from nip. Grr! Should be a way to wrap these
* automatically. Maybe nip could do it if it sees a RW image argument?
*/
int
im_flood_other_copy( IMAGE *mask, IMAGE *out,
IMAGE *test, int x, int y, int serial )
{
IMAGE *t;
if( !(t = im_open_local( out, "im_flood_other_copy", "t" )) ||
im_copy( mask, t ) ||
im_flood_other( t, test, x, y, serial ) ||
im_copy( t, out ) )
return( -1 );
return( 0 );
}

View File

@ -257,6 +257,44 @@ static im_function flood_blob_copy_desc = {
flood_blob_copy_args /* Arg list */
};
/* Args for im_flood_other_copy().
*/
static im_arg_desc flood_other_copy_args[] = {
IM_INPUT_IMAGE( "mask" ),
IM_INPUT_IMAGE( "test" ),
IM_OUTPUT_IMAGE( "out" ),
IM_INPUT_INT( "start_x" ),
IM_INPUT_INT( "start_y" ),
IM_INPUT_INT( "serial" )
};
/* Call im_flood_other_copy() via arg vector.
*/
static int
flood_other_copy_vec( im_object *argv )
{
IMAGE *mask = argv[0];
IMAGE *test = argv[1];
IMAGE *out = argv[2];
int start_x = *((int *) argv[3]);
int start_y = *((int *) argv[4]);
int serial = *((int *) argv[5]);
return( im_flood_other_copy( mask, test, out,
start_x, start_y, serial ) );
}
/* Description of im_flood_other_copy().
*/
static im_function flood_other_copy_desc = {
"im_flood_other_copy", /* Name */
"flood mask with serial number from start_x, start_y while pixel == start pixel",
0, /* Flags */
flood_other_copy_vec, /* Dispatch function */
IM_NUMBER( flood_other_copy_args ),/* Size of arg list */
flood_other_copy_args /* Arg list */
};
/* To do:
* these all need some kind of pel type
*
@ -274,6 +312,7 @@ static im_function flood_blob_copy_desc = {
static im_function *inplace_list[] = {
&circle_desc,
&flood_blob_copy_desc,
&flood_other_copy_desc,
&insertplace_desc,
&line_desc,
&lineset_desc

View File

@ -422,10 +422,11 @@ im_check_complex( const char *domain, IMAGE *im )
}
int
im_check_uchar( const char *domain, IMAGE *im )
im_check_format( const char *domain, IMAGE *im, VipsBandFmt fmt )
{
if( im->BandFmt != IM_BANDFMT_UCHAR ) {
im_error( domain, "%s", _( "image must be uchar" ) );
if( im->BandFmt != fmt ) {
im_error( domain,
_( "image must be %s" ), im_BandFmt2char( fmt ) );
return( -1 );
}
@ -455,7 +456,7 @@ im_check_int( const char *domain, IMAGE *im )
}
int
im_check_size( const char *domain, IMAGE *im1, IMAGE *im2 )
im_check_same_size( const char *domain, IMAGE *im1, IMAGE *im2 )
{
if( im1->Xsize != im2->Xsize || im1->Ysize != im2->Ysize ) {
im_error( domain, "%s", _( "images must match in size" ) );
@ -466,7 +467,7 @@ im_check_size( const char *domain, IMAGE *im1, IMAGE *im2 )
}
int
im_check_bands( const char *domain, IMAGE *im1, IMAGE *im2 )
im_check_same_bands( const char *domain, IMAGE *im1, IMAGE *im2 )
{
if( im1->Bands != im2->Bands ) {
im_error( domain, "%s",
@ -478,7 +479,7 @@ im_check_bands( const char *domain, IMAGE *im1, IMAGE *im2 )
}
int
im_check_format( const char *domain, IMAGE *im1, IMAGE *im2 )
im_check_same_format( const char *domain, IMAGE *im1, IMAGE *im2 )
{
if( im1->BandFmt != im2->BandFmt ) {
im_error( domain, "%s",

View File

@ -291,9 +291,9 @@ blend( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out )
if( im_check_uncoded( "im_blend", c ) ||
im_check_uncoded( "im_blend", a ) ||
im_check_uncoded( "im_blend", b ) ||
im_check_uchar( "im_blend", c ) ||
im_check_format( "im_blend", a, b ) ||
im_check_bands( "im_blend", a, b ) ||
im_check_format( "im_blend", c, IM_BANDFMT_UCHAR ) ||
im_check_same_format( "im_blend", a, b ) ||
im_check_same_bands( "im_blend", a, b ) ||
im_check_bands_1orn( "im_blend", c, a ) ||
im_piocheck( c, out ) ||
im_pincheck( a ) ||

View File

@ -154,11 +154,11 @@ ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out )
/* Check args.
*/
if( im_check_uncoded( "im_ifthenelse", c ) ||
im_check_uchar( "im_ifthenelse", c ) ||
im_check_known_coded( "im_ifthenelse", a ) ||
im_check_known_coded( "im_ifthenelse", b ) ||
im_check_format( "im_ifthenelse", a, b ) ||
im_check_bands( "im_ifthenelse", a, b ) ||
im_check_format( "ifthenelse", c, IM_BANDFMT_UCHAR ) ||
im_check_same_format( "ifthenelse", a, b ) ||
im_check_same_bands( "ifthenelse", a, b ) ||
im_check_bands_1orn( "im_ifthenelse", c, a ) ||
im_piocheck( c, out ) ||
im_pincheck( a ) ||