/* Cored sharpen of LABQ image. * * Usage: * * int im_sharpen( IMAGE *in, IMAGE *out, * int mask_size, * int x1, int x2, * double m1, double m2 ) * * Returns 0 on success and -1 on error * * Copyright: 1995 A. Abbood * Author: A. Abbood * Written on: 30/01/1995 * 15/5/95 JC * - updated for latest 7.3 mods * - m3 parameter removed * - bug fixes and speed-ups * 4/7/95 JC * - x3 parameter added * - xs are now double * 6/7/95 JC * - xs are now ys * - better LUT generation * 12/3/01 JC * - uses seperable convolution for umask * - tiny clean ups * 23/7/01 JC * - fix for band extract index changed * 21/4/04 * - switched to gaussian mask and radius * 20/11/04 * - uses extract_bands() to remove and reattach ab for slight speedup * - accepts LabS as well as LabQ for slight speedup * - small code tidies * - ~15% speed up in total * 29/11/06 * - convolve first to help region sharing */ /* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk */ /* #define DEBUG */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ /* A lut --- we need indexes in the range [-x3,x2], so add x3 to indexes * before starting to index table. */ typedef struct { int *lut; /* Start of lut */ int x1, x2, x3; /* Parameters scaled up to int */ } SharpenLut; /* Make a lut. */ static SharpenLut * build_lut( IMAGE *out, int x1, int x2, int x3, double m1, double m2 ) { int i; SharpenLut *slut = IM_NEW( out, SharpenLut ); if( !slut ) return( NULL ); if( !(slut->lut = IM_ARRAY( out, x2 + x3 + 1, int )) ) return( NULL ); slut->x1 = x1; slut->x2 = x2; slut->x3 = x3; for( i = 0; i < x1; i++ ) { slut->lut[x3 + i] = i*m1; slut->lut[x3 - i] = -i*m1; } for( i = x1; i <= x2; i++ ) slut->lut[x3 + i] = x1*m1 + (i-x1)*m2; for( i = x1; i <= x3; i++ ) slut->lut[x3 - i] = -(x1*m1 + (i-x1)*m2); return( slut ); } /* Take the difference of in1 and in2 and LUT it. */ static void buf_difflut( short **in, short *out, int n, SharpenLut *slut ) { int range = slut->x2 + slut->x3; int *lut = slut->lut; int x3 = slut->x3; short *p1 = in[1]; short *p2 = in[0]; int i; for( i = 0; i < n; i++ ) { int v1 = p1[i]; int v2 = p2[i]; /* v2 is the area average. If this is zero, then we pass the * original image through unaltered. */ if( v2 == 0 ) out[i] = v1; else { /* Find difference. Offset by x3 to get the expected * range of values. */ int s1 = x3 + (v1 - v2); int s2; /* Clip to LUT range. */ if( s1 < 0 ) s1 = 0; else if( s1 > range ) s1 = range; /* Transform! */ s2 = v1 + lut[s1]; /* Clip to LabS range. */ if( s2 < 0 ) s2 = 0; else if( s2 > 32767 ) s2 = 32767; /* And write. */ out[i] = s2; } } } /* Make a 1 line gaussian of a specified radius. */ static INTMASK * sharpen_mask_new( int radius ) { INTMASK *base; INTMASK *line; int total; int i; /* Stop at 20% of max ... bit mean, but means mask radius is roughly * right. */ if( !(base = im_gauss_imask( "big1", radius / 2, 0.2 )) ) return( NULL ); if( !(line = im_create_imask( "sharpen-line", base->xsize, 1 )) ) { im_free_imask( base ); return( NULL ); } total = 0; for( i = 0; i < base->xsize; i++ ) { line->coeff[i] = base->coeff[base->xsize * (base->ysize / 2) + i]; total += line->coeff[i]; } line->scale = total; im_free_imask( base ); #ifdef DEBUG printf( "sharpen_mask_new: created mask:\n" ); im_print_imask( line ); #endif /*DEBUG*/ return( line ); } int im_sharpen( IMAGE *in, IMAGE *out, int mask_size, double x1, double y2, double y3, double m1, double m2 ) { IMAGE *arry[3]; IMAGE *t[4]; INTMASK *mask; SharpenLut *slut; /* Turn y parameters into xs. */ double x2 = (y2 - x1 * (m1 - m2)) / m2; double x3 = (y3 - x1 * (m1 - m2)) / m2; if( in->Coding == IM_CODING_LABQ ) { IMAGE *tc[2]; if( im_open_local_array( out, tc, 2, "im_sharpen:1", "p" ) || im_LabQ2LabS( in, tc[0] ) || im_sharpen( tc[0], tc[1], mask_size, x1, y2, y3, m1, m2 ) || im_LabS2LabQ( tc[1], out ) ) return( -1 ); return( 0 ); } /* Check IMAGE parameters */ if( in->Coding != IM_CODING_NONE || in->Bands != 3 || in->BandFmt != IM_BANDFMT_SHORT ) { im_error( "im_sharpen", _( "input not 3-band short" ) ); return( -1 ); } if( im_piocheck( in, out ) ) return( -1 ); /* Check number range. */ if( x1 < 0 || x2 < 0 || x1 > 99 || x2 > 99 || x1 > x2 || x3 < 0 || x3 > 99 || x1 > x3 ) { im_error( "im_sharpen", _( "parameters out of range" ) ); return( -1 ); } /* Set up data structures we need. First, the convolution mask we will * use. */ if( !(mask = (INTMASK *) im_local( out, (im_construct_fn) sharpen_mask_new, (im_callback_fn) im_free_imask, GINT_TO_POINTER( mask_size ), NULL, NULL )) ) return( -1 ); /* Make the lut we will use. We need to scale up x1, x2, x3 to the * LabS range. */ if( !(slut = build_lut( out, x1 * 327.67, x2 * 327.67, x3 * 327.67, m1, m2 )) ) return( -1 ); /* Open a set of local image descriptors. */ if( im_open_local_array( out, t, 4, "im_sharpen:2", "p" ) ) return( -1 ); /* Extract L and ab, convolve L. */ if( im_extract_band( in, t[0], 0 ) || im_extract_bands( in, t[1], 1, 2 ) || im_convsep( t[0], t[2], mask ) ) return( -1 ); /* Find difference of L channel and convolved L channel, and pass * through LUT. */ if( im_cp_desc( t[3], t[2] ) ) return( -1 ); arry[0] = t[2]; arry[1] = t[0]; arry[2] = NULL; if( im_wrapmany( arry, t[3], (im_wrapmany_fn) buf_difflut, slut, NULL ) ) return( -1 ); /* Reattach ab. */ if( im_bandjoin( t[3], t[1], out ) ) return( -1 ); return( 0 ); }