| /* @(#) Function which erodes a binary VASARI format picture with a mask. |
| * @(#) The mask coefficients are either 255 (object) or 0 (bk) or 128 (any). |
| * @(#) Input image are binary images with either 0 or 255 values, one channel |
| * @(#) only. The program erodes a white object on a black background. |
| * @(#) The center of the mask is at location (m->xsize/2, m->ysize/2) |
| * @(#) integer division. The mask is expected to have an odd width and |
| * @(#) height. |
| * @(#) |
| * @(#) int im_erode(in, out, m) |
| * @(#) IMAGE *in, *out; |
| * @(#) INTMASK *m; |
| * @(#) |
| * @(#) Returns either 0 (sucess) or -1 (fail) |
| * |
| * 19/9/95 JC |
| * - rewrite |
| * 6/7/99 JC |
| * - checks and small tidies |
| * 7/4/04 |
| * - now uses im_embed() with edge stretching on the input, not |
| * the output |
| * - sets Xoffset / Yoffset |
| * 21/4/08 |
| * - only rebuild the buffer offsets if bpl changes |
| * - small cleanups |
| */ |
| |
| /* |
| |
| 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 |
| |
| */ |
| |
| /* |
| #define DEBUG |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif /*HAVE_CONFIG_H*/ |
| #include <vips/intl.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include <vips/vips.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| /* Our sequence value. |
| */ |
| typedef struct { |
| REGION *ir; /* Input region */ |
| |
| int *soff; /* Offsets we check for set */ |
| int ss; /* ... and number we check for set */ |
| int *coff; /* Offsets we check for clear */ |
| int cs; /* ... and number we check for clear */ |
| int last_bpl; /* Avoid recalcing offsets, if we can */ |
| } SeqInfo; |
| |
| /* Stop function. |
| */ |
| static int |
| erode_stop( void *vseq, void *a, void *b ) |
| { |
| SeqInfo *seq = (SeqInfo *) vseq; |
| |
| IM_FREEF( im_region_free, seq->ir ); |
| |
| return( 0 ); |
| } |
| |
| /* Start function. |
| */ |
| static void * |
| erode_start( IMAGE *out, void *a, void *b ) |
| { |
| IMAGE *in = (IMAGE *) a; |
| INTMASK *msk = (INTMASK *) b; |
| SeqInfo *seq; |
| int sz = msk->xsize * msk->ysize; |
| |
| if( !(seq = IM_NEW( out, SeqInfo )) ) |
| return( NULL ); |
| |
| /* Init! |
| */ |
| seq->ir = NULL; |
| seq->soff = NULL; |
| seq->ss = 0; |
| seq->coff = NULL; |
| seq->cs = 0; |
| seq->last_bpl = -1; |
| |
| /* Attach region and arrays. |
| */ |
| seq->ir = im_region_create( in ); |
| seq->soff = IM_ARRAY( out, sz, int ); |
| seq->coff = IM_ARRAY( out, sz, int ); |
| if( !seq->ir || !seq->soff || !seq->coff ) { |
| erode_stop( seq, in, NULL ); |
| return( NULL ); |
| } |
| |
| return( (void *) seq ); |
| } |
| |
| /* Erode! |
| */ |
| static int |
| erode_gen( REGION *or, void *vseq, void *a, void *b ) |
| { |
| SeqInfo *seq = (SeqInfo *) vseq; |
| INTMASK *msk = (INTMASK *) b; |
| REGION *ir = seq->ir; |
| |
| int *soff = seq->soff; |
| int *coff = seq->coff; |
| |
| Rect *r = &or->valid; |
| Rect s; |
| int le = r->left; |
| int to = r->top; |
| int bo = IM_RECT_BOTTOM(r); |
| int sz = IM_REGION_N_ELEMENTS( or ); |
| |
| int *t; |
| |
| int x, y; |
| int result, i; |
| |
| /* Prepare the section of the input image we need. A little larger |
| * than the section of the output image we are producing. |
| */ |
| s = *r; |
| s.width += msk->xsize - 1; |
| s.height += msk->ysize - 1; |
| if( im_prepare( ir, &s ) ) |
| return( -1 ); |
| |
| #ifdef DEBUG |
| printf( "erode_gen: preparing %dx%d pixels\n", s.width, s.height ); |
| #endif /*DEBUG*/ |
| |
| /* Scan mask, building offsets we check when processing. Only do this |
| * if the bpl has changed since the previous im_prepare(). |
| */ |
| if( seq->last_bpl != IM_REGION_LSKIP( ir ) ) { |
| seq->last_bpl = IM_REGION_LSKIP( ir ); |
| |
| seq->ss = 0; |
| seq->cs = 0; |
| for( t = msk->coeff, y = 0; y < msk->ysize; y++ ) |
| for( x = 0; x < msk->xsize; x++, t++ ) |
| switch( *t ) { |
| case 255: |
| soff[seq->ss++] = |
| IM_REGION_ADDR( ir, |
| x + le, y + to ) - |
| IM_REGION_ADDR( ir, le, to ); |
| break; |
| |
| case 128: |
| break; |
| |
| case 0: |
| coff[seq->cs++] = |
| IM_REGION_ADDR( ir, |
| x + le, y + to ) - |
| IM_REGION_ADDR( ir, le, to ); |
| break; |
| |
| default: |
| im_error( "im_erode", |
| _( "bad mask element (%d " |
| "should be 0, 128 or 255)" ), |
| *t ); |
| return( -1 ); |
| } |
| } |
| |
| /* Erode! |
| */ |
| for( y = to; y < bo; y++ ) { |
| PEL *p = (PEL *) IM_REGION_ADDR( ir, le, y ); |
| PEL *q = (PEL *) IM_REGION_ADDR( or, le, y ); |
| |
| /* Loop along line. |
| */ |
| for( x = 0; x < sz; x++, q++, p++ ) { |
| /* Check all set pixels are set. |
| */ |
| result = 255; |
| for( i = 0; i < seq->ss; i++ ) |
| if( !p[soff[i]] ) { |
| /* Found a mismatch! |
| */ |
| result = 0; |
| break; |
| } |
| |
| /* Check all clear pixels are clear. |
| */ |
| if( result ) |
| for( i = 0; i < seq->cs; i++ ) |
| if( p[coff[i]] ) { |
| result = 0; |
| break; |
| } |
| |
| *q = result; |
| } |
| } |
| |
| return( 0 ); |
| } |
| |
| /* Erode an image. |
| */ |
| int |
| im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *m ) |
| { |
| INTMASK *msk; |
| |
| /* Check mask has odd number of elements in width and height. |
| */ |
| if( m->xsize < 1 || !(m->xsize & 0x1) || |
| m->ysize < 1 || !(m->ysize & 0x1) ) { |
| im_error( "im_erode", "%s", _( "mask size not odd" ) ); |
| return( -1 ); |
| } |
| |
| /* Standard checks. |
| */ |
| if( im_piocheck( in, out ) ) |
| return( -1 ); |
| if( in->Coding != IM_CODING_NONE || |
| in->BandFmt != IM_BANDFMT_UCHAR ) { |
| im_error( "im_erode", "%s", _( "1-band uchar uncoded only" ) ); |
| return( -1 ); |
| } |
| if( im_cp_desc( out, in ) ) |
| return( -1 ); |
| |
| /* Prepare output. Consider a 7x7 mask and a 7x7 image --- the output |
| * would be 1x1. |
| */ |
| if( im_cp_desc( out, in ) ) |
| return( -1 ); |
| out->Xsize -= m->xsize - 1; |
| out->Ysize -= m->ysize - 1; |
| if( out->Xsize <= 0 || out->Ysize <= 0 ) { |
| im_error( "im_erode", "%s", _( "image too small for mask" ) ); |
| return( -1 ); |
| } |
| |
| /* Take a copy of m. |
| */ |
| if( !(msk = im_dup_imask( m, "conv_mask" )) ) |
| return( -1 ); |
| if( im_add_close_callback( out, |
| (im_callback_fn) im_free_imask, msk, NULL ) ) { |
| im_free_imask( msk ); |
| return( -1 ); |
| } |
| |
| /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause |
| * too many recalculations on overlaps. |
| */ |
| if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) ) |
| return( -1 ); |
| |
| /* Generate! |
| */ |
| if( im_generate( out, erode_start, erode_gen, erode_stop, in, msk ) ) |
| return( -1 ); |
| |
| out->Xoffset = -m->xsize / 2; |
| out->Yoffset = -m->ysize / 2; |
| |
| return( 0 ); |
| } |
| |
| /* The above, with a border to make out the same size as in. |
| */ |
| int |
| im_erode( IMAGE *in, IMAGE *out, INTMASK *m ) |
| { |
| IMAGE *t1 = im_open_local( out, "im_erode:1", "p" ); |
| |
| if( !t1 || |
| im_embed( in, t1, 1, m->xsize / 2, m->ysize / 2, |
| in->Xsize + m->xsize - 1, |
| in->Ysize + m->ysize - 1 ) || |
| im_erode_raw( t1, out, m ) ) |
| return( -1 ); |
| |
| out->Xoffset = 0; |
| out->Yoffset = 0; |
| |
| return( 0 ); |
| } |