/* @(#) Given a mask of target values and real values, generate a LUT which * @(#) will map reals to targets. Handy for linearising images from * @(#) measurements of a colour chart. All values in [0,1]. Piecewise linear * @(#) interpolation, extrapolate head and tail to 0 and 1. * @(#) * @(#) Eg. input line like: * @(#) * @(#) 0.1 0.2 0.3 0.1 * @(#) * @(#) means a patch with 10% reflectance produces an image with 20% in * @(#) channel 1, 30% in channel 2, and 10% in channel 3. * @(#) * @(#) Inputs don't need to be sorted (we do that). Generate any precision * @(#) LUT ... typically ask for 256 elements. * * Written on: 5/6/01 * Modified on : * 7/7/03 JC * - generate image rather than doublemask (arrg) */ /* 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 */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ /* #define DEBUG */ /* Our state. */ typedef struct { DOUBLEMASK *input; /* Input mask */ IMAGE *output; /* Output lut */ int lut_size; /* Number of output elements to generate */ double **data; /* Rows of unpacked matrix */ } State; /* Use this to sort our input rows by the first column. */ static int compare( const void *a, const void *b ) { double **r1 = (double **) a; double **r2 = (double **) b; double diff = r1[0][0] - r2[0][0]; if( diff > 0 ) return( 1 ); else if( diff == 0 ) return( 0 ); else return( -1 ); } /* Free our state. */ static void free_state( State *state ) { if( state->data ) { int i; for( i = 0; i < state->input->ysize; i++ ) if( state->data[i] ) { im_free( state->data[i] ); state->data[i] = NULL; } im_free( state->data ); state->data = NULL; } } /* Fill our state. */ static int build_state( State *state, DOUBLEMASK *input, IMAGE *output, int lut_size ) { int x, y, i; state->input = input; state->output = output; state->lut_size = lut_size; state->data = NULL; if( !(state->data = IM_ARRAY( NULL, input->ysize, double * )) ) return( -1 ); for( y = 0; y < input->ysize; y++ ) state->data[y] = NULL; for( y = 0; y < input->ysize; y++ ) if( !(state->data[y] = IM_ARRAY( NULL, input->xsize, double )) ) return( -1 ); for( i = 0, y = 0; y < input->ysize; y++ ) for( x = 0; x < input->xsize; x++, i++ ) state->data[y][x] = input->coeff[i]; /* Sanity check for data range. */ for( y = 0; y < input->ysize; y++ ) for( x = 0; x < input->xsize; x++ ) if( state->data[y][x] > 1.0 || state->data[y][x] < 0.0 ) { im_errormsg( "im_invertlut: element out of " "range [0,1]" ); return( -1 ); } /* Sort by 1st column in input. */ qsort( state->data, input->ysize, sizeof( double * ), compare ); #ifdef DEBUG printf( "Input table, sorted by 1st column\n" ); for( y = 0; y < input->ysize; y++ ) { printf( "%.4d ", y ); for( x = 0; x < input->xsize; x++ ) printf( "%.9f ", state->data[y][x] ); printf( "\n" ); } #endif /*DEBUG*/ return( 0 ); } static int invertlut( State *state ) { DOUBLEMASK *input = state->input; int ysize = input->ysize; int xsize = input->xsize; IMAGE *output = state->output; double *odata = (double *) output->data; int bands = xsize - 1; double **data = state->data; int lut_size = state->lut_size; int i; /* Do each output channel separately. */ for( i = 0; i < bands; i++ ) { /* The first and last lut positions we know real values for. */ int first = data[0][i + 1] * (lut_size - 1); int last = data[ysize - 1][i + 1] * (lut_size - 1); int k; double fac; /* Extrapolate bottom and top segments to (0,0) and (1,1). */ fac = data[0][0] / first; for( k = 0; k < first; k++ ) odata[i + k * bands] = k * fac; fac = (1 - data[ysize - 1][0]) / ((lut_size - 1) - last); for( k = last + 1; k < lut_size; k++ ) odata[i + k * bands] = data[ysize - 1][0] + (k - last) * fac; /* Interpolate the data setions. */ for( k = first; k <= last; k++ ) { /* Where we're at in the [0,1] range. */ double ki = (double) k / (lut_size - 1); double irange, orange; int j; /* Search for the lowest real value < ki. There may * not be one: if not, just use 0. Tiny error. */ for( j = ysize - 1; j >= 0; j-- ) if( data[j][i + 1] < ki ) break; if( j == -1 ) j = 0; /* Interpolate k as being between row data[j] and row * data[j + 1]. */ irange = data[j + 1][i + 1] - data[j][i + 1]; orange = data[j + 1][0] - data[j][0]; odata[i + k * bands] = data[j][0] + orange * ((ki - data[j][i + 1]) / irange); } } return( 0 ); } int im_invertlut( DOUBLEMASK *input, IMAGE *output, int lut_size ) { State state; if( !input || input->xsize < 2 || input->ysize < 1 ) { im_errormsg( "im_invertlut: bad input matrix" ); return( -1 ); } if( lut_size < 1 || lut_size > 65536 ) { im_errormsg( "im_invertlut: bad lut_size" ); return( -1 ); } im_initdesc( output, lut_size, 1, input->xsize - 1, IM_BBITS_DOUBLE, IM_BANDFMT_DOUBLE, IM_CODING_NONE, IM_TYPE_HISTOGRAM, 1.0, 1.0, 0, 0 ); if( im_setupout( output ) ) return( -1 ); if( build_state( &state, input, output, lut_size ) || invertlut( &state ) ) { free_state( &state ); return( -1 ); } free_state( &state ); return( 0 ); }