/* $Id: cpl_image_io.c,v 1.140 2007/12/06 14:30:47 yjung Exp $ * * This file is part of the ESO Common Pipeline Library * Copyright (C) 2001-2004 European Southern Observatory * * 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 */ /* * $Author: yjung $ * $Date: 2007/12/06 14:30:47 $ * $Revision: 1.140 $ * $Name: $ */ #ifdef HAVE_CONFIG_H #include #endif /*----------------------------------------------------------------------------- Includes -----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include "cpl_tools.h" #include "cpl_memory.h" #include "cpl_propertylist_impl.h" #include "cpl_image_io.h" #include "cpl_mask.h" #include "cpl_image_bpm.h" #include "cpl_image_defs.h" /*----------------------------------------------------------------------------- Defines -----------------------------------------------------------------------------*/ #define CPL_IMAGE_IO_ALLOC 0 #define CPL_IMAGE_IO_WRAP 1 #define CPL_IMAGE_IO_GET_PIXELS 2 #define CPL_IMAGE_IO_COPY 3 #define CPL_IMAGE_IO_LOAD 4 #define CPL_IMAGE_IO_LOAD_WIN 5 #define CPL_IMAGE_IO_SAVE 6 #define CPL_IMAGE_IO_SET_BADPIXEL 7 #define CPL_IMAGE_IO_GET 8 #define CPL_IMAGE_IO_SET 9 /* Needed for cpl_image_floodfill() */ #define FFSTACK_MAXLINES 10 #define FFSTACK_STACKSZ(IMAGE) (FFSTACK_MAXLINES * IMAGE->ny) #define FFSTACK_BYTES(IMAGE) (FFSTACK_STACKSZ(IMAGE) * 4 * sizeof(int)) /*----------------------------------------------------------------------------*/ /** * @defgroup cpl_image_io Images input and output * * This module provides functions to handle input and output for images. * * Functions are provided here to load and save images from and to FITS * files, to generate empty images or to deallocated an image. * * @par Synopsis: * @code * #include "cpl_image_io.h" * @endcode */ /*----------------------------------------------------------------------------*/ /**@{*/ /*----------------------------------------------------------------------------- Private function prototypes -----------------------------------------------------------------------------*/ static cpl_error_code cpl_image_append(const cpl_image *, const char *, int, cpl_propertylist *); static void cpl_image_floodfill(cpl_image *, void *, int, int, int) ; /*----------------------------------------------------------------------------- Function codes -----------------------------------------------------------------------------*/ #define CPL_OPERATION CPL_IMAGE_IO_ALLOC /*----------------------------------------------------------------------------*/ /** @brief Allocate an image structure and pixel buffer for a image. @param nx Size in x @param ny Size in y @param type The pixel type @return 1 newly allocated cpl_image or NULL in case of an error Allocates space for the cpl_image structure and sets the dimensions and type of pixel data. The pixel buffer is allocated and initialised to zero. The pixel array will contain nx*ny values being the image pixels from the lower left to the upper right line by line. Supported pixel types are CPL_TYPE_INT, CPL_TYPE_FLOAT and CPL_TYPE_DOUBLE. The returned cpl_image must be deallocated using cpl_image_delete(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_ILLEGAL_INPUT if nx or ny is negative - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_new( int nx, int ny, cpl_type type) { cpl_image * image; cpl_ensure( nx > 0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; cpl_ensure( ny > 0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* Switch on the requested image type */ switch (type) { #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL); break ; } return image; } #undef CPL_OPERATION #define CPL_OPERATION CPL_IMAGE_IO_WRAP /*----------------------------------------------------------------------------*/ /** @brief Create a double image using an existing pixel buffer. @param nx Size in x @param ny Size in y @param pixels double * pixel data @return 1 newly allocated cpl_image or NULL in case of an error @see cpl_image_new The pixel array is set to point to that of the argument. The pixel array must contain nx*ny doubles. The allocated image must be deallocated with cpl_image_unwrap(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if nx or ny is negative or zero. */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_wrap_double( int nx, int ny, double * pixels) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS } /*----------------------------------------------------------------------------*/ /** @brief Create a float image using an existing pixel buffer. @param nx Size in x @param ny Size in y @param pixels float * pixel data. @return 1 newly allocated cpl_image or NULL in error case @see cpl_image_wrap_double() */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_wrap_float( int nx, int ny, float * pixels) { #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS } /*----------------------------------------------------------------------------*/ /** @brief Create an integer image using an existing pixel buffer. @param nx Size in x @param ny Size in y @param pixels int * pixel data. @return 1 newly allocated cpl_image or NULL in error case @see cpl_image_wrap_double() */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_wrap_int( int nx, int ny, int * pixels) { #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS } #undef CPL_OPERATION #define CPL_OPERATION CPL_IMAGE_IO_LOAD /*----------------------------------------------------------------------------*/ /** @brief Load an image from a FITS file. @param filename Name of the file to load from. @param im_type Type of the created image @param pnum Plane number in the file. @param xtnum Extension number in the file. @return 1 newly allocated image, or NULL if image cannot be loaded. This function loads an image from a FITS file (NAXIS=2 or 3), using cfitsio. The returned image has to be deallocated with cpl_image_delete(). The passed type for the output image can be : CPL_TYPE_FLOAT, CPL_TYPE_DOUBLE or CPL_TYPE_INT. This type is there to specify the type of the cpl_image that will be created by the function. It is totally independant from the way the data are stored in the FITS file. A FITS image containg float pixels can be loaded as a cpl_image of type double. In this case, the user would specify CPL_TYPE_DOUBLE as im_type. 'xtnum' specifies from which extension the image should be loaded. This could be 0 for the main data section (files without extension), or any number between 1 and N, where N is the number of extensions present in the file. The requested plane number runs from 0 to nplanes-1, where nplanes is the number of planes present in the requested data section. The loaded image has an empty bad pixel map. Examples: @code // Load as a float image the only image in FITS file (a.fits) without ext. // and NAXIS=2. cpl_image * im = cpl_image_load("a.fits", CPL_TYPE_FLOAT, 0, 0); // Load as a double image the first plane in a FITS cube (a.fits) without // extension, NAXIS=3 and NAXIS3=128 cpl_image * im = cpl_image_load("a.fits", CPL_TYPE_DOUBLE, 0, 0); // Load as an integer image the third plane in a FITS cube (a.fits) without // extension, NAXIS=3 and NAXIS3=128 cpl_image * im = cpl_image_load("a.fits", CPL_TYPE_INT, 2, 0); // Load as a double image the first plane from extension 5 cpl_image * im = cpl_image_load("a.fits", CPL_TYPE_DOUBLE, 0, 5); // Load as a double image the third plane in extension 5 cpl_image * im = cpl_image_load("a.fits", CPL_TYPE_DOUBLE, 2, 5); @endcode Possible #_cpl_error_code_ set in this function: - CPL_ERROR_FILE_IO if the file does not exist - CPL_ERROR_BAD_FILE_FORMAT if the data cannot be loaded from the file - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported - CPL_ERROR_ILLEGAL_INPUT if the passed extensƣon number is negative */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_load( const char * filename, cpl_type im_type, int pnum, int xtnum) { fitsfile * fptr ; int fio_status=0 ; cpl_image * im ; char * extname ; int naxis ; long * naxes ; long * fpixel ; /* Check entries */ cpl_ensure(xtnum>=0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; cpl_ensure(pnum>=0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* Open the file extension */ if (xtnum == 0) extname = cpl_sprintf(filename) ; else extname = cpl_sprintf("%s[%d]", filename, xtnum) ; fits_open_file(&fptr, extname, READONLY, &fio_status) ; cpl_free(extname) ; cpl_ensure(fio_status==0, CPL_ERROR_FILE_IO, NULL) ; /* Get the file dimension */ fits_get_img_dim(fptr, &naxis, &fio_status) ; if (naxis != 2 && naxis != 3) { fits_close_file(fptr, &fio_status) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } if (pnum > 0 && naxis==2) { fits_close_file(fptr, &fio_status) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } /* Get the file size */ naxes = cpl_malloc(naxis * sizeof(long)) ; fits_get_img_size(fptr, naxis, naxes, &fio_status) ; if (pnum > 0 && pnum >= naxes[2]) { fits_close_file(fptr, &fio_status) ; cpl_free(naxes) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } /* Create fpixel */ fpixel = cpl_malloc(naxis * sizeof(long)) ; fpixel[0] = fpixel[1] = 1 ; if (naxis == 3) fpixel[2] = pnum+1 ; /* Switch on the requested image type */ switch (im_type) { #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: im = NULL; (void)cpl_error_set(cpl_func, CPL_ERROR_TYPE_MISMATCH); break ; } cpl_free(fpixel) ; cpl_free(naxes) ; if (fio_status) { cpl_image_delete(im); im = NULL; (void)cpl_error_set_message_macro(cpl_func, CPL_ERROR_BAD_FILE_FORMAT, __FILE__, __LINE__, "fits_read_pix" "(filename='%s',pnum=%d,xtnum=%d) " "returned %d: %s", filename, pnum, xtnum, fio_status, cpl_tools_get_cfitsio_msg(fio_status)); fio_status = 0; /* Reset so fits_close_file() works */ } if (fits_close_file(fptr, &fio_status)) { cpl_image_delete(im); im = NULL; (void)cpl_error_set_message_macro(cpl_func, CPL_ERROR_BAD_FILE_FORMAT, __FILE__, __LINE__, "fits_close_file" "(filename='%s',pnum=%d,xtnum=%d) " "returned %d: %s", filename, pnum, xtnum, fio_status, cpl_tools_get_cfitsio_msg(fio_status)); } /* im is NULL if an error happened */ return im ; } #undef CPL_OPERATION #define CPL_OPERATION CPL_IMAGE_IO_LOAD_WIN /*----------------------------------------------------------------------------*/ /** @brief Load an image from a FITS file. @param filename Name of the file to load from. @param im_type Type of the created image @param pnum Plane number in the file. @param xtnum Extension number in the file. @param llx Specifies the window position @param lly Specifies the window position @param urx Specifies the window position @param ury Specifies the window position @return 1 newly allocated image, or NULL if image cannot be loaded. @see cpl_image_load The window position is specified starting with 1 to the number of pixel. Lower left pixel is (1, 1). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_FILE_IO if the file does not exist - CPL_ERROR_BAD_FILE_FORMAT if the data cannot be loaded from the file - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported - CPL_ERROR_ILLEGAL_INPUT if the passed psition is invalid */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_load_window( const char * filename, cpl_type im_type, int pnum, int xtnum, int llx, int lly, int urx, int ury) { fitsfile * fptr ; int fio_status=0 ; cpl_image * im ; char * extname ; int naxis ; long * naxes ; long * fpixel ; long * lpixel ; long * inc ; int i ; /* Check entries */ cpl_ensure(xtnum>=0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; cpl_ensure(pnum>=0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* Open the file extension */ if (xtnum == 0) extname = cpl_sprintf(filename) ; else extname = cpl_sprintf("%s[%d]", filename, xtnum) ; fits_open_file(&fptr, extname, READONLY, &fio_status) ; cpl_free(extname) ; cpl_ensure(fio_status==0, CPL_ERROR_FILE_IO, NULL) ; /* Get the file dimension */ fits_get_img_dim(fptr, &naxis, &fio_status) ; if (naxis != 2 && naxis != 3) { fits_close_file(fptr, &fio_status) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } if (pnum > 0 && naxis==2) { fits_close_file(fptr, &fio_status) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } /* Get the file size */ naxes = cpl_malloc(naxis * sizeof(long)) ; fits_get_img_size(fptr, naxis, naxes, &fio_status) ; if (pnum > 0 && pnum >= naxes[2]) { fits_close_file(fptr, &fio_status) ; cpl_free(naxes) ; cpl_ensure(0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; } /* Check the zones */ if (llx < 1 || lly < 1 || urx > naxes[0] || ury > naxes[1] || urxny; j++) { offpos = j*intimage->nx; for (i=0; inx; i++) { /* Look up if unprocessed pixel */ if (pio[i+offpos]==(int)-1) { /* Flood fill from this pixel with the assigned label */ cpl_image_floodfill(intimage, fftemp, i, j, label); label++; } } } cpl_free(fftemp); if (nbobjs!=NULL) (*nbobjs)=label-1; return intimage; } /*----------------------------------------------------------------------------*/ /** @brief Get the image type @param img a cpl_image object @return The image type (CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT, CPL_TYPE_INT, or CPL_TYPE_INVALID on NULL input). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ cpl_type cpl_image_get_type(const cpl_image * img) { cpl_ensure(img, CPL_ERROR_NULL_INPUT, CPL_TYPE_INVALID); return img->type ; } /*----------------------------------------------------------------------------*/ /** @brief Get the image x size @param img a cpl_image object @return The image x size, or -1 on NULL input Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ int cpl_image_get_size_x(const cpl_image * img) { cpl_ensure(img, CPL_ERROR_NULL_INPUT, -1); return img->nx ; } /*----------------------------------------------------------------------------*/ /** @brief Get the image y size @param img a cpl_image object @return The image y size, or -1 on NULL input Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ int cpl_image_get_size_y(const cpl_image * img) { cpl_ensure(img, CPL_ERROR_NULL_INPUT, -1); return img->ny ; } #define CPL_OPERATION CPL_IMAGE_IO_GET /*----------------------------------------------------------------------------*/ /** @brief Get the value of a pixel at a given position @param image Input image. @param xpos Pixel x position (FITS convention) @param ypos Pixel y position (FITS convention) @param pis_rejected 1 if the pixel is bad, 0 if good, negative on error @return The pixel value (cast to a double) or undefined if *pis_rejected The return value is defined iff the pixel is not flagged as rejected, i. e. when *pis_rejected == 0. In case of an error, the #_cpl_error_code_ code is set. Images can be CPL_TYPE_FLOAT, CPL_TYPE_INT or CPL_TYPE_DOUBLE. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ double cpl_image_get( const cpl_image * image, int xpos, int ypos, int * pis_rejected) { double value; int index; /* Check entries */ cpl_ensure(pis_rejected != NULL, CPL_ERROR_NULL_INPUT, -1); /* This call will check the validity of image, xpos and ypos */ *pis_rejected = cpl_image_is_rejected(image, xpos, ypos); cpl_ensure( *pis_rejected >= 0, cpl_error_get_code(), -2); /* The pixel is flagged as rejected */ if (*pis_rejected) return 0; assert( image->pixels ); index = (xpos - 1) + image->nx * (ypos - 1); /* Get the value */ switch (image->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, -3) ; } return value; } #undef CPL_OPERATION #define CPL_OPERATION CPL_IMAGE_IO_SET /*----------------------------------------------------------------------------*/ /** @brief Set the pixel at the given position to the given value @param image input image. @param xpos pixel x position (FITS convention) @param ypos pixel y position (FITS convention) @param value New pixel value @return CPL_ERROR_NONE or the relevant #_cpl_error_code_ on error Images can be CPL_TYPE_FLOAT, CPL_TYPE_INT, CPL_TYPE_DOUBLE. If the pixel is flagged as rejected, this flag is removed. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ACCESS_OUT_OF_RANGE if the passed position is not in the image - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_image_set( cpl_image * image, int xpos, int ypos, double value) { int index; /* Check entries */ cpl_ensure_code(image != NULL, CPL_ERROR_NULL_INPUT); cpl_ensure_code(xpos >= 1, CPL_ERROR_ACCESS_OUT_OF_RANGE); cpl_ensure_code(ypos >= 1, CPL_ERROR_ACCESS_OUT_OF_RANGE); cpl_ensure_code(xpos <= image->nx, CPL_ERROR_ACCESS_OUT_OF_RANGE); cpl_ensure_code(ypos <= image->ny, CPL_ERROR_ACCESS_OUT_OF_RANGE); assert( image->pixels ); index = (xpos - 1) + image->nx * (ypos - 1); /* Get the value */ switch (image->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: cpl_ensure_code(0, CPL_ERROR_TYPE_MISMATCH) ; } cpl_ensure_code( !cpl_image_accept(image, xpos, ypos),cpl_error_get_code()); return CPL_ERROR_NONE; } #undef CPL_OPERATION /*----------------------------------------------------------------------------*/ /** @brief Gets the pixel data. @param img Image to query. @return A pointer to the image pixel data or NULL in error case. The returned pointer refers to already allocated data. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ void * cpl_image_get_data(cpl_image * img) { cpl_ensure(img, CPL_ERROR_NULL_INPUT, NULL); return img->pixels; } /*----------------------------------------------------------------------------*/ /** @brief Gets the pixel data. @param img Image to query. @return A pointer to the image pixel data or NULL in error case. @see cpl_image_get_data */ /*----------------------------------------------------------------------------*/ const void * cpl_image_get_data_const(const cpl_image * img) { return cpl_image_get_data((cpl_image *)img); } /*----------------------------------------------------------------------------*/ /** @brief Gets the bad pixels map @param img Image to query. @return A pointer to the mask identifiing the bad pixels or NULL. The returned pointer refers to already allocated data. If the bad pixel map is NULL, an empty one is created. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_image_get_bpm(cpl_image * img) { cpl_ensure(img, CPL_ERROR_NULL_INPUT, NULL); if (img->bpm == NULL) { cpl_image * tmp = (cpl_image *)img ; tmp->bpm = cpl_mask_new(cpl_image_get_size_x(img), cpl_image_get_size_y(img)) ; } return img->bpm ; } /*----------------------------------------------------------------------------*/ /** @brief Gets the bad pixels map @param img Image to query. @return A pointer to the mask identifiing the bad pixels or NULL. @see cpl_image_get_bpm */ /*----------------------------------------------------------------------------*/ const cpl_mask * cpl_image_get_bpm_const(const cpl_image * img) { return cpl_image_get_bpm((cpl_image *)img); } #define CPL_OPERATION CPL_IMAGE_IO_GET_PIXELS /*----------------------------------------------------------------------------*/ /** @brief Get the data as a double array @param img a cpl_image object @return pointer to the double data array or NULL in error case. The returned pointer refers to already allocated data. The pixels are stored in a one dimensional array. The pixel value PIXVAL at position (i, j) in the image - (0, 0) is the lower left pixel, i gives the column position from left to right, j gives the row position from bottom to top - is given by : PIXVAL = array[i + j*nx] ; where nx is the x size of the image and array is the data array returned by this function. array can be used to access or modify the pixel value in the image. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_TYPE_MISMATCH if the passed image type is not double */ /*----------------------------------------------------------------------------*/ double * cpl_image_get_data_double(cpl_image * img) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS } /*----------------------------------------------------------------------------*/ /** @brief Get the data as a double array @param img a cpl_image object @return pointer to the double data array or NULL in error case. @see cpl_image_get_data_double */ /*----------------------------------------------------------------------------*/ const double * cpl_image_get_data_double_const(const cpl_image * img) { return cpl_image_get_data_double((cpl_image *)img); } /*----------------------------------------------------------------------------*/ /** @brief Get the data as a float array @param img a cpl_image object @return pointer to the float data array or NULL in error case. @see cpl_image_get_data_double() */ /*----------------------------------------------------------------------------*/ float * cpl_image_get_data_float(cpl_image * img) { #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS } /*----------------------------------------------------------------------------*/ /** @brief Get the data as a float array @param img a cpl_image object @return pointer to the float data array or NULL in error case. @see cpl_image_get_data_float() */ /*----------------------------------------------------------------------------*/ const float * cpl_image_get_data_float_const(const cpl_image * img) { return cpl_image_get_data_float((cpl_image *)img); } /*----------------------------------------------------------------------------*/ /** @brief Get the data as a integer array @param img a cpl_image object @return pointer to the integer data array or NULL in error case. @see cpl_image_get_data_double() */ /*----------------------------------------------------------------------------*/ int * cpl_image_get_data_int(cpl_image * img) { #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS } /*----------------------------------------------------------------------------*/ /** @brief Get the data as a integer array @param img a cpl_image object @return pointer to the integer data array or NULL in error case. @see cpl_image_get_data_int() */ /*----------------------------------------------------------------------------*/ const int * cpl_image_get_data_int_const(const cpl_image * img) { return cpl_image_get_data_int((cpl_image *)img); } #undef CPL_OPERATION /*----------------------------------------------------------------------------*/ /** @brief Free memory associated to an cpl_image object. @param d Image to destroy. @return void Frees all memory associated with a cpl_image. If the passed image is NULL, the function returns without doing anything. */ /*----------------------------------------------------------------------------*/ void cpl_image_delete(cpl_image * d) { if (d == NULL) return ; /* Delete pixels and bad pixel map */ cpl_free(d->pixels); cpl_mask_delete(d->bpm) ; cpl_free(d) ; } /*----------------------------------------------------------------------------*/ /** @brief Free memory associated to an cpl_image object, but the pixel buffer. @param d Image to destroy. @return A pointer to the data array or NULL if the input is NULL. Frees all memory associated to an cpl_image, except the pixel buffer. cpl_image_unwrap() is provided for images that are constructed by passing a pixel buffer to one of cpl_image_wrap_{double,float,int}(). @note The pixel buffer must subsequently be deallocated. Failure to do so will result in a memory leak. */ /*----------------------------------------------------------------------------*/ void * cpl_image_unwrap(cpl_image * d) { void * data; if (d == NULL) return NULL; /* Delete bad pixel map */ cpl_mask_delete(d->bpm); data = (void *) d->pixels; cpl_free(d) ; return data; } #define CPL_OPERATION CPL_IMAGE_IO_COPY /*----------------------------------------------------------------------------*/ /** @brief Copy an image. @param src Source image. @return 1 newly allocated image, or NULL on error. Copy an image into a new image object. The pixels and the bad pixel map are also copied. The returned image must be deallocated using cpl_image_delete(). Images can be CPL_TYPE_FLOAT, CPL_TYPE_INT, CPL_TYPE_DOUBLE. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_duplicate(const cpl_image * src) { cpl_image * dest ; cpl_ensure(src, CPL_ERROR_NULL_INPUT, NULL) ; assert ( src->pixels ) ; switch (src->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL) ; } /* Bad pixel map */ if (src->bpm == NULL) dest->bpm = NULL ; else dest->bpm = cpl_mask_duplicate(src->bpm) ; return dest; } #undef CPL_OPERATION /*----------------------------------------------------------------------------*/ /** @brief convert a cpl_image to a given type @param im The image to convert. @param type The destination type @return the newly allocated cpl_image or NULL in error case Supported allowed types are CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT or CPL_TYPE_INT. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the passed type is invalid - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ cpl_image * cpl_image_cast( const cpl_image * im, cpl_type type) { cpl_image * new_im ; float * pfi, * pfo ; double * pdi, * pdo ; int * pii, * pio ; int i ; /* Check entries */ cpl_ensure(im!=NULL, CPL_ERROR_NULL_INPUT, NULL) ; cpl_ensure(type!=CPL_TYPE_INVALID, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* The image has already the destination type */ if (im->type == type) return cpl_image_duplicate(im) ; /* Switch on the requested type */ switch (type) { case CPL_TYPE_FLOAT: new_im = cpl_image_new(im->nx, im->ny, CPL_TYPE_FLOAT) ; pfo = (float*)new_im->pixels ; /* Switch on the original type */ switch(im->type) { case CPL_TYPE_DOUBLE: pdi = (double*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pfo[i] = (float)pdi[i] ; break ; case CPL_TYPE_INT: pii = (int*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pfo[i] = (float)pii[i] ; break ; default: cpl_image_delete(new_im) ; cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL); } break ; case CPL_TYPE_DOUBLE: new_im = cpl_image_new(im->nx, im->ny, CPL_TYPE_DOUBLE) ; pdo = (double*)new_im->pixels ; /* Switch on the original type */ switch(im->type) { case CPL_TYPE_FLOAT: pfi = (float*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pdo[i] = (double)pfi[i] ; break ; case CPL_TYPE_INT: pii = (int*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pdo[i] = (double)pii[i] ; break ; default: cpl_image_delete(new_im) ; cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL); } break ; case CPL_TYPE_INT: new_im = cpl_image_new(im->nx, im->ny, CPL_TYPE_INT) ; pio = (int*)new_im->pixels ; /* Switch on the original type */ switch(im->type) { case CPL_TYPE_FLOAT: pfi = (float*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pio[i] = (int)pfi[i] ; break ; case CPL_TYPE_DOUBLE: pdi = (double*)im->pixels ; for (i=0 ; inx*im->ny ; i++) pio[i] = (int)pdi[i] ; break ; default: cpl_image_delete(new_im) ; cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL); } break ; default: cpl_ensure(0, CPL_ERROR_TYPE_MISMATCH, NULL); } /* Bad pixel map */ if (im->bpm == NULL) new_im->bpm = NULL ; else new_im->bpm = cpl_mask_duplicate(im->bpm) ; return new_im ; } #define CPL_OPERATION CPL_IMAGE_IO_SET_BADPIXEL /*----------------------------------------------------------------------------*/ /** @brief Set the bad pixels in an image to a fixed value @param im The image to modify. @param a The fixed value @return the #_cpl_error_code_ or CPL_ERROR_NONE Images can be CPL_TYPE_FLOAT, CPL_TYPE_INT, CPL_TYPE_DOUBLE. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_image_fill_rejected( cpl_image * im, double a) { int nz ; cpl_binary * pbpm ; /* Check entries and Initialise */ cpl_ensure_code(im, CPL_ERROR_NULL_INPUT); /* If no bad pixel */ if (im->bpm == NULL) return CPL_ERROR_NONE ; /* Return if no bad pixel map to update */ nz = cpl_mask_count(im->bpm) ; if (nz == 0) return CPL_ERROR_NONE ; pbpm = cpl_mask_get_data(im->bpm) ; switch (im->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: cpl_ensure_code(0, CPL_ERROR_TYPE_MISMATCH); } return CPL_ERROR_NONE ; } #undef CPL_OPERATION #define CPL_OPERATION CPL_IMAGE_IO_SAVE /*----------------------------------------------------------------------------*/ /** @brief Save an image to a FITS file @param to_save Image to write to disk or NULL @param filename Name of the file to write @param bpp Bits per pixel @param pl Property list for the output header or NULL @param mode The desired output options (combined with bitwise or) @return CPL_ERROR_NONE or the relevant #_cpl_error_code_ on error This function saves an image to a FITS file, using cfitsio. If a property list is provided, it is written to the named file before the pixels are written. If the image is not provided, the created file will only contain the primary header. This can be useful to create multiple extension files. The requested pixel depth (bpp) follows the FITS convention. Possible values are CPL_BPP_8_UNSIGNED (8), CPL_BPP_16_SIGNED (16), CPL_BPP_16_UNSIGNED (-16), CPL_BPP_32_SIGNED (32), CPL_BPP_IEEE_FLOAT (-32), CPL_BPP_IEEE_DOUBLE (-64) In the case of CPL_BPP_16_UNSIGNED, the BITPIX stays 16, the data are stored as signed (32768 is subtracted to the image) and BZERO is set to 32768 as specified by the FITS standard. Supported image types are CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT, CPL_TYPE_INT. Supported output modes are CPL_IO_DEFAULT (create a new file) and CPL_IO_EXTEND (append to an existing file) If you are in append mode, make sure that the file has writing permissions. You may have problems if you create a file in your application and append something to it with the umask set to 222. In this case, the file created by your application would not be writable, and the append would fail. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the bpp value or the mode is not supported - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported - CPL_ERROR_FILE_NOT_CREATED if the output file cannot be created - CPL_ERROR_FILE_IO if the data cannot be written in the file */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_image_save( const cpl_image * to_save, const char * filename, cpl_type_bpp bpp, const cpl_propertylist * pl, unsigned mode) { fitsfile * fptr ; char * sval ; int fio_status=0 ; long naxes[2] ; cpl_type_bpp bpp_loc ; int ival ; double dval, bzero ; cpl_image * outima ; /* Test entries */ cpl_ensure_code(filename, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(bpp==CPL_BPP_8_UNSIGNED || bpp==CPL_BPP_16_SIGNED || bpp==CPL_BPP_16_UNSIGNED || bpp==CPL_BPP_32_SIGNED || bpp==CPL_BPP_IEEE_FLOAT || bpp==CPL_BPP_IEEE_DOUBLE, CPL_ERROR_ILLEGAL_INPUT); cpl_ensure_code(mode <= CPL_IO_MAX, CPL_ERROR_ILLEGAL_INPUT) ; /* In append mode, call the dedicated function */ if (mode & CPL_IO_EXTEND) return cpl_image_append(to_save, filename, bpp, (cpl_propertylist*)pl); /* Handle CPL_BPP_16_UNSIGNED */ if (bpp == CPL_BPP_16_UNSIGNED) { bpp_loc = CPL_BPP_16_SIGNED ; if (to_save != NULL) { outima = cpl_image_subtract_scalar_create(to_save, 32768.0) ; bzero = 32768.0 ; } else { outima = NULL ; bzero = 0.0 ; } } else { bpp_loc = bpp ; bzero = 0.0 ; outima = (cpl_image *)to_save ; } /* Create the file */ sval = cpl_sprintf("!%s", filename) ; fits_create_file(&fptr, sval, &fio_status); cpl_free(sval) ; if (fio_status != 0) { if (bpp == CPL_BPP_16_UNSIGNED && outima != NULL) cpl_image_delete(outima) ; fits_close_file(fptr, &fio_status) ; cpl_ensure_code(0, CPL_ERROR_FILE_IO) ; } /* Create the image in the primary unit */ if (outima != NULL) { naxes[0] = outima->nx ; naxes[1] = outima->ny ; fits_create_img(fptr, bpp_loc, 2, naxes, &fio_status); switch (outima->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: fits_close_file(fptr, &fio_status) ; if (bpp == CPL_BPP_16_UNSIGNED) cpl_image_delete(outima) ; cpl_ensure_code(0, CPL_ERROR_TYPE_MISMATCH) ; } if (bpp == CPL_BPP_16_UNSIGNED) cpl_image_delete(outima) ; } else { /* Create minimal header */ fits_create_hdu(fptr, &fio_status); ival = 1 ; /* FIXME: Cast string literal to avoid compiler warnings - and pray that CFITSIO does not try to modify them :-(((( */ fits_write_key(fptr, TLOGICAL, (char*)"SIMPLE", &ival, (char*)"Fits format", &fio_status); fits_write_key(fptr, TINT, (char*)"BITPIX", &bpp_loc, (char*)"Bits per pixel", &fio_status); ival = 0 ; fits_write_key(fptr, TINT, (char*)"NAXIS", &ival, (char*)"Empty unit", &fio_status); ival = 1 ; fits_write_key(fptr, TLOGICAL, (char*)"EXTEND", &ival, (char*)"There may be FITS extensions", &fio_status); } /* Add BSCALE BZERO DATE */ dval = 1.0 ; /* FIXME: Cast string literal to avoid compiler warnings - and pray that CFITSIO does not try to modify them :-(((( */ fits_write_key(fptr, TDOUBLE, (char*)"BSCALE", &dval, (char*)"Scale factor", &fio_status); fits_write_key(fptr, TDOUBLE, (char*)"BZERO", &bzero, (char*)"Offset factor", &fio_status); fits_write_date(fptr, &fio_status); /* Check */ if (fio_status != 0) { fits_close_file(fptr, &fio_status) ; cpl_ensure_code(0, CPL_ERROR_FILE_IO) ; } /* Add the property list */ if (cpl_fits_add_properties(fptr, pl, CPL_FITS_BADKEYS_PRIM "|" CPL_FITS_COMPRKEYS)!=CPL_ERROR_NONE) { fits_close_file(fptr, &fio_status) ; cpl_ensure_code(0, CPL_ERROR_ILLEGAL_INPUT) ; } /* Close and write on disk */ fits_close_file(fptr, &fio_status) ; return CPL_ERROR_NONE ; } #undef CPL_OPERATION /**@}*/ #define CPL_OPERATION CPL_IMAGE_IO_SAVE /*----------------------------------------------------------------------------*/ /** @brief Append an image to a FITS file @param to_save Image to write in extension @param filename Name of the file to write @param bpp Bits per pixel @param pl Property list for the output header @return the #_cpl_error_code_ or CPL_ERROR_NONE @see cpl_image_save() This function appends an image to a FITS file, using cfitsio. If a property list is provided, it is used as the extension header. If the image is not provided, the extension will only contain the header. The requested pixel depth (bpp) follows the FITS convention. Possible values are CPL_BPP_8_UNSIGNED (8), CPL_BPP_16_SIGNED (16), CPL_BPP_16_UNSIGNED (-16), CPL_BPP_32_SIGNED (32), CPL_BPP_IEEE_FLOAT (-32), CPL_BPP_IEEE_DOUBLE (-64) In the case of CPL_BPP_16_UNSIGNED, the BITPIX stays 16, the data are stored as signed (32768 is subtracted to the image) and BZERO is set to 32768 as specified by the FITS standard. Supported types are CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT, CPL_TYPE_INT. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the bpp value or the mode is not supported - CPL_ERROR_TYPE_MISMATCH if the passed image type is not supported - CPL_ERROR_FILE_NOT_CREATED if the output file cannot be created - CPL_ERROR_FILE_IO if the data cannot be written in the file */ /*----------------------------------------------------------------------------*/ static cpl_error_code cpl_image_append( const cpl_image * to_save, const char * filename, const int bpp, cpl_propertylist * pl) { fitsfile * fptr ; int fio_status=0 ; long naxes[2] ; cpl_type_bpp bpp_loc ; int ival ; double dval, bzero ; cpl_image * outima ; /* Test entries */ cpl_ensure_code(filename, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(bpp==CPL_BPP_8_UNSIGNED || bpp==CPL_BPP_16_SIGNED || bpp==CPL_BPP_16_UNSIGNED || bpp==CPL_BPP_32_SIGNED || bpp==CPL_BPP_IEEE_FLOAT || bpp==CPL_BPP_IEEE_DOUBLE, CPL_ERROR_ILLEGAL_INPUT); /* Handle CPL_BPP_16_UNSIGNED */ if (bpp == CPL_BPP_16_UNSIGNED) { bpp_loc = CPL_BPP_16_SIGNED ; if (to_save != NULL) { outima = cpl_image_subtract_scalar_create(to_save, 32768.0) ; bzero = 32768.0 ; } else { outima = NULL ; bzero = 0.0 ; } } else { bpp_loc = bpp ; bzero = 0.0 ; outima = (cpl_image *)to_save ; } /* Open the file */ fits_open_file(&fptr, filename, READWRITE, &fio_status); if (fio_status != 0) { if (bpp == CPL_BPP_16_UNSIGNED && outima != NULL) cpl_image_delete(outima) ; cpl_ensure_code(0, CPL_ERROR_FILE_IO) ; } /* Create the image and append it */ if (outima != NULL) { naxes[0] = outima->nx ; naxes[1] = outima->ny ; fits_create_img(fptr, bpp_loc, 2, naxes, &fio_status); switch (outima->type) { #define CPL_CLASS CPL_CLASS_DOUBLE #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_FLOAT #include "cpl_image_io_body.h" #undef CPL_CLASS #define CPL_CLASS CPL_CLASS_INT #include "cpl_image_io_body.h" #undef CPL_CLASS default: if (bpp == CPL_BPP_16_UNSIGNED) cpl_image_delete(outima) ; cpl_ensure_code(0, CPL_ERROR_TYPE_MISMATCH) ; } if (bpp == CPL_BPP_16_UNSIGNED) cpl_image_delete(outima) ; } else { /* Create minimal header */ fits_create_hdu(fptr, &fio_status); /* FIXME: Cast string literal to avoid compiler warnings - and pray that CFITSIO does not try to modify them :-(((( */ fits_write_key(fptr, TSTRING, (char*)"XTENSION", (char*)"IMAGE", (char*)"FITS Image Extension", &fio_status) ; fits_write_key(fptr, TINT, (char*)"BITPIX", &bpp_loc, (char*)"Bits per pixel", &fio_status); ival = 0 ; fits_write_key(fptr, TINT, (char*)"NAXIS", &ival, (char*)"Empty unit", &fio_status); } /* Add BSCALE BZERO */ /* FIXME: Cast string literal to avoid compiler warnings - and pray that CFITSIO does not try to modify them :-(((( */ dval = 1.0 ; fits_write_key(fptr, TDOUBLE, (char*)"BSCALE", &dval, (char*)"Scale factor", &fio_status); fits_write_key(fptr, TDOUBLE, (char*)"BZERO", &bzero, (char*)"Offset factor", &fio_status); /* Check */ if (fio_status != 0) { fits_close_file(fptr, &fio_status) ; cpl_ensure_code(0, CPL_ERROR_FILE_IO) ; } /* Add the property list */ if (cpl_fits_add_properties(fptr, pl, CPL_FITS_BADKEYS_EXT "|" CPL_FITS_COMPRKEYS)!=CPL_ERROR_NONE) { fits_close_file(fptr, &fio_status) ; cpl_ensure_code(0, CPL_ERROR_ILLEGAL_INPUT) ; } /* Close and write on disk */ fits_close_file(fptr, &fio_status) ; return CPL_ERROR_NONE ; } #undef CPL_OPERATION #define FFSTACK_PUSH(Y, XL, XR, DY) \ if (sp=wy1 && Y+(DY)<=wy2) \ {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} #define FFSTACK_POP(Y, XL, XR, DY) \ {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} /*----------------------------------------------------------------------------*/ /* @brief Fill a zone with label. @param intimage input label image @param fftemp Pre-allocated work-space @param x x position @param y y position @param label current label This code was pulled out the Usenet and freely adapted to cpl. Credits - Paul Heckbert (posted on comp.graphics 28 Apr 1988) It is highly unreadable and makes use of goto and other fairly bad programming practices, but works fine and fast. */ /*----------------------------------------------------------------------------*/ static void cpl_image_floodfill(cpl_image * lab, void * fftemp, int x, int y, int label) { struct { int y, xl, xr, dy ; } * stack, * sp ; int wx1, wx2, wy1, wy2 ; int l, x1, x2, dy ; int ov ; const int stacksz = FFSTACK_STACKSZ(lab); int * pii ; stack = fftemp; sp = stack ; wx1 = 0; wx2 = lab->nx-1 ; wy1 = 0 ; wy2 = lab->ny-1 ; pii = cpl_image_get_data_int(lab) ; ov = pii[x+y*lab->nx] ; if (ov==label || xwx2 || ywy2) return; FFSTACK_PUSH(y, x, x, 1); /* needed in some cases */ FFSTACK_PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */ while (sp>stack) { /* pop segment off stack and fill a neighboring scan line */ FFSTACK_POP(y, x1, x2, dy); /* * segment of scan line y-dy for x1<=x<=x2 was previously filled, * now explore adjacent pixels in scan line y */ for (x=x1; x>=wx1 && pii[x+y*lab->nx]==ov; x--) pii[x+y*lab->nx] = label ; if (x>=x1) goto skip; l = x+1; if (lnx]==ov; x++) pii[x+y*lab->nx] = label ; FFSTACK_PUSH(y, l, x-1, dy); if (x>x2+1) FFSTACK_PUSH(y, x2+1, x-1, -dy); /* leak on right? */ skip: for (x++; x<=x2 && pii[x+y*lab->nx]!=ov; x++); l = x; } while (x<=x2); } }