IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Nov 15, 2007, 3:04:56 PM (18 years ago)
Author:
Paul Price
Message:

Integrating the code to choose the output BITPIX (and quantising floating-point images if necessary) and custom floating-point schemes with the functions to read and write images. More detailed description, from my e-mail: psLib has always read any of the (legal) FITS types, and written a FITS image with the appropriate BITPIX for the image->type.type. Now the output BITPIX can be forced by setting psFits.bitpix to the desired BITPIX. Floating point images being quantised to integer types have BSCALE and BZERO set in such a fashion as to maintain the dynamic range of the image; quantisation after applying the scale and zero includes adding a random number [0,1) to preserve the expectation of the floating-point value. On reading, the BSCALE and BZERO are applied by cfitsio without any assistance by psLib. I've also added Gene's custom 16-bit floats (1S 5E 10M). In anticipation of further flavours down the road and to maintain compatibility, I've labelled this as FLOAT_16_0. An F32 image may be written out with this scheme by setting psFits.floatType = PS_FITS_FLOAT_16_0. The image is converted and written out with BITPIX=16, and the header keyword PSBITPIX added with the custom floating-point scheme name. Such images are automatically converted to F32 on reading (if psFits.conventions.psBitpix is true, which it is by default). One thing to keep in mind is that I'm not sure how NANs and INFs are handled in these processes. These additions are made on the top of the compression feature --- quantised or custom floating-point images may also be compressed (though the custom floating-point images don't gain much from it).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/fits/psFitsFloat.c

    r15335 r15630  
    44
    55#include <ieee754.h>
     6#include <string.h>
     7#include <assert.h>
    68
    79#include "psAbort.h"
     
    911#include "psError.h"
    1012#include "psImage.h"
    11 #include "psMetadata.h"
    12 #include "psRandom.h"
    1313#include "psFits.h"
     14#include "psTrace.h"
     15#include "psMemory.h"
    1416
    1517#include "psFitsFloat.h"
    1618
    17 #define BIAS_FLOAT_16_0 10              // Exponent bias
     19#define BIAS_FLOAT_16_0              10 // Exponent bias for FLOAT_16_0
     20
     21//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     22// Private functions
     23//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    1824
    1925union float_16_0 {
     
    2733    } f16;
    2834};
    29 
    3035
    3136
     
    5762}
    5863
     64//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     65// Public functions
     66//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    5967
    60 psImage *psFitsScaleGenerate(psFits *fits, const psImage *image, int bitpix, psRandom *rng)
    61 {
    62     PS_ASSERT_FITS_NON_NULL(fits, NULL);
    63     PS_ASSERT_FITS_WRITABLE(fits, NULL);
    64     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
    65     PS_ASSERT_IMAGE_TYPE_F32_OR_F64(image, NULL);
    66     PS_ASSERT_RANDOM_NON_NULL(rng, NULL);
    6768
    68     psElemType outType;                 // Type for output image
    69     // Choosing to use signed types because those don't require BSCALE,BZERO to represent them in the FITS
    70     // file.
    71     switch (bitpix) {
    72       case 8:
    73         outType = PS_TYPE_S8;
    74         break;
    75       case 16:
    76         outType = PS_TYPE_S16;
    77         break;
    78       case 32:
    79         outType = PS_TYPE_S32;
    80         break;
    81       case 64:
    82         outType = PS_TYPE_S64;
    83         break;
    84       default:
    85         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Target bitpix (%d) is not one of 8,16,32,64", bitpix);
    86         return NULL;
    87     }
    88 
    89     int numCols = image->numCols, numRows = image->numRows;
    90     double range = pow(2.0, bitpix);    // Range of values for target BITPIX
    91     psImage *out = psImageAlloc(numCols, numRows, outType); // Output scaled image
    92 
    93 #define SCALE_GENERATE_CASE(IN, INTYPE, OUT, OUTTYPE) \
    94     case PS_TYPE_##OUTTYPE: { \
    95         for (int y = 0; y < numRows; y++) { \
    96             for (int x = 0; x < numCols; x++) { \
    97                 ps##INTYPE random = psRandomUniform(rng) - 0.5; /* Randomise the quantisation */ \
    98                 (OUT)->data.OUTTYPE[y][x] = ((IN)->data.INTYPE[y][x] - zero) * scale + random; \
    99             } \
    100         } \
    101         break; \
    102     }
    103 
    104 #define SCALE_DETERMINE_CASE(IN, INTYPE, OUT) \
    105     case PS_TYPE_##INTYPE: { \
    106         ps##INTYPE min = INFINITY, max = -INFINITY; /* Minimum and maximum values */ \
    107         for (int y = 0; y < numRows; y++) { \
    108             for (int x = 0; x < numCols; x++) { \
    109                 ps##INTYPE value = (IN)->data.INTYPE[y][x]; /* Value of interest */ \
    110                 if (isfinite(value)) { \
    111                     if (value < min) { \
    112                         min = value; \
    113                     } \
    114                     if (value > max) { \
    115                         max = value; \
    116                     } \
    117                 } \
    118             } \
    119         } \
    120         ps##INTYPE scale = range / (max - min); \
    121         ps##INTYPE zero = min - 0.5 * range; /* Minimum value for twos-complement int is - 2^(bitpix-1) */ \
    122         fits->bscale = 1.0 / scale; \
    123         fits->bzero = zero; \
    124         switch (outType) { \
    125             SCALE_GENERATE_CASE(IN, INTYPE, OUT, S8); \
    126             SCALE_GENERATE_CASE(IN, INTYPE, OUT, S16); \
    127             SCALE_GENERATE_CASE(IN, INTYPE, OUT, S32); \
    128             SCALE_GENERATE_CASE(IN, INTYPE, OUT, S64); \
    129           default: \
    130             psAbort("Should be unreachable."); \
    131         } \
    132         break; \
    133     }
    134 
    135     switch (image->type.type) {
    136         SCALE_DETERMINE_CASE(image, F32, out);
    137         SCALE_DETERMINE_CASE(image, F64, out);
    138       default:
    139         psAbort("Should be unreachable.");
    140     }
    141 
    142     return out;
    143 }
    144 
    145 psImage *psFitsScaleApply(const psImage *image, double bscale, double bzero, psElemType type)
     69psImage *psFitsFloatImageWrite(const psImage *image, psFitsFloat type)
    14670{
    14771    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
    148 
    149     if (bscale == 0.0) {
    150         return psMemIncrRefCounter(image);
    151     }
    152 
    153     switch (
    154 
    155 
    156 psImage *psFitsFloatImageWrite(const psImage *image,
    157                                psMetadata *header,
    158                                psFitsFloat type
    159     )
    160 {
    161     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
    162     PS_ASSERT_METADATA_NON_NULL(header, NULL); // Need the header, so we can mark it as compressed
     72    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
    16373
    16474    psImage *output = NULL;             // Output image, to return
    165     psString convName = NULL;           // Convention name
    16675
    16776    switch (type) {
     77      case PS_FITS_FLOAT_NONE:
     78        // No conversion to be performed
     79        return psMemIncrRefCounter((psImage*)image); // Casting away "const"
    16880      case PS_FITS_FLOAT_16_0:
    16981        output = psImageAlloc(image->numCols, image->numRows, PS_TYPE_S16); // Output image
     
    17385            }
    17486        }
    175         convName = psStringCopy("FLOAT_16_0");
    17687        break;
    17788      default:
     
    18091    }
    18192
    182     psMetadataAddStr(header, PS_LIST_TAIL, "PSBITPIX", PS_META_REPLACE, "Pan-STARRS bit encoding", convName);
    183 
    18493    return output;
    18594}
    18695
    187 psImage *psFitsFloatImageRead(const psFits *fits,
    188                               const psImage *image
    189     )
     96
     97psImage *psFitsFloatImageRead(psImage *out, const psImage *in, psFitsFloat type)
    19098{
    191     PS_ASSERT_FITS_NON_NULL(fits, NULL);
    192     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
     99    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
    193100
    194     bool mdok;                          // Status of MD lookup
    195     const char *convName = psMetadataLookupStr(&mdok, header, "PSBITPIX"); // Convention used
    196     if (!mdok || !convName) {
    197         // It's not a custom floating-point image
    198         return psMemIncrRefCounter(image);
     101    psElemType elem = psFitsFloatImageType(type); // Type for elements
     102    if (elem == 0) {
     103        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to recognise convention: %x", type);
     104        return NULL;
    199105    }
    200106
    201     psImage *output = NULL;             // Output image, to return
     107    int numCols = in->numCols, numRows = in->numRows; // Size of image
    202108
    203     if (strcmp(convName, "FLOAT_16_0") == 0 && image->type.type == PS_TYPE_S16) {
    204         output = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32); // Output image
    205         for (int y = 0; y < image->numRows; y++) {
    206             for (int x = 0; x < image->numCols; x++) {
    207                 output->data.F32[y][x] = convertF32fromFloat16_0(image->data.S16[y][x]);
     109    out = psImageRecycle(out, numCols, numRows, elem);
     110
     111    switch (type) {
     112      case PS_FITS_FLOAT_16_0:
     113        if (in->type.type != PS_TYPE_S16) {
     114            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     115                    "Convention claims to be FLOAT_16_0, but image type is not S16.");
     116            return NULL;
     117        }
     118        assert(out->type.type == PS_TYPE_F32);
     119        for (int y = 0; y < numRows; y++) {
     120            for (int x = 0; x < numCols; x++) {
     121                out->data.F32[y][x] = convertF32fromFloat16_0(in->data.S16[y][x]);
    208122            }
    209123        }
    210         return output;
     124        return out;
     125      case PS_FITS_FLOAT_NONE:
     126      default:
     127        psAbort("Should be unreachable");
     128    }
     129    return NULL;
     130}
     131
     132psElemType psFitsFloatImageType(psFitsFloat type)
     133{
     134    switch (type) {
     135      case PS_FITS_FLOAT_16_0:
     136        return PS_TYPE_F32;
     137      case PS_FITS_FLOAT_NONE:
     138      default:
     139        return 0;                       // Doesn't correspond to ANY type --- should flag a real error
     140    }
     141}
     142
     143
     144psFitsFloat psFitsFloatTypeFromString(const char *string)
     145{
     146    PS_ASSERT_STRING_NON_EMPTY(string, PS_FITS_FLOAT_NONE);
     147
     148    if (strcmp(string, "FLOAT_16_0") == 0) {
     149        return PS_FITS_FLOAT_16_0;
    211150    }
    212151
    213     psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to recognise PSBITPIX convention name: %s", convName);
    214     return NULL;
    215 
    216     return output;
     152    psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     153            "Unable to recognise custom floating-point convention name: %s", string);
     154    return PS_FITS_FLOAT_NONE;
    217155}
Note: See TracChangeset for help on using the changeset viewer.