IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 3, 2007, 11:27:21 AM (19 years ago)
Author:
Paul Price
Message:

Merging branch with FITS compression development.

File:
1 edited

Legend:

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

    r14460 r15179  
    77 *  @author Robert DeSonia, MHPCC
    88 *
    9  *  @version $Revision: 1.34 $ $Name: not supported by cvs2svn $
    10  *  @date $Date: 2007-08-10 02:23:42 $
     9 *  @version $Revision: 1.35 $ $Name: not supported by cvs2svn $
     10 *  @date $Date: 2007-10-03 21:27:21 $
    1111 *
    1212 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
     
    1919#include <unistd.h>
    2020
     21#include "psAssert.h"
    2122#include "psFits.h"
    2223#include "string.h"
     
    3233
    3334#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
     35#define NUM_EMPTY_KEYS 8                // Number of keywords before header is considered practically empty
    3436
    3537// list of FITS header keys to ignore; NULL-terminated
    36 static char* ignoreFitsKeys[] = {"", NULL};
     38static const char* ignoreFitsKeys[] = { "", NULL};
    3739
    3840// List of FITS header keys that may be duplicated; NULL-terminated
    39 static char *duplicateFitsKeys[] = {"COMMENT", "HIERARCH", "HISTORY", NULL};
     41static const char *duplicateFitsKeys[] = { "COMMENT", "HIERARCH", "HISTORY", NULL};
    4042
    4143// List of FITS header keys not to write (handled by cfitsio); NULL-terminated
    42 static char *noWriteFitsKeys[] = {"SIMPLE", "XTENSION", "BITPIX", "NAXIS", "EXTNAME", "BSCALE", "BZERO",
    43                                   "TFIELDS", NULL};
     44static const char *noWriteFitsKeys[] = { "SIMPLE", "XTENSION", "BITPIX", "NAXIS", "EXTNAME", "BSCALE",
     45                                         "BZERO", "TFIELDS", "PCOUNT", "GCOUNT", "ZIMAGE", "ZBITPIX",
     46                                         "ZCMPTYPE", NULL};
     47
    4448// List of the start of FITS header keys not to write (handled by cfitsio); NULL-terminated
    45 static char *noWriteFitsKeyStarts[] = {"NAXIS", "TTYPE", "TFORM", NULL};
    46 
    47 // List of FITS header keys to be written with fits_write_comment
    48 
    49 psMetadata* psFitsReadHeader(psMetadata* out,
    50                              const psFits* fits)
    51 {
    52     if (fits == NULL) {
    53         psError(PS_ERR_BAD_PARAMETER_NULL, true,
    54                 _("The input psFits object can not NULL."));
    55         return NULL;
    56     }
    57 
    58     if (out == NULL) {
    59         out = psMetadataAlloc();
    60     }
     49static const char *noWriteFitsKeyStarts[] = { "NAXIS", "TTYPE", "TFORM", "ZNAXIS", "ZTILE", "ZNAME", "ZVAL",
     50                                              NULL};
     51
     52// List of FITS header keys that may be present if the header is considered "empty"; NULL-terminated
     53static const char *emptyKeys[] = { "SIMPLE", "BITPIX", "NAXIS", "EXTEND", "COMMENT", "CHECKSUM", "DATASUM",
     54                                   NULL };
     55
     56// Compare a keyword with a list of keywords; return true if it's in the list
     57static bool keywordInList(const char *keyword, // Keyword to check
     58                          const char **list // List of keywords
     59    )
     60{
     61    for (const char **check = list; *check; ++check) {
     62        if (strcmp(keyword, *check) == 0) {
     63            return true;
     64        }
     65    }
     66    return false;
     67}
     68
     69
     70bool psFitsCheckSingleCompressedImagePHU(const psFits *fits, psMetadata *header)
     71{
     72    PS_ASSERT_FITS_NON_NULL(fits, false);
     73
     74    if (!fits->conventions.compression) {
     75        // User has turned off compression conventions; doesn't want any nasty surprises
     76        return false;
     77    }
     78
     79    if (psFitsGetExtNum(fits) != 0) {
     80        // It's not the PHU, so it can't be the PHU for a single compressed image!
     81        return false;
     82    }
     83
     84    if (psFitsGetSize(fits) != 2) {
     85        // No second extension, or multiple extensions
     86        return false;
     87    }
     88
     89    int numKeys;                        // Number of keywords in the header
     90    int status = 0;                     // CFITSIO status
     91    fits_get_hdrspace(fits->fd, &numKeys, 0, &status);
     92    if (numKeys > NUM_EMPTY_KEYS) {
     93        return false;
     94    }
     95
     96    int bitpix, naxis;                  // Bits per pixel and number of axes
     97    long naxes[MAX_COMPRESS_DIM];       // Dimensions
     98    fits_get_img_param(fits->fd, MAX_COMPRESS_DIM, &bitpix, &naxis, naxes, &status);
     99    if (naxis != 0) {
     100        return false;
     101    }
     102
     103    if (!header) {
     104        for (int i = 1; i <= numKeys; i++) {
     105            // Just want to read the keyword names, without parsing the values and stuffing into a metadata
     106            char keyName[MAX_STRING_LENGTH];// Keyword name
     107            char keyValue[MAX_STRING_LENGTH]; // Corresponding value
     108            char keyComment[MAX_STRING_LENGTH]; // Corresponding comment
     109            fits_read_keyn(fits->fd, i, keyName, keyValue, keyComment, &status);
     110            if (!keywordInList(keyName, emptyKeys)) {
     111                return false;
     112            }
     113        }
     114    } else {
     115        psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
     116        psMetadataItem *item;           // Item from iteration
     117        while ((item = psMetadataGetAndIncrement(iter))) {
     118            if (!keywordInList(item->name, emptyKeys)) {
     119                psFree(iter);
     120                return false;
     121            }
     122        }
     123        psFree(iter);
     124    }
     125
     126    if (!psFitsMoveExtNum(fits, 1, false)) {
     127        psWarning("Unable to examine first extension as suspect compressed image.");
     128        return false;
     129    }
     130
     131    if (fits_is_compressed_image(fits->fd, &status)) {
     132        return true;
     133    }
     134
     135    // It's not a single compressed image PHU --- move back to the PHU for the user
     136    if (!psFitsMoveExtNum(fits, 0, false)) {
     137        psWarning("Unable to examine first extension as suspect compressed image.");
     138        return false;
     139    }
     140
     141    return false;
     142}
     143
     144char *p_psFitsHeaderParseString(char *string)
     145{
     146    if (!string || strlen(string) == 0) {
     147        return string;
     148    }
     149
     150    char *fixed = string;       // Fixed version of the string
     151    // remove the single-quotes at front/end
     152    if (fixed[0] == '\'' && fixed[strlen(string)-1] == '\'') {
     153        string[strlen(string)-1] = '\0'; // Remove the trailing quote
     154        fixed += 1; // Advance past the leading quote
     155    }
     156    // Remove trailing spaces, which are not significant, according to the FITS standard
     157    // http://archive.stsci.edu/fits/fits_standard/node31.html
     158    char *lastSpace = NULL; // The last space in the string
     159    while (strlen(fixed) > 1 && (lastSpace = strrchr(fixed, ' ')) && lastSpace[1] == '\0') {
     160        // This is a trailing space, not a leading space.
     161        lastSpace[0] = '\0'; // Truncate the string here
     162    }
     163
     164    return fixed;
     165}
     166
     167// Read the header
     168static psMetadata *readHeader(const psFits *fits // FITS file from which to read header
     169                              )
     170{
     171    assert(fits);
     172
     173    psMetadata *header = psMetadataAlloc(); // Header, to return
    61174
    62175    // Get number of key names
     
    74187
    75188        // Check to see if the keyword should be ignored
    76         bool ignoreKey = false;         // Ignore this keyword?
    77         for (int i = 0; ignoreFitsKeys[i] && !ignoreKey; i++) {
    78             if (strcmp(keyName, ignoreFitsKeys[i]) == 0) {
    79                 ignoreKey = true;
    80             }
    81         }
    82         if (ignoreKey) {
     189        if (keywordInList(keyName, ignoreFitsKeys)) {
    83190            // We're done here; skip to the next key
    84191            continue;
    85192        }
    86193
    87 #if 0
    88         // This doesn't seem to be necessary
    89         if (strncmp(keyName, "HIERARCH ", 9) == 0) {
    90             char temp[MAX_STRING_LENGTH];
    91             strcpy(temp, &keyName[9]);
    92             strcpy(keyName, temp);
    93         }
    94 #endif
    95 
    96194        // Check to see if the keyword should be duplicated
    97195        int dupFlag = 0;                // Duplicate flag
    98         for (int i = 0; duplicateFitsKeys[i] && !dupFlag; i++) {
    99             if (strcmp(keyName, duplicateFitsKeys[i]) == 0) {
    100                 dupFlag = PS_META_DUPLICATE_OK;
    101             }
     196        if (keywordInList(keyName, duplicateFitsKeys)) {
     197            dupFlag = PS_META_DUPLICATE_OK;
    102198        }
    103199
     
    116212        case 'X': // bit
    117213        case 'B': // byte
    118             success = psMetadataAddS8(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
     214            success = psMetadataAddS8(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
    119215            break;
    120216        case 'I': // short int.
     
    122218            // Trap NAN, INF and -INF, which cfitsio doesn't handle.
    123219            if (strncasecmp(keyValue, "NAN", 3) == 0) {
    124                 success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
     220                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
    125221            } else if (strncasecmp(keyValue, "INF", 3) == 0) {
    126                 success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
     222                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
    127223            } else if (strncasecmp(keyValue, "-INF", 4) == 0) {
    128                 success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
     224                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
    129225            } else {
    130                 success = psMetadataAddS32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
     226                success = psMetadataAddS32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment,
     227                                           atoi(keyValue));
    131228            }
    132229            break;
    133230        case 'J': // int.
    134             success = psMetadataAddS32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
     231            success = psMetadataAddS32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atoi(keyValue));
    135232            break;
    136233        case 'U': // unsigned int.
    137             success = psMetadataAddU32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atol(keyValue));
     234            success = psMetadataAddU32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atol(keyValue));
    138235            break;
    139236
    140237        case 'K': // long int. can't all fit in a psS32, put in psF64
    141238        case 'F':
    142             success = psMetadataAddF64(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, atof(keyValue));
     239            success = psMetadataAddF64(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, atof(keyValue));
    143240            break;
    144241        case 'C': {
    145                 char *keyValueFixed = keyValue; // Fixed version of the string
    146                 // remove the single-quotes at front/end
    147                 if (keyValueFixed[0] == '\'' && keyValueFixed[strlen(keyValue)-1] == '\'') {
    148                     keyValue[strlen(keyValue)-1] = '\0'; // Remove the trailing quote
    149                     keyValueFixed += 1; // Advance past the leading quote
    150                 }
    151                 // Remove trailing spaces, which are not significant, according to the FITS standard
    152                 // http://archive.stsci.edu/fits/fits_standard/node31.html
    153                 char *lastSpace = NULL; // The last space in the string
    154                 while (strlen(keyValueFixed) > 1 && (lastSpace = strrchr(keyValueFixed, ' ')) &&
    155                         lastSpace[1] == '\0') {
    156                     // This is a trailing space, not a leading space.
    157                     lastSpace[0] = '\0'; // Truncate the string here
    158                 }
    159 
    160                 // Need to trap NAN, INF and -INF written by psFitsWriteHeader.
    161                 // cfitsio won't write these, so we write them as strings, and then have to trap them on read.
    162                 if (strcasecmp(keyValueFixed, "NAN") == 0) {
    163                     success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
    164                 } else if (strcasecmp(keyValueFixed, "INF") == 0) {
    165                     success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
    166                 } else if (strcasecmp(keyValueFixed, "-INF") == 0) {
    167                     success = psMetadataAddF32(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
    168                 } else {
    169                     success = psMetadataAddStr(out, PS_LIST_TAIL, keyName, dupFlag, keyComment,
    170                                                keyValueFixed);
    171                 }
    172                 break;
     242            char *keyValueFixed = p_psFitsHeaderParseString(keyValue); // Fixed version of the string
     243
     244            // Need to trap NAN, INF and -INF written by psFitsWriteHeader.
     245            // cfitsio won't write these, so we write them as strings, and then have to trap them on read.
     246            if (strcasecmp(keyValueFixed, "NAN") == 0) {
     247                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, NAN);
     248            } else if (strcasecmp(keyValueFixed, "INF") == 0) {
     249                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, INFINITY);
     250            } else if (strcasecmp(keyValueFixed, "-INF") == 0) {
     251                success = psMetadataAddF32(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, -INFINITY);
     252            } else if (!fits->conventions.compression ||
     253                       (strcmp(keyName, "EXTNAME") != 0 || strcmp(keyValueFixed, "COMPRESSED_IMAGE") != 0)) {
     254                // Ignore EXTNAME=COMPRESSED_IMAGE if compression convention is to be respected
     255                success = psMetadataAddStr(header, PS_LIST_TAIL, keyName, dupFlag, keyComment,
     256                                           keyValueFixed);
    173257            }
     258            break;
     259        }
    174260        case 'L': {
    175                 bool temp = (keyValue[0] == 'T') ? 1 : 0;
    176                 success = psMetadataAddBool(out, PS_LIST_TAIL, keyName, dupFlag, keyComment, temp);
    177                 break;
    178             }
     261            bool temp = (keyValue[0] == 'T') ? 1 : 0;
     262            success = psMetadataAddBool(header, PS_LIST_TAIL, keyName, dupFlag, keyComment, temp);
     263            break;
     264        }
    179265        default:
    180266            psError(PS_ERR_IO, true, _("Specified FITS metadata type, %c, is not supported."), keyType);
    181             return out;
     267            psFree(header);
     268            return NULL;
    182269        }
    183270
    184271        if (!success) {
    185272            psError(PS_ERR_UNKNOWN, false, _("Failed to add metadata item, %s."), keyName);
    186             return out;
     273            psFree(header);
     274            return NULL;
    187275        }
    188276
     
    193281        (void)fits_get_errstatus(status, fitsErr);
    194282        psError(PS_ERR_IO, true, _("Failed to add metadata item, %s."), fitsErr);
    195         return false;
    196     }
    197 
     283        psFree(header);
     284        return false;
     285    }
     286
     287    return header;
     288}
     289
     290
     291psMetadata* psFitsReadHeader(psMetadata* out,
     292                             const psFits* fits)
     293{
     294    PS_ASSERT_FITS_NON_NULL(fits, NULL);
     295
     296    psMetadata *header = readHeader(fits); // Header
     297    if (!header) {
     298        return NULL;
     299    }
     300
     301    // Explore the potential case that this is an empty PHU, and the first extension contains the sole image,
     302    // which is compressed.
     303    if (psFitsCheckSingleCompressedImagePHU(fits, header)) {
     304        // This is really what we want, not the empty PHU
     305        psTrace("psLib.fits", 1,
     306                "This PHU should really be a compressed image --- getting that header instead.");
     307        psFree(header);
     308        header = readHeader(fits);
     309        if (!header) {
     310            return NULL;
     311        }
     312    }
     313
     314    if (!out) {
     315        return header;
     316    }
     317
     318    // Need to move header onto the nominated metadata
     319    psMetadataIterator *iter = psMetadataIteratorAlloc(header, PS_LIST_HEAD, NULL); // Iterator
     320    psMetadataItem *item;           // Item from iteration
     321    while ((item = psMetadataGetAndIncrement(iter))) {
     322        // Need to look for MULTI, which won't be picked up using the iterator.
     323        psMetadataItem *multiCheckItem = psMetadataLookup(header, item->name);
     324        assert(multiCheckItem);
     325        unsigned int flag = 0;      // Flag to indicate MULTI; otherwise default action
     326        if (multiCheckItem->type == PS_DATA_METADATA_MULTI) {
     327            flag = PS_META_DUPLICATE_OK;
     328        }
     329        if (!psMetadataAddItem(out, item, PS_LIST_TAIL, flag)) {
     330            psError(PS_ERR_UNKNOWN, false, "Unable to add header item %s to extant metadata.",
     331                    item->name);
     332            psFree(iter);
     333            psFree(header);
     334            return NULL;
     335        }
     336    }
     337    psFree(iter);
     338    psFree(header);
    198339    return out;
    199340}
     
    201342psMetadata* psFitsReadHeaderSet(psMetadata* out, const psFits* fits)
    202343{
    203     if (fits == NULL) {
    204         psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
    205         psFree(out);
    206         return NULL;
    207     }
    208 
    209     if (out == NULL) {
     344    PS_ASSERT_FITS_NON_NULL(fits, NULL);
     345
     346    if (!out) {
    210347        out = psMetadataAlloc();
    211348    }
     
    279416            // image, the NAXISn haven't been changed; or after converting to F32, the BITPIX hasn't been
    280417            // changed) so we'll take care of that for them.
    281             bool writeKey = true;           // Should we write this keyword?
    282             for (int i = 0; noWriteFitsKeys[i] && writeKey; i++) {
    283                 if (strcmp(item->name, noWriteFitsKeys[i]) == 0) {
    284                     writeKey = false;
    285                 }
    286             }
    287             if (!writeKey) {
     418            if (keywordInList(item->name, noWriteFitsKeys)) {
    288419                // Don't write it; skip to the next key
    289420                continue;
     
    294425                // that go in are correct.  However, when we're writing a "blank" HDU (header only), we want
    295426                // to preserve NAXISn etc for reference, so we don't do this.
     427                bool writeKey = true;   // Write this keyword?
    296428                for (int i = 0; noWriteFitsKeyStarts[i] && writeKey; i++) {
    297429                    if (strncmp(item->name, noWriteFitsKeyStarts[i], strlen(noWriteFitsKeyStarts[i])) == 0) {
     
    349481                        }
    350482                    } else {
    351                         fits_update_key(fits->fd, TFLOAT, item->name, &item->data.F32, item->comment, &status);
     483                        fits_update_key(fits->fd, TFLOAT, item->name, &item->data.F32, item->comment,
     484                                        &status);
    352485                    }
    353486                    break;
     
    364497                        }
    365498                    } else {
    366                         fits_update_key(fits->fd, TDOUBLE, item->name, &item->data.F64, item->comment, &status);
     499                        fits_update_key(fits->fd, TDOUBLE, item->name, &item->data.F64, item->comment,
     500                                        &status);
    367501                    }
    368502                    break;
     
    395529                      )
    396530{
    397     if (!fits) {
    398         psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
    399         return false;
    400     }
    401 
    402     if (!output) {
    403         psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed."));
    404         return false;
    405     }
     531    PS_ASSERT_FITS_NON_NULL(fits, false);
     532    PS_ASSERT_METADATA_NON_NULL(output, false);
    406533
    407534    return fitsWriteHeader(fits, output, true);
     
    413540                     )
    414541{
    415     if (!fits) {
    416         psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psFits object can not NULL."));
    417         return false;
    418     }
     542    PS_ASSERT_FITS_NON_NULL(fits, false);
    419543
    420544    // We allow output == NULL in order to write a minimal header.
     
    458582bool psFitsHeaderValidate(psMetadata *header)
    459583{
    460     if (header == NULL) {
    461         psError(PS_ERR_BAD_PARAMETER_NULL, true, _("The input psMetadata was NULL.  Need a non-NULL psMetadata for operation to be performed."));
    462         return false;
    463     }
     584    PS_ASSERT_METADATA_NON_NULL(header, false);
    464585
    465586    // Traverse the metadata list and inspect at each key
Note: See TracChangeset for help on using the changeset viewer.