Fix some bugs in keyword functions; add stupid image read/write

This commit is contained in:
eddyem 2019-02-20 23:21:17 +03:00
parent 82b214db95
commit b8dc08bbfd
4 changed files with 231 additions and 67 deletions

View File

@ -94,7 +94,7 @@ link_directories(${${PROJ}_LIBRARY_DIRS})
add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\" add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
-DPACKAGE_VERSION=\"${PROJ_VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" -DPACKAGE_VERSION=\"${PROJ_VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
-DMAJOR_VERSION=\"${MAJOR_VESION}\") -DMAJOR_VERSION=\"${MAJOR_VESION}\" -DVERSION=\"${PROJ_VERSION}\")
# -l # -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES}) target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES})

View File

@ -105,7 +105,9 @@ typedef struct{
typedef struct{ typedef struct{
int width; // width int width; // width
int height; // height int height; // height
int dtype; // picture data type int bitpix; // original bitpix
int dtype; // type of stored data
int pxsz; // number of bytes for one pixel data
void *data; // picture data void *data; // picture data
} FITSimage; } FITSimage;
@ -153,7 +155,7 @@ typedef enum{
*/ */
void keylist_free(KeyList **list); void keylist_free(KeyList **list);
KeyList *keylist_add_record(KeyList **list, char *rec); KeyList *keylist_add_record(KeyList **list, char *rec, int check);
KeyList *keylist_find_key(KeyList *list, char *key); KeyList *keylist_find_key(KeyList *list, char *key);
void keylist_remove_key(KeyList **list, char *key); void keylist_remove_key(KeyList **list, char *key);
KeyList *keylist_modify_key(KeyList *list, char *key, char *newval); KeyList *keylist_modify_key(KeyList *list, char *key, char *newval);
@ -173,9 +175,9 @@ void table_print_all(FITS *fits);
void image_free(FITSimage **ima); void image_free(FITSimage **ima);
FITSimage *image_read(FITS *fits); FITSimage *image_read(FITS *fits);
#define image_datatype_size(d) (abs(d)/8) int image_datatype_size(int bitpix, int *dtype);
void *image_data_malloc(size_t w, size_t h, int dtype); void *image_data_malloc(size_t w, size_t h, int pxbytes);
FITSimage *image_new(size_t w, size_t h, int dtype); FITSimage *image_new(size_t w, size_t h, int bitpix);
FITSimage *image_mksimilar(FITSimage *in); FITSimage *image_mksimilar(FITSimage *in);
FITSimage *image_copy(FITSimage *in); FITSimage *image_copy(FITSimage *in);
//FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata); //FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata);
@ -184,6 +186,7 @@ void FITS_free(FITS **fits);
FITS *FITS_read(char *filename); FITS *FITS_read(char *filename);
FITS *FITS_open(char *filename); FITS *FITS_open(char *filename);
bool FITS_write(char *filename, FITS *fits); bool FITS_write(char *filename, FITS *fits);
bool FITS_rewrite(FITS *fits);
/************************************************************************************** /**************************************************************************************
* fileops.c * * fileops.c *

View File

@ -16,6 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/*
This example allows to list all keywords in given FITS-file, list amount and types of HDUs in file,
add new keywords, modify old keywords.
New data could be saved to new file or in place into opened file.
*/
#include "common.h" #include "common.h"
#include <string.h> #include <string.h>
@ -24,6 +30,8 @@ typedef struct{
int list; // print keyword list int list; // print keyword list
int contents; // print short description of file contents int contents; // print short description of file contents
char **addrec; // add some records to keywords in first HDU char **addrec; // add some records to keywords in first HDU
char *outfile; // output file name
char **modify; // keys which values should be modified
} glob_pars; } glob_pars;
/* /*
@ -43,7 +51,9 @@ myoption cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"contents",NO_ARGS, NULL, 'c', arg_none, APTR(&G.contents), _("show short file contents")}, {"contents",NO_ARGS, NULL, 'c', arg_none, APTR(&G.contents), _("show short file contents")},
{"list", NO_ARGS, NULL, 'l', arg_none, APTR(&G.list), _("list all keywords")}, {"list", NO_ARGS, NULL, 'l', arg_none, APTR(&G.list), _("list all keywords")},
{"addrec", MULT_PAR, NULL, 'a', arg_string, APTR(&G.addrec), _("add record to file (you can add more than one record in once, point more -a)")}, {"addrec", MULT_PAR, NULL, 'a', arg_string, APTR(&G.addrec), _("add record to first HDU (you can add more than one record in once, point more -a)")},
{"output", NEED_ARG, NULL, 'o', arg_string, APTR(&G.outfile), _("save result to file (else save to same file)")},
{"modify", MULT_PAR, NULL, 'm', arg_string, APTR(&G.modify), _("modify values values of given keys (each param should be \"key = new_value\")")},
end_option end_option
}; };
@ -86,7 +96,7 @@ int main(int argc, char *argv[]){
} }
if(G.contents){ if(G.contents){
green("\n\nFile consists of %d HDUs:\n", N); green("\n\nFile consists of %d HDUs:\n", N);
for(int i = 0; i <= N; ++i){ for(int i = 1; i <= N; ++i){
printf("\tHDU #%d - ", i); printf("\tHDU #%d - ", i);
switch(f->HDUs[i].hdutype){ switch(f->HDUs[i].hdutype){
case IMAGE_HDU: case IMAGE_HDU:
@ -108,9 +118,30 @@ int main(int argc, char *argv[]){
char **ptr = G.addrec; char **ptr = G.addrec;
while(*ptr){ while(*ptr){
printf("record: %s\n", *ptr); printf("record: %s\n", *ptr);
keylist_add_record(&(f->HDUs[1].keylist), *ptr++, 1);
}
}
if(G.modify){
char **ptr = G.modify;
while(*ptr){
printf("modify: %s\n", *ptr);
char *val = strchr(*ptr, '=');
if(!val){
WARNX("should be: 'parameter = value / comment'");
continue;
}
*val++ = 0; // now `val` is value + comment; ptr is key
if(!keylist_modify_key(f->HDUs[1].keylist, *ptr, val)){
WARNX("key %s not found", *ptr);
}
++ptr; ++ptr;
} }
} }
if(G.outfile){ // save result to new file
FITS_write(G.outfile, f);
}else{
FITS_rewrite(f);
}
return 0; return 0;
} }

248
fits.c
View File

@ -24,6 +24,7 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <usefull_macros.h> #include <usefull_macros.h>
@ -59,16 +60,29 @@ KeyList *keylist_get_end(KeyList *list){
} }
/** /**
* @brief keylist_add_record - add record to keylist * @brief keylist_add_record - add record to keylist with optional check
* @param list (io) - pointer to root of list or NULL * @param list (io) - pointer to root of list or NULL
* if *root == NULL, created node will be placed there * if *root == NULL, created node will be placed there
* @param rec (i) - data inserted * @param rec (i) - data inserted
* @return pointer to created node * @param check - !=0 to check `rec` with fits_parse_template
* @return pointer to created node (if list == NULL - don't add created record to any list)
*/ */
KeyList *keylist_add_record(KeyList **list, char *rec){ KeyList *keylist_add_record(KeyList **list, char *rec, int check){
if(!rec) return NULL;
KeyList *node, *last; KeyList *node, *last;
if((node = (KeyList*) MALLOC(KeyList, 1)) == 0) return NULL; // allocation error if((node = (KeyList*) MALLOC(KeyList, 1)) == 0) return NULL; // allocation error
node->record = strdup(rec); // insert data int tp = 0, st = 0;
if(check){
char card[FLEN_CARD];
fits_parse_template(rec, card, &tp, &st);
if(st){
fits_report_error(stderr, st);
return NULL;
}
DBG("\n WAS: %s\nBECOME: %s\ntp=%d", rec, card, tp);
rec = card;
}
node->record = strdup(rec);
if(!node->record){ if(!node->record){
/// "îÅ ÍÏÇÕ ÓËÏÐÉÒÏ×ÁÔØ ÄÁÎÎÙÅ" /// "îÅ ÍÏÇÕ ÓËÏÐÉÒÏ×ÁÔØ ÄÁÎÎÙÅ"
WARNX(_("Can't copy data")); WARNX(_("Can't copy data"));
@ -95,14 +109,17 @@ KeyList *keylist_add_record(KeyList **list, char *rec){
KeyList *keylist_find_key(KeyList *list, char *key){ KeyList *keylist_find_key(KeyList *list, char *key){
if(!list || !key) return NULL; if(!list || !key) return NULL;
size_t L = strlen(key); size_t L = strlen(key);
DBG("try to find %s", key);
do{ do{
if(list->record){ if(list->record){
if(strncmp(list->record, key, L) == 0){ // key found if(strncasecmp(list->record, key, L) == 0){ // key found
DBG("found:\n%s", list->record);
return list; return list;
} }
} }
list = list->next; list = list->next;
}while(list); }while(list);
DBG("not found");
return NULL; return NULL;
} }
@ -115,13 +132,17 @@ KeyList *keylist_find_key(KeyList *list, char *key){
*/ */
KeyList *keylist_modify_key(KeyList *list, char *key, char *newval){ KeyList *keylist_modify_key(KeyList *list, char *key, char *newval){
// TODO: look modhead.c in cexamples for protected keys // TODO: look modhead.c in cexamples for protected keys
char buf[FLEN_CARD]; char buf[FLEN_CARD], test[2*FLEN_CARD];
KeyList *rec = keylist_find_key(list, key); KeyList *rec = keylist_find_key(list, key);
if(!rec) return NULL; if(!rec) return NULL;
char *comm = strchr(rec->record, '/'); snprintf(test, 2*FLEN_CARD, "%s = %s", key, newval);
if(!comm) comm = ""; int tp, st = 0;
// TODO: use fits_parse_template fits_parse_template(test, buf, &tp, &st);
snprintf(buf, FLEN_CARD, "%-8s=%21s %s", key, newval, comm); if(st){
fits_report_error(stderr, st);
return NULL;
}
DBG("new record:\n%s", buf);
FREE(rec->record); FREE(rec->record);
rec->record = strdup(buf); rec->record = strdup(buf);
return rec; return rec;
@ -222,7 +243,7 @@ KeyList *keylist_copy(KeyList *list){
int n = 0; int n = 0;
#endif #endif
do{ do{
keylist_add_record(&newlist, list->record); keylist_add_record(&newlist, list->record, 0);
list = list->next; list = list->next;
#ifdef EBUG #ifdef EBUG
++n; ++n;
@ -269,7 +290,7 @@ KeyList *keylist_read(FITS *fits){
fits_read_record(fits->fp, j, card, &fst); fits_read_record(fits->fp, j, card, &fst);
if(fst) fits_report_error(stderr, fst); if(fst) fits_report_error(stderr, fst);
else{ else{
KeyList *kl = keylist_add_record(&list, card); KeyList *kl = keylist_add_record(&list, card, 0);
if(!kl){ if(!kl){
/// "îÅ ÍÏÇÕ ÄÏÂÁ×ÉÔØ ÚÁÐÉÓØ × ÓÐÉÓÏË" /// "îÅ ÍÏÇÕ ÄÏÂÁ×ÉÔØ ÚÁÐÉÓØ × ÓÐÉÓÏË"
WARNX(_("Can't add record to list")); WARNX(_("Can't add record to list"));
@ -767,47 +788,102 @@ returning:
return fits; return fits;
} }
static bool keylist_write(KeyList *kl, fitsfile *fp){
int st = 0;
bool ret = TRUE;
if(!fp || !kl) return FALSE;
while(kl){
if(kl->keyclass > TYP_CMPRS_KEY){ // this record should be written
fits_write_record(fp, kl->record, &st);
DBG("Write %s, st = %d", kl->record, st);
if(st){fits_report_error(stderr, st); st = 0; ret = FALSE;}
}
kl = kl->next;
}
return ret;
}
/**
* @brief FITS_write - write FITS file to disk
* @param filename - new filename
* @param fits - structure to write
* @return TRUE if all OK
*/
bool FITS_write(char *filename, FITS *fits){ bool FITS_write(char *filename, FITS *fits){
if(!filename || !fits) return FALSE; if(!filename || !fits) return FALSE;
/*int w = fits->width, h = fits->height, fst = 0;
long naxes[2] = {w, h};
size_t sz = w * h;
fitsfile *fp; fitsfile *fp;
int fst = 0;
fits_create_diskfile(&fp, filename, &fst); fits_create_diskfile(&fp, filename, &fst);
DBG("create file %s", filename);
if(fst){fits_report_error(stderr, fst); return FALSE;} if(fst){fits_report_error(stderr, fst); return FALSE;}
// TODO: save FITS files in original (or given by user) data format! int N = fits->NHDUs;
// check fits->dtype - does all data fits it for(int i = 1; i <= N; ++i){
fits_create_img(fp, fits->dtype, 2, naxes, &fst); FITSHDU *hdu = &fits->HDUs[i];
if(fst){fits_report_error(stderr, fst); return FALSE;} if(!hdu) continue;
if(fits->keylist){ // there's keys FITSimage *img;
KeyList *records = fits->keylist; long naxes[2] = {0,0};
while(records){ KeyList *records = hdu->keylist;
char *rec = records->record; DBG("HDU #%d (type %d)", i, hdu->hdutype);
records = records->next; switch(hdu->hdutype){
// TODO: check types of headers from each record! case IMAGE_HDU:
if(strncmp(rec, "SIMPLE", 6) == 0 || strncmp(rec, "EXTEND", 6) == 0) // key "file does conform ..." img = hdu->contents.image;
continue; if(!img && records){ // something wrong - just write keylist
// comment of obligatory key in FITS head DBG("create empty image with records");
else if(strncmp(rec, "COMMENT FITS", 14) == 0 || strncmp(rec, "COMMENT and Astrophysics", 26) == 0) fits_create_img(fp, SHORT_IMG, 0, naxes, &fst);
continue; if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
else if(strncmp(rec, "NAXIS", 5) == 0 || strncmp(rec, "BITPIX", 6) == 0) // NAXIS, NAXISxxx, BITPIX keylist_write(records, fp);
continue; DBG("OK");
int ret = fits_write_record(fp, rec, &fst); continue;
if(fst){fits_report_error(stderr, fst);} }
else if(ret) WARNX(_("Can't write record %s"), rec); naxes[0] = img->width, naxes[1] = img->height;
// DBG("write key: %s", rec); DBG("create, bitpix: %d, naxes = {%zd, %zd}", img->bitpix, naxes[0], naxes[1]);
//fits_create_img(fp, img->bitpix, 2, naxes, &fst);
fits_create_img(fp, SHORT_IMG, 2, naxes, &fst);
if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
keylist_write(records, fp);
DBG("OK, now write image");
// TODO: change to original data type according to bitpix or more whide according to min/max
DBG("bitpix: %d, dtype: %d", SHORT_IMG, img->dtype);
//int bscale = 1, bzero = 32768, status = 0;
//fits_set_bscale(fp, bscale, bzero, &status);
if(img && img->data){
fits_write_img(fp, TUSHORT, 1, img->width * img->height, img->data, &fst);
//fits_write_img(fp, img->dtype, 1, img->width * img->height, img->data, &fst);
DBG("status: %d", fst);
if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
}
break;
case BINARY_TBL:
case ASCII_TBL:
// TODO: save table
break;
} }
} }
//fits->lasthdu = 1;
//FITSFUN(fits_write_record, fp, "COMMENT modified by simple test routine");
fits_write_img(fp, TDOUBLE, 1, sz, fits->data, &fst);
if(fst){fits_report_error(stderr, fst); return FALSE;}
if(fits->tables) table_write(fits, fp);
fits_close_file(fp, &fst); fits_close_file(fp, &fst);
if(fst){fits_report_error(stderr, fst);}*/ if(fst){fits_report_error(stderr, fst);}
return TRUE; return TRUE;
} }
/**
* @brief FITS_rewrite - rewrite file in place
* @param fits - pointer to FITS structure
* @return TRUE if all OK
*/
bool FITS_rewrite(FITS *fits){
FNAME();
char *nm = tmpnam(NULL);
if(!nm){WARN("tmpnam()"); return FALSE;}
char *fnm = strrchr(nm, '/');
if(!fnm){WARN("strrchr()"); return FALSE;}
++fnm;
DBG("make link: %s -> %s", fits->filename, fnm);
if(link(fits->filename, fnm)){
WARN("link()");
return FALSE;
}
return TRUE;
}
/************************************************************************************** /**************************************************************************************
* FITS images * * FITS images *
@ -819,30 +895,74 @@ void image_free(FITSimage **img){
} }
/** /**
* @brief image_malloc - allocate memory for given data type * @brief image_datatype_size - calculate size of one data element for given bitpix
* @param w - image width * @param bitpix - value of BITPIX
* @param h - image height * @param dtype (o) - nearest type of data to fit input type
* @param dtype - data type * @return amount of space need to store one pixel data
*/
int image_datatype_size(int bitpix, int *dtype){
int s = bitpix/8;
if(dtype){
switch(s){
case 1:
case 2:
case 4:
*dtype = TINT;
s = 4;
break;
case 8:
*dtype = TLONG;
break;
case -4:
case -8:
*dtype = TDOUBLE;
s = 8;
break;
default:
return 0; // wrong bitpix
}
}
DBG("bitpix: %d, dtype=%d, imgs=%d", bitpix, *dtype, s);
return s;
}
/**
* @brief image_malloc - allocate memory for given bitpix
* @param w - image width
* @param h - image height
* @param pxbytes- amount of bytes to store data from one pixel
* @return allocated memory * @return allocated memory
*/ */
void *image_data_malloc(size_t w, size_t h, int dtype){ void *image_data_malloc(size_t w, size_t h, int pxbytes){
void *data = calloc(w*h, image_datatype_size(dtype)); if(!pxbytes || !w || !h) return NULL;
void *data = calloc(w*h, pxbytes);
DBG("Allocate %zd members of size %d", w*h, pxbytes);
if(!data) ERR(_("calloc()")); if(!data) ERR(_("calloc()"));
return data; return data;
} }
/** /**
* @brief image_new - create an empty image without headers, assign data type to "dtype" * @brief image_new - create an empty image without headers, assign BITPIX to "bitpix"
* @param w - image width * @param w - image width
* @param h - image height * @param h - image height
* @param dtype - image data type * @param dtype - image data type
* @return * @return
*/ */
FITSimage *image_new(size_t w, size_t h, int dtype){ FITSimage *image_new(size_t w, size_t h, int bitpix){
FITSimage *out = MALLOC(FITSimage, 1); FITSimage *out = MALLOC(FITSimage, 1);
out->data = image_data_malloc(w, h, dtype); int dtype, pxsz = image_datatype_size(bitpix, &dtype);
if(w && h){
out->data = image_data_malloc(w, h, pxsz);
if(!out->data){
WARNX(_("Bad w, h or pxsz"));
FREE(out);
return NULL;
}
}
out->width = w; out->width = w;
out->height = h; out->height = h;
out->pxsz = pxsz;
out->bitpix = bitpix;
out->dtype = dtype; out->dtype = dtype;
return out; return out;
} }
@ -907,7 +1027,7 @@ FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata){
*/ */
FITSimage *image_mksimilar(FITSimage *img){ FITSimage *image_mksimilar(FITSimage *img){
if(!img || img->height < 1 || img->width < 1) return NULL; if(!img || img->height < 1 || img->width < 1) return NULL;
return image_new(img->width, img->height, img->dtype); return image_new(img->width, img->height, img->bitpix);
} }
/** /**
@ -917,7 +1037,7 @@ FITSimage *image_copy(FITSimage *in){
FITSimage *out = image_mksimilar(in); FITSimage *out = image_mksimilar(in);
if(!out) return NULL; if(!out) return NULL;
// TODO: size of data as in original! // TODO: size of data as in original!
memcpy(out->data, in->data, sizeof(double)*in->width*in->height); memcpy(out->data, in->data, (in->pxsz)*(in->width)*(in->height));
return out; return out;
} }
@ -929,20 +1049,30 @@ FITSimage *image_copy(FITSimage *in){
FITSimage *image_read(FITS *fits){ FITSimage *image_read(FITS *fits){
// TODO: open not only 2-dimensional files! // TODO: open not only 2-dimensional files!
// get image dimensions // get image dimensions
int naxis, fst = 0, dtype; int naxis, fst = 0, bitpix;
long naxes[2] = {0,0}; long naxes[2] = {0,0};
fits_get_img_param(fits->fp, 2, &dtype, &naxis, naxes, &fst); fits_get_img_param(fits->fp, 2, &bitpix, &naxis, naxes, &fst);
if(fst){fits_report_error(stderr, fst); return NULL;} if(fst){fits_report_error(stderr, fst); return NULL;}
if(naxis > 2){ if(naxis > 2){
WARNX(_("Images with > 2 dimensions are not supported")); WARNX(_("Images with > 2 dimensions are not supported"));
return NULL; return NULL;
} }/*
DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], dtype); if(naxis < 2){
WARNX(_("Not an image: NAXIS = %d"), naxis);
return NULL;
}*/
DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], bitpix);
FITSimage *img = image_new(naxes[0], naxes[1], dtype); FITSimage *img = image_new(naxes[0], naxes[1], bitpix);
int stat = 0; int stat = 0;
fits_read_img(fits->fp, dtype, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst); if(!img) return NULL;
if(!img->data) return img; // empty "image" - no data inside
DBG("try to read, dt=%d, sz=%ld", img->dtype, naxes[0]*naxes[1]);
//int bscale = 1, bzero = 32768, status = 0;
//fits_set_bscale(fits->fp, bscale, bzero, &status);
//fits_read_img(fits->fp, img->dtype, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst);
fits_read_img(fits->fp, TUSHORT, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst);
if(fst){ if(fst){
fits_report_error(stderr, fst); fits_report_error(stderr, fst);
image_free(&img); image_free(&img);