Esempio n. 1
0
int 
im_freqflt( IMAGE *in, IMAGE *mask, IMAGE *out )
{
	IMAGE *dummy;

	/* Placeholder for memory free.
	 */
	if( !(dummy = im_open( "memory-1", "p" )) )
		return( -1 );

	if( im_iscomplex( in ) ) {
		/* Easy case! Assume it has already been transformed.
		 */
		IMAGE *t1 = im_open_local( dummy, "im_freqflt-1", "p" );

		if( !t1 ||
			im_multiply( in, mask, t1 ) ||
			im_invfftr( t1, out ) ) {
			im_close( dummy );
			return( -1 );
		}
	}
	else {
		/* Harder - fft first, then mult, then force back to start
		 * type.
		 * 
		 * Optimisation: output of im_invfft() is float buffer, we 
		 * will usually chagetype to char, so rather than keeping a
		 * large float buffer and partial to char from that, do
		 * changetype to a memory buffer, and copy to out from that.
		 */
		IMAGE *t[3];
		IMAGE *t3;

		if( im_open_local_array( dummy, t, 3, "im_freqflt-1", "p" ) ||
			!(t3 = im_open_local( out, "im_freqflt-3", "t" )) ||
			im_fwfft( in, t[0] ) ||
			im_multiply( t[0], mask, t[1] ) ||
			im_invfftr( t[1], t[2] ) ||
			im_clip2fmt( t[2], t3, in->BandFmt ) ||
			im_copy( t3, out ) ) {
			im_close( dummy );
			return( -1 );
		}	
	}

	im_close( dummy );

	return( 0 );
}
Esempio n. 2
0
/**
 * im_hsp:
 * @in: input image
 * @ref: reference histogram 
 * @out: output image
 *
 * Maps image @in to image @out, adjusting the histogram to match image @ref.
 * Both images should have the same number of bands.
 *
 * See also: im_histspec(), im_histgr().
 *
 * Returns: 0 on success, -1 on error
 */
int 
im_hsp( IMAGE *in, IMAGE *ref, IMAGE *out )
{
	IMAGE *t[3];

	if( im_open_local_array( out, t, 3, "im_hsp", "p" ) ||
		im_histgr( in, t[0], -1 ) || 
		im_histgr( ref, t[1], -1 ) ||
		im_histspec( t[0], t[1], t[2] ) ||
		im_maplut( in, out, t[2] ) )
		return( -1 );

	return( 0 );
}
Esempio n. 3
0
/**
 * im_label_regions:
 * @test: image to test
 * @mask: write labelled regions here
 * @segments: return number of regions here
 *
 * im_label_regions() repeatedly scans @test for regions of 4-connected pixels
 * with the same pixel value. Every time a region is discovered, those
 * pixels are marked in @mask with a unique serial number. Once all pixels
 * have been labelled, the operation returns, setting @segments to the number
 * of discrete regions which were detected.
 *
 * @mask is always a 1-band %IM_BANDFMT_UINT image of the same dimensions as
 * @test.
 *
 * This operation is useful for, for example, blob counting. You can use the
 * morphological operators to detect and isolate a series of objects, then use
 * im_label_regions() to number them all.
 *
 * Use im_histindexed() to (for example) find blob coordinates.
 *
 * See also: im_histindexed()
 *
 * Returns: 0 on success, -1 on error.
 */
int
im_label_regions( IMAGE *test, IMAGE *mask, int *segments )
{
	IMAGE *t[2];
	int serial;
	int *m;
	int x, y;

	/* Create the zero mask image.
	 */
	if( im_open_local_array( mask, t, 2, "im_label_regions", "p" ) ||
		im_black( t[0], test->Xsize, test->Ysize, 1 ) ||
		im_clip2fmt( t[0], t[1], IM_BANDFMT_INT ) ) 
		return( -1 );

	/* Search the mask image, flooding as we find zero pixels.
	 */
	if( im_rwcheck( t[1] ) )
		return( -1 );
	serial = 0;
	m = (int *) t[1]->data;
	for( y = 0; y < test->Ysize; y++ ) {
		for( x = 0; x < test->Xsize; x++ ) {
			if( !m[x] ) {
				/*
				if( im_flood_other_old( t[1], test, 
					x, y, serial ) )
				 */
				if( im_flood_other( test, t[1], 
					x, y, serial, NULL ) )
//				if( im_flood_other_old( t[1], test,
//					x, y, serial ) )
					return( -1 );

				serial += 1;
			}
		}

		m += test->Xsize;
	}

	/* Copy result to mask.
	 */
	if( im_copy( t[1], mask ) )
		return( -1 );
	if( segments )
		*segments = serial;

	return( 0 );
}
Esempio n. 4
0
/**
 * im_grey:
 * @out: output image
 * @xsize: image size
 * @ysize: image size
 *
 * Create a one-band uchar image with the left-most column zero and the
 * right-most 255. Intermediate pixels are a linear ramp.
 *
 * See also: im_fgrey(), im_make_xy(), im_identity().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_grey( IMAGE *out, const int xsize, const int ysize )
{
	IMAGE *t[2];

	/* Change range to [0,255].
	 */
	if( im_open_local_array( out, t, 2, "im_grey", "p" ) ||
		im_fgrey( t[0], xsize, ysize ) || 
		im_lintra( 255.0, t[0], 0.0, t[1] ) ||
		im_clip2fmt( t[1], out, IM_BANDFMT_UCHAR ) )
		return( -1 );

	return( 0 );
}
Esempio n. 5
0
/**
 * im_lindetect:
 * @in: input image
 * @out: output image
 * @mask: convolution mask
 *
 * @in is convolved four times with @mask, each time @mask is rotated by 45
 * degrees. Each output pixel is the largest absolute value of the four
 * convolutions.
 *
 * See also: im_compass(), im_gradient(), im_conv().
 *
 * Returns: 0 on success, -1 on error
 */
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 = im_local_imask( out, 
				im_rotate_imask45( mask, mask->filename ) )) )
			return( -1 );
	}

	for( i = 0; i < 4; i++ ) 
		if( im_abs( filtered[i], absed[i] ) )
			return( -1 );

	return( im_maxvalue( absed, out, 4 ) );
}
Esempio n. 6
0
/**
 * im_tone_analyse:
 * @in: input image
 * @out: output image
 * @Ps: shadow point (eg. 0.2)
 * @Pm: mid-tone point (eg. 0.5)
 * @Ph: highlight point (eg. 0.8)
 * @S: shadow adjustment (+/- 30)
 * @M: mid-tone adjustment (+/- 30)
 * @H: highlight adjustment (+/- 30)
 *
 * As im_tone_build(), but analyse the histogram of @in and use it to
 * pick the 0.1% and 99.9% points for @Lb and @Lw.
 *
 * See also: im_tone_build().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_tone_analyse(
    IMAGE *in,
    IMAGE *out,
    double Ps, double Pm, double Ph,
    double S, double M, double H )
{
    IMAGE *t[4];
    int low, high;
    double Lb, Lw;

    if( im_open_local_array( out, t, 4, "im_tone_map", "p" ) )
        return( -1 );

    /* If in is IM_CODING_LABQ, unpack.
     */
    if( in->Coding == IM_CODING_LABQ ) {
        if( im_LabQ2LabS( in, t[0] ) )
            return( -1 );
    }
    else
        t[0] = in;

    /* Should now be 3-band short.
     */
    if( im_check_uncoded( "im_tone_analyse", t[0] ) ||
            im_check_bands( "im_tone_analyse", t[0], 3 ) ||
            im_check_format( "im_tone_analyse", t[0], IM_BANDFMT_SHORT ) )
        return( -1 );

    if( im_extract_band( t[0], t[1], 0 ) ||
            im_clip2fmt( t[1], t[2], IM_BANDFMT_USHORT ) ||
            im_histgr( t[2], t[3], -1 ) )
        return( -1 );

    if( im_mpercent_hist( t[3], 0.1 / 100.0, &high ) ||
            im_mpercent_hist( t[3], 99.9 / 100.0, &low ) )
        return( -1 );

    Lb = 100 * low / 32768;
    Lw = 100 * high / 32768;

    im_diag( "im_tone_analyse", "set Lb = %g, Lw = %g", Lb, Lw );

    return( im_tone_build( out, Lb, Lw, Ps, Pm, Ph, S, M, H ) );
}
Esempio n. 7
0
/* Read a ppm/pgm file using mmap().
 */
static int
read_mmap( FILE *fp, const char *filename, int msb_first, IMAGE *out )
{
	const int header_offset = ftell( fp );
	IMAGE *t[2];

	if( im_open_local_array( out, t, 2, "im_ppm2vips", "p" ) ||
		im_raw2vips( filename, t[0], 
			out->Xsize, out->Ysize, 
			IM_IMAGE_SIZEOF_PEL( out ), header_offset ) ||
		im_copy_morph( t[0], t[1],
			out->Bands, out->BandFmt, out->Coding ) ||
		im_copy_native( t[1], out, msb_first ) ) 
		return( -1 );

	return( 0 );
}
Esempio n. 8
0
/* Two images overlap in an area ... make a mask the size of the area, which
 * has 255 for every pixel where both images are non-zero.
 */
static int
make_overlap_mask( IMAGE *ref, IMAGE *sec, IMAGE *mask, 
	Rect *rarea, Rect *sarea )
{
	IMAGE *t[6];

	if( im_open_local_array( mask, t, 6, "mytemps", "p" ) ||
		extract_rect( ref, t[0], rarea ) ||
		extract_rect( sec, t[1], sarea ) ||
		im_extract_band( t[0], t[2], 0 ) ||
		im_extract_band( t[1], t[3], 0 ) ||
		im_notequalconst( t[2], t[4], 0.0 ) || 
		im_notequalconst( t[3], t[5], 0.0 ) || 
		im_andimage( t[4], t[5], mask ) ) 
		return( -1 );

	return( 0 );
}
Esempio n. 9
0
/* Find stats on an area of an IMAGE ... consider only pixels for which the
 * mask is true.
 */
static DOUBLEMASK *
find_image_stats( IMAGE *in, IMAGE *mask, Rect *area )
{
	DOUBLEMASK *stats;
	IMAGE *t[4];
	gint64 count;

	/* Extract area, build black image, mask out pixels we want.
	 */
	if( im_open_local_array( in, t, 4, "find_image_stats", "p" ) ||
		extract_rect( in, t[0], area ) ||
		im_black( t[1], t[0]->Xsize, t[0]->Ysize, t[0]->Bands ) ||
		im_clip2fmt( t[1], t[2], t[0]->BandFmt ) ||
		im_ifthenelse( mask, t[0], t[2], t[3] ) )
		return( NULL );

	/* Get stats from masked image.
	 */
	if( !(stats = local_mask( in, im_stats( t[3] ) )) ) 
		return( NULL );

	/* Number of non-zero pixels in mask.
	 */
	if( count_nonzero( mask, &count ) )
		return( NULL );

	/* And scale masked average to match.
	 */
	stats->coeff[4] *= (double) count / 
		((double) mask->Xsize * mask->Ysize);

	/* Yuk! Zap the deviation column with the pixel count. Used later to
	 * determine if this is likely to be a significant overlap.
	 */
	stats->coeff[5] = count;

#ifdef DEBUG
	if( count == 0 )
		im_warn( "global_balance", _( "empty overlap!" ) );
#endif /*DEBUG*/

	return( stats );
}
Esempio n. 10
0
static int 
disp_ps( IMAGE *dummy, IMAGE *in, IMAGE *out )
{
	IMAGE *t[3];

	if( im_open_local_array( out, t, 3, "im_disp_ps temp 1", "p" ) )
		return( -1 );

	if( in->BandFmt != IM_BANDFMT_COMPLEX ) {
		if( im_fwfft( in, t[0] ) )
			return( -1 );
		in = t[0];
	}

	if( im_abs( in, t[1] ) ||
		im_scaleps( t[1], t[2] ) || 
		im_rotquad( t[2], out ) )
		return( -1 );

	return( 0 );
}
Esempio n. 11
0
/**
 * im_gammacorrect:
 * @in: input image
 * @out: output image
 * @exponent: gamma factor
 *
 * Gamma-correct an 8- or 16-bit unsigned image with a lookup table. The
 * output format is the same as the input format.
 *
 * See also: im_identity(), im_powtra(), im_maplut()
 *
 * Returns: 0 on success, -1 on error
 */
int 
im_gammacorrect( IMAGE *in, IMAGE *out, double exponent )
{
	IMAGE *t[4];
	double mx1, mx2;

	if( im_open_local_array( out, t, 4, "im_gammacorrect", "p" ) ||
		im_check_u8or16( "im_gammacorrect", in ) ||
		im_piocheck( in, out ) ||
		(in->BandFmt == IM_BANDFMT_UCHAR ?
			im_identity( t[0], 1 ) :
			im_identity_ushort( t[0], 1, 65536 )) ||
		im_powtra( t[0], t[1], exponent ) ||
		im_max( t[0], &mx1 ) ||
		im_max( t[1], &mx2 ) ||
		im_lintra( mx1 / mx2, t[1], 0, t[2] ) ||
		im_clip2fmt( t[2], t[3], in->BandFmt ) ||
		im_maplut( in, out, t[3] ) )
		return( -1 );

	return( 0 );
}
Esempio n. 12
0
int
im_addgnoise( IMAGE *in, IMAGE *out, double sigma ){
#define FUNCTION_NAME "im_addgnoise"

  if( im_piocheck( in, out ))
    return -1;
  {
    int i;
    IMAGE **temps= IM_ARRAY( out, in-> Bands, IMAGE* );
    IMAGE *joined_temps= im_open_local( out, FUNCTION_NAME ": joined_temps", "p" );

    if( ! temps || ! joined_temps || im_open_local_array( out, temps, in-> Bands, FUNCTION_NAME ": temps", "p" ))
      return -1;

    for( i= 0; i < in-> Bands; ++i )
      if( im_gaussnoise( temps[i], in-> Xsize, in-> Ysize, 0.0, sigma ))
        return -1;

    return im_gbandjoin( temps, joined_temps, in-> Bands ) || im_add( in, joined_temps, out );
  }
#undef FUNCTION_NAME
}
Esempio n. 13
0
/**
 * im_gradient:
 * @in: input image
 * @out: output image
 * @mask: convolution mask
 *
 * @in is convolved with @mask and with @mask after a 90 degree rotation. The
 * result is the sum of the absolute value of the two convolutions. 
 *
 * See also: im_lindetect(), im_gradient(), im_conv().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask )
{
	IMAGE *t[4];
	INTMASK *rmask;

	if( im_open_local_array( out, t, 4, "im_gradient", "p" ) )
		return( -1 );

	if( !(rmask = im_local_imask( out, 
		im_rotate_imask90( mask, mask->filename ) )) ) 
		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 );
}
Esempio n. 14
0
/* The common part of most binary inplace operators.
 *
 * Unlike im__formatalike() and friends, we can only change one of the images,
 * since the other is being updated.
 */
VipsImage *
im__inplace_base( const char *domain,
                  VipsImage *main, VipsImage *sub, VipsImage *out )
{
    VipsImage *t[2];

    if( im_rwcheck( main ) ||
            im_pincheck( sub ) ||
            im_check_coding_known( domain, main ) ||
            im_check_coding_same( domain, main, sub ) ||
            im_check_bands_1orn_unary( domain, sub, main->Bands ) )
        return( NULL );

    /* Cast sub to match main in bands and format.
     */
    if( im_open_local_array( out, t, 2, domain, "p" ) ||
            im__bandup( domain, sub, t[0], main->Bands ) ||
            im_clip2fmt( t[0], t[1], main->BandFmt ) )
        return( NULL );

    return( t[1] );
}
Esempio n. 15
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 );
}
Esempio n. 16
0
int
im__colour_unary( const char *domain,
	IMAGE *in, IMAGE *out, VipsType type,
	im_wrapone_fn buffer_fn, void *a, void *b )
{
	IMAGE *t[1];

	if( im_check_uncoded( domain, in ) ||
		im_check_bands( domain, in, 3 ) ||
		im_open_local_array( out, t, 1, domain, "p" ) ||
		im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) )
		return( -1 );

	if( im_cp_desc( out, t[0] ) )
		return( -1 );
	out->Type = type;

	if( im_wrapone( t[0], out, 
		(im_wrapone_fn) buffer_fn, a, b ) )
		return( -1 );

	return( 0 );
}
Esempio n. 17
0
/**
 * im_ifthenelse:
 * @c: condition #IMAGE
 * @a: then #IMAGE
 * @b: else #IMAGE
 * @out: output #IMAGE
 *
 * This operation scans the condition image @c 
 * and uses it to select pixels from either the then image @a or the else
 * image @b. Non-zero means @a, 0 means @b.
 *
 * Any image can have either 1 band or n bands, where n is the same for all
 * the non-1-band images. Single band images are then effectively copied to 
 * make n-band images.
 *
 * Images @a and @b are cast up to the smallest common format.
 *
 * If the images differ in size, the smaller image is enlarged to match the
 * larger by adding zero pixels along the bottom and right.
 *
 * See also: im_blend(), im_equal().
 *
 * Returns: 0 on success, -1 on error
 */
int
im_ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out )
{
	IMAGE *t[9];

	if( im_open_local_array( out, t, 9, "im_ifthenelse", "p" ) )
		return( -1 );

	/* Make a and b match in bands and format. Don't make c match: we
	 * special-case this in code above ^^^ for speed.
	 */
	if( im__formatalike( a, b, t[0], t[1] ) ||
		im__bandalike( "im_ifthenelse", t[0], t[1], t[2], t[3] ) )
		return( -1 );

	/* All 3 must match in size.
	 */
	t[4] = c;
	if( im__sizealike_vec( t + 2, t + 5, 3 ) )
		return( -1 );
	c = t[7];

	/* If c is not uchar, do (!=0) to make a uchar image.
	 */
	if( c->BandFmt != IM_BANDFMT_UCHAR ) {
		if( im_notequalconst( c, t[8], 0 ) )
			return( -1 );

		c = t[8];
	}

	if( ifthenelse( c, t[5], t[6], out ) )
		return( -1 );

	return( 0 );
}
Esempio n. 18
0
static int
shrink_factor( IMAGE *in, IMAGE *out, 
	int shrink, double residual, VipsInterpolate *interp )
{
	IMAGE *t[9];
	VipsImage **s = (VipsImage **) 
		vips_object_local_array( VIPS_OBJECT( out ), 1 );
	IMAGE *x;
	int tile_width;
	int tile_height;
	int nlines;

	if( im_open_local_array( out, t, 9, "thumbnail", "p" ) )
		return( -1 );
	x = in;

	/* Unpack the two coded formats we support to float for processing.
	 */
	if( x->Coding == IM_CODING_LABQ ) {
		if( verbose ) 
			printf( "unpacking LAB to RGB\n" );

		if( im_LabQ2disp( x, t[1], im_col_displays( 7 ) ) )
			return( -1 );
		x = t[1];
	}
	else if( x->Coding == IM_CODING_RAD ) {
		if( verbose ) 
			printf( "unpacking Rad to float\n" );

		if( im_rad2float( x, t[1] ) )
			return( -1 );
		x = t[1];
	}

	if( im_shrink( x, t[2], shrink, shrink ) )
		return( -1 );

	/* We want to make sure we read the image sequentially.
	 * However, the convolution we may be doing later will force us 
	 * into SMALLTILE or maybe FATSTRIP mode and that will break
	 * sequentiality.
	 *
	 * So ... read into a cache where tiles are scanlines, and make sure
	 * we keep enough scanlines to be able to serve a line of tiles.
	 */
	vips_get_tile_size( t[2], 
		&tile_width, &tile_height, &nlines );
	if( vips_tilecache( t[2], &s[0], 
		"tile_width", t[2]->Xsize,
		"tile_height", 10,
		"max_tiles", (nlines * 2) / 10,
		"strategy", VIPS_CACHE_SEQUENTIAL,
		NULL ) ||
		im_affinei_all( s[0], t[4], 
			interp, residual, 0, 0, residual, 0, 0 ) )
		return( -1 );
	x = t[4];

	/* If we are upsampling, don't sharpen, since nearest looks dumb
	 * sharpened.
	 */
	if( shrink > 1 && residual <= 1.0 && !nosharpen ) {
		if( verbose ) 
			printf( "sharpening thumbnail\n" );

		if( im_conv( x, t[5], sharpen_filter() ) )
			return( -1 );
		x = t[5];
	}

	/* Colour management: we can transform the image if we have an output
	 * profile and an input profile. The input profile can be in the
	 * image, or if there is no profile there, supplied by the user.
	 */
	if( export_profile &&
		(im_header_get_typeof( x, IM_META_ICC_NAME ) || 
		 import_profile) ) {
		if( im_header_get_typeof( x, IM_META_ICC_NAME ) ) {
			if( verbose ) 
				printf( "importing with embedded profile\n" );

			if( im_icc_import_embedded( x, t[6], 
				IM_INTENT_RELATIVE_COLORIMETRIC ) )
				return( -1 );
		}
		else {
			if( verbose ) 
				printf( "importing with profile %s\n",
					import_profile );

			if( im_icc_import( x, t[6], 
				import_profile, 
				IM_INTENT_RELATIVE_COLORIMETRIC ) )
				return( -1 );
		}

		if( verbose ) 
			printf( "exporting with profile %s\n", export_profile );

		if( im_icc_export_depth( t[6], t[7], 
			8, export_profile, 
			IM_INTENT_RELATIVE_COLORIMETRIC ) )
			return( -1 );

		x = t[7];
	}

	if( delete_profile ) {
		if( verbose )
			printf( "deleting profile from output image\n" );

		if( im_meta_get_typeof( x, IM_META_ICC_NAME ) &&
			!im_meta_remove( x, IM_META_ICC_NAME ) )
			return( -1 );
	}

	if( im_copy( x, out ) )
		return( -1 );

	return( 0 );
}
Esempio n. 19
0
int 
im__find_lroverlap( IMAGE *ref_in, IMAGE *sec_in, IMAGE *out,
	int bandno_in, 
	int xref, int yref, int xsec, int ysec, 
	int halfcorrelation, int halfarea,
	int *dx0, int *dy0,
	double *scale1, double *angle1, double *dx1, double *dy1 )
{
	Rect left, right, overlap;
	IMAGE *ref, *sec;
	IMAGE *t[6];
	TIE_POINTS points, *p_points;
	TIE_POINTS newpoints, *p_newpoints;
	int dx, dy;
	int i;

	/* Test cor and area.
	 */
	if( halfcorrelation < 0 || halfarea < 0 || 
		halfarea < halfcorrelation ) {
		im_error( "im_lrmosaic", "%s", _( "bad area parameters" ) );
		return( -1 );
	}

	/* Set positions of left and right.
	 */
	left.left = 0;
	left.top = 0;
	left.width = ref_in->Xsize;
	left.height = ref_in->Ysize;
	right.left = xref - xsec;
	right.top = yref - ysec;
	right.width = sec_in->Xsize;
	right.height = sec_in->Ysize;

	/* Find overlap.
	 */
	im_rect_intersectrect( &left, &right, &overlap );
	if( overlap.width < 2 * halfarea + 1 ||
		overlap.height < 2 * halfarea + 1 ) {
		im_error( "im_lrmosaic", 
			"%s", _( "overlap too small for search" ) );
		return( -1 );
	}

	/* Extract overlaps as 8-bit, 1 band.
	 */
	if( !(ref = im_open_local( out, "temp_one", "t" )) ||
		!(sec = im_open_local( out, "temp_two", "t" )) ||
		im_open_local_array( out, t, 6, "im_lrmosaic", "p" ) ||
		im_extract_area( ref_in, t[0], 
			overlap.left, overlap.top, 
			overlap.width, overlap.height ) ||
		im_extract_area( sec_in, t[1], 
			overlap.left - right.left, overlap.top - right.top, 
			overlap.width, overlap.height ) )
		return( -1 );
	if( ref_in->Coding == IM_CODING_LABQ ) {
		if( im_LabQ2Lab( t[0], t[2] ) || 
			im_LabQ2Lab( t[1], t[3] ) ||
	    		im_Lab2disp( t[2], t[4], im_col_displays( 1 ) ) || 
			im_Lab2disp( t[3], t[5], im_col_displays( 1 ) ) ||
			im_extract_band( t[4], ref, 1 ) ||
			im_extract_band( t[5], sec, 1 ) )
			return( -1 );
	}
	else if( ref_in->Coding == IM_CODING_NONE ) {
		if( im_extract_band( t[0], t[2], bandno_in ) ||
			im_extract_band( t[1], t[3], bandno_in ) ||
			im_scale( t[2], ref ) ||
			im_scale( t[3], sec ) )
			return( -1 );
	}
	else {
		im_error( "im_lrmosaic", "%s", _( "unknown Coding type" ) );
		return( -1 );
	}

	/* Initialise and fill TIE_POINTS 
	 */
	p_points = &points;
	p_newpoints = &newpoints;
	p_points->reference = ref_in->filename;
	p_points->secondary = sec_in->filename;
	p_points->nopoints = IM_MAXPOINTS;
	p_points->deltax = 0;
	p_points->deltay = 0;
	p_points->halfcorsize = halfcorrelation; 	
	p_points->halfareasize = halfarea;

	/* Initialise the structure 
	 */
	for( i = 0; i < IM_MAXPOINTS; i++ ) {
		p_points->x_reference[i] = 0;
		p_points->y_reference[i] = 0;
		p_points->x_secondary[i] = 0;
		p_points->y_secondary[i] = 0;
		p_points->contrast[i] = 0;
		p_points->correlation[i] = 0.0;
		p_points->dx[i] = 0.0;
		p_points->dy[i] = 0.0;
		p_points->deviation[i] = 0.0;
	}

	/* Search ref for possible tie-points. Sets: p_points->contrast, 
	 * p_points->x,y_reference.
 	 */
	if( im__lrcalcon( ref, p_points ) )
		return( -1 ); 

	/* For each candidate point, correlate against corresponding part of
	 * sec. Sets x,y_secondary and fills correlation and dx, dy.
 	 */
	if( im__chkpair( ref, sec, p_points ) )
		return( -1 );

	/* First call to im_clinear().
	 */
  	if( im__initialize( p_points ) )
		return( -1 );

	/* Improve the selection of tiepoints until all abs(deviations) are 
	 * < 1.0 by deleting all wrong points.
 	 */
	if( im__improve( p_points, p_newpoints ) )
		return( -1 );

	/* Average remaining offsets.
	 */
	if( im__avgdxdy( p_newpoints, &dx, &dy ) )
		return( -1 );

	/* Offset with overlap position.
	 */
	*dx0 = -right.left + dx;
	*dy0 = -right.top + dy;

	/* Write 1st order parameters too.
	 */
	*scale1 = newpoints.l_scale;
	*angle1 = newpoints.l_angle;
	*dx1 = newpoints.l_deltax;
	*dy1 = newpoints.l_deltay;

	return( 0 );
}
Esempio n. 20
0
/* Convert to a saveable format. 
 *
 * im__saveable_t gives the general type of image
 * we make: vanilla 1/3 bands (eg. PPM), with an optional alpha (eg. PNG), or
 * with CMYK as an option (eg. JPEG). 
 *
 * format_table[] says how to convert each input format. 
 *
 * Need to im_close() the result IMAGE.
 */
IMAGE *
im__convert_saveable( IMAGE *in, 
	im__saveable_t saveable, int format_table[10] ) 
{
	IMAGE *out;

	if( !(out = im_open( "convert-for-save", "p" )) )
		return( NULL );

	/* If this is an IM_CODING_LABQ, we can go straight to RGB.
	 */
	if( in->Coding == IM_CODING_LABQ ) {
		IMAGE *t = im_open_local( out, "conv:1", "p" );
		static void *table = NULL;

		/* Make sure fast LabQ2disp tables are built. 7 is sRGB.
		 */
		if( !table ) 
			table = im_LabQ2disp_build_table( NULL, 
				im_col_displays( 7 ) );

		if( !t || im_LabQ2disp_table( in, t, table ) ) {
			im_close( out );
			return( NULL );
		}

		in = t;
	}

	/* If this is an IM_CODING_RAD, we go to float RGB or XYZ. We should
	 * probably un-gamma-correct the RGB :(
	 */
	if( in->Coding == IM_CODING_RAD ) {
		IMAGE *t;

		if( !(t = im_open_local( out, "conv:1", "p" )) || 
			im_rad2float( in, t ) ) {
			im_close( out );
			return( NULL );
		}

		in = t;
	}

	/* Get the bands right. 
	 */
	if( in->Coding == IM_CODING_NONE ) {
		if( in->Bands == 2 && saveable != IM__RGBA ) {
			IMAGE *t = im_open_local( out, "conv:1", "p" );

			if( !t || im_extract_band( in, t, 0 ) ) {
				im_close( out );
				return( NULL );
			}

			in = t;
		}
		else if( in->Bands > 3 && saveable == IM__RGB ) {
			IMAGE *t = im_open_local( out, "conv:1", "p" );

			if( !t ||
				im_extract_bands( in, t, 0, 3 ) ) {
				im_close( out );
				return( NULL );
			}

			in = t;
		}
		else if( in->Bands > 4 && 
			(saveable == IM__RGB_CMYK || saveable == IM__RGBA) ) {
			IMAGE *t = im_open_local( out, "conv:1", "p" );

			if( !t ||
				im_extract_bands( in, t, 0, 4 ) ) {
				im_close( out );
				return( NULL );
			}

			in = t;
		}

		/* Else we have saveable IM__ANY and we don't chop bands down.
		 */
	}

	/* Interpret the Type field for colorimetric images.
	 */
	if( in->Bands == 3 && in->BandFmt == IM_BANDFMT_SHORT && 
		in->Type == IM_TYPE_LABS ) {
		IMAGE *t = im_open_local( out, "conv:1", "p" );

		if( !t || im_LabS2LabQ( in, t ) ) {
			im_close( out );
			return( NULL );
		}

		in = t;
	}

	if( in->Coding == IM_CODING_LABQ ) {
		IMAGE *t = im_open_local( out, "conv:1", "p" );

		if( !t || im_LabQ2Lab( in, t ) ) {
			im_close( out );
			return( NULL );
		}

		in = t;
	}

	if( in->Coding != IM_CODING_NONE ) {
		im_close( out );
		return( NULL );
	}

	if( in->Bands == 3 && in->Type == IM_TYPE_LCH ) {
		IMAGE *t[2];

                if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
			im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
			im_LCh2Lab( t[0], t[1] ) ) {
			im_close( out );
			return( NULL );
		}

		in = t[1];
	}

	if( in->Bands == 3 && in->Type == IM_TYPE_YXY ) {
		IMAGE *t[2];

                if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
			im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
			im_Yxy2XYZ( t[0], t[1] ) ) {
			im_close( out );
			return( NULL );
		}

		in = t[1];
	}

	if( in->Bands == 3 && in->Type == IM_TYPE_UCS ) {
		IMAGE *t[2];

                if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
			im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
			im_UCS2XYZ( t[0], t[1] ) ) {
			im_close( out );
			return( NULL );
		}

		in = t[1];
	}

	if( in->Bands == 3 && in->Type == IM_TYPE_LAB ) {
		IMAGE *t[2];

                if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
			im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
			im_Lab2XYZ( t[0], t[1] ) ) {
			im_close( out );
			return( NULL );
		}

		in = t[1];
	}

	if( in->Bands == 3 && in->Type == IM_TYPE_XYZ ) {
		IMAGE *t[2];

                if( im_open_local_array( out, t, 2, "conv-1", "p" ) ||
			im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) ||
			im_XYZ2disp( t[0], t[1], im_col_displays( 7 ) ) ) {
			im_close( out );
			return( NULL );
		}

		in = t[1];
	}

	/* Cast to the output format.
	 */
	{
		IMAGE *t = im_open_local( out, "conv:1", "p" );

		if( !t || im_clip2fmt( in, t, format_table[in->BandFmt] ) ) {
			im_close( out );
			return( NULL );
		}

		in = t;
	}

	if( im_copy( in, out ) ) {
		im_close( out );
		return( NULL );
	}

	return( out );
}
Esempio n. 21
0
/* The main part of the benchmark ... transform labq to labq. Chain several of
 * these together to get a CPU-bound operation.
 */
static int
benchmark( IMAGE *in, IMAGE *out )
{
	IMAGE *t[18];
	double one[3] = { 1.0, 1.0, 1.0 };
	double zero[3] = { 0.0, 0.0, 0.0 };
	double darken[3] = { 1.0 / 1.18, 1.0, 1.0 };
	double whitepoint[3] = { 1.06, 1.0, 1.01 };
	double shadow[3] = { -2, 0, 0 };
	double white[3] = { 100, 0, 0 };
	DOUBLEMASK *d652d50 = im_create_dmaskv( "d652d50", 3, 3,
		1.13529, -0.0604663, -0.0606321,
		0.0975399, 0.935024, -0.0256156,
		-0.0336428, 0.0414702, 0.994135 );

	im_add_close_callback( out, 
		(im_callback_fn) im_free_dmask, d652d50, NULL );

	return( 	
		/* Set of descriptors for this operation.
		 */
		im_open_local_array( out, t, 18, "im_benchmark", "p" ) ||

		/* Unpack to float.
		 */
		im_LabQ2Lab( in, t[0] ) ||

		/* Crop 100 pixels off all edges.
		 */
		im_extract_area( t[0], t[1], 
			100, 100, t[0]->Xsize - 200, t[0]->Ysize - 200 ) ||

		/* Shrink by 10%, bilinear interp.
		 */
		im_affinei_all( t[1], t[2],
			vips_interpolate_bilinear_static(),
			0.9, 0, 0, 0.9, 
			0, 0 ) || 

		/* Find L ~= 100 areas (white surround).
		 */
		im_extract_band( t[2], t[3], 0 ) ||
		im_moreconst( t[3], t[4], 99 ) ||

		/* Adjust white point and shadows.
		 */
		im_lintra_vec( 3, darken, t[2], zero, t[5] ) ||
		im_Lab2XYZ( t[5], t[6] ) ||
		im_recomb( t[6], t[7], d652d50 ) ||
		im_lintra_vec( 3, whitepoint, t[7], zero, t[8] ) ||
		im_lintra( 1.5, t[8], 0.0, t[9] ) ||
		im_XYZ2Lab( t[9], t[10] ) ||
		im_lintra_vec( 3, one, t[10], shadow, t[11] ) ||

		/* Make a solid white image.
		 */
		im_black( t[12], t[4]->Xsize, t[4]->Ysize, 3 ) ||
		im_lintra_vec( 3, zero, t[12], white, t[13] ) ||

		/* Reattach border.
		 */
		im_ifthenelse( t[4], t[13], t[11], t[14] ) ||

		/* Sharpen.
		 */
		im_Lab2LabQ( t[14], t[15] ) ||
		im_sharpen( t[15], out, 11, 2.5, 40, 20, 0.5, 1.5 ) 
	);
}
Esempio n. 22
0
/* Like rotjoin, but do a search to refine the tie-points.
 */
static int
rotjoin_search( IMAGE *ref, IMAGE *sec, IMAGE *out, joinfn jfn,
	int bandno,
	int xr1, int yr1, int xs1, int ys1, 
	int xr2, int yr2, int xs2, int ys2,
	int halfcorrelation, int halfarea,
	int balancetype,
	int mwidth )
{ 
	Transformation trn;
	double cor1, cor2;
	double a, b, dx, dy;
	double xs3, ys3;
	double xs4, ys4;
	int xs5, ys5;
	int xs6, ys6;
	double xs7, ys7;
	double xs8, ys8;

	/* Temps.
	 */
	IMAGE *t[3];

	if( im_open_local_array( out, t, 3, "rotjoin_search", "p" ) )
		return( -1 );

	/* Unpack LABQ to LABS for correlation.
	 */
	if( ref->Coding == IM_CODING_LABQ ) {
		if( im_LabQ2LabS( ref, t[0] ) )
			return( -1 );
	}
	else
		t[0] = ref;
	if( sec->Coding == IM_CODING_LABQ ) {
		if( im_LabQ2LabS( sec, t[1] ) )
			return( -1 );
	}
	else
		t[1] = sec;

	/* Solve to get scale + rot + disp.
	 */
	if( im__coeff( xr1, yr1, xs1, ys1, xr2, yr2, xs2, ys2, 
		&a, &b, &dx, &dy ) ||
		apply_similarity( &trn, t[1], t[2], a, b, dx, dy ) ) 
		return( -1 );

	/* Map points on sec to rotated image.
	 */
	im__transform_forward( &trn, xs1, ys1, &xs3, &ys3 );
	im__transform_forward( &trn, xs2, ys2, &xs4, &ys4 );

	/* Refine tie-points on rotated image. Remember the clip
	 * im__transform_set_area() has set, and move the sec tie-points 
	 * accordingly.
	 */
	if( im_correl( t[0], t[2], xr1, yr1, 
		xs3 - trn.oarea.left, ys3 - trn.oarea.top,
		halfcorrelation, halfarea, &cor1, &xs5, &ys5 ) )
		return( -1 );
	if( im_correl( t[0], t[2], xr2, yr2, 
		xs4 - trn.oarea.left, ys4 - trn.oarea.top,
		halfcorrelation, halfarea, &cor2, &xs6, &ys6 ) )
		return( -1 );

#ifdef DEBUG
	printf( "rotjoin_search: nudged pair 1 from %d, %d to %d, %d\n",
		xs3 - trn.oarea.left, ys3 - trn.oarea.top,
		xs5, ys5 );
	printf( "rotjoin_search: nudged pair 2 from %d, %d to %d, %d\n",
		xs4 - trn.oarea.left, ys4 - trn.oarea.top,
		xs6, ys6 );
#endif /*DEBUG*/

	/* Put the sec tie-points back into output space.
	 */
	xs5 += trn.oarea.left;
	ys5 += trn.oarea.top;
	xs6 += trn.oarea.left;
	ys6 += trn.oarea.top;

	/* ... and now back to input space again.
	 */
	im__transform_inverse( &trn, xs5, ys5, &xs7, &ys7 );
	im__transform_inverse( &trn, xs6, ys6, &xs8, &ys8 );

	/* Recalc the transform using the refined points.
	 */
	if( im__coeff( xr1, yr1, xs7, ys7, xr2, yr2, xs8, ys8, 
		&a, &b, &dx, &dy ) )
		return( -1 );

	/* Scale and rotate final.
	 */
	if( jfn( ref, sec, out, a, b, dx, dy, mwidth ) )
		return( -1 );

	return( 0 );
}
int
main (int argc, char **argv) {
    const int NTMPS = 3;
	VipsImage *in, *out;
    VipsImage *tmps[NTMPS];
    INTMASK *mask;
    int stat;
    const char *ifile, *ofile;
    int extractTop = 100, extractBtm = 200;

    check(argc == 3, "Syntax: %s <input> <output>", argv[0]);
    ifile = argv[1];
    ofile = argv[2];

    timer_start(ifile, "Setup");

    if (im_init_world (argv[0])) error_exit ("unable to start VIPS");

    in = im_open( ifile, "r" );
    if (!in) vips_error_exit( "unable to read %s", ifile );
    check(in->Ysize > 5 && in->Xsize > 5,
          "Input image must be larger than 5 in both dimensions",
          extractBtm);

    stat = im_open_local_array(in, tmps, NTMPS, "tt", "p");
    check(!stat, "Unable to create temps.");

    mask = mk_convmat();

    timer_done();

    /* Reduce the extraction size if it's bigger than the image. */
    if (extractBtm + extractTop >= in->Ysize ||
        extractBtm + extractTop >= in->Xsize) {
        extractTop = 2;
        extractBtm = 2;
    }/* if */

    timer_start(ifile, "im_extract_area");
    check(
        !im_extract_area(in, tmps[0], extractTop, extractTop, in->Xsize - extractBtm,
                         in->Ysize - extractBtm),
        "extract failed.");
    timer_done();

    timer_start(ifile, "im_affine");
    check(
        !im_affine(tmps[0], tmps[1], 0.9, 0, 0, 0.9, 0, 0,
                   0, 0, in->Xsize * 0.9, in->Ysize * 0.9),
        "im_affine failed.");
    timer_done();

    timer_start(ifile, "im_conv");
    check(
        !im_conv (tmps[1], tmps[2], mask),
        "im_conv failed.");
    timer_done();
        
    timer_start(ofile, "writing output");
    out = im_open(ofile, "w");
    check(!!out, "file output failed.");

    im_copy(tmps[2], out);
    timer_done();
    

    timer_start(ofile, "teardown");
    im_close(out);
    im_close(in);
    timer_done();

    print_times();

    return 0;
}/* main */
Esempio n. 24
0
/* Start here!
 */
int
main( int argc, char **argv )
{	
	IMAGE *im = NULL;
	FILE *out = stdout;
	int width = -1;
	int height = -1;
	int dpi = -1;
	int max = 0;
	int rotate = 0;
	int one2one = 0;
	PrinterGeometry *geo = find_printer( "2500cp" );
	char *mode;
	int i;

	if( im_init_world( argv[0] ) )
	        error_exit( "unable to start VIPS" );

	argv0 = argv[0];

	if( argc <= 1 ) {
		printf( 
"usage:\n"
"\t%s [options] <image file>\n"
"convert RGB, LAB, CMYK and mono image files to postscript\n"
"\tRGB converted to LAB, assuming sRGB\n"
"\tLAB printed with printer colour management\n"
"\tCMYK sent directly as dot percent\n"
"\tmono prints as K only\n"
"options include:\n"
"\t-printer <name>\tformat for printer <name>\n"
"\t-3500cp\t\tfor HP 3500CP printer (default 2500cp)\n"
"\t-max\t\tprint as large as possible\n"
"\t-rotate\t\trotate, if necessary, to fill the page\n"
"\t-1:1\t\tsize the image to print at 1:1 ... resolution in\n"
"\t\t\timage header must be set for this\n"
"\t-width <n>\tforce specified width, in points\n"
"\t-height <n>\tforce specified height, in points\n"
"\t-dpi <n>\tforce specified resolution (default 150dpi)\n"
"\t-a5, -a4, -a3, -a2, -a1, -a0\n"
"\t\t\tforce specified height (width ignored)\n"
"\t-o <file>\toutput to file (default stdout)\n",
			argv0 );
		printf( "supported printers:\n" );
		print_printers();
		return( 1 );
	}

	/* Decode args .. just look for file names and our three options.
	 */
	for( i = 1; i < argc; i++ )
		if( *argv[i] == '-' ) {
			if( strcmp( argv[i]+1, "width" ) == 0 ) {
				if( !argv[i+1] || sscanf( argv[i+1], 
					"%d", &width ) != 1 || width <= 10 )
					error_exit( "bad width" );
				i++;
			}
			else if( strcmp( argv[i]+1, "height" ) == 0 ) {
				if( !argv[i+1] || sscanf( argv[i+1], 
					"%d", &height ) != 1 || height <= 10 )
					error_exit( "bad height" );
				i++;
			}
			else if( strcmp( argv[i]+1, "3500cp" ) == 0 ) {
				geo = find_printer( "3500cp" );
			}
			else if( strcmp( argv[i]+1, "printer" ) == 0 ) {
				if( !argv[i+1] || 
					!(geo = find_printer( argv[i+1] )) )
					error_exit( "bad printer model" );
				i++;
			}
			else if( strcmp( argv[i]+1, "dpi" ) == 0 ) {
				if( !argv[i+1] || sscanf( argv[i+1], 
					"%d", &dpi ) != 1 || dpi <= 1 ||
					dpi >= 600 )
					error_exit( "bad dpi" );
				i++;
			}
			else if( strcmp( argv[i]+1, "o" ) == 0 ) {
				if( !argv[i+1] || !(out = fopen( 
					argv[i+1], "w" )) )
					error_exit( "bad output name" );
				i++;
			}
			else if( strcmp( argv[i]+1, "1:1" ) == 0 ) 
				one2one = 1;	
			else if( strcmp( argv[i]+1, "a5" ) == 0 ) 
				height = 595;
			else if( strcmp( argv[i]+1, "a4" ) == 0 ) 
				height = 839;
			else if( strcmp( argv[i]+1, "a3" ) == 0 ) 
				height = 1187;
			else if( strcmp( argv[i]+1, "a2" ) == 0 ) 
				height = 1678;
			else if( strcmp( argv[i]+1, "a1" ) == 0 ) 
				height = 2373;
			else if( strcmp( argv[i]+1, "a0" ) == 0 ) 
				height = 3356;
			else if( strcmp( argv[i]+1, "max" ) == 0 ) 
				max = 1;
			else if( strcmp( argv[i]+1, "rotate" ) == 0 ) 
				rotate = 1;
			else
				error_exit( "bad flag" );
		}
		else {
			/* Try to open the file. 
			 */
			if( im != NULL || !(im = im_open( argv[i], "r" )) )
				error_exit( "bad input image" );
		}

	if( im == NULL ) 
		error_exit( "no input image" );

	/* Turn 3-band uchar images into LABQ. Yuk! But convenient.
	 */
	if( im->Coding == IM_CODING_NONE &&
		im->Bands == 3 && im->BandFmt == IM_BANDFMT_UCHAR ) {
		IMAGE *t[3];

		if( im_open_local_array( im, t, 3, "vips2dj", "p" ) ||
			im_sRGB2XYZ( im, t[0] ) ||
			im_XYZ2Lab( t[0], t[1] ) ||
			im_Lab2LabQ( t[1], t[2] ) )
			error_exit( "error converting to LAB" );

		im = t[2];
	}

	/* Stop used-before-set complaints on mode.
	 */
	mode = "lab";

	/* Pick a PS mode.
	 */
	if( im->Coding == IM_CODING_LABQ )
		mode = "lab";
	else if( im->Coding == IM_CODING_NONE && 
		im->Bands == 4 && im->BandFmt == IM_BANDFMT_UCHAR )
		mode = "cmyk";
	else if( im->Coding == IM_CODING_NONE && 
		im->Bands == 1 && im->BandFmt == IM_BANDFMT_UCHAR )
		mode = "mono";
	else 
		error_exit( "unsupported image type "
			"(IM_CODING_LABQ, mono, IM_TYPE_CMYK only)" );

	/* Autorotate image to fill the page. We ought to get PS to do the
	 * rotate, really.
	 */
	if( rotate ) {
		float iaspect = (float) im->Xsize / im->Ysize;
		float paspect = (float) geo->width / geo->length;

		if( iaspect > paspect ) {
			IMAGE *t[1];

			if( im_open_local_array( im, t, 1, "vips2dj", "p" ) ||
				im_rot90( im, t[0] ) )
				error_exit( "error rotating" );

			im = t[0];
		}
	}

	/* Make sure width and height are both set.
	 */
	if( one2one ) {
		/* Set width/height from res.
		 */
		if( im->Xres <= 0 || im->Xres >= 100 ||
			im->Yres <= 0 || im->Yres >= 100 )
			error_exit( "uanble to print 1:1 - resolution not "
				"set in image" );

		height = (((im->Ysize / im->Yres) / 10.0) / 2.54) * 72.0;
		width = (((im->Xsize / im->Xres) / 10.0) / 2.54) * 72.0;
	}
	else if( max ) {
		float iaspect = (float) im->Xsize / im->Ysize;
		float paspect = (float) geo->width / geo->length;

		if( iaspect > paspect ) 
			/* Image aspect ratio > paper ... fit width.
			 */
			width = geo->width;
		else
			height = geo->length;
	}
	else if( dpi > 0 ) {
		/* Given res ... set width/height.
		 */
		height = (im->Ysize / (float) dpi) * 72.0;
		width = (im->Xsize / (float) dpi) * 72.0;
	}

	if( width >= 0 || height >= 0 ) {
		/* Given width or height or both --- set other one.
		 */
		if( height < 0 ) {
			float fdpi = im->Xsize / (width / 72.0);
			height = (im->Ysize / fdpi) * 72.0;
		}
		else {
			float fdpi = im->Ysize / (height / 72.0);
			width = (im->Xsize / fdpi) * 72.0;
		}
	}
	else {
		/* Nothing set ... default to 150 dpi.
		 */
		height = (im->Ysize / 150.0) * 72.0;
		width = (im->Xsize / 150.0) * 72.0;
	}

	if( send_file( geo, im, mode, out, width, height ) )
		error_exit( "error sending file" );

	return( 0 );
}