728 lines
14 KiB
C
728 lines
14 KiB
C
/* @(#) Function which read a mask from a file.
|
|
* @(#) Result is written in the structure IMASK or DMASK depending on whether
|
|
* @(#) the input mask is integer or double. The structure of the mask is
|
|
* @(#) given in mask.h
|
|
* @(#) The mask coefficients can be either int (INTMASK)
|
|
* @(#) or double (DOUBLEMASK).
|
|
* @(#)
|
|
*
|
|
* Copyright: 1990, N. Dessipris.
|
|
*
|
|
* Author: Nicos Dessipris
|
|
* Written on: 29/04/1991
|
|
* Modified on: 10/8/1992, J.Cupitt
|
|
* - Mask reading routines no longer fail if scale and offset are missing.
|
|
* Instead, they set default values of scale=1, offset=0.
|
|
* - Code tidied up, better error recovery.
|
|
* - Bugs fixed in im_dup_*mask. No longer coredump.
|
|
* - Bugs fixed in im_write_*mask. Now work for non-square matricies.
|
|
* - im_copy_*mask_matrix, im_copy_matrix_*mask added: copy VIPS mask
|
|
* structures into Numerical Recipies in C style matricies and vice
|
|
* versa. Both structures should have been built before copy attempted.
|
|
* See im_create_*mask, im_*mat_alloc. The matrix should be indexed by 0
|
|
* to size-1.
|
|
* 9/7/93 JC
|
|
* - some ANSIfication and tidies
|
|
* - im_free_*mask() now return zero, so they can be used as close
|
|
* callbacks.
|
|
* 7/10/94 JC
|
|
* - new IM_NEW(), IM_ARRAY() macros added
|
|
* 27/4/95 JC
|
|
* - oops! forgot to init IM_ARRAY() memory to zero
|
|
* 7/8/96 JC
|
|
* - im_scale_dmask rewritten
|
|
* 7/5/98 JC
|
|
* - im_read_*mask() rewritten, now more robust
|
|
* - im_write_*mask() rewritten
|
|
* - new functions im_write_*mask_name()
|
|
* 28/7/99 JC
|
|
* - im_create_imaskv(), im_create_dmaskv() make masks and init from
|
|
* varargs
|
|
* - tabs allowed as column separators
|
|
* 9/2/05
|
|
* - "," allowed as column separator ... helps CSV read
|
|
* 31/5/06
|
|
* - use g_ascii_strtod() and friends
|
|
* 2006-09-08 tcv
|
|
* - add im_norm_dmask()
|
|
*/
|
|
|
|
/*
|
|
|
|
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 <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
/* Size of line buffer for reading.
|
|
*/
|
|
#define IM_MAX_LINE (4096)
|
|
|
|
/* Free mask structure and any attached arrays. Return zero, so we can use
|
|
* these functions as close callbacks.
|
|
*/
|
|
int
|
|
im_free_imask( INTMASK *m )
|
|
{
|
|
if( ! m )
|
|
return 0;
|
|
|
|
if( m->coeff )
|
|
im_free( m->coeff );
|
|
if( m->filename )
|
|
im_free( m->filename );
|
|
im_free( m );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
int
|
|
im_free_dmask( DOUBLEMASK *m )
|
|
{
|
|
if( ! m )
|
|
return 0;
|
|
|
|
if( m->coeff )
|
|
im_free( m->coeff );
|
|
if( m->filename )
|
|
im_free( m->filename );
|
|
im_free( m );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Create structures.
|
|
*/
|
|
INTMASK *
|
|
im_create_imask( const char *filename, int xs, int ys )
|
|
{
|
|
INTMASK *m;
|
|
int size = xs * ys;
|
|
|
|
/* Check args.
|
|
*/
|
|
if( xs <= 0 || ys <= 0 || filename == NULL ) {
|
|
im_error( "im_create_imask", "%s", _( "bad arguments" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Allocate and initialise structure.
|
|
*/
|
|
if( !(m = IM_NEW( NULL, INTMASK )) )
|
|
return( NULL );
|
|
m->coeff = NULL;
|
|
m->filename = NULL;
|
|
m->scale = 1;
|
|
m->offset = 0;
|
|
m->xsize = 0;
|
|
m->ysize = 0;
|
|
|
|
if( !(m->coeff = IM_ARRAY( NULL, size, int )) ) {
|
|
im_free_imask( m );
|
|
return( NULL );
|
|
}
|
|
(void) memset( (char *) m->coeff, 0, size * sizeof( int ) );
|
|
if( !(m->filename = im_strdup( NULL, filename )) ) {
|
|
im_free_imask( m );
|
|
return( NULL );
|
|
}
|
|
m->xsize = xs; m->ysize = ys;
|
|
|
|
return( m );
|
|
}
|
|
|
|
INTMASK *
|
|
im_create_imaskv( const char *filename, int xs, int ys, ... )
|
|
{
|
|
va_list ap;
|
|
|
|
INTMASK *m;
|
|
int i;
|
|
|
|
if( !(m = im_create_imask( filename, xs, ys )) )
|
|
return( NULL );
|
|
|
|
va_start( ap, ys );
|
|
for( i = 0; i < xs * ys; i++ )
|
|
m->coeff[i] = va_arg( ap, int );
|
|
va_end( ap );
|
|
|
|
return( m );
|
|
}
|
|
|
|
DOUBLEMASK *
|
|
im_create_dmask( const char *filename, int xs, int ys )
|
|
{
|
|
DOUBLEMASK *m;
|
|
int size = xs * ys;
|
|
|
|
/* Check args.
|
|
*/
|
|
if( xs <= 0 || ys <= 0 || filename == NULL ) {
|
|
im_error( "im_create_dmask", "%s", _( "bad arguments" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Allocate and initialise structure.
|
|
*/
|
|
if( !(m = IM_NEW( NULL, DOUBLEMASK )) )
|
|
return( NULL );
|
|
m->coeff = NULL;
|
|
m->filename = NULL;
|
|
m->scale = 1.0;
|
|
m->offset = 0.0;
|
|
m->xsize = 0;
|
|
m->ysize = 0;
|
|
|
|
if( !(m->coeff = IM_ARRAY( NULL, size, double )) ) {
|
|
im_free_dmask( m );
|
|
return( NULL );
|
|
}
|
|
(void) memset( (char *) m->coeff, 0, size * sizeof( double ) );
|
|
if( !(m->filename = im_strdup( NULL, filename )) ) {
|
|
im_free_dmask( m );
|
|
return( NULL );
|
|
}
|
|
m->xsize = xs; m->ysize = ys;
|
|
|
|
return( m );
|
|
}
|
|
|
|
DOUBLEMASK *
|
|
im_create_dmaskv( const char *filename, int xs, int ys, ... )
|
|
{
|
|
va_list ap;
|
|
|
|
DOUBLEMASK *m;
|
|
int i;
|
|
|
|
if( !(m = im_create_dmask( filename, xs, ys )) )
|
|
return( NULL );
|
|
|
|
va_start( ap, ys );
|
|
for( i = 0; i < xs * ys; i++ )
|
|
m->coeff[i] = va_arg( ap, double );
|
|
va_end( ap );
|
|
|
|
return( m );
|
|
}
|
|
|
|
/* Open for read.
|
|
*/
|
|
static FILE *
|
|
open_read( const char *name )
|
|
{
|
|
FILE *fp;
|
|
|
|
if( !(fp = fopen( name, "r" )) ) {
|
|
im_error( "read_mask", _( "Unable to open \"%s\" for input" ),
|
|
name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( fp );
|
|
}
|
|
|
|
/* Read a line from a file!
|
|
*/
|
|
static int
|
|
get_line( FILE *fp, char *buf )
|
|
{
|
|
if( !fgets( buf, IM_MAX_LINE, fp ) ) {
|
|
im_error( "read_mask", "%s", _( "unexpected EOF" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* width, height, optional scale, optional offset.
|
|
*/
|
|
static int
|
|
read_header( FILE *fp, int *xs, int *ys, double *scale, double *offset )
|
|
{
|
|
char buf[IM_MAX_LINE];
|
|
char *p, *q;
|
|
double v[4];
|
|
int i;
|
|
|
|
/* Read the first line: should contain size and optional
|
|
* scale + offset.
|
|
*/
|
|
if( get_line( fp, buf ) )
|
|
return( -1 );
|
|
|
|
/* Read as space separated doubles. \n is in the break list because
|
|
* our line will (usually) have a trailing \n which we want to count
|
|
* as whitespace.
|
|
*/
|
|
p = buf;
|
|
for( i = 0, p = buf;
|
|
i < 4 && (q = im_break_token( p, " \t\n" ));
|
|
i++, p = q )
|
|
v[i] = g_ascii_strtod( p, NULL );
|
|
|
|
if( (i != 2 && i != 4) ||
|
|
ceil( v[0] ) != v[0] ||
|
|
ceil( v[1] ) != v[1] ||
|
|
v[0] <= 0 ||
|
|
v[1] <= 0 ) {
|
|
im_error( "read_header",
|
|
"%s", _( "error reading matrix header" ) );
|
|
return( -1 );
|
|
}
|
|
if( i == 4 && v[2] == 0 ) {
|
|
im_error( "read_header",
|
|
"%s", _( "scale should be non-zero" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
*xs = v[0];
|
|
*ys = v[1];
|
|
if( i == 2 ) {
|
|
*scale = 1.0;
|
|
*offset = 0.0;
|
|
}
|
|
else {
|
|
*scale = v[2];
|
|
*offset = v[3];
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Read matrix files.
|
|
*/
|
|
DOUBLEMASK *
|
|
im_read_dmask( const char *maskfile )
|
|
{
|
|
FILE *fp;
|
|
double sc, off;
|
|
int xs, ys;
|
|
DOUBLEMASK *m;
|
|
int x, y, i, size;
|
|
char buf[IM_MAX_LINE];
|
|
|
|
if( !(fp = open_read( maskfile )) )
|
|
return( NULL );
|
|
|
|
if( read_header( fp, &xs, &ys, &sc, &off ) ) {
|
|
fclose( fp );
|
|
return( NULL );
|
|
}
|
|
|
|
if( !(m = im_create_dmask( maskfile, xs, ys )) ) {
|
|
fclose( fp );
|
|
return( NULL );
|
|
}
|
|
m->scale = sc;
|
|
m->offset = off;
|
|
size = xs * ys;
|
|
|
|
for( i = 0, y = 0; y < ys; y++ ) {
|
|
char *p;
|
|
|
|
if( get_line( fp, buf ) ) {
|
|
im_free_dmask( m );
|
|
fclose( fp );
|
|
return( NULL );
|
|
}
|
|
|
|
for( p = buf, x = 0; p && x < xs;
|
|
x++, i++, p = im_break_token( p, " \t,\";" ) )
|
|
m->coeff[i] = g_ascii_strtod( p, NULL );
|
|
}
|
|
fclose( fp );
|
|
|
|
return( m );
|
|
}
|
|
|
|
/* INTMASK ... read as double, check for intness.
|
|
*/
|
|
INTMASK *
|
|
im_read_imask( const char *maskfile )
|
|
{
|
|
DOUBLEMASK *dmask;
|
|
INTMASK *imask;
|
|
int i;
|
|
|
|
if( !(dmask = im_read_dmask( maskfile )) )
|
|
return( NULL );
|
|
|
|
if( ceil( dmask->scale ) != dmask->scale ||
|
|
ceil( dmask->offset ) != dmask->offset ) {
|
|
im_free_dmask( dmask );
|
|
im_error( "im_read_imask",
|
|
"%s", _( "scale and offset should be int" ) );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
for( i = 0; i < dmask->xsize * dmask->ysize; i++ )
|
|
if( ceil( dmask->coeff[i] ) != dmask->coeff[i] ) {
|
|
im_free_dmask( dmask );
|
|
im_error( "im_read_imask", _( "cofficient at "
|
|
"position (%d, %d) is not int" ),
|
|
i % dmask->xsize,
|
|
i / dmask->xsize );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
if( !(imask = im_create_imask( maskfile,
|
|
dmask->xsize, dmask->ysize )) ) {
|
|
im_free_dmask( dmask );
|
|
return( NULL );
|
|
}
|
|
imask->scale = dmask->scale;
|
|
imask->offset = dmask->offset;
|
|
for( i = 0; i < dmask->xsize * dmask->ysize; i++ )
|
|
imask->coeff[i] = dmask->coeff[i];
|
|
|
|
im_free_dmask( dmask );
|
|
|
|
return( imask );
|
|
}
|
|
|
|
INTMASK *
|
|
im_scale_dmask( DOUBLEMASK *m, const char *name )
|
|
{
|
|
const int size = m->xsize * m->ysize;
|
|
|
|
INTMASK *out;
|
|
double maxval, dsum;
|
|
int i;
|
|
int isum;
|
|
|
|
if( !name || m->xsize <= 0 || m->ysize <= 0 ) {
|
|
im_error( "im_scale_dmask", "%s", _( "bad arguments" ) );
|
|
return( NULL );
|
|
}
|
|
if( !(out = im_create_imask( name, m->xsize, m->ysize )) )
|
|
return( NULL );
|
|
|
|
/* Find mask max.
|
|
*/
|
|
maxval = m->coeff[0];
|
|
for( i = 0; i < size; i++ )
|
|
if( m->coeff[i] > maxval )
|
|
maxval = m->coeff[i];
|
|
|
|
/* Copy and scale, setting max to 100.
|
|
*/
|
|
for( i = 0; i < size; i++ )
|
|
out->coeff[i] = IM_RINT( m->coeff[i] * 100.0 / maxval );
|
|
out->offset = m->offset;
|
|
|
|
/* Set the scale to match the adjustment to max.
|
|
*/
|
|
isum = 0;
|
|
dsum = 0.0;
|
|
for( i = 0; i < size; i++ ) {
|
|
isum += out->coeff[i];
|
|
dsum += m->coeff[i];
|
|
}
|
|
|
|
if( dsum == m->scale )
|
|
out->scale = isum;
|
|
else
|
|
out->scale = IM_RINT( m->scale * isum / dsum );
|
|
|
|
return( out );
|
|
}
|
|
|
|
void
|
|
im_norm_dmask( DOUBLEMASK *mask )
|
|
{
|
|
const int n = mask->xsize * mask->ysize;
|
|
const double scale = 1.0 / mask->scale;
|
|
|
|
int i;
|
|
|
|
if( 1.0 == scale && 0.0 == mask->offset )
|
|
return;
|
|
|
|
for( i = 0; i < n; i++ )
|
|
mask->coeff[i] = mask->coeff[i] * scale + mask->offset;
|
|
|
|
mask->scale = 1.0;
|
|
mask->offset = 0.0;
|
|
}
|
|
|
|
INTMASK *
|
|
im_dup_imask( INTMASK *m, const char *name )
|
|
{
|
|
const int xs = m->xsize;
|
|
const int ys = m->ysize;
|
|
const int size = xs * ys;
|
|
|
|
INTMASK *new;
|
|
int i;
|
|
|
|
if( !(new = im_create_imask( name, xs, ys )) )
|
|
return( NULL );
|
|
|
|
new->offset = m->offset;
|
|
new->scale = m->scale;
|
|
|
|
for( i = 0; i < size; i++ )
|
|
new->coeff[i] = m->coeff[i];
|
|
|
|
return( new );
|
|
}
|
|
|
|
DOUBLEMASK *
|
|
im_dup_dmask( DOUBLEMASK *m, const char *name )
|
|
{
|
|
DOUBLEMASK *new;
|
|
int xs = m->xsize;
|
|
int ys = m->ysize;
|
|
int size = xs * ys;
|
|
int i;
|
|
double *pnt1, *pnt2;
|
|
|
|
if( !(new = im_create_dmask( name, xs, ys )) )
|
|
return( NULL );
|
|
|
|
new->offset = m->offset;
|
|
new->scale = m->scale;
|
|
|
|
pnt1 = m->coeff;
|
|
pnt2 = new->coeff;
|
|
for( i = 0; i < size; i++ )
|
|
*pnt2++ = *pnt1++;
|
|
|
|
return( new );
|
|
}
|
|
|
|
/* Open for write.
|
|
*/
|
|
static FILE *
|
|
open_write( const char *name )
|
|
{
|
|
FILE *fp;
|
|
|
|
if( !(fp = fopen( name, "w" )) ) {
|
|
im_error( "write_mask", _( "unable to open \"%s\" for output" ),
|
|
name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( fp );
|
|
}
|
|
|
|
/* Write to file.
|
|
*/
|
|
static int
|
|
write_line( FILE *fp, const char *fmt, ... )
|
|
{
|
|
va_list ap;
|
|
|
|
va_start( ap, fmt );
|
|
if( !vfprintf( fp, fmt, ap ) ) {
|
|
im_error( "write_mask", "%s", _( "write error, disc full?" ) );
|
|
return( -1 );
|
|
}
|
|
va_end( ap );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
write_double( FILE *fp, double d )
|
|
{
|
|
char buf[G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
fprintf( fp, "%s", g_ascii_dtostr( buf, sizeof( buf ), d ) );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write the INTMASK m into name.
|
|
*/
|
|
int
|
|
im_write_imask_name( INTMASK *m, const char *name )
|
|
{
|
|
FILE *fp;
|
|
int x, y, i;
|
|
|
|
if( !(fp = open_write( name )) )
|
|
return( -1 );
|
|
|
|
if( write_line( fp, "%d %d %d %d\n",
|
|
m->xsize, m->ysize, m->scale, m->offset ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
|
|
for( i = 0, y = 0; y < m->ysize; y++ ) {
|
|
for( x = 0; x < m->xsize; x++, i++ )
|
|
if( write_line( fp, "%d ", m->coeff[i] ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
|
|
if( write_line( fp, "\n" ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
}
|
|
fclose( fp );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write the INTMASK m into m->filename
|
|
*/
|
|
int
|
|
im_write_imask( INTMASK *m )
|
|
{
|
|
if( !m->filename ) {
|
|
im_error( "im_write_imask", "%s", _( "filename not set" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( im_write_imask_name( m, m->filename ) );
|
|
}
|
|
|
|
/* Write the DOUBLEMASK m into name.
|
|
*/
|
|
int
|
|
im_write_dmask_name( DOUBLEMASK *m, const char *name )
|
|
{
|
|
FILE *fp;
|
|
int x, y, i;
|
|
|
|
if( !(fp = open_write( name )) )
|
|
return( -1 );
|
|
|
|
if( write_line( fp, "%d %d ", m->xsize, m->ysize ) ||
|
|
write_double( fp, m->scale ) ||
|
|
write_line( fp, " " ) ||
|
|
write_double( fp, m->offset ) ||
|
|
write_line( fp, "\n" ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
|
|
for( i = 0, y = 0; y < m->ysize; y++ ) {
|
|
for( x = 0; x < m->xsize; x++, i++ )
|
|
if( write_double( fp, m->coeff[i] ) ||
|
|
write_line( fp, " " ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
|
|
if( write_line( fp, "\n" ) ) {
|
|
fclose( fp );
|
|
return( -1 );
|
|
}
|
|
}
|
|
fclose( fp );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write the DOUBLEMASK m into m->filename
|
|
*/
|
|
int
|
|
im_write_dmask( DOUBLEMASK *m )
|
|
{
|
|
if( !m->filename ) {
|
|
im_error( "im_write_dmask", "%s", _( "filename not set" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( im_write_dmask_name( m, m->filename ) );
|
|
}
|
|
|
|
/* Copy an imask into a matrix. Only used internally by matrix package for
|
|
* invert.
|
|
*/
|
|
void
|
|
im_copy_imask_matrix( INTMASK *mask, int **matrix )
|
|
{
|
|
int x, y;
|
|
int *p = mask->coeff;
|
|
|
|
for( y = 0; y < mask->ysize; y++ )
|
|
for( x = 0; x < mask->xsize; x++ )
|
|
matrix[x][y] = *p++;
|
|
}
|
|
|
|
|
|
/* Copy a matrix into an imask.
|
|
*/
|
|
void
|
|
im_copy_matrix_imask( int **matrix, INTMASK *mask )
|
|
{
|
|
int x, y;
|
|
int *p = mask->coeff;
|
|
|
|
for( y = 0; y < mask->ysize; y++ )
|
|
for( x = 0; x < mask->xsize; x++ )
|
|
*p++ = matrix[x][y];
|
|
}
|
|
|
|
/* Copy a dmask into a matrix.
|
|
*/
|
|
void
|
|
im_copy_dmask_matrix( DOUBLEMASK *mask, double **matrix )
|
|
{
|
|
int x, y;
|
|
double *p = mask->coeff;
|
|
|
|
for( y = 0; y < mask->ysize; y++ )
|
|
for( x = 0; x < mask->xsize; x++ )
|
|
matrix[x][y] = *p++;
|
|
}
|
|
|
|
/* Copy a matrix to a dmask.
|
|
*/
|
|
void
|
|
im_copy_matrix_dmask( double **matrix, DOUBLEMASK *mask )
|
|
{
|
|
int x, y;
|
|
double *p = mask->coeff;
|
|
|
|
for( y = 0; y < mask->ysize; y++ )
|
|
for( x = 0; x < mask->xsize; x++ )
|
|
*p++ = matrix[x][y];
|
|
}
|