diff --git a/ChangeLog b/ChangeLog index 40d0536e..d5746fa8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,7 +13,8 @@ im_moreeq*(), im_remainder*(), im_and*(), im_or*(), im_eor*(), im_shift*(), im_pow*(), im_exp*(), im_ifthenelse(), im_blend(), im_c2amph(), im_c2rect(), im_bandmean(), im_c2real(), im_c2imag(), im_ri2c(), im_jpeg*2vips(), - im_vips2jpeg*(), im_tiff2vips(), im_vips2tiff(), im_exr2vips() + im_vips2jpeg*(), im_tiff2vips(), im_vips2tiff(), im_exr2vips(), + im_fits2vips(), im_vips2fits() redone as classes - added argument priorites to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index 7f1a87bb..2a38ba8f 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,18 @@ +- fits bandjoin is rubbish + + perhaps we should always allocate the output in the loader? it's make fits + bandjoin simpler + + + + - "header fred.png" does not work, since header uses im_open() which uses VipsForeign + - make the old format/vips.c into a stub as well? move format/* to deprecated diff --git a/configure.in b/configure.in index 34a1c58d..26b276b4 100644 --- a/configure.in +++ b/configure.in @@ -492,6 +492,12 @@ if test x"$with_cfitsio" != "xno"; then ]) fi +if test x"$with_cfitsio" = x"yes"; then + AM_CONDITIONAL(HAVE_CFITSIO, true) +else + AM_CONDITIONAL(HAVE_CFITSIO, false) +fi + # pangoft2 AC_ARG_WITH([pangoft2], AS_HELP_STRING([--without-pangoft2], diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index c6848de1..a26cb51f 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -19,6 +19,20 @@ EXTRA_DIST += \ openexrload.c endif +if HAVE_CFITSIO +libforeign_la_SOURCES += \ + fits.h \ + fits.c \ + fitssave.c \ + fitsload.c +else +EXTRA_DIST += \ + fits.h \ + fits.c \ + fitssave.c \ + fitsload.c +endif + if HAVE_TIFF libforeign_la_SOURCES += \ tiff.h \ diff --git a/libvips/foreign/fits.c b/libvips/foreign/fits.c new file mode 100644 index 00000000..100c7b2b --- /dev/null +++ b/libvips/foreign/fits.c @@ -0,0 +1,739 @@ +/* Read FITS files with cfitsio + * + * 26/10/10 + * - from matlab.c + * 27/10/10 + * - oops, forgot to init status in close + * 30/11/10 + * - set RGB16/GREY16 if appropriate + * - allow up to 10 dimensions as long as they are empty + * 27/1/11 + * - lazy read + * 31/1/11 + * - read in planes and combine with im_bandjoin() + * - read whole tiles with fits_read_subset() when we can + * 17/3/11 + * - renames, updates etc. ready for adding fits write + * - fits write! + * 21/3/11 + * - read/write metadata as whole records to avoid changing things + * - cast input to a supported format + * - bandsplit for write + * 13/12/11 + * - redo as a set of fns ready for wrapping in a new-style 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "fits.h" + +/* + + TODO + + - ask Doug for a test colour image + + found WFPC2u5780205r_c0fx.fits on the fits samples page, + but it's tiny + + - test performance + + - vips__fits_read() makes rather ugly bandjoins, fix + + */ + +/* vips only supports 3 dimensions, but we allow up to MAX_DIMENSIONS as long + * as the higher dimensions are all empty. If you change this value, change + * fits2vips_get_header() as well. + */ +#define MAX_DIMENSIONS (10) + +/* What we track during a cfitsio-file read or write. + */ +typedef struct { + char *filename; + VipsImage *image; + + fitsfile *fptr; + int datatype; + int naxis; + long long int naxes[MAX_DIMENSIONS]; + + GMutex *lock; /* Lock fits_*() calls with this */ + + /* Set this to -1 to read all bands, or a +ve int to read a specific + * band. + */ + int band_select; + + /* We split bands up for write into this buffer. + */ + PEL *buffer; +} VipsFits; + +const char *vips__fits_suffs[] = { ".fits", NULL }; + +static void +vips_fits_error( int status ) +{ + char buf[80]; + + fits_get_errstatus( status, buf ); + vips_error( "fits", "%s", buf ); +} + +/* Shut down. Can be called many times. + */ +static void +vips_fits_close( VipsFits *fits ) +{ + VIPS_FREE( fits->filename ); + VIPS_FREEF( g_mutex_free, fits->lock ); + + if( fits->fptr ) { + int status; + + status = 0; + + if( fits_close_file( fits->fptr, &status ) ) + vips_fits_error( status ); + + fits->fptr = NULL; + } + + VIPS_FREE( fits->buffer ); +} + +static void +vips_fits_close_cb( VipsImage *image, VipsFits *fits ) +{ + vips_fits_close( fits ); +} + +static VipsFits * +vips_fits_new_read( const char *filename, VipsImage *out, int band_select ) +{ + VipsFits *fits; + int status; + + if( !(fits = VIPS_NEW( out, VipsFits )) ) + return( NULL ); + + fits->filename = vips_strdup( NULL, filename ); + fits->image = out; + fits->fptr = NULL; + fits->lock = NULL; + fits->band_select = band_select; + fits->buffer = NULL; + g_signal_connect( out, "close", + G_CALLBACK( vips_fits_close_cb ), fits ); + + status = 0; + if( fits_open_file( &fits->fptr, filename, READONLY, &status ) ) { + vips_error( "fits", _( "unable to open \"%s\"" ), filename ); + vips_fits_error( status ); + return( NULL ); + } + + fits->lock = g_mutex_new(); + + return( fits ); +} + +/* fits image types -> VIPS band formats. VIPS doesn't have 64-bit int, so no + * entry for LONGLONG_IMG (64). + */ +static int fits2vips_formats[][3] = { + { BYTE_IMG, VIPS_FORMAT_UCHAR, TBYTE }, + { SHORT_IMG, VIPS_FORMAT_USHORT, TUSHORT }, + { LONG_IMG, VIPS_FORMAT_UINT, TUINT }, + { FLOAT_IMG, VIPS_FORMAT_FLOAT, TFLOAT }, + { DOUBLE_IMG, VIPS_FORMAT_DOUBLE, TDOUBLE } +}; + +static int +vips_fits_get_header( VipsFits *fits, VipsImage *out ) +{ + int status; + int bitpix; + + int width, height, bands, format, type; + int keysexist; + int i; + + status = 0; + + if( fits_get_img_paramll( fits->fptr, + 10, &bitpix, &fits->naxis, fits->naxes, &status ) ) { + vips_fits_error( status ); + return( -1 ); + } + +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); + for( i = 0; i < fits->naxis; i++ ) + VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); +#endif /*VIPS_DEBUG*/ + + width = 1; + height = 1; + bands = 1; + switch( fits->naxis ) { + /* If you add more dimensions here, adjust data read below. See also + * the definition of MAX_DIMENSIONS above. + */ + case 10: + case 9: + case 8: + case 7: + case 6: + case 5: + case 4: + for( i = fits->naxis; i > 3; i-- ) + if( fits->naxes[i - 1] != 1 ) { + vips_error( "fits", + "%s", _( "dimensions above 3 " + "must be size 1" ) ); + return( -1 ); + } + + case 3: + bands = fits->naxes[2]; + + case 2: + height = fits->naxes[1]; + + case 1: + width = fits->naxes[0]; + break; + + default: + vips_error( "fits", _( "bad number of axis %d" ), fits->naxis ); + return( -1 ); + } + + /* Are we in one-band mode? + */ + if( fits->band_select != -1 ) + bands = 1; + + /* Get image format. We want the 'raw' format of the image, our caller + * can convert using the meta info if they want. + */ + for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) + if( fits2vips_formats[i][0] == bitpix ) + break; + if( i == VIPS_NUMBER( fits2vips_formats ) ) { + vips_error( "fits", _( "unsupported bitpix %d\n" ), + bitpix ); + return( -1 ); + } + format = fits2vips_formats[i][1]; + fits->datatype = fits2vips_formats[i][2]; + + if( bands == 1 ) { + if( format == VIPS_FORMAT_USHORT ) + type = VIPS_INTERPRETATION_GREY16; + else + type = VIPS_INTERPRETATION_B_W; + } + else if( bands == 3 ) { + if( format == VIPS_FORMAT_USHORT ) + type = VIPS_INTERPRETATION_RGB16; + else + type = VIPS_INTERPRETATION_RGB; + } + else + type = VIPS_INTERPRETATION_MULTIBAND; + + vips_image_init_fields( out, + width, height, bands, + format, + VIPS_CODING_NONE, type, 1.0, 1.0 ); + vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + + /* Read all keys into meta. + */ + if( fits_get_hdrspace( fits->fptr, &keysexist, NULL, &status ) ) { + vips_fits_error( status ); + return( -1 ); + } + + for( i = 0; i < keysexist; i++ ) { + char record[81]; + char vipsname[100]; + + if( fits_read_record( fits->fptr, i + 1, record, &status ) ) { + vips_fits_error( status ); + return( -1 ); + } + + VIPS_DEBUG_MSG( "fits2vips: setting meta on vips image:\n" ); + VIPS_DEBUG_MSG( " record == \"%s\"\n", record ); + + /* FITS lets keys repeat. For example, HISTORY appears many + * times, each time with a fresh line of history attached. We + * have to include the key index in the vips name we assign. + */ + + vips_snprintf( vipsname, 100, "fits-%d", i ); + vips_image_set_string( out, vipsname, record ); + } + + return( 0 ); +} + +int +vips__fits_read_header( const char *filename, VipsImage *out ) +{ + VipsFits *fits; + + VIPS_DEBUG_MSG( "fits2vips_header: reading \"%s\"\n", filename ); + + if( !(fits = vips_fits_new_read( filename, out, -1 )) || + vips_fits_get_header( fits, out ) ) + return( -1 ); + + return( 0 ); +} + +static int +fits2vips_generate( VipsRegion *out, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsFits *fits = (VipsFits *) a; + Rect *r = &out->valid; + + PEL *q; + int z; + int status; + + long fpixel[MAX_DIMENSIONS]; + long lpixel[MAX_DIMENSIONS]; + long inc[MAX_DIMENSIONS]; + + status = 0; + + VIPS_DEBUG_MSG( "fits2vips_generate: " + "generating left = %d, top = %d, width = %d, height = %d\n", + r->left, r->top, r->width, r->height ); + + /* Special case: the region we are writing to is exactly the width we + * need, ie. we can read a rectangular area into it. + */ + if( VIPS_REGION_LSKIP( out ) == VIPS_REGION_SIZEOF_LINE( out ) ) { + VIPS_DEBUG_MSG( "fits2vips_generate: block read\n" ); + + for( z = 0; z < MAX_DIMENSIONS; z++ ) + fpixel[z] = 1; + fpixel[0] = r->left + 1; + fpixel[1] = r->top + 1; + fpixel[2] = fits->band_select + 1; + + for( z = 0; z < MAX_DIMENSIONS; z++ ) + lpixel[z] = 1; + lpixel[0] = VIPS_RECT_RIGHT( r ); + lpixel[1] = VIPS_RECT_BOTTOM( r ); + lpixel[2] = fits->band_select + 1; + + for( z = 0; z < MAX_DIMENSIONS; z++ ) + inc[z] = 1; + + q = (PEL *) VIPS_REGION_ADDR( out, r->left, r->top ); + + /* Break on ffgsv() for this call. + */ + g_mutex_lock( fits->lock ); + if( fits_read_subset( fits->fptr, fits->datatype, + fpixel, lpixel, inc, + NULL, q, NULL, &status ) ) { + vips_fits_error( status ); + g_mutex_unlock( fits->lock ); + return( -1 ); + } + g_mutex_unlock( fits->lock ); + } + else { + int y; + + for( y = r->top; y < VIPS_RECT_BOTTOM( r ); y ++ ) { + for( z = 0; z < MAX_DIMENSIONS; z++ ) + fpixel[z] = 1; + fpixel[0] = r->left + 1; + fpixel[1] = y + 1; + fpixel[2] = fits->band_select + 1; + + for( z = 0; z < MAX_DIMENSIONS; z++ ) + lpixel[z] = 1; + lpixel[0] = VIPS_RECT_RIGHT( r ); + lpixel[1] = y + 1; + lpixel[2] = fits->band_select + 1; + + for( z = 0; z < MAX_DIMENSIONS; z++ ) + inc[z] = 1; + + q = (PEL *) VIPS_REGION_ADDR( out, r->left, y ); + + /* Break on ffgsv() for this call. + */ + g_mutex_lock( fits->lock ); + if( fits_read_subset( fits->fptr, fits->datatype, + fpixel, lpixel, inc, + NULL, q, NULL, &status ) ) { + vips_fits_error( status ); + g_mutex_unlock( fits->lock ); + return( -1 ); + } + g_mutex_unlock( fits->lock ); + } + } + + return( 0 ); +} + +static int +fits2vips( const char *filename, VipsImage *out, int band_select ) +{ + VipsFits *fits; + + /* The -1 mode is just for reading the header. + */ + g_assert( band_select >= 0 ); + + if( !(fits = vips_fits_new_read( filename, out, band_select )) ) + return( -1 ); + if( vips_fits_get_header( fits, out ) || + vips_image_generate( out, + NULL, fits2vips_generate, NULL, fits, NULL ) ) { + vips_fits_close( fits ); + return( -1 ); + } + + /* Don't vips_fits_close(), we need it to stick around for the + * generate. + */ + + return( 0 ); +} + +int +vips__fits_read( const char *filename, VipsImage *out ) +{ + VipsImage *t; + int n_bands; + + VIPS_DEBUG_MSG( "fits2vips: reading \"%s\"\n", filename ); + + /* fits is naturally a band-separated format. For single-band images + * we can just read out. For many bands we read each band out + * separately then join them. + */ + + t = vips_image_new(); + vips_object_local( out, t ); + if( vips__fits_read_header( filename, t ) ) + return( -1 ); + n_bands = t->Bands; + + if( n_bands == 1 ) { + t = vips_image_new(); + vips_object_local( out, t ); + if( fits2vips( filename, t, 0 ) ) + return( -1 ); + } + else { + VipsImage *acc; + int i; + + acc = NULL; + for( i = 0; i < n_bands; i++ ) { + t = vips_image_new(); + vips_object_local( out, t ); + if( fits2vips( filename, t, i ) ) + return( -1 ); + + if( !acc ) + acc = t; + else { + if( vips_bandjoin2( acc, t, &acc, NULL ) ) + return( -1 ); + vips_object_local( out, acc ); + } + } + + t = acc; + } + + if( vips_image_write( t, out ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__fits_isfits( const char *filename ) +{ + fitsfile *fptr; + int status; + + VIPS_DEBUG_MSG( "isfits: testing \"%s\"\n", filename ); + + status = 0; + + if( fits_open_image( &fptr, filename, READONLY, &status ) ) { + VIPS_DEBUG_MSG( "isfits: error reading \"%s\"\n", filename ); +#ifdef VIPS_DEBUG + vips_fits_error( status ); +#endif /*VIPS_DEBUG*/ + + return( 0 ); + } + fits_close_file( fptr, &status ); + + return( 1 ); +} + +static VipsFits * +vips_fits_new_write( VipsImage *in, const char *filename ) +{ + VipsFits *fits; + int status; + + status = 0; + + if( !(fits = VIPS_NEW( in, VipsFits )) ) + return( NULL ); + fits->filename = vips_strdup( in, filename ); + fits->image = in; + fits->fptr = NULL; + fits->lock = NULL; + fits->band_select = -1; + fits->buffer = NULL; + g_signal_connect( in, "close", + G_CALLBACK( vips_fits_close_cb ), fits ); + + if( !(fits->filename = vips_strdup( NULL, filename )) ) + return( NULL ); + + /* We need to be able to hold one scanline of one band. + */ + if( !(fits->buffer = VIPS_ARRAY( NULL, + VIPS_IMAGE_SIZEOF_ELEMENT( in ) * in->Xsize, PEL )) ) + return( NULL ); + + /* fits_create_file() will fail if there's a file of thet name, unless + * we put a "!" in front ofthe filename. This breaks conventions with + * the rest of vips, so just unlink explicitly. + */ + g_unlink( filename ); + + if( fits_create_file( &fits->fptr, filename, &status ) ) { + vips_error( "fits", + _( "unable to write to \"%s\"" ), filename ); + vips_fits_error( status ); + return( NULL ); + } + + fits->lock = g_mutex_new(); + + return( fits ); +} + +static void * +vips_fits_write_meta( VipsImage *image, + const char *field, GValue *value, void *a ) +{ + VipsFits *fits = (VipsFits *) a; + + int status; + const char *value_str; + + status = 0; + + /* We want fields which start "fits-". + */ + if( !vips_isprefix( "fits-", field ) ) + return( NULL ); + + /* The value should be a refstring, since we wrote it in fits2vips + * above ^^. + */ + value_str = vips_value_get_ref_string( value, NULL ); + + VIPS_DEBUG_MSG( "vips_fits_write_meta: setting meta on fits image:\n" ); + VIPS_DEBUG_MSG( " value == \"%s\"\n", value_str ); + + if( fits_write_record( fits->fptr, value_str, &status ) ) { + vips_fits_error( status ); + return( a ); + } + + return( NULL ); +} + +static int +vips_fits_set_header( VipsFits *fits, VipsImage *in ) +{ + int status; + int bitpix; + int i; + + status = 0; + + fits->naxis = 3; + fits->naxes[0] = in->Xsize; + fits->naxes[1] = in->Ysize; + fits->naxes[2] = in->Bands; + + for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) + if( fits2vips_formats[i][1] == in->BandFmt ) + break; + if( i == VIPS_NUMBER( fits2vips_formats ) ) { + vips_error( "fits", + _( "unsupported BandFmt %d\n" ), in->BandFmt ); + return( -1 ); + } + bitpix = fits2vips_formats[i][0]; + fits->datatype = fits2vips_formats[i][2]; + +#ifdef VIPS_DEBUG + VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); + for( i = 0; i < fits->naxis; i++ ) + VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); + VIPS_DEBUG_MSG( "bitpix = %d\n", bitpix ); +#endif /*VIPS_DEBUG*/ + + if( fits_create_imgll( fits->fptr, bitpix, fits->naxis, + fits->naxes, &status ) ) { + vips_fits_error( status ); + return( -1 ); + } + + if( vips_image_map( in, + (VipsImageMapFn) vips_fits_write_meta, fits ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_fits_write( VipsRegion *region, VipsRect *area, void *a ) +{ + VipsFits *fits = (VipsFits *) a; + VipsImage *image = fits->image; + int es = VIPS_IMAGE_SIZEOF_ELEMENT( image ); + int ps = VIPS_IMAGE_SIZEOF_PEL( image ); + + int status; + int y, b, x, k; + + status = 0; + + VIPS_DEBUG_MSG( "vips_fits_write: " + "writing left = %d, top = %d, width = %d, height = %d\n", + area->left, area->top, area->width, area->height ); + + /* We need to write a band at a time. We can't bandsplit in vips, + * since vips_sink_disc() can't loop over many images at once, sadly. + */ + + for( y = 0; y < area->height; y++ ) { + PEL *p = (PEL *) VIPS_REGION_ADDR( region, + area->left, area->top + y ); + + for( b = 0; b < image->Bands; b++ ) { + PEL *p1, *q; + long fpixel[3]; + + p1 = p + b * es; + q = fits->buffer; + + for( x = 0; x < area->width; x++ ) { + for( k = 0; k < es; k++ ) + q[k] = p1[k]; + + q += es; + p1 += ps; + } + + fpixel[0] = area->left + 1; + fpixel[1] = area->top + y + 1; + fpixel[2] = b + 1; + + /* No need to lock, write functions are single-threaded. + */ + + if( fits_write_pix( fits->fptr, fits->datatype, + fpixel, area->width, fits->buffer, + &status ) ) { + vips_fits_error( status ); + return( -1 ); + } + } + } + + return( 0 ); +} + +int +vips__fits_write( VipsImage *in, const char *filename ) +{ + VipsFits *fits; + + VIPS_DEBUG_MSG( "vips2fits: writing \"%s\"\n", filename ); + + if( !(fits = vips_fits_new_write( in, filename )) ) + return( -1 ); + + if( vips_fits_set_header( fits, fits->image ) || + vips_sink_disc( fits->image, vips_fits_write, fits ) ) { + vips_fits_close( fits ); + return( -1 ); + } + vips_fits_close( fits ); + + return( 0 ); +} diff --git a/libvips/foreign/fits.h b/libvips/foreign/fits.h new file mode 100644 index 00000000..b85e8d97 --- /dev/null +++ b/libvips/foreign/fits.h @@ -0,0 +1,49 @@ +/* defs for fits read/write + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU 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 + + */ + +#ifndef VIPS_FITS_H +#define VIPS_FITS_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +extern const char *vips__fits_suffs[]; + +int vips__fits_isfits( const char *filename ); +int vips__fits_read_header( const char *filename, VipsImage *out ); +int vips__fits_read( const char *filename, VipsImage *out ); + +int vips__fits_write( VipsImage *in, const char *filename ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_FITS_H*/ diff --git a/libvips/foreign/fitsload.c b/libvips/foreign/fitsload.c new file mode 100644 index 00000000..71197492 --- /dev/null +++ b/libvips/foreign/fitsload.c @@ -0,0 +1,119 @@ +/* load fits from a file + * + * 5/12/11 + * - from openslideload.c + */ + +/* + + 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 +#include +#include + +#include "fits.h" + +typedef struct _VipsForeignLoadFits { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadFits; + +typedef VipsForeignLoadClass VipsForeignLoadFitsClass; + +G_DEFINE_TYPE( VipsForeignLoadFits, vips_foreign_load_fits, + VIPS_TYPE_FOREIGN_LOAD ); + +static int +vips_foreign_load_fits_header( VipsForeignLoad *load ) +{ + VipsForeignLoadFits *fits = (VipsForeignLoadFits *) load; + + if( vips__fits_read_header( fits->filename, load->out ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_fits_load( VipsForeignLoad *load ) +{ + VipsForeignLoadFits *fits = (VipsForeignLoadFits *) load; + + if( vips__fits_read( fits->filename, load->real ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_fits_class_init( VipsForeignLoadFitsClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "fitsload"; + object_class->description = _( "load a FITS image" ); + + foreign_class->suffs = vips__fits_suffs; + + load_class->is_a = vips__fits_isfits; + load_class->header = vips_foreign_load_fits_header; + load_class->load = vips_foreign_load_fits_load; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadFits, filename ), + NULL ); +} + +static void +vips_foreign_load_fits_init( VipsForeignLoadFits *fits ) +{ +} diff --git a/libvips/foreign/fitssave.c b/libvips/foreign/fitssave.c new file mode 100644 index 00000000..ca9a79c5 --- /dev/null +++ b/libvips/foreign/fitssave.c @@ -0,0 +1,130 @@ +/* save to fits + * + * 2/12/11 + * - wrap a class around the fits writer + */ + +/* + + 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_VERBOSE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "fits.h" + +typedef struct _VipsForeignSaveFits { + VipsForeignSave parent_object; + + /* Filename for save. + */ + char *filename; + +} VipsForeignSaveFits; + +typedef VipsForeignSaveClass VipsForeignSaveFitsClass; + +G_DEFINE_TYPE( VipsForeignSaveFits, vips_foreign_save_fits, + VIPS_TYPE_FOREIGN_SAVE ); + +static int +vips_foreign_save_fits_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSaveFits *fits = (VipsForeignSaveFits *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_fits_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__fits_write( save->ready, fits->filename ) ) + return( -1 ); + + return( 0 ); +} + +/* Save a bit of typing. + */ +#define UC VIPS_FORMAT_UCHAR +#define C VIPS_FORMAT_CHAR +#define US VIPS_FORMAT_USHORT +#define S VIPS_FORMAT_SHORT +#define UI VIPS_FORMAT_UINT +#define I VIPS_FORMAT_INT +#define F VIPS_FORMAT_FLOAT +#define X VIPS_FORMAT_COMPLEX +#define D VIPS_FORMAT_DOUBLE +#define DX VIPS_FORMAT_DPCOMPLEX + +static int bandfmt_fits[10] = { +/* UC C US S UI I F X D DX */ + UC, UC, US, US, UI, UI, F, X, D, DX +}; + +static void +vips_foreign_save_fits_class_init( VipsForeignSaveFitsClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "fitssave"; + object_class->description = _( "save image to fits file" ); + object_class->build = vips_foreign_save_fits_build; + + foreign_class->suffs = vips__fits_suffs; + + save_class->saveable = VIPS_SAVEABLE_ANY; + save_class->format_table = bandfmt_fits; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveFits, filename ), + NULL ); +} + +static void +vips_foreign_save_fits_init( VipsForeignSaveFits *fits ) +{ +} diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index e2371fd8..84bf16b2 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1154,6 +1154,8 @@ vips_foreign_write_options( VipsImage *in, const char *filename ) void vips_foreign_operation_init( void ) { + extern GType vips_foreign_load_fits_get_type( void ); + extern GType vips_foreign_save_fits_get_type( void ); extern GType vips_foreign_load_openexr_get_type( void ); extern GType vips_foreign_load_openslide_get_type( void ); extern GType vips_foreign_load_jpeg_file_get_type( void ); @@ -1183,6 +1185,11 @@ vips_foreign_operation_init( void ) vips_foreign_load_openslide_get_type(); #endif /*HAVE_OPENSLIDE*/ +#ifdef HAVE_CFITSIO + vips_foreign_load_fits_get_type(); + vips_foreign_save_fits_get_type(); +#endif /*HAVE_CFITSIO*/ + #ifdef HAVE_OPENEXR vips_foreign_load_openexr_get_type(); #endif /*HAVE_OPENEXR*/ @@ -1600,3 +1607,53 @@ vips_openslideload( const char *filename, VipsImage **out, ... ) return( result ); } + +/** + * vips_fitsload: + * @filename: file to load + * @out: decompressed image + * @...: %NULL-terminated list of optional named arguments + * + * Read a FITS image file into a VIPS image. + * + * See also: vips_image_new_from_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_fitsload( const char *filename, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "fitsload", ap, filename, out ); + va_end( ap ); + + return( result ); +} + +/** + * vips_fitssave: + * @in: image to save + * @filename: file to write to + * @...: %NULL-terminated list of optional named arguments + * + * Write a VIPS image to a file as FITS. + * + * See also: vips_image_write_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_fitssave( VipsImage *in, const char *filename, ... ) +{ + va_list ap; + int result; + + va_start( ap, filename ); + result = vips_call_split( "fitssave", ap, in, filename ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/format/fits.c b/libvips/format/fits.c index 5101823a..fadfc083 100644 --- a/libvips/format/fits.c +++ b/libvips/format/fits.c @@ -1,24 +1,7 @@ /* Read FITS files with cfitsio * - * 26/10/10 - * - from matlab.c - * 27/10/10 - * - oops, forgot to init status in close - * 30/11/10 - * - set RGB16/GREY16 if appropriate - * - allow up to 10 dimensions as long as they are empty - * 27/1/11 - * - lazy read - * 31/1/11 - * - read in planes and combine with im_bandjoin() - * - read whole tiles with fits_read_subset() when we can - * 17/3/11 - * - renames, updates etc. ready for adding fits write - * - fits write! - * 21/3/11 - * - read/write metadata as whole records to avoid changing things - * - cast input to a supported format - * - bandsplit for write + * 13/12/11 + * - just a compat stub now */ /* @@ -47,32 +30,11 @@ */ -/* -#define VIPS_DEBUG - */ - #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include -#ifndef HAVE_CFITSIO - -#include - -/* We call this from format_dispatch.c so we need a stub. - */ -int -im_fits2vips( const char *filename, VipsImage *out ) -{ - im_error( "im_fits2vips", "%s", - _( "FITS support disabled" ) ); - - return( -1 ); -} - -#else /*HAVE_CFITSIO*/ - #include #include #include @@ -81,741 +43,37 @@ im_fits2vips( const char *filename, VipsImage *out ) #include #include -#include - -/* - - TODO - - - ask Doug for a test colour image - - found WFPC2u5780205r_c0fx.fits on the fits samples page, - but it's tiny - - - test performance - - */ - -/* vips only supports 3 dimensions, but we allow up to MAX_DIMENSIONS as long - * as the higher dimensions are all empty. If you change this value, change - * fits2vips_get_header() as well. - */ -#define MAX_DIMENSIONS (10) - -/* What we track during a cfitsio-file read or write. - */ -typedef struct { - char *filename; - VipsImage *image; - - fitsfile *fptr; - int datatype; - int naxis; - long long int naxes[MAX_DIMENSIONS]; - - GMutex *lock; /* Lock fits_*() calls with this */ - - /* Set this to -1 to read all bands, or a +ve int to read a specific - * band. - */ - int band_select; - - /* We split bands up for write into this buffer. - */ - PEL *buffer; -} VipsFits; - -static void -vips_fits_error( int status ) -{ - char buf[80]; - - fits_get_errstatus( status, buf ); - im_error( "fits", "%s", buf ); -} - -/* Shut down. Can be called many times. - */ -static void -vips_fits_close( VipsFits *fits ) -{ - VIPS_FREE( fits->filename ); - VIPS_FREEF( g_mutex_free, fits->lock ); - - if( fits->fptr ) { - int status; - - status = 0; - - if( fits_close_file( fits->fptr, &status ) ) - vips_fits_error( status ); - - fits->fptr = NULL; - } - - VIPS_FREE( fits->buffer ); -} - -static void -vips_fits_close_cb( VipsImage *image, VipsFits *fits ) -{ - vips_fits_close( fits ); -} - -static VipsFits * -vips_fits_new_read( const char *filename, VipsImage *out, int band_select ) -{ - VipsFits *fits; - int status; - - if( !(fits = VIPS_NEW( out, VipsFits )) ) - return( NULL ); - - fits->filename = im_strdup( NULL, filename ); - fits->image = out; - fits->fptr = NULL; - fits->lock = NULL; - fits->band_select = band_select; - fits->buffer = NULL; - g_signal_connect( out, "close", - G_CALLBACK( vips_fits_close_cb ), fits ); - - status = 0; - if( fits_open_file( &fits->fptr, filename, READONLY, &status ) ) { - im_error( "fits", _( "unable to open \"%s\"" ), filename ); - vips_fits_error( status ); - return( NULL ); - } - - fits->lock = g_mutex_new(); - - return( fits ); -} - -/* fits image types -> VIPS band formats. VIPS doesn't have 64-bit int, so no - * entry for LONGLONG_IMG (64). - */ -static int fits2vips_formats[][3] = { - { BYTE_IMG, VIPS_FORMAT_UCHAR, TBYTE }, - { SHORT_IMG, VIPS_FORMAT_USHORT, TUSHORT }, - { LONG_IMG, VIPS_FORMAT_UINT, TUINT }, - { FLOAT_IMG, VIPS_FORMAT_FLOAT, TFLOAT }, - { DOUBLE_IMG, VIPS_FORMAT_DOUBLE, TDOUBLE } -}; - -static int -vips_fits_get_header( VipsFits *fits, VipsImage *out ) -{ - int status; - int bitpix; - - int width, height, bands, format, type; - int keysexist; - int i; - - status = 0; - - if( fits_get_img_paramll( fits->fptr, - 10, &bitpix, &fits->naxis, fits->naxes, &status ) ) { - vips_fits_error( status ); - return( -1 ); - } - -#ifdef VIPS_DEBUG - VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); - for( i = 0; i < fits->naxis; i++ ) - VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); -#endif /*VIPS_DEBUG*/ - - width = 1; - height = 1; - bands = 1; - switch( fits->naxis ) { - /* If you add more dimensions here, adjust data read below. See also - * the definition of MAX_DIMENSIONS above. - */ - case 10: - case 9: - case 8: - case 7: - case 6: - case 5: - case 4: - for( i = fits->naxis; i > 3; i-- ) - if( fits->naxes[i - 1] != 1 ) { - im_error( "fits", "%s", _( "dimensions above 3 " - "must be size 1" ) ); - return( -1 ); - } - - case 3: - bands = fits->naxes[2]; - - case 2: - height = fits->naxes[1]; - - case 1: - width = fits->naxes[0]; - break; - - default: - im_error( "fits", _( "bad number of axis %d" ), fits->naxis ); - return( -1 ); - } - - /* Are we in one-band mode? - */ - if( fits->band_select != -1 ) - bands = 1; - - /* Get image format. We want the 'raw' format of the image, our caller - * can convert using the meta info if they want. - */ - for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) - if( fits2vips_formats[i][0] == bitpix ) - break; - if( i == VIPS_NUMBER( fits2vips_formats ) ) { - im_error( "fits", _( "unsupported bitpix %d\n" ), - bitpix ); - return( -1 ); - } - format = fits2vips_formats[i][1]; - fits->datatype = fits2vips_formats[i][2]; - - if( bands == 1 ) { - if( format == VIPS_FORMAT_USHORT ) - type = VIPS_INTERPRETATION_GREY16; - else - type = VIPS_INTERPRETATION_B_W; - } - else if( bands == 3 ) { - if( format == VIPS_FORMAT_USHORT ) - type = VIPS_INTERPRETATION_RGB16; - else - type = VIPS_INTERPRETATION_RGB; - } - else - type = VIPS_INTERPRETATION_MULTIBAND; - - im_initdesc( out, - width, height, bands, - im_bits_of_fmt( format ), format, - VIPS_CODING_NONE, type, 1.0, 1.0, 0, 0 ); - - /* Read all keys into meta. - */ - if( fits_get_hdrspace( fits->fptr, &keysexist, NULL, &status ) ) { - vips_fits_error( status ); - return( -1 ); - } - - for( i = 0; i < keysexist; i++ ) { - char record[81]; - char vipsname[100]; - - if( fits_read_record( fits->fptr, i + 1, record, &status ) ) { - vips_fits_error( status ); - return( -1 ); - } - - VIPS_DEBUG_MSG( "fits2vips: setting meta on vips image:\n" ); - VIPS_DEBUG_MSG( " record == \"%s\"\n", record ); - - /* FITS lets keys repeat. For example, HISTORY appears many - * times, each time with a fresh line of history attached. We - * have to include the key index in the vips name we assign. - */ - - im_snprintf( vipsname, 100, "fits-%d", i ); - if( im_meta_set_string( out, vipsname, record ) ) - return( -1 ); - } - - return( 0 ); -} - -static int -fits2vips_header( const char *filename, VipsImage *out ) -{ - VipsFits *fits; - - VIPS_DEBUG_MSG( "fits2vips_header: reading \"%s\"\n", filename ); - - if( !(fits = vips_fits_new_read( filename, out, -1 )) || - vips_fits_get_header( fits, out ) ) - return( -1 ); - - return( 0 ); -} - -static int -fits2vips_generate( VipsRegion *out, void *seq, void *a, void *b ) -{ - VipsFits *fits = (VipsFits *) a; - Rect *r = &out->valid; - - PEL *q; - int z; - int status; - - long fpixel[MAX_DIMENSIONS]; - long lpixel[MAX_DIMENSIONS]; - long inc[MAX_DIMENSIONS]; - - status = 0; - - VIPS_DEBUG_MSG( "fits2vips_generate: " - "generating left = %d, top = %d, width = %d, height = %d\n", - r->left, r->top, r->width, r->height ); - - /* Special case: the region we are writing to is exactly the width we - * need, ie. we can read a rectangular area into it. - */ - if( VIPS_REGION_LSKIP( out ) == VIPS_REGION_SIZEOF_LINE( out ) ) { - VIPS_DEBUG_MSG( "fits2vips_generate: block read\n" ); - - for( z = 0; z < MAX_DIMENSIONS; z++ ) - fpixel[z] = 1; - fpixel[0] = r->left + 1; - fpixel[1] = r->top + 1; - fpixel[2] = fits->band_select + 1; - - for( z = 0; z < MAX_DIMENSIONS; z++ ) - lpixel[z] = 1; - lpixel[0] = VIPS_RECT_RIGHT( r ); - lpixel[1] = VIPS_RECT_BOTTOM( r ); - lpixel[2] = fits->band_select + 1; - - for( z = 0; z < MAX_DIMENSIONS; z++ ) - inc[z] = 1; - - q = (PEL *) VIPS_REGION_ADDR( out, r->left, r->top ); - - /* Break on ffgsv() for this call. - */ - g_mutex_lock( fits->lock ); - if( fits_read_subset( fits->fptr, fits->datatype, - fpixel, lpixel, inc, - NULL, q, NULL, &status ) ) { - vips_fits_error( status ); - g_mutex_unlock( fits->lock ); - return( -1 ); - } - g_mutex_unlock( fits->lock ); - } - else { - int y; - - for( y = r->top; y < VIPS_RECT_BOTTOM( r ); y ++ ) { - for( z = 0; z < MAX_DIMENSIONS; z++ ) - fpixel[z] = 1; - fpixel[0] = r->left + 1; - fpixel[1] = y + 1; - fpixel[2] = fits->band_select + 1; - - for( z = 0; z < MAX_DIMENSIONS; z++ ) - lpixel[z] = 1; - lpixel[0] = VIPS_RECT_RIGHT( r ); - lpixel[1] = y + 1; - lpixel[2] = fits->band_select + 1; - - for( z = 0; z < MAX_DIMENSIONS; z++ ) - inc[z] = 1; - - q = (PEL *) VIPS_REGION_ADDR( out, r->left, y ); - - /* Break on ffgsv() for this call. - */ - g_mutex_lock( fits->lock ); - if( fits_read_subset( fits->fptr, fits->datatype, - fpixel, lpixel, inc, - NULL, q, NULL, &status ) ) { - vips_fits_error( status ); - g_mutex_unlock( fits->lock ); - return( -1 ); - } - g_mutex_unlock( fits->lock ); - } - } - - return( 0 ); -} - -static int -fits2vips( const char *filename, VipsImage *out, int band_select ) -{ - VipsFits *fits; - - /* The -1 mode is just for reading the header. - */ - g_assert( band_select >= 0 ); - - if( !(fits = vips_fits_new_read( filename, out, band_select )) ) - return( -1 ); - if( vips_fits_get_header( fits, out ) || - im_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ) || - im_generate( out, - NULL, fits2vips_generate, NULL, fits, NULL ) ) { - vips_fits_close( fits ); - return( -1 ); - } - - /* Don't vips_fits_close(), we need it to stick around for the - * generate. - */ - - return( 0 ); -} - -/** - * im_fits2vips: - * @filename: file to load - * @out: image to write to - * - * Read a FITS image file into a VIPS image. - * - * See also: im_vips2fits(), #VipsFormat. - * - * Returns: 0 on success, -1 on error. - */ int im_fits2vips( const char *filename, VipsImage *out ) { VipsImage *t; - int n_bands; - VIPS_DEBUG_MSG( "im_fits2vips: reading \"%s\"\n", filename ); - - /* fits is naturally a band-separated format. For single-band images, - * we can just read out. For many bands, we read each band out - * separately then join them. - */ - - if( !(t = vips_image_new()) || - vips_object_local( out, t ) || - fits2vips_header( filename, t ) ) + if( vips_fitsload( filename, &t, NULL ) ) return( -1 ); - n_bands = t->Bands; - - if( n_bands == 1 ) { - if( !(t = vips_image_new()) || - vips_object_local( out, t ) || - fits2vips( filename, t, 0 ) ) - return( -1 ); - } - else { - VipsImage *acc; - int i; - - acc = NULL; - for( i = 0; i < n_bands; i++ ) { - if( !(t = vips_image_new()) || - vips_object_local( out, t ) || - fits2vips( filename, t, i ) ) - return( -1 ); - - if( !acc ) - acc = t; - else { - VipsImage *t2; - - if( !(t2 = vips_image_new()) || - vips_object_local( out, t2 ) || - im_bandjoin( acc, t, t2 ) ) - return( -1 ); - acc = t2; - } - } - - t = acc; - } - - /* fits has inverted y. - */ - if( im_flipver( t, out ) ) + if( vips_image_write( t, out ) ) { + g_object_unref( t ); return( -1 ); + } + g_object_unref( t ); return( 0 ); } -static int -isfits( const char *filename ) -{ - fitsfile *fptr; - int status; - - VIPS_DEBUG_MSG( "isfits: testing \"%s\"\n", filename ); - - status = 0; - - if( fits_open_image( &fptr, filename, READONLY, &status ) ) { - VIPS_DEBUG_MSG( "isfits: error reading \"%s\"\n", filename ); -#ifdef VIPS_DEBUG - vips_fits_error( status ); -#endif /*VIPS_DEBUG*/ - - return( 0 ); - } - fits_close_file( fptr, &status ); - - return( 1 ); -} - -/* Save a bit of typing. - */ -#define UC IM_BANDFMT_UCHAR -#define C IM_BANDFMT_CHAR -#define US IM_BANDFMT_USHORT -#define S IM_BANDFMT_SHORT -#define UI IM_BANDFMT_UINT -#define I IM_BANDFMT_INT -#define F IM_BANDFMT_FLOAT -#define X IM_BANDFMT_COMPLEX -#define D IM_BANDFMT_DOUBLE -#define DX IM_BANDFMT_DPCOMPLEX - -/* Type promotion for fits write. fits only has the unsigned int types, plus - * float and double. - */ -static int vips_fits_bandfmt[10] = { -/* UC C US S UI I F X D DX */ - UC, UC, US, US, UI, UI, F, X, D, DX -}; - -static VipsFits * -vips_fits_new_write( VipsImage *in, const char *filename ) -{ - VipsImage *flip; - VipsImage *type; - VipsFits *fits; - int status; - - status = 0; - - if( im_check_noncomplex( "im_vips2fits", in ) || - im_check_uncoded( "im_vips2fits", in ) ) - return( NULL ); - - /* Cast to a supported format. - */ - if( !(type = vips_image_new()) || - vips_object_local( in, type ) || - im_clip2fmt( in, type, vips_fits_bandfmt[in->BandFmt] ) ) - return( NULL ); - in = type; - - /* FITS has (0,0) in the bottom left, we need to flip. - */ - if( !(flip = vips_image_new()) || - vips_object_local( in, flip ) || - im_flipver( in, flip ) ) - return( NULL ); - in = flip; - - if( !(fits = VIPS_NEW( in, VipsFits )) ) - return( NULL ); - fits->filename = im_strdup( NULL, filename ); - fits->image = in; - fits->fptr = NULL; - fits->lock = NULL; - fits->band_select = -1; - fits->buffer = NULL; - g_signal_connect( in, "close", - G_CALLBACK( vips_fits_close_cb ), fits ); - - if( !(fits->filename = im_strdup( NULL, filename )) ) - return( NULL ); - - /* We need to be able to hold one scanline of one band. - */ - if( !(fits->buffer = VIPS_ARRAY( NULL, - VIPS_IMAGE_SIZEOF_ELEMENT( in ) * in->Xsize, PEL )) ) - return( NULL ); - - /* fits_create_file() will fail if there's a file of thet name, unless - * we put a "!" in front ofthe filename. This breaks conventions with - * the rest of vips, so just unlink explicitly. - */ - g_unlink( filename ); - - if( fits_create_file( &fits->fptr, filename, &status ) ) { - im_error( "fits", _( "unable to write to \"%s\"" ), filename ); - vips_fits_error( status ); - return( NULL ); - } - - fits->lock = g_mutex_new(); - - return( fits ); -} - -static void * -vips_fits_write_meta( VipsImage *image, - const char *field, GValue *value, void *a ) -{ - VipsFits *fits = (VipsFits *) a; - - int status; - const char *value_str; - - status = 0; - - /* We want fields which start "fits-". - */ - if( !im_isprefix( "fits-", field ) ) - return( NULL ); - - /* The value should be a refstring, since we wrote it in fits2vips - * above ^^. - */ - value_str = im_ref_string_get( value ); - - VIPS_DEBUG_MSG( "vips_fits_write_meta: setting meta on fits image:\n" ); - VIPS_DEBUG_MSG( " value == \"%s\"\n", value_str ); - - if( fits_write_record( fits->fptr, value_str, &status ) ) { - vips_fits_error( status ); - return( a ); - } - - return( NULL ); -} - -static int -vips_fits_set_header( VipsFits *fits, VipsImage *in ) -{ - int status; - int bitpix; - int i; - - status = 0; - - fits->naxis = 3; - fits->naxes[0] = in->Xsize; - fits->naxes[1] = in->Ysize; - fits->naxes[2] = in->Bands; - - for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) - if( fits2vips_formats[i][1] == in->BandFmt ) - break; - if( i == VIPS_NUMBER( fits2vips_formats ) ) { - im_error( "fits", _( "unsupported BandFmt %d\n" ), - in->BandFmt ); - return( -1 ); - } - bitpix = fits2vips_formats[i][0]; - fits->datatype = fits2vips_formats[i][2]; - -#ifdef VIPS_DEBUG - VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); - for( i = 0; i < fits->naxis; i++ ) - VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); - VIPS_DEBUG_MSG( "bitpix = %d\n", bitpix ); -#endif /*VIPS_DEBUG*/ - - if( fits_create_imgll( fits->fptr, bitpix, fits->naxis, - fits->naxes, &status ) ) { - vips_fits_error( status ); - return( -1 ); - } - - if( im_header_map( in, - (im_header_map_fn) vips_fits_write_meta, fits ) ) - return( -1 ); - - return( 0 ); -} - -static int -vips_fits_write( VipsRegion *region, VipsRect *area, void *a ) -{ - VipsFits *fits = (VipsFits *) a; - VipsImage *image = fits->image; - int es = VIPS_IMAGE_SIZEOF_ELEMENT( image ); - int ps = VIPS_IMAGE_SIZEOF_PEL( image ); - - int status; - int y, b, x, k; - - status = 0; - - VIPS_DEBUG_MSG( "vips_fits_write: " - "writing left = %d, top = %d, width = %d, height = %d\n", - area->left, area->top, area->width, area->height ); - - /* We need to write a band at a time. We can't bandsplit in vips, - * since vips_sink_disc() can't loop over many images at once, sadly. - */ - - for( y = 0; y < area->height; y++ ) { - PEL *p = (PEL *) VIPS_REGION_ADDR( region, - area->left, area->top + y ); - - for( b = 0; b < image->Bands; b++ ) { - PEL *p1, *q; - long fpixel[3]; - - p1 = p + b * es; - q = fits->buffer; - - for( x = 0; x < area->width; x++ ) { - for( k = 0; k < es; k++ ) - q[k] = p1[k]; - - q += es; - p1 += ps; - } - - fpixel[0] = area->left + 1; - fpixel[1] = area->top + y + 1; - fpixel[2] = b + 1; - - /* No need to lock, write functions are single-threaded. - */ - - if( fits_write_pix( fits->fptr, fits->datatype, - fpixel, area->width, fits->buffer, - &status ) ) { - vips_fits_error( status ); - return( -1 ); - } - } - } - - return( 0 ); -} - -/** - * im_vips2fits: - * @in: image to write - * @filename: file to write to - * - * Write @in to @filename in FITS format. - * - * See also: im_fits2vips(), #VipsFormat. - * - * Returns: 0 on success, -1 on error. - */ int im_vips2fits( VipsImage *in, const char *filename ) { - VipsFits *fits; - - VIPS_DEBUG_MSG( "im_vips2fits: writing \"%s\"\n", filename ); - - if( !(fits = vips_fits_new_write( in, filename )) ) + if( vips_fitssave( in, filename, NULL ) ) return( -1 ); - if( vips_fits_set_header( fits, fits->image ) || - vips_sink_disc( fits->image, vips_fits_write, fits ) ) { - vips_fits_close( fits ); - return( -1 ); - } - vips_fits_close( fits ); - return( 0 ); } +static int +isfits( const char *name ) +{ + return( vips_foreign_is_a( "fitsload", name ) ); +} + static const char *fits_suffs[] = { ".fits", NULL }; /* fits format adds no new members. @@ -833,7 +91,7 @@ vips_format_fits_class_init( VipsFormatFitsClass *class ) object_class->description = _( "FITS" ); format_class->is_a = isfits; - format_class->header = fits2vips_header; + format_class->header = im_fits2vips; format_class->load = im_fits2vips; format_class->save = im_vips2fits; format_class->suffs = fits_suffs; @@ -846,4 +104,3 @@ vips_format_fits_init( VipsFormatFits *object ) G_DEFINE_TYPE( VipsFormatFits, vips_format_fits, VIPS_TYPE_FORMAT ); -#endif /*HAVE_CFITSIO*/ diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index a197d5b6..0308af97 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -323,6 +323,11 @@ int vips_tiffsave( VipsImage *in, const char *filename, ... ) int vips_openexrload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_fitsload( const char *filename, VipsImage **out, ... ) + __attribute__((sentinel)); +int vips_fitssave( VipsImage *in, const char *filename, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/