int im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) { IMAGE *filtered[4]; IMAGE *absed[4]; int i; if( im_open_local_array( out, filtered, 4, "im_lindetect:1", "p" ) || im_open_local_array( out, absed, 4, "im_lindetect:2", "p" ) ) return( -1 ); for( i = 0; i < 4; i++ ) { if( im_conv( in, filtered[i], mask ) || !(mask = (INTMASK *) im_local( out, (im_construct_fn) im_rotate_imask45, (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) return( -1 ); } for( i = 0; i < 4; i++ ) if( im_abs( filtered[i], absed[i] ) ) return( -1 ); return( im_maxvalue( absed, out, 4 ) ); }
/* Make an array of things local to a descriptor ... eg. make 6 local temp * images. */ int im_local_array( IMAGE *im, void **out, int n, im_construct_fn cons, im_callback_fn dest, void *a, void *b, void *c ) { int i; for( i = 0; i < n; i++ ) if( !(out[i] = im_local( im, cons, dest, a, b, c )) ) return( -1 ); return( 0 ); }
int im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) { IMAGE *t[GTEMPS]; INTMASK *rmask; if( im_open_local_array( out, t, GTEMPS, "im_gradient", "p" ) ) return( -1 ); if( !(rmask = (INTMASK *) im_local( out, (im_construct_fn) im_rotate_imask90, (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) return( -1 ); if( im_conv( in, t[0], mask ) || im_conv( in, t[1], rmask ) || im_abs( t[0], t[2] ) || im_abs( t[1], t[3] ) || im_add( t[2], t[3], out ) ) return( -1 ); return( 0 ); }
/* Break a mask into boxes. */ static Boxes * boxes_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, int n_layers, int cluster ) { const int size = mask->xsize * mask->ysize; Boxes *boxes; double sum; int x, y, z; /* Check parameters. */ if( im_piocheck( in, out ) || im_check_uncoded( "im_aconv", in ) || vips_check_dmask( "im_aconv", mask ) ) return( NULL ); boxes = VIPS_NEW( out, Boxes ); boxes->in = in; boxes->out = out; if( !(boxes->mask = (DOUBLEMASK *) im_local( out, (im_construct_fn) im_dup_dmask, (im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) ) return( NULL ); boxes->n_layers = n_layers; boxes->cluster = cluster; boxes->n_hline = 0; boxes->n_velement = 0; boxes->n_vline = 0; /* Break into a set of hlines. */ if( boxes_break( boxes ) ) return( NULL ); /* Cluster to find groups of lines. */ VIPS_DEBUG_MSG( "boxes_new: clustering with thresh %d ...\n", cluster ); while( boxes_cluster2( boxes, cluster ) ) ; /* Renumber to remove holes created by clustering. */ boxes_renumber( boxes ); /* Find a set of vlines for the remaining hlines. */ boxes_vline( boxes ); /* Find the area of the lines and the length of the longest hline. */ boxes->area = 0; boxes->max_line = 0; for( y = 0; y < boxes->n_velement; y++ ) { x = boxes->velement[y].band; z = boxes->hline[x].end - boxes->hline[x].start; boxes->area += boxes->velement[y].factor * z; if( z > boxes->max_line ) boxes->max_line = z; } /* Strength reduction: if all lines are divisible by n, we can move * that n out into the ->area factor. The aim is to produce as many * factor 1 lines as we can and to reduce the chance of overflow. */ x = boxes->velement[0].factor; for( y = 1; y < boxes->n_velement; y++ ) x = gcd( x, boxes->velement[y].factor ); for( y = 0; y < boxes->n_velement; y++ ) boxes->velement[y].factor /= x; boxes->area *= x; /* Find the area of the original mask. */ sum = 0; for( z = 0; z < size; z++ ) sum += mask->coeff[z]; boxes->area = rint( sum * boxes->area / mask->scale ); boxes->rounding = (boxes->area + 1) / 2 + mask->offset * boxes->area; #ifdef DEBUG boxes_hprint( boxes ); boxes_vprint( boxes ); #endif /*DEBUG*/ /* With 512x512 tiles, each hline requires 3mb of intermediate per * thread ... 300 lines is about a gb per thread, ouch. */ if( boxes->n_hline > 150 ) { im_error( "im_aconv", "%s", _( "mask too complex" ) ); return( NULL ); } return( boxes ); }
/* Break a mask into lines. */ static Lines * lines_new( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, int n_layers ) { const int width = mask->xsize * mask->ysize; Lines *lines; double max; double min; double depth; double sum; int layers_above; int layers_below; int z, n, x; /* Check parameters. */ if( im_piocheck( in, out ) || im_check_uncoded( "im_aconvsep", in ) || vips_check_dmask_1d( "im_aconvsep", mask ) ) return( NULL ); lines = VIPS_NEW( out, Lines ); lines->in = in; lines->out = out; if( !(lines->mask = (DOUBLEMASK *) im_local( out, (im_construct_fn) im_dup_dmask, (im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) ) return( NULL ); lines->n_layers = n_layers; lines->n_lines = 0; VIPS_DEBUG_MSG( "lines_new: breaking into %d layers ...\n", n_layers ); /* Find mask range. We must always include the zero axis in the mask. */ max = 0; min = 0; for( x = 0; x < width; x++ ) { if( mask->coeff[x] > max ) max = mask->coeff[x]; if( mask->coeff[x] < min ) min = mask->coeff[x]; } /* The zero axis must fall on a layer boundary. Estimate the * depth, find n-lines-above-zero, get exact depth, then calculate a * fixed n-lines which includes any negative parts. */ depth = (max - min) / n_layers; layers_above = ceil( max / depth ); depth = max / layers_above; layers_below = floor( min / depth ); n_layers = layers_above - layers_below; VIPS_DEBUG_MSG( "depth = %g, n_layers = %d\n", depth, n_layers ); /* For each layer, generate a set of lines which are inside the * perimeter. Work down from the top. */ for( z = 0; z < n_layers; z++ ) { double y = max - (1 + z) * depth; /* y plus half depth ... ie. the layer midpoint. */ double y_ph = y + depth / 2; /* Odd, but we must avoid rounding errors that make us miss 0 * in the line above. */ int y_positive = z < layers_above; int inside; /* Start outside the perimeter. */ inside = 0; for( x = 0; x < width; x++ ) { /* The vertical line from mask[z] to 0 is inside. Is * our current square (x, y) part of that line? */ if( (y_positive && mask->coeff[x] >= y_ph) || (!y_positive && mask->coeff[x] <= y_ph) ) { if( !inside ) { lines_start( lines, x, y_positive ? 1 : -1 ); inside = 1; } } else { if( inside ) { if( lines_end( lines, x ) ) return( NULL ); inside = 0; } } } if( inside && lines_end( lines, width ) ) return( NULL ); } /* Can we common up any lines? Search for lines with identical * start/end. */ for( z = 0; z < lines->n_lines; z++ ) { for( n = z + 1; n < lines->n_lines; n++ ) { if( lines->start[z] == lines->start[n] && lines->end[z] == lines->end[n] ) { lines->factor[z] += lines->factor[n]; /* n can be deleted. Do this in a separate * pass below. */ lines->factor[n] = 0; } } } /* Now we can remove all factor 0 lines. */ for( z = 0; z < lines->n_lines; z++ ) { if( lines->factor[z] == 0 ) { for( x = z; x < lines->n_lines; x++ ) { lines->start[x] = lines->start[x + 1]; lines->end[x] = lines->end[x + 1]; lines->factor[x] = lines->factor[x + 1]; } lines->n_lines -= 1; } } /* Find the area of the lines. */ lines->area = 0; for( z = 0; z < lines->n_lines; z++ ) lines->area += lines->factor[z] * (lines->end[z] - lines->start[z]); /* Strength reduction: if all lines are divisible by n, we can move * that n out into the ->area factor. The aim is to produce as many * factor 1 lines as we can and to reduce the chance of overflow. */ x = lines->factor[0]; for( z = 1; z < lines->n_lines; z++ ) x = gcd( x, lines->factor[z] ); for( z = 0; z < lines->n_lines; z++ ) lines->factor[z] /= x; lines->area *= x; /* Find the area of the original mask. */ sum = 0; for( z = 0; z < width; z++ ) sum += mask->coeff[z]; lines->area = rint( sum * lines->area / mask->scale ); lines->rounding = (lines->area + 1) / 2 + mask->offset * lines->area; /* ASCII-art layer drawing. printf( "lines:\n" ); for( z = 0; z < lines->n_lines; z++ ) { printf( "%3d - %2d x ", z, lines->factor[z] ); for( x = 0; x < 55; x++ ) { int rx = x * (width + 1) / 55; if( rx >= lines->start[z] && rx < lines->end[z] ) printf( "#" ); else printf( " " ); } printf( " %3d .. %3d\n", lines->start[z], lines->end[z] ); } printf( "area = %d\n", lines->area ); printf( "rounding = %d\n", lines->rounding ); */ return( lines ); }