int im_stdif_raw( IMAGE *in, IMAGE *out, double a, double m0, double b, double s0, int xwin, int ywin ) { StdifInfo *inf; if( xwin > in->Xsize || ywin > in->Ysize ) { im_error( "im_stdif", "%s", _( "window too large" ) ); return( -1 ); } if( xwin <= 0 || ywin <= 0 ) { im_error( "im_lhisteq", "%s", _( "window too small" ) ); return( -1 ); } if( m0 < 0 || m0 > 255 || a < 0 || a > 1.0 || b < 0 || b > 2 || s0 < 0 || s0 > 255 ) { im_error( "im_stdif", "%s", _( "parameters out of range" ) ); return( -1 ); } if( im_check_format( "im_stdif", in, IM_BANDFMT_UCHAR ) || im_check_uncoded( "im_stdif", in ) || im_check_mono( "im_stdif", in ) || im_piocheck( in, out ) ) return( -1 ); if( im_cp_desc( out, in ) ) return( -1 ); out->Xsize -= xwin; out->Ysize -= ywin; /* Save parameters. */ if( !(inf = IM_NEW( out, StdifInfo )) ) return( -1 ); inf->xwin = xwin; inf->ywin = ywin; inf->a = a; inf->m0 = m0; inf->b = b; inf->s0 = s0; /* 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 ); /* Write the hist. */ if( im_generate( out, im_start_one, stdif_gen, im_stop_one, in, inf ) ) return( -1 ); return( 0 ); }
/** * im_grad_x: * @in: input image * @out: output image * * Find horizontal differences between adjacent pixels. * * Generates an image where the value of each pixel is the difference between * it and the pixel to its right. The output has the same height as the input * and one pixel less width. One-band integer formats only. The result is * always %IM_BANDFMT_INT. * * This operation is much faster than (though equivalent to) im_conv() with the * mask [[-1, 1]]. * * See also: im_grad_y(), im_conv(). * * Returns: 0 on success, -1 on error */ int im_grad_x( IMAGE *in, IMAGE *out ){ #define FUNCTION_NAME "im_grad_x" if( im_piocheck( in, out ) ) return -1; if( im_check_uncoded( "im_grad_x", in ) || im_check_mono( "im_grad_x", in ) || im_check_int( "im_grad_x", in ) ) return( -1 ); if( im_cp_desc( out, in ) ) return -1; -- out-> Xsize; out-> BandFmt= IM_BANDFMT_INT; /* do not change without updating im_gradcor() */ if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) return -1; #define RETURN_GENERATE( TYPE ) return im_generate( out, im_start_one, xgrad_gen_ ## TYPE, im_stop_one, (void*) in, NULL ) switch( in-> BandFmt ){ case IM_BANDFMT_UCHAR: RETURN_GENERATE( guint8 ); case IM_BANDFMT_CHAR: RETURN_GENERATE( gint8 ); case IM_BANDFMT_USHORT: RETURN_GENERATE( guint16 ); case IM_BANDFMT_SHORT: RETURN_GENERATE( gint16 ); case IM_BANDFMT_UINT: RETURN_GENERATE( guint32 ); case IM_BANDFMT_INT: RETURN_GENERATE( gint32 ); #if 0 case IM_BANDFMT_FLOAT: RETURN_GENERATE( float ); case IM_BANDFMT_DOUBLE: RETURN_GENERATE( double ); #endif #undef RETURN_GENERATE default: g_assert( 0 ); } /* Keep gcc happy. */ return 0; #undef FUNCTION_NAME }
int im__arith_binary_const( const char *domain, IMAGE *in, IMAGE *out, int n, double *c, VipsBandFmt vfmt, int format_table[10], im_wrapone_fn fn1, im_wrapone_fn fnn ) { PEL *vector; if( im_piocheck( in, out ) || im_check_vector( domain, n, in ) || im_check_uncoded( domain, in ) ) return( -1 ); if( im_cp_desc( out, in ) ) return( -1 ); out->BandFmt = format_table[in->BandFmt]; /* Some operations need the vector in the input type (eg. * im_equal_vec() where the output type is always uchar and is useless * for comparisons), some need it in the output type (eg. * im_andimage_vec() where we want to get the double to an int so we * can do bitwise-and without having to cast for each pixel), some * need a fixed type (eg. im_powtra_vec(), where we want to keep it as * double). * * Therefore pass in the desired vector type as a param. */ if( !(vector = make_pixel( out, vfmt, n, c )) ) return( -1 ); /* Band-up the input image if we have a >1 vector and * a 1-band image. */ if( n > 1 && out->Bands == 1 ) { IMAGE *t; if( !(t = im_open_local( out, domain, "p" )) || im__bandup( domain, in, t, n ) ) return( -1 ); in = t; } if( n == 1 ) { if( im_wrapone( in, out, fn1, vector, in ) ) return( -1 ); } else { if( im_wrapone( in, out, fnn, vector, in ) ) return( -1 ); } return( 0 ); }
int im_contrast_surface_raw (IMAGE * in, IMAGE * out, int half_win_size, int spacing) { #define FUNCTION_NAME "im_contrast_surface_raw" cont_surf_params_t *params; if (im_piocheck (in, out) || im_check_uncoded (FUNCTION_NAME, in) || im_check_mono (FUNCTION_NAME, in) || im_check_format (FUNCTION_NAME, in, IM_BANDFMT_UCHAR)) return -1; if (half_win_size < 1 || spacing < 1) { im_error (FUNCTION_NAME, "%s", _("bad parameters")); return -1; } if (DOUBLE (half_win_size) >= LESSER (in->Xsize, in->Ysize)) { im_error (FUNCTION_NAME, "%s", _("parameters would result in zero size output image")); return -1; } params = IM_NEW (out, cont_surf_params_t); if (!params) return -1; params->half_win_size = half_win_size; params->spacing = spacing; if (im_cp_desc (out, in)) return -1; out->BandFmt = IM_BANDFMT_UINT; out->Xsize = 1 + ((in->Xsize - DOUBLE_ADD_ONE (half_win_size)) / spacing); out->Ysize = 1 + ((in->Ysize - DOUBLE_ADD_ONE (half_win_size)) / spacing); out->Xoffset = -half_win_size; out->Yoffset = -half_win_size; if (im_demand_hint (out, IM_FATSTRIP, in, NULL)) return -1; return im_generate (out, im_start_one, cont_surf_gen, im_stop_one, in, params); #undef FUNCTION_NAME }
int im_gradcor_raw( IMAGE *large, IMAGE *small, IMAGE *out ){ #define FUNCTION_NAME "im_gradcor_raw" if( im_piocheck( large, out ) || im_pincheck( small ) ) return -1; if( im_check_uncoded( "im_gradcor", large ) || im_check_mono( "im_gradcor", large ) || im_check_uncoded( "im_gradcor", small ) || im_check_mono( "im_gradcor", small ) || im_check_format_same( "im_gradcor", large, small ) || im_check_int( "im_gradcor", large ) ) return( -1 ); if( large-> Xsize < small-> Xsize || large-> Ysize < small-> Ysize ){ im_error( FUNCTION_NAME, "second image must be smaller than first" ); return -1; } if( im_cp_desc( out, large ) ) return -1; out-> Xsize= 1 + large-> Xsize - small-> Xsize; out-> Ysize= 1 + large-> Ysize - small-> Ysize; out-> BandFmt= IM_BANDFMT_FLOAT; if( im_demand_hint( out, IM_FATSTRIP, large, NULL ) ) return -1; { IMAGE *xgrad= im_open_local( out, FUNCTION_NAME ": xgrad", "t" ); IMAGE *ygrad= im_open_local( out, FUNCTION_NAME ": ygrad", "t" ); IMAGE **grads= im_allocate_input_array( out, xgrad, ygrad, NULL ); return ! xgrad || ! ygrad || ! grads || im_grad_x( small, xgrad ) || im_grad_y( small, ygrad ) || im_generate( out, gradcor_start, gradcor_gen, gradcor_stop, (void*) large, (void*) grads ); } #undef FUNCTION_NAME }
/* Normalise an image using the rules noted above. */ static int normalise( IMAGE *in, IMAGE *out ) { if( im_check_uncoded( "im_histplot", in ) || im_check_noncomplex( "im_histplot", in ) ) return( -1 ); if( vips_bandfmt_isuint( in->BandFmt ) ) { if( im_copy( in, out ) ) return( -1 ); } else if( vips_bandfmt_isint( in->BandFmt ) ) { IMAGE *t1; double min; /* Move min up to 0. */ if( !(t1 = im_open_local( out, "im_histplot", "p" )) || im_min( in, &min ) || im_lintra( 1.0, in, -min, t1 ) ) return( -1 ); } else { /* Float image: scale min--max to 0--any. Output square * graph. */ IMAGE *t1; DOUBLEMASK *stats; double min, max; int any; if( in->Xsize == 1 ) any = in->Ysize; else any = in->Xsize; if( !(stats = im_stats( in )) ) return( -1 ); min = VIPS_MASK( stats, 0, 0 ); max = VIPS_MASK( stats, 1, 0 ); im_free_dmask( stats ); if( !(t1 = im_open_local( out, "im_histplot", "p" )) || im_lintra( any / (max - min), in, -min * any / (max - min), out ) ) return( -1 ); } return( 0 ); }
/** * im_c2amph: * @in: input image * @out: output image * * Convert a complex image from rectangular to polar coordinates. Angles are * expressed in degrees. * * See also: im_c2rect(), im_abs(). * * Returns: 0 on success, -1 on error */ int im_c2amph( IMAGE *in, IMAGE *out ) { if( im_check_uncoded( "im_c2amph", in ) || im_check_complex( "im_c2amph", in ) || im_cp_desc( out, in ) ) return( -1 ); if( im_wrapone( in, out, (im_wrapone_fn) buffer_c2amph, in, NULL ) ) return( -1 ); return( 0 ); }
/** * im_abs: * @in: input #IMAGE * @out: output #IMAGE * * This operation finds the absolute value of an image. It does a copy for * unsigned integer types, negate for negative values in * signed integer types, <function>fabs(3)</function> for * float types, and calculate modulus for complex * types. * * See also: im_exp10tra(), im_sign(). * * Returns: 0 on success, -1 on error */ int im_abs( IMAGE *in, IMAGE *out ) { if( im_piocheck( in, out ) || im_check_uncoded( "im_abs", in ) ) return( -1 ); /* Is this one of the unsigned types? Degenerate to im_copy() if it * is. */ if( vips_bandfmt_isuint( in->BandFmt ) ) return( im_copy( in, out ) ); /* Prepare output header. Output type == input type, except for * complex. */ if( im_cp_desc( out, in ) ) return( -1 ); switch( in->BandFmt ) { case IM_BANDFMT_CHAR: case IM_BANDFMT_SHORT: case IM_BANDFMT_INT: case IM_BANDFMT_FLOAT: case IM_BANDFMT_DOUBLE: /* No action. */ break; case IM_BANDFMT_COMPLEX: out->BandFmt = IM_BANDFMT_FLOAT; break; case IM_BANDFMT_DPCOMPLEX: out->BandFmt = IM_BANDFMT_DOUBLE; break; default: im_error( "im_abs", "%s", _( "unknown input type" ) ); return( -1 ); } /* Generate! */ if( im_wrapone( in, out, (im_wrapone_fn) abs_gen, in, NULL ) ) return( -1 ); return( 0 ); }
int im_lhisteq_raw( IMAGE *in, IMAGE *out, int xwin, int ywin ) { LhistInfo *inf; if( im_check_mono( "im_lhisteq", in ) || im_check_uncoded( "im_lhisteq", in ) || im_check_format( "im_lhisteq", in, IM_BANDFMT_UCHAR ) || im_piocheck( in, out ) ) return( -1 ); if( xwin > in->Xsize || ywin > in->Ysize ) { im_error( "im_lhisteq", "%s", _( "window too large" ) ); return( -1 ); } if( xwin <= 0 || ywin <= 0 ) { im_error( "im_lhisteq", "%s", _( "window too small" ) ); return( -1 ); } if( im_cp_desc( out, in ) ) return( -1 ); out->Xsize -= xwin - 1; out->Ysize -= ywin - 1; /* Save parameters. */ if( !(inf = IM_NEW( out, LhistInfo )) ) return( -1 ); inf->xwin = xwin; inf->ywin = ywin; inf->npels = xwin * ywin; /* 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 ); if( im_generate( out, im_start_one, lhist_gen, im_stop_one, in, inf ) ) return( -1 ); out->Xoffset = -xwin / 2; out->Yoffset = -xwin / 2; return( 0 ); }
/** * im_circle: * @im: image to draw on * @cx: centre of circle * @cy: centre of circle * @radius: circle radius * @intensity: value to draw * * Draws a circle on a 1-band 8-bit image. * * This an inplace operation, so @im is changed. It does not thread and will * not work well as part of a pipeline. On 32-bit machines it will be limited * to 2GB images. * * See also: im_fastline(). * * Returns: 0 on success, or -1 on error. */ int im_circle( IMAGE *im, int cx, int cy, int radius, int intensity ) { PEL ink[1]; if( im_rwcheck( im ) || im_check_uncoded( "im_circle", im ) || im_check_mono( "im_circle", im ) || im_check_format( "im_circle", im, IM_BANDFMT_UCHAR ) ) return( -1 ); ink[0] = intensity; return( im_draw_circle( im, cx, cy, radius, FALSE, ink ) ); }
/** * im_zerox: * @in: input image * @out: output image * @sign: detect positive or negative zero crossings * * im_zerox() detects the positive or negative zero crossings @in, * depending on @sign. If @sign is -1, negative zero crossings are returned, * if @sign is 1, positive zero crossings are returned. * * The output image is byte with zero crossing set to 255 and all other values * set to zero. Input can have any number of channels, and be any non-complex * type. * * See also: im_conv(), im_rot90. * * Returns: 0 on success, -1 on error */ int im_zerox( IMAGE *in, IMAGE *out, int sign ) { IMAGE *t1; if( sign != -1 && sign != 1 ) { im_error( "im_zerox", "%s", _( "flag not -1 or 1" ) ); return( -1 ); } if( in->Xsize < 2 ) { im_error( "im_zerox", "%s", _( "image too narrow" ) ); return( -1 ); } if( !(t1 = im_open_local( out, "im_zerox" , "p" )) || im_piocheck( in, t1 ) || im_check_uncoded( "im_zerox", in ) || im_check_noncomplex( "im_zerox", in ) ) return( -1 ); if( vips_bandfmt_isuint( in->BandFmt ) ) /* Unsigned type, therefore there will be no zero-crossings. */ return( im_black( out, in->Xsize, in->Ysize, in->Bands ) ); /* Force output to be BYTE. Output is narrower than input by 1 pixel. */ if( im_cp_desc( t1, in ) ) return( -1 ); t1->BandFmt = IM_BANDFMT_UCHAR; t1->Xsize -= 1; /* Set hints - THINSTRIP is ok with us. */ if( im_demand_hint( t1, IM_THINSTRIP, NULL ) ) return( -1 ); /* Generate image. */ if( im_generate( t1, im_start_one, zerox_gen, im_stop_one, in, GINT_TO_POINTER( sign ) ) ) return( -1 ); /* Now embed it in a larger image. */ if( im_embed( t1, out, 0, 0, 0, in->Xsize, in->Ysize ) ) return( -1 ); return( 0 ); }
/* Raw fastcor, with no borders. */ int im_fastcor_raw( IMAGE *in, IMAGE *ref, IMAGE *out ) { /* PIO between in and out; WIO from ref. */ if( im_piocheck( in, out ) || im_incheck( ref ) ) return( -1 ); /* Check sizes. */ if( in->Xsize < ref->Xsize || in->Ysize < ref->Ysize ) { im_error( "im_fastcor", "%s", _( "ref not smaller than or equal to in" ) ); return( -1 ); } /* Check types. */ if( im_check_uncoded( "im_fastcor", in ) || im_check_mono( "im_fastcor", in ) || im_check_format( "im_fastcor", in, IM_BANDFMT_UCHAR ) || im_check_coding_same( "im_fastcor", in, ref ) || im_check_bands_same( "im_fastcor", in, ref ) || im_check_format_same( "im_fastcor", in, ref ) ) return( -1 ); /* Prepare the output image. */ if( im_cp_descv( out, in, ref, NULL ) ) return( -1 ); out->BandFmt = IM_BANDFMT_UINT; out->Xsize = in->Xsize - ref->Xsize + 1; out->Ysize = in->Ysize - ref->Ysize + 1; /* FATSTRIP is good for us, as THINSTRIP will cause * too many recalculations on overlaps. */ if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) || im_generate( out, im_start_one, fastcor_gen, im_stop_one, in, ref ) ) return( -1 ); out->Xoffset = -ref->Xsize / 2; out->Yoffset = -ref->Ysize / 2; return( 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 ) ); }
/** * im_copy_swap: * @in: input image * @out: output image * * Copy an image, swapping byte order between little and big endian. This * really does change image pixels and does not just alter the header. * * See also: im_copy(), im_amiMSBfirst(), im_isMSBfirst(). * * Returns: 0 on success, -1 on error. */ int im_copy_swap( IMAGE *in, IMAGE *out ) { if( im_piocheck( in, out ) || im_check_uncoded( "im_copy_swap", in ) || im_cp_desc( out, in ) ) return( -1 ); switch( in->BandFmt ) { case IM_BANDFMT_CHAR: case IM_BANDFMT_UCHAR: if( im_copy( in, out ) ) return( -1 ); break; case IM_BANDFMT_SHORT: case IM_BANDFMT_USHORT: if( im_wrapone( in, out, (im_wrapone_fn) im_copy_swap2_gen, in, NULL ) ) return( -1 ); break; case IM_BANDFMT_INT: case IM_BANDFMT_UINT: case IM_BANDFMT_FLOAT: case IM_BANDFMT_COMPLEX: if( im_wrapone( in, out, (im_wrapone_fn) im_copy_swap4_gen, in, NULL ) ) return( -1 ); break; case IM_BANDFMT_DOUBLE: case IM_BANDFMT_DPCOMPLEX: if( im_wrapone( in, out, (im_wrapone_fn) im_copy_swap8_gen, in, NULL ) ) return( -1 ); break; default: im_error( "im_copy_swap", "%s", _( "unsupported image type" ) ); return( -1 ); } return( 0 ); }
/** * im_measure_area: * @im: image to measure * @left: area of image containing chart * @top: area of image containing chart * @width: area of image containing chart * @height: area of image containing chart * @h: patches across chart * @v: patches down chart * @sel: array of patch numbers to measure (numbered from 1 in row-major order) * @nsel: length of patch number array * @name: name to give to returned @DOUBLEMASK * * Analyse a grid of colour patches, producing a #DOUBLEMASK of patch averages. * The mask has a row for each measured patch, and a column for each image * band. The operations issues a warning if any patch has a deviation more * than 20% of * the mean. Only the central 50% of each patch is averaged. If @sel is %NULL * then all patches are measured. * * Example: 6 band image of 4x2 block of colour patches. * * <tgroup cols='4' align='left' colsep='1' rowsep='1'> * <tbody> * <row> * <entry>1</entry> * <entry>2</entry> * <entry>3</entry> * <entry>4</entry> * </row> * <row> * <entry>5</entry> * <entry>6</entry> * <entry>7</entry> * <entry>8</entry> * </row> * </tbody> * </tgroup> * * Then call im_measure( im, box, 4, 2, { 2, 4 }, 2, "fred" ) makes a mask * "fred" which has 6 columns, two rows. The first row contains the averages * for patch 2, the second for patch 4. * * See also: im_avg(), im_deviate(), im_stats(). * * Returns: #DOUBLEMASK of measurements. */ DOUBLEMASK * im_measure_area( IMAGE *im, int left, int top, int width, int height, int u, int v, int *sel, int nsel, const char *name ) { DOUBLEMASK *mask; /* Check input image. */ if( im->Coding == IM_CODING_LABQ ) { IMAGE *t1; if( !(t1 = im_open( "measure-temp", "p" )) ) return( NULL ); if( im_LabQ2Lab( im, t1 ) || !(mask = im_measure_area( t1, left, top, width, height, u, v, sel, nsel, name )) ) { im_close( t1 ); return( NULL ); } im_close( t1 ); return( mask ); } if( im_check_uncoded( "im_measure", im ) || im_check_noncomplex( "im_measure", im ) ) return( NULL ); /* Default to all patches if sel == NULL. */ if( sel == NULL ) { int i; nsel = u * v; if( !(sel = IM_ARRAY( im, nsel, int )) ) return( NULL ); for( i = 0; i < nsel; i++ ) sel[i] = i + 1; }
static Mask * mask_new( VipsImage *im, int x, int y, PEL *ink, VipsImage *mask_im ) { Mask *mask; Rect area, image; if( im_check_coding_noneorlabq( "im_draw_mask", im ) || im_incheck( mask_im ) || im_check_mono( "im_draw_mask", mask_im ) || im_check_uncoded( "im_draw_mask", mask_im ) || im_check_format( "im_draw_mask", mask_im, IM_BANDFMT_UCHAR ) || !(mask = IM_NEW( NULL, Mask )) ) return( NULL ); if( !im__draw_init( DRAW( mask ), im, ink ) ) { mask_free( mask ); return( NULL ); } mask->x = x; mask->y = y; mask->mask_im = mask_im; /* Find the area we draw on the image. */ area.left = x; area.top = y; area.width = mask_im->Xsize; area.height = mask_im->Ysize; image.left = 0; image.top = 0; image.width = im->Xsize; image.height = im->Ysize; im_rect_intersectrect( &area, &image, &mask->image_clip ); /* And the area of the mask image we use. */ mask->mask_clip = mask->image_clip; mask->mask_clip.left -= x; mask->mask_clip.top -= y; return( mask ); }
/** * im_maxpos_avg: * @im: image to scan * @xpos: returned X position * @ypos: returned Y position * @out: returned value * * Function to find the maximum of an image. Returns coords and value at * double precision. In the event of a draw, returns average of all * drawing coords. * * See also: im_maxpos(), im_min(), im_stats(). * * Returns: 0 on success, -1 on error */ int im_maxpos_avg( IMAGE *in, double *xpos, double *ypos, double *out ) { Maxposavg *global_maxposavg; if( im_pincheck( in ) || im_check_uncoded( "im_maxpos_avg", in ) ) return( -1 ); if( !(global_maxposavg = IM_NEW( in, Maxposavg )) ) return( -1 ); if( im__value( in, &global_maxposavg->max ) ) return( -1 ); global_maxposavg->xpos = 0; global_maxposavg->ypos = 0; global_maxposavg->occurences = 1; /* We use square mod for scanning, for speed. */ if( vips_band_format_iscomplex( in->BandFmt ) ) global_maxposavg->max *= global_maxposavg->max; if( vips_sink( in, maxposavg_start, maxposavg_scan, maxposavg_stop, in, global_maxposavg ) ) return( -1 ); /* Back to modulus. */ if( vips_band_format_iscomplex( in->BandFmt ) ) global_maxposavg->max = sqrt( global_maxposavg->max ); if( xpos ) *xpos = (double) global_maxposavg->xpos / global_maxposavg->occurences; if( ypos ) *ypos = (double) global_maxposavg->ypos / global_maxposavg->occurences; if( out ) *out = global_maxposavg->max; return( 0 ); }
/** * im_c2real: * @in: input image * @out: output image * * Extract the real part of a complex image. * * See also: im_c2imag(). * * Returns: 0 on success, -1 on error */ int im_c2real( IMAGE *in, IMAGE *out ) { if( im_check_uncoded( "im_c2real", in ) || im_check_complex( "im_c2real", in ) || im_cp_desc( out, in ) ) return( -1 ); /* Output will be float or double. */ if( in->BandFmt == IM_BANDFMT_DPCOMPLEX ) out->BandFmt = IM_BANDFMT_DOUBLE; else out->BandFmt = IM_BANDFMT_FLOAT; if( im_wrapone( in, out, (im_wrapone_fn) buffer_c2real, in, NULL ) ) return( -1 ); return( 0 ); }
/** * im_vips2csv: * @in: image to save * @filename: file to write to * * Save a CSV (comma-separated values) file. The image is written * one line of text per scanline. Complex numbers are written as * "(real,imaginary)" and will need extra parsing I guess. The image must * have a single band. * * Write options can be embedded in the filename. The options can be given * in any order and are: * * <itemizedlist> * <listitem> * <para> * <emphasis>sep:separator-string</emphasis> * The string to use to separate numbers in the output. * The default is "\\t" (tab). * </para> * </listitem> * </itemizedlist> * * For example: * * |[ * im_csv2vips( in, "fred.csv:sep:\t" ); * ]| * * Will write to fred.csv, separating numbers with tab characters. * * See also: #VipsFormat, im_csv2vips(), im_write_dmask(), im_vips2ppm(). * * Returns: 0 on success, -1 on error. */ int im_vips2csv( IMAGE *in, const char *filename ) { char *separator = "\t"; char name[FILENAME_MAX]; char mode[FILENAME_MAX]; FILE *fp; char *p, *q, *r; /* Parse mode string. */ im_filename_split( filename, name, mode ); p = &mode[0]; while( (q = im_getnextoption( &p )) ) { if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) ) separator = r; } if( im_incheck( in ) || im_check_mono( "im_vips2csv", in ) || im_check_uncoded( "im_vips2csv", in ) ) return( -1 ); if( !(fp = fopen( name, "w" )) ) { im_error( "im_cvips2csv", _( "unable to open \"%s\"" ), name ); return( -1 ); } if( vips2csv( in, fp, separator ) ) { fclose( fp ); return( -1 ); } fclose( fp ); return( 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 ); }
static DOUBLEMASK * internal_im_measure_area( IMAGE *im, int left, int top, int width, int height, int u, int v, int *sel, int nsel, const char *name ) { DOUBLEMASK *mask; if( im_check_uncoded( "im_measure", im ) || im_check_noncomplex( "im_measure", im ) ) return( NULL ); /* Default to all patches if sel == NULL. */ if( sel == NULL ) { int i; nsel = u * v; if( !(sel = IM_ARRAY( im, nsel, int )) ) return( NULL ); for( i = 0; i < nsel; i++ ) sel[i] = i + 1; }
/** * im_profile: * @in: input image * @out: output image * @dir: search direction * * im_profile() searches inward from the edge of @in and finds the * first non-zero pixel. It outputs an image containing a list of the offsets * for each row or column. * * If @dir == 0, then im_profile() searches down from the top edge, writing an * image as wide as the input image, but only 1 pixel high, containing the * number of pixels down to the first non-zero pixel for each column of input * pixels. * * If @dir == 1, then im_profile() searches across from the left edge, * writing an image as high as the input image, but only 1 pixel wide, * containing the number of pixels across to the * first non-zero pixel for each row of input pixels. * * See also: im_cntlines(). * * Returns: 0 on success, -1 on error */ int im_profile( IMAGE *in, IMAGE *out, int dir ) { int sz; unsigned short *buf; int x, y, b; /* If in is not uchar, do (!=0) to make a uchar image. */ if( in->BandFmt != IM_BANDFMT_UCHAR ) { IMAGE *t; if( !(t = im_open_local( out, "im_profile", "p" )) || im_notequalconst( in, t, 0 ) ) return( -1 ); in = t; } /* Check im. */ if( im_iocheck( in, out ) || im_check_uncoded( "im_profile", in ) || im_check_format( "im_profile", in, IM_BANDFMT_UCHAR ) ) return( -1 ); if( dir != 0 && dir != 1 ) { im_error( "im_profile", "%s", _( "dir not 0 or 1" ) ); return( -1 ); } if( im_cp_desc( out, in ) ) return( -1 ); out->Type = IM_TYPE_HISTOGRAM; if( dir == 0 ) { out->Xsize = in->Xsize; out->Ysize = 1; } else { out->Xsize = 1; out->Ysize = in->Ysize; } out->BandFmt = IM_BANDFMT_USHORT; if( im_setupout( out ) ) return( -1 ); sz = IM_IMAGE_N_ELEMENTS( out ); if( !(buf = IM_ARRAY( out, sz, unsigned short )) ) return( -1 ); if( dir == 0 ) { /* Find vertical lines. */ for( x = 0; x < sz; x++ ) { PEL *p = (PEL *) IM_IMAGE_ADDR( in, 0, 0 ) + x; int lsk = IM_IMAGE_SIZEOF_LINE( in ); for( y = 0; y < in->Ysize; y++ ) { if( *p ) break; p += lsk; } buf[x] = y; } if( im_writeline( 0, out, (PEL *) buf ) ) return( -1 ); } else { /* Search horizontal lines. */ for( y = 0; y < in->Ysize; y++ ) { PEL *p = (PEL *) IM_IMAGE_ADDR( in, 0, y ); for( b = 0; b < in->Bands; b++ ) { PEL *p1; p1 = p + b; for( x = 0; x < in->Xsize; x++ ) { if( *p1 ) break; p1 += in->Bands; } buf[b] = x; } if( im_writeline( y, out, (PEL *) buf ) ) return( -1 ); } } return( 0 ); }
static Morph * morph_new( IMAGE *in, IMAGE *out, INTMASK *mask, MorphOp op ) { const int n_mask = mask->xsize * mask->ysize; Morph *morph; int i; /* If in is not uchar, do (!=0) to make a uchar image. */ if( in->BandFmt != IM_BANDFMT_UCHAR ) { IMAGE *t; if( !(t = im_open_local( out, "morph_new", "p" )) || im_notequalconst( in, t, 0 ) ) return( NULL ); in = t; } if( im_piocheck( in, out ) || im_check_uncoded( "morph", in ) || im_check_format( "morph", in, IM_BANDFMT_UCHAR ) || im_check_imask( "morph", mask ) ) return( NULL ); for( i = 0; i < n_mask; i++ ) if( mask->coeff[i] != 0 && mask->coeff[i] != 128 && mask->coeff[i] != 255 ) { im_error( "morph", _( "bad mask element (%d " "should be 0, 128 or 255)" ), mask->coeff[i] ); return( NULL ); } if( !(morph = IM_NEW( out, Morph )) ) return( NULL ); morph->in = in; morph->out = out; morph->mask = NULL; morph->op = op; morph->n_pass = 0; for( i = 0; i < MAX_PASS; i++ ) morph->pass[i].vector = NULL; if( im_add_close_callback( out, (im_callback_fn) morph_close, morph, NULL ) || !(morph->mask = im_dup_imask( mask, "morph" )) ) return( NULL ); /* Generate code for this mask / image, if possible. */ if( vips_vector_isenabled() ) { if( pass_compile( morph ) ) pass_free( morph ); } return( morph ); }
static VipsFits * vips_fits_new_write( VipsImage *in, const char *filename ) { VipsImage *flip; VipsImage *type; VipsFits *fits; int status; status = 0; if( im_check_noncomplex( "im_vips2fits", in ) || im_check_uncoded( "im_vips2fits", in ) ) return( NULL ); /* Cast to a supported format. */ if( !(type = vips_image_new()) || vips_object_local( in, type ) || im_clip2fmt( in, type, vips_fits_bandfmt[in->BandFmt] ) ) return( NULL ); in = type; /* FITS has (0,0) in the bottom left, we need to flip. */ if( !(flip = vips_image_new()) || vips_object_local( in, flip ) || im_flipver( in, flip ) ) return( NULL ); in = flip; if( !(fits = VIPS_NEW( in, VipsFits )) ) return( NULL ); fits->filename = im_strdup( NULL, filename ); fits->image = in; fits->fptr = NULL; fits->lock = NULL; fits->band_select = -1; fits->buffer = NULL; g_signal_connect( in, "close", G_CALLBACK( vips_fits_close_cb ), fits ); if( !(fits->filename = im_strdup( NULL, filename )) ) return( NULL ); /* We need to be able to hold one scanline of one band. */ if( !(fits->buffer = VIPS_ARRAY( NULL, VIPS_IMAGE_SIZEOF_ELEMENT( in ) * in->Xsize, PEL )) ) return( NULL ); /* fits_create_file() will fail if there's a file of thet name, unless * we put a "!" in front ofthe filename. This breaks conventions with * the rest of vips, so just unlink explicitly. */ g_unlink( filename ); if( fits_create_file( &fits->fptr, filename, &status ) ) { im_error( "fits", _( "unable to write to \"%s\"" ), filename ); vips_fits_error( status ); return( NULL ); } fits->lock = g_mutex_new(); return( fits ); }
/* 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 ); }
/** * im_vips2mask: * @in: input image * @filename: name for output mask * * Make a mask from an image. All images are cast to %IM_BANDFMT_DOUBLE * before processing. There are two cases for handling bands: * * If the image has a single band, im_vips2mask() will write a mask the same * size as the image. * * If the image has more than one band, it must be one pixel high or wide. In * this case the output mask uses that axis to represent band values. * * See also: im_mask2vips(), im_measure_area(). * * Returns: a #DOUBLEMASK with @outname set as the name, or NULL on error */ DOUBLEMASK * im_vips2mask( IMAGE *in, const char *filename ) { int width, height; DOUBLEMASK *out; /* double* only: cast if necessary. */ if( in->BandFmt != IM_BANDFMT_DOUBLE ) { IMAGE *t; if( !(t = im_open( "im_vips2mask", "p" )) ) return( NULL ); if( im_clip2fmt( in, t, IM_BANDFMT_DOUBLE ) || !(out = im_vips2mask( t, filename )) ) { im_close( t ); return( NULL ); } im_close( t ); return( out ); } /* Check the image. */ if( im_incheck( in ) || im_check_uncoded( "im_vips2mask", in ) ) return( NULL ); if( in->Bands == 1 ) { width = in->Xsize; height = in->Ysize; } else if( in->Xsize == 1 ) { width = in->Bands; height = in->Ysize; } else if( in->Ysize == 1 ) { width = in->Xsize; height = in->Bands; } else { im_error( "im_vips2mask", "%s", _( "one band, nx1, or 1xn images only" ) ); return( NULL ); } if( !(out = im_create_dmask( filename, width, height )) ) return( NULL ); if( in->Bands > 1 && in->Ysize == 1 ) { double *data = (double *) in->data; int x, y; /* Need to transpose: the image is RGBRGBRGB, we need RRRGGGBBB. */ for( y = 0; y < height; y++ ) for( x = 0; x < width; x++ ) out->coeff[x + y * width] = data[x * height + y]; } else memcpy( out->coeff, in->data, width * height * sizeof( double ) ); out->scale = vips_image_get_scale( in ); out->offset = vips_image_get_offset( in ); return( out ); }
INTMASK * im_vips2imask( IMAGE *in, const char *filename ) { int width, height; INTMASK *out; double *data; int x, y; double double_result; int int_result; /* double* only: cast if necessary. */ if( in->BandFmt != IM_BANDFMT_DOUBLE ) { IMAGE *t; if( !(t = im_open( "im_vips2imask", "p" )) ) return( NULL ); if( im_clip2fmt( in, t, IM_BANDFMT_DOUBLE ) || !(out = im_vips2imask( t, filename )) ) { im_close( t ); return( NULL ); } im_close( t ); return( out ); } /* Check the image. */ if( im_incheck( in ) || im_check_uncoded( "im_vips2imask", in ) ) return( NULL ); if( in->Bands == 1 ) { width = in->Xsize; height = in->Ysize; } else if( in->Xsize == 1 ) { width = in->Bands; height = in->Ysize; } else if( in->Ysize == 1 ) { width = in->Xsize; height = in->Bands; } else { im_error( "im_vips2imask", "%s", _( "one band, nx1, or 1xn images only" ) ); return( NULL ); } data = (double *) in->data; if( !(out = im_create_imask( filename, width, height )) ) return( NULL ); /* We want to make an intmask which has the same input to output ratio * as the double image. * * Imagine convolving with the double image, what's the ratio of * brightness between input and output? We want the same ratio for the * int version, if we can. * * Imaging an input image where every pixel is 1, what will the output * be? */ double_result = 0; for( y = 0; y < height; y++ ) for( x = 0; x < width; x++ ) double_result += data[x + width * y]; double_result /= vips_image_get_scale( in ); for( y = 0; y < height; y++ ) for( x = 0; x < width; x++ ) if( in->Bands > 1 && in->Ysize == 1 ) /* Need to transpose: the image is RGBRGBRGB, * we need RRRGGGBBB. */ out->coeff[x + y * width] = VIPS_RINT( data[x * height + y] ); else out->coeff[x + y * width] = VIPS_RINT( data[x + y * width] ); out->scale = VIPS_RINT( vips_image_get_scale( in ) ); if( out->scale == 0 ) out->scale = 1; out->offset = VIPS_RINT( vips_image_get_offset( in ) ); /* Now convolve a 1 everywhere image with the int version we've made, * what do we get? */ int_result = 0; for( y = 0; y < height; y++ ) for( x = 0; x < width; x++ ) int_result += out->coeff[x + width * y]; int_result /= out->scale; /* And adjust the scale to get as close to a match as we can. */ out->scale = VIPS_RINT( out->scale + (int_result - double_result) ); if( out->scale == 0 ) out->scale = 1; return( out ); }
/* 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 ); }