diff --git a/ChangeLog b/ChangeLog index 3d8ac188..b5a2ee69 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,7 +5,7 @@ - rewrite im_buildlut(), im_identity*(), im_maplut(), im_falsecolour(), im_gammacorrect(), im_histgr(), im_histcum(), im_histnorm(), im_heq(), im_histnD(), im_histindexed(), im_histspec(), im_invertlut(), im_lhisteq(), - im_stdif(), im_project() as classes + im_stdif(), im_project(), im_tone_build*() as classes - vips_hist_local(), vips_stdif() do any number of bands - thin vips8 wrapper for im_histplot() - added vips_error_freeze() / vips_error_thaw() diff --git a/TODO b/TODO index a3db3007..28b06d64 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- need to do im_profile() before we can finish hist_percent() + - try: $ vips extract_band 50310.svs x.tif[pyramid,tile,compression=jpeg,tile-width=256,tile-height=256] 0 --n 3 diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index e5624dd2..60812e38 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -5,6 +5,7 @@ libcreate_la_SOURCES = \ pcreate.h \ buildlut.c \ invertlut.c \ + tonelut.c \ identity.c \ point.c \ point.h \ diff --git a/libvips/create/create.c b/libvips/create/create.c index d9543933..61b6e234 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -122,6 +122,7 @@ vips_create_operation_init( void ) extern GType vips_sines_get_type( void ); extern GType vips_buildlut_get_type( void ); extern GType vips_invertlut_get_type( void ); + extern GType vips_tonelut_get_type( void ); extern GType vips_identity_get_type( void ); vips_black_get_type(); @@ -136,6 +137,7 @@ vips_create_operation_init( void ) vips_sines_get_type(); vips_buildlut_get_type(); vips_invertlut_get_type(); + vips_tonelut_get_type(); vips_identity_get_type(); } diff --git a/libvips/create/tonelut.c b/libvips/create/tonelut.c new file mode 100644 index 00000000..c1ce41ee --- /dev/null +++ b/libvips/create/tonelut.c @@ -0,0 +1,356 @@ +/* Build a tone curve. + * + * Author: John Cupitt + * Written on: 18/7/1995 + * 17/9/96 JC + * - restrictions on Ps, Pm, Ph relaxed + * - restrictions on S, M, H relaxed + * 25/7/01 JC + * - patched for im_extract_band() change + * 11/7/04 + * - generalised to im_tone_build_range() ... so you can use it for any + * image, not just LabS + * 26/3/10 + * - cleanups + * - gtkdoc + * 20/9/13 + * - redo as a class + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsTonelut { + VipsCreate parent_instance; + + /* Parameters for tone curve formation. + */ + double Lb, Lw; + double Ps, Pm, Ph; + double S, M, H; + + /* Range we process. + */ + int in_max; + int out_max; + + /* Derived values. + */ + double Ls, Lm, Lh; +} VipsTonelut; + +typedef VipsCreateClass VipsTonelutClass; + +G_DEFINE_TYPE( VipsTonelut, vips_tonelut, VIPS_TYPE_CREATE ); + +/* Calculate shadow curve. + */ +static double +shad( VipsTonelut *lut, double x ) +{ + double x1 = (x - lut->Lb) / (lut->Ls - lut->Lb); + double x2 = (x - lut->Ls) / (lut->Lm - lut->Ls); + + double out; + + if( x < lut->Lb ) + out = 0; + else if( x < lut->Ls ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < lut->Lm ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate mid-tone curve. + */ +static double +mid( VipsTonelut *lut, double x ) +{ + double x1 = (x - lut->Ls) / (lut->Lm - lut->Ls); + double x2 = (x - lut->Lm) / (lut->Lh - lut->Lm); + + double out; + + if( x < lut->Ls ) + out = 0; + else if( x < lut->Lm ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < lut->Lh ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Calculate highlight curve. + */ +static double +high( VipsTonelut *lut, double x ) +{ + double x1 = (x - lut->Lm) / (lut->Lh - lut->Lm); + double x2 = (x - lut->Lh) / (lut->Lw - lut->Lh); + + double out; + + if( x < lut->Lm ) + out = 0; + else if( x < lut->Lh ) + out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; + else if( x < lut->Lw ) + out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; + else + out = 0; + + return( out ); +} + +/* Generate a point on the tone curve. Everything is 0-100. + */ +static double +tone_curve( VipsTonelut *lut, double x ) +{ + double out; + + out = x + + lut->S * shad( lut, x ) + + lut->M * mid( lut, x ) + + lut->H * high( lut, x ); + + return( out ); +} + +static int +vips_tonelut_build( VipsObject *object ) +{ + VipsCreate *create = VIPS_CREATE( object ); + VipsTonelut *lut = (VipsTonelut *) object; + + int i; + unsigned short buf[65536]; + + if( VIPS_OBJECT_CLASS( vips_tonelut_parent_class )->build( object ) ) + return( -1 ); + + g_assert( lut->in_max > 0 && lut->in_max < 65536 ); + g_assert( lut->out_max > 0 && lut->out_max < 65536 ); + + /* Note derived params. + */ + lut->Ls = lut->Lb + lut->Ps * (lut->Lw - lut->Lb); + lut->Lm = lut->Lb + lut->Pm * (lut->Lw - lut->Lb); + lut->Lh = lut->Lb + lut->Ph * (lut->Lw - lut->Lb); + + /* Generate curve. + */ + for( i = 0; i <= lut->in_max; i++ ) { + int v = (lut->out_max / 100.0) * + tone_curve( lut, 100.0 * i / lut->in_max ); + + if( v < 0 ) + v = 0; + else if( v > lut->out_max ) + v = lut->out_max; + + buf[i] = v; + } + + /* Make the output image. + */ + vips_image_init_fields( create->out, + lut->in_max + 1, 1, 1, + VIPS_FORMAT_USHORT, VIPS_CODING_NONE, + VIPS_INTERPRETATION_HISTOGRAM, 1.0, 1.0 ); + if( vips_image_write_line( create->out, 0, (VipsPel *) buf ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_tonelut_class_init( VipsTonelutClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "tonelut"; + vobject_class->description = _( "build a look-up table" ); + vobject_class->build = vips_tonelut_build; + + VIPS_ARG_INT( class, "in_max", 4, + _( "In-max" ), + _( "Size of LUT to build" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, in_max ), + 1, 65535, 32767 ); + + VIPS_ARG_INT( class, "out_max", 5, + _( "Out-max" ), + _( "Maximum value in output LUT" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, out_max ), + 1, 65535, 32767 ); + + VIPS_ARG_DOUBLE( class, "Lb", 6, + _( "Black point" ), + _( "Lowest value in output" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, Lb ), + 0, 100, 0 ); + + VIPS_ARG_DOUBLE( class, "Lw", 7, + _( "White point" ), + _( "Highest value in output" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, Lw ), + 0, 100, 100 ); + + VIPS_ARG_DOUBLE( class, "Ps", 8, + _( "Shadow point" ), + _( "Position of shadow" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, Ps ), + 0, 1, 0.2 ); + + VIPS_ARG_DOUBLE( class, "Pm", 9, + _( "Mid-tone point" ), + _( "Position of mid-tones" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, Pm ), + 0, 1, 0.5 ); + + VIPS_ARG_DOUBLE( class, "Ph", 10, + _( "Highlight point" ), + _( "Position of highlights" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, Ph ), + 0, 1, 0.8 ); + + VIPS_ARG_DOUBLE( class, "S", 11, + _( "Shadow adjust" ), + _( "Adjust shadows by this much" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, S ), + -30, 30, 0 ); + + VIPS_ARG_DOUBLE( class, "M", 12, + _( "Mid-tone adjust" ), + _( "Adjust mid-tones by this much" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, M ), + -30, 30, 0 ); + + VIPS_ARG_DOUBLE( class, "H", 13, + _( "Highlight adjust" ), + _( "Adjust highlights by this much" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsTonelut, H ), + -30, 30, 0 ); + +} + +static void +vips_tonelut_init( VipsTonelut *lut ) +{ + lut->in_max = 32767; + lut->out_max = 32767; + lut->Lb = 0.0; + lut->Lw = 100.0; + lut->Ps = 0.2; + lut->Pm = 0.5; + lut->Ph = 0.8; + lut->S = 0.0; + lut->M = 0.0; + lut->H = 0.0; +} + +/** + * vips_tonelut: + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @in_max: input range + * @out_max: output range + * @Lb: black-point [0-100] + * @Lw: white-point [0-100] + * @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) + * + * vips_tonelut() generates a tone curve for the adjustment of image + * levels. It is mostly designed for adjusting the L* part of a LAB image in + * way suitable for print work, but you can use it for other things too. + * + * The curve is an unsigned 16-bit image with (@in_max + 1) entries, + * each in the range [0, @out_max]. + * + * @Lb, @Lw are expressed as 0-100, as in LAB colour space. You + * specify the scaling for the input and output images with the @in_max and + * @out_max parameters. + * + * See also: vips_tone_map(), vips_tone_analyse(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_tonelut( VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "tonelut", ap, out ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 4251a799..3fc527f9 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -2575,6 +2575,54 @@ im_invertlut( DOUBLEMASK *input, VipsImage *out, int size ) return( 0 ); } +int +im_tone_build_range( IMAGE *out, + int in_max, int out_max, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + VipsImage *t; + + if( vips_tonelut( &t, + "in_max", in_max, + "out_max", out_max, + "Lb", Lb, + "Lw", Lw, + "Ps", Ps, + "Pm", Pm, + "Ph", Ph, + "S", S, + "M", M, + "H", H, + NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +int +im_tone_build( IMAGE *out, + double Lb, double Lw, + double Ps, double Pm, double Ph, + double S, double M, double H ) +{ + IMAGE *t1; + + if( !(t1 = im_open_local( out, "im_tone_build", "p" )) || + im_tone_build_range( t1, 32767, 32767, + Lb, Lw, Ps, Pm, Ph, S, M, H ) || + im_clip2fmt( t1, out, IM_BANDFMT_SHORT ) ) + return( -1 ); + + return( 0 ); +} + int im_rightshift_size( IMAGE *in, IMAGE *out, int xshift, int yshift, int band_fmt ) diff --git a/libvips/foreign/webpload.c b/libvips/foreign/webpload.c index d29314f2..609e541d 100644 --- a/libvips/foreign/webpload.c +++ b/libvips/foreign/webpload.c @@ -66,8 +66,6 @@ vips_foreign_load_webp_get_flags( VipsForeignLoad *load ) static int vips_foreign_load_webp_build( VipsObject *object ) { - VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) object; - if( VIPS_OBJECT_CLASS( vips_foreign_load_webp_parent_class )-> build( object ) ) return( -1 ); @@ -127,7 +125,6 @@ vips_foreign_load_webp_file_is_a( const char *filename ) static int vips_foreign_load_webp_file_header( VipsForeignLoad *load ) { - VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load; if( vips__webp_read_file_header( file->filename, load->out ) ) @@ -139,7 +136,6 @@ vips_foreign_load_webp_file_header( VipsForeignLoad *load ) static int vips_foreign_load_webp_file_load( VipsForeignLoad *load ) { - VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load; if( vips__webp_read_file( file->filename, load->real ) ) @@ -202,7 +198,6 @@ G_DEFINE_TYPE( VipsForeignLoadWebpBuffer, vips_foreign_load_webp_buffer, static int vips_foreign_load_webp_buffer_header( VipsForeignLoad *load ) { - VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpBuffer *buffer = (VipsForeignLoadWebpBuffer *) load; if( vips__webp_read_buffer_header( buffer->buf->data, @@ -215,7 +210,6 @@ vips_foreign_load_webp_buffer_header( VipsForeignLoad *load ) static int vips_foreign_load_webp_buffer_load( VipsForeignLoad *load ) { - VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpBuffer *buffer = (VipsForeignLoadWebpBuffer *) load; if( vips__webp_read_buffer( buffer->buf->data, buffer->buf->length, diff --git a/libvips/histogram/hist_cum.c b/libvips/histogram/hist_cum.c index 9a4ec629..7a3836bf 100644 --- a/libvips/histogram/hist_cum.c +++ b/libvips/histogram/hist_cum.c @@ -159,6 +159,7 @@ vips_hist_cum_init( VipsHistCum *hist_cum ) * vips_hist_cum: * @in: input image * @out: output image + * @...: %NULL-terminated list of optional named arguments * * Form cumulative histogram. * diff --git a/libvips/histogram/hist_equal.c b/libvips/histogram/hist_equal.c index a6cfd683..9c380c54 100644 --- a/libvips/histogram/hist_equal.c +++ b/libvips/histogram/hist_equal.c @@ -134,6 +134,7 @@ vips_hist_equal_init( VipsHistEqual *equal ) * vips_hist_equal: * @in: input image * @out: output image + * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * diff --git a/libvips/histogram/hist_norm.c b/libvips/histogram/hist_norm.c index c74cd993..3baf5c64 100644 --- a/libvips/histogram/hist_norm.c +++ b/libvips/histogram/hist_norm.c @@ -159,7 +159,8 @@ vips_hist_norm_init( VipsHistNorm *hist_norm ) * vips_hist_norm: * @in: input image * @out: output image - * + * @...: %NULL-terminated list of optional named arguments + * * Normalise histogram ... normalise range to make it square (ie. max == * number of elements). Normalise each band separately. * diff --git a/libvips/histogram/hist_percent.c b/libvips/histogram/hist_percent.c new file mode 100644 index 00000000..b55b5603 --- /dev/null +++ b/libvips/histogram/hist_percent.c @@ -0,0 +1,166 @@ +/* find percent of pixels + * + * Copyright: 1990, N. Dessipris + * + * Author: N. Dessipris + * Written on: 02/08/1990 + * Modified on : 29/4/93 K.Martinez for Sys5 + * 20/2/95 JC + * - now returns result through parameter + * - ANSIfied a little + * 19/1/07 + * - redone with the vips hist operators + * 25/3/10 + * - gtkdoc + * 20/9/13 + * - wrap as a class + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +typedef struct _VipsHistPercent { + VipsOperation parent_instance; + + VipsImage *in; + double percent; + int threshold; + +} VipsHistPercent; + +typedef VipsOperationClass VipsHistPercentClass; + +G_DEFINE_TYPE( VipsHistPercent, vips_hist_percent, VIPS_TYPE_OPERATION ); + +static int +vips_hist_percent_build( VipsObject *object ) +{ + VipsHistPercent *percent = (VipsHistPercent *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 6 ); + + double threshold; + + if( VIPS_OBJECT_CLASS( vips_hist_percent_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips_hist_find( percent->in, &t[0], NULL ) || + vips_hist_cum( t[0], &t[1], NULL ) || + vips_hist_norm( t[1], &t[2], NULL ) || + vips_less_const1( t[2], &t[3], + percent->percent * t[2]->Xsize, NULL ) || + vips_flip( t[3], &t[4], VIPS_DIRECTION_HORIZONTAL, NULL ) || + im_profile( t[4], &t[5], 1 ) || + vips_avg( t[5], &threshold ) ) + return( -1 ); + + percent->threshold = threshold; + + return( 0 ); +} + +static void +vips_hist_percent_class_init( VipsHistPercentClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "hist_percent"; + object_class->description = _( "find threshold for percent of pixels" ); + object_class->build = vips_hist_percent_build; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistPercent, in ) ); + + VIPS_ARG_DOUBLE( class, "percent", 2, + _( "Percent" ), + _( "percent of pixels" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsHistPercent, percent ), + 0, 100, 50 ); + + VIPS_ARG_INT( class, "threshold", 2, + _( "threshold" ), + _( "threshold above which lie percent of pixels" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsHistPercent, threshold ), + 0, 65535, 0 ); + +} + +static void +vips_hist_percent_init( VipsHistPercent *percent ) +{ +} + +/** + * vips_percent: + * @in: input image + * @percent: threshold percentage + * @threshold: output threshold value + * @...: %NULL-terminated list of optional named arguments + * + * vips_percent() returns (through the @threshold parameter) the threshold + * above which there are @percent values of @in. If for example percent=.1, the + * number of pels of the input image with values greater than @threshold + * will correspond to 10% of all pels of the image. + * + * The function works for uchar and ushort images only. It can be used + * to threshold the scaled result of a filtering operation. + * + * See also: vips_hist_find(), vips_profile(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_hist_percent( VipsImage *in, double percent, int *threshold, ... ) +{ + va_list ap; + int result; + + va_start( ap, threshold ); + result = vips_call_split( "hist_percent", ap, in, percent, threshold ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/histogram/hist_plot.c b/libvips/histogram/hist_plot.c index 7b52e9fe..eb6c34e3 100644 --- a/libvips/histogram/hist_plot.c +++ b/libvips/histogram/hist_plot.c @@ -360,6 +360,7 @@ vips_hist_plot_init( VipsHistPlot *hist_plot ) * vips_hist_plot: * @in: input image * @out: output image + * @...: %NULL-terminated list of optional named arguments * * Plot a 1 by any or any by 1 image file as a max by any or * any by max image using these rules: diff --git a/libvips/histogram/stdif.c b/libvips/histogram/stdif.c index 3765f427..b64052c8 100644 --- a/libvips/histogram/stdif.c +++ b/libvips/histogram/stdif.c @@ -361,6 +361,7 @@ vips_stdif_init( VipsStdif *stdif ) * @out: output image * @width: width of region * @height: height of region + * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * diff --git a/libvips/histogram/tone.c b/libvips/histogram/tone.c index 1ac3539d..410fd2b0 100644 --- a/libvips/histogram/tone.c +++ b/libvips/histogram/tone.c @@ -53,266 +53,6 @@ #include -/* Parameters for tone curve formation. - */ -typedef struct { - /* Parameters. - */ - double Lb, Lw; - double Ps, Pm, Ph; - double S, M, H; - - /* Derived values. - */ - double Ls, Lm, Lh; -} ToneShape; - -/* Calculate shadow curve. - */ -static double -shad( ToneShape *ts, double x ) -{ - double x1 = (x - ts->Lb) / (ts->Ls - ts->Lb); - double x2 = (x - ts->Ls) / (ts->Lm - ts->Ls); - double out; - - if( x < ts->Lb ) - out = 0; - else if( x < ts->Ls ) - out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; - else if( x < ts->Lm ) - out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; - else - out = 0; - - return( out ); -} - -/* Calculate mid-tone curve. - */ -static double -mid( ToneShape *ts, double x ) -{ - double x1 = (x - ts->Ls) / (ts->Lm - ts->Ls); - double x2 = (x - ts->Lm) / (ts->Lh - ts->Lm); - double out; - - if( x < ts->Ls ) - out = 0; - else if( x < ts->Lm ) - out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; - else if( x < ts->Lh ) - out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; - else - out = 0; - - return( out ); -} - -/* Calculate highlight curve. - */ -static double -high( ToneShape *ts, double x ) -{ - double x1 = (x - ts->Lm) / (ts->Lh - ts->Lm); - double x2 = (x - ts->Lh) / (ts->Lw - ts->Lh); - double out; - - if( x < ts->Lm ) - out = 0; - else if( x < ts->Lh ) - out = 3.0 * x1 * x1 - 2.0 * x1 * x1 * x1; - else if( x < ts->Lw ) - out = 1.0 - 3.0 * x2 * x2 + 2.0 * x2 * x2 * x2; - else - out = 0; - - return( out ); -} - -/* Generate a point on the tone curve. Everything is 0-100. - */ -static double -tone_curve( ToneShape *ts, double x ) -{ - double out; - - out = x + - ts->S * shad( ts, x ) + ts->M * mid( ts, x ) + - ts->H * high( ts, x ); - - return( out ); -} - -/** - * im_tone_build_range: - * @out: output image - * @in_max: input range - * @out_max: output range - * @Lb: black-point [0-100] - * @Lw: white-point [0-100] - * @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) - * - * im_tone_build_range() generates a tone curve for the adjustment of image - * levels. It is mostly designed for adjusting the L* part of a LAB image in - * way suitable for print work, but you can use it for other things too. - * - * The curve is an unsigned 16-bit image with (@in_max + 1) entries, - * each in the range [0, @out_max]. - * - * @Lb, @Lw are expressed as 0-100, as in LAB colour space. You - * specify the scaling for the input and output images with the @in_max and - * @out_max parameters. - * - * See also: im_ismonotonic(), im_tone_map(), im_tone_analyse(). - * - * Returns: 0 on success, -1 on error - */ -int -im_tone_build_range( IMAGE *out, - int in_max, int out_max, - double Lb, double Lw, - double Ps, double Pm, double Ph, - double S, double M, double H ) -{ - ToneShape *ts; - unsigned short lut[65536]; - int i; - - /* Check args. - */ - if( !(ts = IM_NEW( out, ToneShape )) || - im_outcheck( out ) ) - return( -1 ); - if( in_max < 0 || in_max > 65535 || - out_max < 0 || out_max > 65535 ) { - im_error( "im_tone_build", - "%s", _( "bad in_max, out_max parameters" ) ); - return( -1 ); - } - if( Lb < 0 || Lb > 100 || Lw < 0 || Lw > 100 || Lb > Lw ) { - im_error( "im_tone_build", - "%s", _( "bad Lb, Lw parameters" ) ); - return( -1 ); - } - if( Ps < 0.0 || Ps > 1.0 ) { - im_error( "im_tone_build", - "%s", _( "Ps not in range [0.0,1.0]" ) ); - return( -1 ); - } - if( Pm < 0.0 || Pm > 1.0 ) { - im_error( "im_tone_build", - "%s", _( "Pm not in range [0.0,1.0]" ) ); - return( -1 ); - } - if( Ph < 0.0 || Ph > 1.0 ) { - im_error( "im_tone_build", - "%s", _( "Ph not in range [0.0,1.0]" ) ); - return( -1 ); - } - if( S < -30 || S > 30 ) { - im_error( "im_tone_build", - "%s", _( "S not in range [-30,+30]" ) ); - return( -1 ); - } - if( M < -30 || M > 30 ) { - im_error( "im_tone_build", - "%s", _( "M not in range [-30,+30]" ) ); - return( -1 ); - } - if( H < -30 || H > 30 ) { - im_error( "im_tone_build", - "%s", _( "H not in range [-30,+30]" ) ); - return( -1 ); - } - - /* Note params. - */ - ts->Lb = Lb; - ts->Lw = Lw; - ts->Ps = Ps; - ts->Pm = Pm; - ts->Ph = Ph; - ts->S = S; - ts->M = M; - ts->H = H; - - /* Note derived params. - */ - ts->Ls = Lb + Ps * (Lw - Lb); - ts->Lm = Lb + Pm * (Lw - Lb); - ts->Lh = Lb + Ph * (Lw - Lb); - - /* Generate curve. - */ - for( i = 0; i <= in_max; i++ ) { - int v = (out_max / 100.0) * - tone_curve( ts, 100.0 * i / in_max ); - - if( v < 0 ) - v = 0; - else if( v > out_max ) - v = out_max; - - lut[i] = v; - } - - /* Make the output image. - */ - im_initdesc( out, - in_max + 1, 1, 1, IM_BBITS_SHORT, IM_BANDFMT_USHORT, - IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); - if( im_setupout( out ) ) - return( -1 ); - - if( im_writeline( 0, out, (VipsPel *) lut ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_tone_build: - * @out: output image - * @Lb: black-point [0-100] - * @Lw: white-point [0-100] - * @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_range(), but set 32767 and 32767 as values for @in_max - * and @out_max. This makes a curve suitable for correcting LABS - * images, the most common case. - * - * See also: im_tone_build_range(). - * - * Returns: 0 on success, -1 on error - */ -int -im_tone_build( IMAGE *out, - double Lb, double Lw, - double Ps, double Pm, double Ph, - double S, double M, double H ) -{ - IMAGE *t1; - - if( !(t1 = im_open_local( out, "im_tone_build", "p" )) || - im_tone_build_range( t1, 32767, 32767, - Lb, Lw, Ps, Pm, Ph, S, M, H ) || - im_clip2fmt( t1, out, IM_BANDFMT_SHORT ) ) - return( -1 ); - - return( 0 ); -} - /** * im_ismonotonic: * @lut: lookup-table to test diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index 3f0f6bdf..abaa7167 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -66,6 +66,8 @@ int vips_buildlut( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_invertlut( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_tonelut( VipsImage **out, ... ) + __attribute__((sentinel)); int im_benchmarkn( VipsImage *in, VipsImage *out, int n ); int im_benchmark2( VipsImage *in, double *out ); diff --git a/libvips/include/vips/histogram.h b/libvips/include/vips/histogram.h index bc5fb123..b0e92009 100644 --- a/libvips/include/vips/histogram.h +++ b/libvips/include/vips/histogram.h @@ -61,13 +61,6 @@ int im_ismonotonic( VipsImage *lut, int *out ); int im_mpercent( VipsImage *in, double percent, int *out ); int im_mpercent_hist( VipsImage *hist, double percent, int *out ); -int im_tone_build_range( VipsImage *out, - int in_max, int out_max, - double Lb, double Lw, double Ps, double Pm, double Ph, - double S, double M, double H ); -int im_tone_build( VipsImage *out, - double Lb, double Lw, double Ps, double Pm, double Ph, - double S, double M, double H ); int im_tone_analyse( VipsImage *in, VipsImage *out, double Ps, double Pm, double Ph, double S, double M, double H ); int im_tone_map( VipsImage *in, VipsImage *out, VipsImage *lut ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 0194b0e8..04774bfe 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -739,6 +739,14 @@ int im_invertlut( DOUBLEMASK *input, VipsImage *output, int lut_size ); int im_identity( VipsImage *lut, int bands ); int im_identity_ushort( VipsImage *lut, int bands, int sz ); +int im_tone_build_range( VipsImage *out, + int in_max, int out_max, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); +int im_tone_build( VipsImage *out, + double Lb, double Lw, double Ps, double Pm, double Ph, + double S, double M, double H ); + int im_system( VipsImage *im, const char *cmd, char **out ); VipsImage *im_system_image( VipsImage *im, const char *in_format, const char *out_format, const char *cmd_format,