/** * im_lab_morph: * @in: input image * @out: output image * @mask: cast correction table * @L_offset: L adjustment * @L_scale: L adjustment * @a_scale: a scale * @b_scale: b scale * * Morph an image in CIELAB colour space. Useful for certain types of gamut * mapping, or correction of greyscales on some printers. * * We perform three adjustments: * * <itemizedlist> * <listitem> * <para> * <emphasis>cast</emphasis> * * Pass in @mask containing CIELAB readings for a neutral greyscale. For * example: * * <tgroup cols='3' align='left' colsep='1' rowsep='1'> * <tbody> * <row> * <entry>3</entry> * <entry>4</entry> * </row> * <row> * <entry>14.23</entry> * <entry>4.8</entry> * <entry>-3.95</entry> * </row> * <row> * <entry>18.74</entry> * <entry>2.76</entry> * <entry>-2.62</entry> * </row> * <row> * <entry>23.46</entry> * <entry>1.4</entry> * <entry>-1.95</entry> * </row> * <row> * <entry>27.53</entry> * <entry>1.76</entry> * <entry>-2.01</entry> * </row> * </tbody> * </tgroup> * * Interpolation from this makes cast corrector. The top and tail are * interpolated towards [0, 0, 0] and [100, 0, 0], intermediate values are * interpolated along straight lines fitted between the specified points. * Rows may be in any order (ie. they need not be sorted on L*). * * Each pixel is displaced in a/b by the amount specified for that L in the * table. * </para> * </listitem> * <listitem> * <para> * <emphasis>L*</emphasis> * * Pass in scale and offset for L. L' = (L + offset) * scale. * </para> * </listitem> * <listitem> * <para> * <emphasis>saturation</emphasis> * * scale a and b by these amounts, eg. 1.5 increases saturation. * </para> * </listitem> * </itemizedlist> * * Find the top two by generating and printing a greyscale. Find the bottom * by printing a Macbeth and looking at a/b spread * * Returns: 0 on success, -1 on error. */ int im_lab_morph( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, double L_offset, double L_scale, double a_scale, double b_scale ) { Params *parm; /* Recurse for coded images. */ if( in->Coding == IM_CODING_LABQ ) { IMAGE *t[2]; if( im_open_local_array( out, t, 2, "im_lab_morph", "p" ) || im_LabQ2Lab( in, t[0] ) || im_lab_morph( t[0], t[1], mask, L_offset, L_scale, a_scale, b_scale ) || im_Lab2LabQ( t[1], out ) ) return( -1 ); return( 0 ); } if( !(parm = IM_NEW( out, Params )) || morph_init( parm, in, out, L_scale, L_offset, mask, a_scale, b_scale ) ) return( -1 ); return( im__colour_unary( "im_lab_morph", in, out, IM_TYPE_LAB, (im_wrapone_fn) morph_buffer, parm, NULL ) ); }
/* Morph an image. */ int im_lab_morph( IMAGE *in, IMAGE *out, DOUBLEMASK *mask, double L_offset, double L_scale, double a_scale, double b_scale ) { Params *parm; /* Recurse for coded images. */ if( in->Coding == IM_CODING_LABQ ) { IMAGE *t1 = im_open_local( out, "im_lab_morph:1", "p" ); IMAGE *t2 = im_open_local( out, "im_lab_morph:2", "p" ); if( !t1 || !t2 || im_LabQ2Lab( in, t1 ) || im_lab_morph( t1, t2, mask, L_offset, L_scale, a_scale, b_scale ) || im_Lab2LabQ( t2, out ) ) return( -1 ); return( 0 ); } if( in->Coding != IM_CODING_NONE ) { im_errormsg( "im_lab_morph: must be uncoded or IM_CODING_LABQ" ); return( -1 ); } if( in->BandFmt != IM_BANDFMT_FLOAT && in->BandFmt != IM_BANDFMT_DOUBLE ) { im_errormsg( "im_lab_morph: must be uncoded float or double" ); return( -1 ); } if( in->Bands != 3 ) { im_errormsg( "im_lab_morph: must be 3 bands" ); return( -1 ); } if( !(parm = IM_NEW( out, Params )) || morph_init( parm, in, out, L_scale, L_offset, mask, a_scale, b_scale ) ) return( -1 ); if( im_cp_desc( out, in ) ) return( -1 ); out->Type = IM_TYPE_LAB; if( im_wrapone( in, out, (im_wrapone_fn) morph_buffer, parm, NULL ) ) 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; }
/* Call im_LabQ2Lab() via arg vector. */ static int LabQ2Lab_vec( im_object *argv ) { return( im_LabQ2Lab( argv[0], argv[1] ) ); }
/* 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 ) ); }
/* 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 ); }
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 ); }
int im__find_tboverlap( 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 ) { IMAGE *ref, *sec; TIE_POINTS points, *p_points; /* defined in mosaic.h */ TIE_POINTS newpoints, *p_newpoints; int i; int dx, dy; Rect top, bottom, overlap; /* Check ref and sec are compatible. */ if( ref_in->Bands != sec_in->Bands || ref_in->BandFmt != sec_in->BandFmt || ref_in->Coding != sec_in->Coding ) { im_errormsg( "im_tbmosaic: input images incompatible" ); return( -1 ); } /* Test cor and area. */ if( halfcorrelation < 0 || halfarea < 0 || halfarea < halfcorrelation ) { im_errormsg( "im_tbmosaic: bad area parameters" ); return( -1 ); } /* Set positions of top and bottom. */ top.left = 0; top.top = 0; top.width = ref_in->Xsize; top.height = ref_in->Ysize; bottom.left = xref - xsec; bottom.top = yref - ysec; bottom.width = sec_in->Xsize; bottom.height = sec_in->Ysize; /* Find overlap. */ im_rect_intersectrect( &top, &bottom, &overlap ); if( overlap.width < 2*halfarea + 1 || overlap.height < 2*halfarea + 1 ) { im_errormsg( "im_tbmosaic: overlap too small for search" ); return( -1 ); } /* Extract overlaps. */ ref = im_open_local( out, "temp_one", "t" ); sec = im_open_local( out, "temp_two", "t" ); if( !ref || !sec ) return( -1 ); if( ref_in->Coding == IM_CODING_LABQ ) { IMAGE *t1 = im_open_local( out, "temp:3", "p" ); IMAGE *t2 = im_open_local( out, "temp:4", "p" ); IMAGE *t3 = im_open_local( out, "temp:5", "p" ); IMAGE *t4 = im_open_local( out, "temp:6", "p" ); IMAGE *t5 = im_open_local( out, "temp:7", "p" ); IMAGE *t6 = im_open_local( out, "temp:8", "p" ); if( !t1 || !t2 || !t3 || !t4 || !t5 || !t6 ) return( -1 ); if( im_extract_area( ref_in, t1, overlap.left, overlap.top, overlap.width, overlap.height ) ) return( -1 ); if( im_extract_area( sec_in, t2, overlap.left - bottom.left, overlap.top - bottom.top, overlap.width, overlap.height ) ) return( -1 ); if( im_LabQ2Lab( t1, t3 ) || im_LabQ2Lab( t2, t4 ) || im_Lab2disp( t3, t5, im_col_displays( 1 ) ) || im_Lab2disp( t4, t6, im_col_displays( 1 ) ) ) return( -1 ); /* Extract the green. */ if( im_extract_band( t5, ref, 1 ) || im_extract_band( t6, sec, 1 ) ) return( -1 ); } else if( ref_in->Coding == IM_CODING_NONE ) { IMAGE *t1 = im_open_local( out, "temp:9", "p" ); IMAGE *t2 = im_open_local( out, "temp:10", "p" ); IMAGE *t3 = im_open_local( out, "temp:11", "p" ); IMAGE *t4 = im_open_local( out, "temp:12", "p" ); if( !t1 || !t2 || !t3 || !t4 ) return( -1 ); if( im_extract_area( ref_in, t1, overlap.left, overlap.top, overlap.width, overlap.height ) ) return( -1 ); if( im_extract_area( sec_in, t2, overlap.left - bottom.left, overlap.top - bottom.top, overlap.width, overlap.height ) ) return( -1 ); if( im_extract_band( t1, t3, bandno_in ) || im_extract_band( t2, t4, bandno_in ) ) return( -1 ); if( im_scale( t3, ref ) || im_scale( t4, sec ) ) return( -1 ); } else { im_errormsg( "im_tbmosaic: 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__tbcalcon( 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 = -bottom.left + dx; *dy0 = -bottom.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 ); }