source: trunk/src/Cubes/saveImage.cc @ 1100

Last change on this file since 1100 was 1100, checked in by MatthewWhiting, 12 years ago

Several changes to the saving of FITS files, including working round the main problem with #167 - we now save the mask FITS file as integers, rather than shorts (removing the type
conversion). Have also improved the use of cfitsio writing functions so that they better match what we're trying to do, and are consistent across the different functions.

File size: 20.5 KB
Line 
1// -----------------------------------------------------------------------
2// saveImage.cc: Write a wavelet-reconstructed or smoothed array to a
3//               FITS file.
4// -----------------------------------------------------------------------
5// Copyright (C) 2006, Matthew Whiting, ATNF
6//
7// This program is free software; you can redistribute it and/or modify it
8// under the terms of the GNU General Public License as published by the
9// Free Software Foundation; either version 2 of the License, or (at your
10// option) any later version.
11//
12// Duchamp is distributed in the hope that it will be useful, but WITHOUT
13// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15// for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with Duchamp; if not, write to the Free Software Foundation,
19// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20//
21// Correspondence concerning Duchamp may be directed to:
22//    Internet email: Matthew.Whiting [at] atnf.csiro.au
23//    Postal address: Dr. Matthew Whiting
24//                    Australia Telescope National Facility, CSIRO
25//                    PO Box 76
26//                    Epping NSW 1710
27//                    AUSTRALIA
28// -----------------------------------------------------------------------
29#include <iostream>
30#include <sstream>
31#include <string>
32#include <string.h>
33#include <wcslib/wcs.h>
34#include <wcslib/wcshdr.h>
35#include <wcslib/wcsunits.h>
36#define WCSLIB_GETWCSTAB
37// define this so that we don't try and redefine wtbarr
38// (this is a problem when using cfitsio v.3 and g++ v.4)
39
40#include <fitsio.h>
41#include <duchamp/duchamp.hh>
42#include <duchamp/Cubes/cubes.hh>
43#include <duchamp/PixelMap/Voxel.hh>
44#include <duchamp/PixelMap/Object3D.hh>
45
46namespace duchamp
47{
48
49  /// @brief Write FITS headers in correct format for reconstructed array output
50  void writeReconHeaderInfo(fitsfile *fptr, Param &par, std::string nature);
51
52  /// @brief Write FITS headers in correct format for smoothed array output
53  void writeSmoothHeaderInfo(fitsfile *fptr, Param &par);
54
55  /// @brief Write FITS headers in correct format for mask array output
56  void writeMaskHeaderInfo(fitsfile *fptr, Param &par);
57
58  /// @brief Write FITS headers in correct format for moment-0 array output
59  void writeMomentMapHeaderInfo(fitsfile *fptr, Param &par);
60
61  //---------------------------------------------------------------------------
62
63
64  void duchampFITSerror(int status, std::string subroutine, std::string error)
65  {
66    if(status){
67      DUCHAMPWARN(subroutine,error);
68      fits_report_error(stderr, status);
69    }
70  }
71
72
73  OUTCOME Cube::saveMomentMapImage()
74  {
75    /// @details
76    ///  A function to save the moment-0 map to a FITS file.
77   
78    int newbitpix = FLOAT_IMG;
79    long *fpixel = new long[this->head.WCS().naxis];
80    for(int i=0;i<this->header().WCS().naxis;i++) fpixel[i]=1;
81    int status = 0;  /* MUST initialize status */
82    fitsfile *fptr;         
83
84    std::string fileout = "!" + this->par.outputMomentMapFile();
85    // the ! is there so that it writes over an existing file.
86
87    status = 0;
88    if(fits_create_file(&fptr,fileout.c_str(),&status)){
89      duchampFITSerror(status,"saveMomentMapImage","Error creating file:");
90      return FAILURE;
91    }
92    else {
93
94      if(this->writeBasicHeader(fptr, newbitpix,true)==FAILURE){
95        DUCHAMPWARN("write Recon Cube", "Failure writing to header");
96        return FAILURE;
97      }
98
99      writeMomentMapHeaderInfo(fptr, this->par);
100       
101      long size = this->axisDim[0] * this->axisDim[1];
102      float *momentMap = new float[size];
103      std::vector<bool> detectionMap = this->getMomentMap(momentMap);
104      status=0;
105      if(fits_write_pix(fptr, TFLOAT, fpixel, size, momentMap, &status)){
106        duchampFITSerror(status,"saveMomentMapImage","Error writing data");
107      }
108      status = 0;
109      if(fits_close_file(fptr, &status)){
110        duchampFITSerror(status,"saveMomentMapImage","Error closing file");
111      }
112
113      delete [] momentMap;
114
115    }
116
117    delete [] fpixel;
118
119    return SUCCESS;
120
121  }
122
123  OUTCOME Cube::saveMaskCube()
124  {
125    /// @details
126    ///  A function to save a mask to a FITS file, indicating where the
127    ///  detections where made. The value of the detected pixels is
128    ///  determined by the flagMaskWithObjectNum parameter: if true,
129    ///  the value of the pixels is given by the corresponding object
130    ///  ID number; if false, they take the value 1 for all
131    ///  objects. Pixels not in a detected object have the value 0.
132
133    int newbitpix = LONG_IMG;
134    long *fpixel = new long[this->head.WCS().naxis];
135    for(int i=0;i<this->header().WCS().naxis;i++) fpixel[i]=1;
136    int status = 0;  /* MUST initialize status */
137    fitsfile *fptrNew;         
138
139    std::string fileout = "!" + this->par.outputMaskFile();
140    // the ! is there so that it writes over an existing file.
141
142    status = 0;
143    if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
144      duchampFITSerror(status,"saveMask","Error creating file:");
145      return FAILURE;
146    }
147    else {
148      if(this->writeBasicHeader(fptrNew, newbitpix)==FAILURE){
149        DUCHAMPWARN("write Mask Cube", "Failure writing to header");
150        return FAILURE;
151      }
152
153      writeMaskHeaderInfo(fptrNew, this->par);
154
155      char *comment = new char[FLEN_COMMENT];
156      strcpy(comment,"");
157      char *keyword = new char[FLEN_KEYWORD];
158      std::string newunits;
159      if(this->par.getFlagMaskWithObjectNum())
160        newunits = "Object ID";
161      else
162        newunits = "Detection flag";
163      strcpy(keyword,"BUNIT");
164      if(fits_update_key(fptrNew, TSTRING, keyword, (char *)newunits.c_str(), comment, &status)){
165        duchampFITSerror(status,"saveMask","Error writing BUNIT header:");
166      }
167      delete [] comment;
168      delete [] keyword;
169       
170      int *mask = new int[this->numPixels];
171      for(size_t i=0;i<this->numPixels;i++) mask[i]=0;
172      std::vector<Detection>::iterator obj;
173      for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
174        std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
175        std::vector<PixelInfo::Voxel>::iterator vox;
176        for(vox=voxlist.begin();vox<voxlist.end();vox++){
177          size_t pixelpos = vox->getX() + this->axisDim[0]*vox->getY() +
178            this->axisDim[0]*this->axisDim[1]*vox->getZ();
179          if(this->par.getFlagMaskWithObjectNum()) mask[pixelpos] = obj->getID();
180          else mask[pixelpos] = 1;
181        }
182      }
183      status=0;
184      long group=0;
185      LONGLONG first=1;
186      LONGLONG nelem=LONGLONG(this->numPixels);
187      if(fits_write_img_int(fptrNew, group, first, nelem, mask, &status)){
188        duchampFITSerror(status,"saveMask","Error writing mask array!:");
189      }
190      status = 0;
191      if(fits_close_file(fptrNew, &status)){
192        duchampFITSerror(status,"saveMask","Error closing file:");
193      }
194
195      delete [] mask;
196
197    }
198
199    delete [] fpixel;
200
201    return SUCCESS;
202  }
203 
204  //---------------------------------------------------------------------------
205
206  OUTCOME Cube::saveSmoothedCube()
207  {
208    /// @brief
209    ///   A function to save the smoothed arrays to a FITS file.
210    ///   Additional header keywords are written as well, indicating the
211    ///   width of the Hanning filter or the dimensions of the Gaussian
212    ///   kernel.
213    ///   The file is always written -- if the filename (as calculated
214    ///    based on the parameters) exists, then it is overwritten.
215 
216    float blankval = this->par.getBlankPixVal();
217
218    int status = 0;  /* MUST initialize status */
219    fitsfile *fptrNew;         
220
221    if(this->par.getFlagOutputSmooth()){
222      std::string fileout = "!" + this->par.outputSmoothFile();
223      // the ! is there so that it writes over an existing file.
224
225      status = 0;
226      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
227        duchampFITSerror(status,"saveSmoothedCube","Error creating smoothed FITS file:");
228        return FAILURE;
229      }
230      else {
231
232          if(this->writeBasicHeader(fptrNew, FLOAT_IMG)==FAILURE){
233            DUCHAMPWARN("write Smoothed Cube", "Failure writing to header");
234            return FAILURE;
235          }
236
237        writeSmoothHeaderInfo(fptrNew, this->par);
238
239        long group=0;
240        if(this->par.getFlagBlankPix())
241          fits_write_imgnull_flt(fptrNew, group, 1, this->numPixels, this->recon, blankval, &status);
242        else
243          fits_write_img_flt(fptrNew, group, 1, this->numPixels, this->recon, &status);
244        if(status){
245          duchampFITSerror(status,"saveSmothedCube","Error writing smoothed array:");
246          return FAILURE;
247        }
248
249        status = 0;
250        if(fits_close_file(fptrNew, &status)){
251          duchampFITSerror(status,"saveSmoothedCube","Error closing file:");
252        }
253      }
254    }
255
256    return SUCCESS;
257
258  }
259
260  //---------------------------------------------------------------------------
261
262  OUTCOME Cube::saveReconstructedCube()
263  {
264    /// @details
265    ///  A function to save the reconstructed and/or residual arrays.
266    ///   A number of header keywords are written as well, indicating the
267    ///    nature of the reconstruction that has been done.
268    ///   The file is always written -- if the filename (as calculated
269    ///    based on the recon parameters) exists, then it is overwritten.
270 
271    float blankval = this->par.getBlankPixVal();
272
273    if(!this->reconAllocated){
274      DUCHAMPERROR("saveReconCube","Have not allocated reconstructed array, so cannot save");
275      return FAILURE;
276    }
277
278    int status = 0;  /* MUST initialize status */
279    long group=0;
280    fitsfile *fptrNew;         
281 
282    if(this->par.getFlagOutputRecon()){
283      std::string fileout = "!" + this->par.outputReconFile();
284      // the ! is there so that it writes over an existing file.
285
286      status = 0;
287      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
288        duchampFITSerror(status,"saveReconCube","Error creating file:");
289        return FAILURE;
290      }
291      else
292        {
293
294          if(this->writeBasicHeader(fptrNew, FLOAT_IMG)==FAILURE){
295            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
296            return FAILURE;
297          }
298         
299          writeReconHeaderInfo(fptrNew, this->par, "recon");
300
301          status=0;
302          if(this->par.getFlagBlankPix())
303            fits_write_imgnull_flt(fptrNew, group, 1, this->numPixels, this->recon, blankval, &status);
304          else 
305            fits_write_img_flt(fptrNew, group, 1, this->numPixels, this->recon, &status);
306          if(status){
307            duchampFITSerror(status,"saveReconCube","Error writing reconstructed array:");
308            return FAILURE;
309          }
310
311          status = 0;
312          if(fits_close_file(fptrNew, &status)){
313            duchampFITSerror(status,"saveReconCube","Error closing file:");
314          }
315        }
316    }
317
318
319    if(this->par.getFlagOutputResid()){
320      float *resid = new float[this->numPixels];
321      for(size_t i=0;i<this->numPixels;i++)
322        resid[i] = this->array[i] - this->recon[i];
323
324      std::string fileout = "!" + this->par.outputResidFile();
325      // the ! is there so that it writes over an existing file.
326      status = 0;
327      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
328        duchampFITSerror(status,"saveResidualCube","Error creating new file:");
329        return FAILURE;
330      }
331      else
332        {
333
334          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
335            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
336            return FAILURE;
337          }
338
339          writeReconHeaderInfo(fptrNew, this->par, "resid");
340
341          if(this->par.getFlagBlankPix())
342            fits_write_imgnull_flt(fptrNew, group, 1, this->numPixels, resid, blankval, &status);
343          else 
344            fits_write_img_flt(fptrNew, group, 1, this->numPixels, resid, &status);
345          if(status){
346            duchampFITSerror(status,"saveResidualCube","Error writing reconstructed array:");
347            return FAILURE;
348          }
349
350          status=0;
351          if(fits_close_file(fptrNew, &status)){
352            duchampFITSerror(status,"saveResidualCube","Error closing file:");
353          }
354        }
355      delete [] resid;
356    }
357
358    return SUCCESS;
359  }
360
361  //---------------------------------------------------------------------------
362
363  void writeReconHeaderInfo(fitsfile *fptr, Param &par, std::string nature)
364  {
365    /// @details
366    ///   A simple function that writes all the necessary keywords and comments
367    ///    to the FITS header pointed to by fptr.
368    ///   The keyword names and comments are taken from duchamp.hh
369    ///   The parameter "nature" indicates what type of file is being written:
370    ///    should be either "recon" or "resid".
371
372    int status = 0;
373    std::string explanation = "",ReconResid="";
374
375    fits_write_history(fptr, (char *)header_reconHistory1.c_str(), &status);
376                                   
377    fits_write_history(fptr, (char *)header_reconHistory2.c_str(), &status);
378
379    fits_write_history(fptr, (char *)header_reconHistory_input.c_str(), &status);
380
381    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
382
383    if(par.getFlagSubsection()){
384      fits_write_comment(fptr,(char *)header_reconSubsection_comment.c_str(),
385                         &status);
386      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
387                     (char *)par.getSubsection().c_str(),
388                     (char *)comment_subsection.c_str(), &status);
389    }
390   
391    fits_write_comment(fptr, (char *)header_atrous_comment.c_str(), &status);
392
393    float valf = par.getAtrousCut();
394    fits_write_key(fptr, TFLOAT, (char *)keyword_snrRecon.c_str(), &valf,
395                   (char *)comment_snrRecon.c_str(), &status);
396
397    int vali = par.getReconDim();
398    fits_write_key(fptr, TINT, (char *)keyword_reconDim.c_str(), &vali,
399                   (char *)comment_reconDim.c_str(), &status);
400
401    vali = par.getMinScale();
402    fits_write_key(fptr, TINT, (char *)keyword_scaleMin.c_str(), &vali,
403                   (char *)comment_scaleMin.c_str(), &status);
404
405    vali = par.getFilterCode();
406    fits_write_key(fptr, TINT, (char *)keyword_filterCode.c_str(), &vali,
407                   (char *)comment_filterCode.c_str(), &status);
408
409    if(nature == "recon"){
410      explanation = "Duchamp: This is the RECONSTRUCTED cube";
411      ReconResid = "RECON";
412    }
413    else if(nature == "resid"){
414      explanation = "Duchamp: This is the RESIDUAL cube";
415      ReconResid = "RESID";
416    }
417    else DUCHAMPWARN("write_header_info","explanation not present");
418    fits_write_comment(fptr, (char *)explanation.c_str(), &status);
419    fits_write_key(fptr, TSTRING, (char *)keyword_ReconResid.c_str(),
420                   (char *)ReconResid.c_str(),
421                   (char *)comment_ReconResid.c_str(), &status);
422
423  }
424
425  //---------------------------------------------------------------------------
426
427  void writeSmoothHeaderInfo(fitsfile *fptr, Param &par)
428  {
429    /// @details
430    ///   A simple function that writes all the necessary keywords and comments
431    ///    to the FITS header pointed to by fptr.
432    ///   The keyword names and comments are taken from duchamp.hh
433
434    int status = 0;
435
436    fits_write_history(fptr, (char *)header_smoothHistory.c_str(), &status);
437    status = 0;
438    fits_write_history(fptr, (char *)header_smoothHistory_input.c_str(),&status);
439    status = 0;
440    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
441
442    if(par.getFlagSubsection()){
443      status = 0;
444      fits_write_comment(fptr,(char *)header_smoothSubsection_comment.c_str(),
445                         &status);
446      status = 0;
447      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
448                     (char *)par.getSubsection().c_str(),
449                     (char *)comment_subsection.c_str(), &status);
450    }
451   
452    if(par.getSmoothType()=="spatial"){
453      // if kernMin is negative (not defined), make it equal to kernMaj
454      if(par.getKernMin() < 0) par.setKernMin(par.getKernMaj());
455
456      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
457                     (char *)header_smoothSpatial.c_str(),
458                     (char *)comment_smoothtype.c_str(), &status);
459      float valf = par.getKernMaj();
460      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmaj.c_str(), &valf,
461                     (char *)comment_kernmaj.c_str(), &status);
462      valf = par.getKernMin();
463      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmin.c_str(), &valf,
464                     (char *)comment_kernmin.c_str(), &status);
465      valf = par.getKernPA();
466      fits_write_key(fptr, TFLOAT, (char *)keyword_kernpa.c_str(), &valf,
467                     (char *)comment_kernpa.c_str(), &status);
468    }
469    else if(par.getSmoothType()=="spectral"){
470      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
471                     (char *)header_smoothSpectral.c_str(),
472                     (char *)comment_smoothtype.c_str(), &status);
473      int vali = par.getHanningWidth();
474      fits_write_key(fptr, TINT, (char *)keyword_hanningwidth.c_str(), &vali,
475                     (char *)comment_hanningwidth.c_str(), &status);
476    }
477  }
478
479  //---------------------------------------------------------------------------
480
481  void writeMaskHeaderInfo(fitsfile *fptr, Param &par)
482  {
483    /// @details
484    ///   A simple function that writes all the necessary keywords and comments
485    ///    to the FITS header pointed to by fptr.
486    ///   The keyword names and comments are taken from duchamp.hh
487
488    int status = 0;
489
490    fits_write_history(fptr, (char *)header_maskHistory.c_str(), &status);
491    status = 0;
492    fits_write_history(fptr, (char *)header_maskHistory_input.c_str(),&status);
493    status = 0;
494    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
495
496    if(par.getFlagSubsection()){
497      status = 0;
498      fits_write_comment(fptr,(char *)header_maskSubsection_comment.c_str(),
499                         &status);
500      status = 0;
501      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
502                     (char *)par.getSubsection().c_str(),
503                     (char *)comment_subsection.c_str(), &status);
504    }
505  }
506
507  //---------------------------------------------------------------------------
508
509  void writeMomentMapHeaderInfo(fitsfile *fptr, Param &par)
510  {
511    /// @details
512    ///   A simple function that writes all the necessary keywords and comments
513    ///    to the FITS header pointed to by fptr.
514    ///   The keyword names and comments are taken from duchamp.hh
515
516    int status = 0;
517
518    fits_write_history(fptr, (char *)header_moment0History.c_str(), &status);
519    status = 0;
520    fits_write_history(fptr, (char *)header_moment0History_input.c_str(),&status);
521    status = 0;
522    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
523
524    if(par.getFlagSubsection()){
525      status = 0;
526      fits_write_comment(fptr,(char *)header_moment0Subsection_comment.c_str(),
527                         &status);
528      status = 0;
529      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
530                     (char *)par.getSubsection().c_str(),
531                     (char *)comment_subsection.c_str(), &status);
532    }
533  }
534
535
536  //---------------------------------------------------------------------------
537
538  OUTCOME Cube::writeBasicHeader(fitsfile *fptr, int bitpix, bool is2D)
539  {
540    char *header, *hptr, keyname[9];
541    int  i, nkeyrec, status = 0;
542   
543    const size_t naxis=this->numDim;
544    long* naxes = new long[this->numDim];
545    for(size_t i=0;i<naxis;i++) naxes[i]=this->axisDim[i];
546    if(is2D) naxes[this->head.WCS().spec]=1;
547    // write the required header keywords
548    std::cerr << "Creating image with bitpix = " << bitpix << "\n";
549    fits_write_imghdr(fptr, bitpix, naxis, naxes,  &status);
550
551    // Write beam information
552    this->head.beam().writeToFITS(fptr);
553
554    // Write bunit information
555    status = 0;
556    strcpy(keyname,"BUNIT");
557    if (fits_update_key(fptr, TSTRING, keyname, (char *)this->head.getFluxUnits().c_str(), NULL, &status)){
558      DUCHAMPWARN("saveImage","Error writing bunit info:");
559      fits_report_error(stderr, status);
560      return FAILURE;
561    }
562
563    // convert the wcsprm struct to a set of 80-char keys
564    if ((status = wcshdo(WCSHDO_all, this->head.getWCS(), &nkeyrec, &header))) {
565      DUCHAMPWARN("saveImage","Could not convert WCS information to FITS header. WCS Error Code = "<<status<<": "<<wcs_errmsg[status]);
566      return FAILURE;
567    }
568
569    hptr = header;
570    strncpy(keyname,hptr,8);
571    for (i = 0; i < nkeyrec; i++, hptr += 80) {
572      status=0;
573      if(fits_update_card(fptr,keyname,hptr,&status)){
574        DUCHAMPWARN("saveImage","Error writing header card");
575        fits_report_error(stderr,status);
576        return FAILURE;
577      }
578    }
579   
580    if(bitpix>0){
581      if(this->par.getFlagBlankPix()){
582        strcpy(keyname,"BSCALE");
583        float bscale=this->head.getBscaleKeyword();
584        if(fits_update_key(fptr, TFLOAT, keyname, &bscale, NULL, &status)){
585          duchampFITSerror(status,"saveImage","Error writing BSCALE header:");
586        }
587        strcpy(keyname,"BZERO");
588        float bzero=this->head.getBzeroKeyword();
589        if(fits_update_key(fptr, TFLOAT, keyname, &bzero, NULL, &status)){
590          duchampFITSerror(status,"saveImage","Error writing BZERO header:");
591        }
592        strcpy(keyname,"BLANK");
593        int blank=this->head.getBlankKeyword();
594        if(fits_update_key(fptr, TINT, keyname, &blank, NULL, &status)){
595          duchampFITSerror(status,"saveImage","Error writing BLANK header:");
596        }
597        if(fits_set_imgnull(fptr, blank, &status)){
598          duchampFITSerror(status, "saveImage", "Error setting null value:");
599        }
600        if(fits_set_bscale(fptr, bscale, bzero, &status)){
601          duchampFITSerror(status,"saveImage","Error setting scale:");
602        }
603      }
604    }
605
606    delete [] naxes;
607
608    return SUCCESS;
609
610  }
611
612}
Note: See TracBrowser for help on using the repository browser.