source: tags/release-1.2.1/src/Cubes/saveImage.cc @ 1441

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

Minor changes, improving the warning message

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 = SHORT_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      short *mask = new short[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      if(fits_write_pix(fptrNew, TSHORT, fpixel, this->numPixels, mask, &status)){
185        duchampFITSerror(status,"saveMask","Error writing mask array:");
186      }
187      status = 0;
188      if(fits_close_file(fptrNew, &status)){
189        duchampFITSerror(status,"saveMask","Error closing file:");
190      }
191
192      delete [] mask;
193
194    }
195
196    delete [] fpixel;
197
198    return SUCCESS;
199  }
200 
201  //---------------------------------------------------------------------------
202
203  OUTCOME Cube::saveSmoothedCube()
204  {
205    /// @brief
206    ///   A function to save the smoothed arrays to a FITS file.
207    ///   Additional header keywords are written as well, indicating the
208    ///   width of the Hanning filter or the dimensions of the Gaussian
209    ///   kernel.
210    ///   The file is always written -- if the filename (as calculated
211    ///    based on the parameters) exists, then it is overwritten.
212 
213    float blankval = this->par.getBlankPixVal();
214
215    int status = 0;  /* MUST initialize status */
216    fitsfile *fptrNew;         
217
218    if(this->par.getFlagOutputSmooth()){
219      std::string fileout = "!" + this->par.outputSmoothFile();
220      // the ! is there so that it writes over an existing file.
221
222      status = 0;
223      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
224        duchampFITSerror(status,"saveSmoothedCube","Error creating smoothed FITS file:");
225        return FAILURE;
226      }
227      else {
228
229          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
230            DUCHAMPWARN("write Smoothed Cube", "Failure writing to header");
231            return FAILURE;
232          }
233
234        writeSmoothHeaderInfo(fptrNew, this->par);
235
236        if(this->par.getFlagBlankPix())
237          fits_write_imgnull(fptrNew, TFLOAT, 1, this->numPixels, this->recon, &blankval, &status);
238        else
239          fits_write_img(fptrNew, TFLOAT, 1, this->numPixels, this->recon, &status);
240        if(status){
241          duchampFITSerror(status,"saveSmothedCube","Error writing smoothed array:");
242          return FAILURE;
243        }
244
245        status = 0;
246        if(fits_close_file(fptrNew, &status)){
247          duchampFITSerror(status,"saveSmoothedCube","Error closing file:");
248        }
249      }
250    }
251
252    return SUCCESS;
253
254  }
255
256  //---------------------------------------------------------------------------
257
258  OUTCOME Cube::saveReconstructedCube()
259  {
260    /// @details
261    ///  A function to save the reconstructed and/or residual arrays.
262    ///   A number of header keywords are written as well, indicating the
263    ///    nature of the reconstruction that has been done.
264    ///   The file is always written -- if the filename (as calculated
265    ///    based on the recon parameters) exists, then it is overwritten.
266 
267    float blankval = this->par.getBlankPixVal();
268
269    if(!this->reconAllocated){
270      DUCHAMPERROR("saveReconCube","Have not allocated reconstructed array, so cannot save");
271      return FAILURE;
272    }
273
274    int status = 0;  /* MUST initialize status */
275    fitsfile *fptrNew;         
276 
277    if(this->par.getFlagOutputRecon()){
278      std::string fileout = "!" + this->par.outputReconFile();
279      // the ! is there so that it writes over an existing file.
280
281      status = 0;
282      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
283        duchampFITSerror(status,"saveReconCube","Error creating file:");
284        return FAILURE;
285      }
286      else
287        {
288
289          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
290            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
291            return FAILURE;
292          }
293         
294          writeReconHeaderInfo(fptrNew, this->par, "recon");
295
296          status=0;
297          long *fpixel = new long[this->header().WCS().naxis];
298          for(int i=0;i<this->numDim;i++) fpixel[i]=1;
299          long group=0;
300          if(this->par.getFlagBlankPix())
301            fits_write_imgnull_flt(fptrNew, group, 1, this->numPixels, this->recon, blankval, &status);
302          else 
303            fits_write_img_flt(fptrNew, group, 1, this->numPixels, this->recon, &status);
304          if(status){
305            duchampFITSerror(status,"saveReconCube","Error writing reconstructed array:");
306            return FAILURE;
307          }
308          delete [] fpixel;
309
310          status = 0;
311          if(fits_close_file(fptrNew, &status)){
312            duchampFITSerror(status,"saveReconCube","Error closing file:");
313          }
314        }
315    }
316
317
318    if(this->par.getFlagOutputResid()){
319      float *resid = new float[this->numPixels];
320      for(size_t i=0;i<this->numPixels;i++)
321        resid[i] = this->array[i] - this->recon[i];
322
323      std::string fileout = "!" + this->par.outputResidFile();
324      // the ! is there so that it writes over an existing file.
325      status = 0;
326      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
327        duchampFITSerror(status,"saveResidualCube","Error creating new file:");
328        return FAILURE;
329      }
330      else
331        {
332
333          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
334            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
335            return FAILURE;
336          }
337
338          writeReconHeaderInfo(fptrNew, this->par, "resid");
339
340          if(this->par.getFlagBlankPix())
341            fits_write_imgnull(fptrNew, TFLOAT, 1, this->numPixels, resid, &blankval, &status);
342          else 
343            fits_write_img(fptrNew, TFLOAT, 1, this->numPixels, resid, &status);
344          if(status){
345            duchampFITSerror(status,"saveResidualCube","Error writing reconstructed array:");
346            return FAILURE;
347          }
348
349          status=0;
350          if(fits_close_file(fptrNew, &status)){
351            duchampFITSerror(status,"saveResidualCube","Error closing file:");
352          }
353        }
354      delete [] resid;
355    }
356
357    return SUCCESS;
358  }
359
360  //---------------------------------------------------------------------------
361
362  void writeReconHeaderInfo(fitsfile *fptr, Param &par, std::string nature)
363  {
364    /// @details
365    ///   A simple function that writes all the necessary keywords and comments
366    ///    to the FITS header pointed to by fptr.
367    ///   The keyword names and comments are taken from duchamp.hh
368    ///   The parameter "nature" indicates what type of file is being written:
369    ///    should be either "recon" or "resid".
370
371    int status = 0;
372    std::string explanation = "",ReconResid="";
373
374    fits_write_history(fptr, (char *)header_reconHistory1.c_str(), &status);
375                                   
376    fits_write_history(fptr, (char *)header_reconHistory2.c_str(), &status);
377
378    fits_write_history(fptr, (char *)header_reconHistory_input.c_str(), &status);
379
380    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
381
382    if(par.getFlagSubsection()){
383      fits_write_comment(fptr,(char *)header_reconSubsection_comment.c_str(),
384                         &status);
385      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
386                     (char *)par.getSubsection().c_str(),
387                     (char *)comment_subsection.c_str(), &status);
388    }
389   
390    fits_write_comment(fptr, (char *)header_atrous_comment.c_str(), &status);
391
392    float valf = par.getAtrousCut();
393    fits_write_key(fptr, TFLOAT, (char *)keyword_snrRecon.c_str(), &valf,
394                   (char *)comment_snrRecon.c_str(), &status);
395
396    int vali = par.getReconDim();
397    fits_write_key(fptr, TINT, (char *)keyword_reconDim.c_str(), &vali,
398                   (char *)comment_reconDim.c_str(), &status);
399
400    vali = par.getMinScale();
401    fits_write_key(fptr, TINT, (char *)keyword_scaleMin.c_str(), &vali,
402                   (char *)comment_scaleMin.c_str(), &status);
403
404    vali = par.getFilterCode();
405    fits_write_key(fptr, TINT, (char *)keyword_filterCode.c_str(), &vali,
406                   (char *)comment_filterCode.c_str(), &status);
407
408    if(nature == "recon"){
409      explanation = "Duchamp: This is the RECONSTRUCTED cube";
410      ReconResid = "RECON";
411    }
412    else if(nature == "resid"){
413      explanation = "Duchamp: This is the RESIDUAL cube";
414      ReconResid = "RESID";
415    }
416    else DUCHAMPWARN("write_header_info","explanation not present");
417    fits_write_comment(fptr, (char *)explanation.c_str(), &status);
418    fits_write_key(fptr, TSTRING, (char *)keyword_ReconResid.c_str(),
419                   (char *)ReconResid.c_str(),
420                   (char *)comment_ReconResid.c_str(), &status);
421
422  }
423
424  //---------------------------------------------------------------------------
425
426  void writeSmoothHeaderInfo(fitsfile *fptr, Param &par)
427  {
428    /// @details
429    ///   A simple function that writes all the necessary keywords and comments
430    ///    to the FITS header pointed to by fptr.
431    ///   The keyword names and comments are taken from duchamp.hh
432
433    int status = 0;
434
435    fits_write_history(fptr, (char *)header_smoothHistory.c_str(), &status);
436    status = 0;
437    fits_write_history(fptr, (char *)header_smoothHistory_input.c_str(),&status);
438    status = 0;
439    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
440
441    if(par.getFlagSubsection()){
442      status = 0;
443      fits_write_comment(fptr,(char *)header_smoothSubsection_comment.c_str(),
444                         &status);
445      status = 0;
446      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
447                     (char *)par.getSubsection().c_str(),
448                     (char *)comment_subsection.c_str(), &status);
449    }
450   
451    if(par.getSmoothType()=="spatial"){
452      // if kernMin is negative (not defined), make it equal to kernMaj
453      if(par.getKernMin() < 0) par.setKernMin(par.getKernMaj());
454
455      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
456                     (char *)header_smoothSpatial.c_str(),
457                     (char *)comment_smoothtype.c_str(), &status);
458      float valf = par.getKernMaj();
459      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmaj.c_str(), &valf,
460                     (char *)comment_kernmaj.c_str(), &status);
461      valf = par.getKernMin();
462      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmin.c_str(), &valf,
463                     (char *)comment_kernmin.c_str(), &status);
464      valf = par.getKernPA();
465      fits_write_key(fptr, TFLOAT, (char *)keyword_kernpa.c_str(), &valf,
466                     (char *)comment_kernpa.c_str(), &status);
467    }
468    else if(par.getSmoothType()=="spectral"){
469      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
470                     (char *)header_smoothSpectral.c_str(),
471                     (char *)comment_smoothtype.c_str(), &status);
472      int vali = par.getHanningWidth();
473      fits_write_key(fptr, TINT, (char *)keyword_hanningwidth.c_str(), &vali,
474                     (char *)comment_hanningwidth.c_str(), &status);
475    }
476  }
477
478  //---------------------------------------------------------------------------
479
480  void writeMaskHeaderInfo(fitsfile *fptr, Param &par)
481  {
482    /// @details
483    ///   A simple function that writes all the necessary keywords and comments
484    ///    to the FITS header pointed to by fptr.
485    ///   The keyword names and comments are taken from duchamp.hh
486
487    int status = 0;
488
489    fits_write_history(fptr, (char *)header_maskHistory.c_str(), &status);
490    status = 0;
491    fits_write_history(fptr, (char *)header_maskHistory_input.c_str(),&status);
492    status = 0;
493    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
494
495    if(par.getFlagSubsection()){
496      status = 0;
497      fits_write_comment(fptr,(char *)header_maskSubsection_comment.c_str(),
498                         &status);
499      status = 0;
500      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
501                     (char *)par.getSubsection().c_str(),
502                     (char *)comment_subsection.c_str(), &status);
503    }
504  }
505
506  //---------------------------------------------------------------------------
507
508  void writeMomentMapHeaderInfo(fitsfile *fptr, Param &par)
509  {
510    /// @details
511    ///   A simple function that writes all the necessary keywords and comments
512    ///    to the FITS header pointed to by fptr.
513    ///   The keyword names and comments are taken from duchamp.hh
514
515    int status = 0;
516
517    fits_write_history(fptr, (char *)header_moment0History.c_str(), &status);
518    status = 0;
519    fits_write_history(fptr, (char *)header_moment0History_input.c_str(),&status);
520    status = 0;
521    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
522
523    if(par.getFlagSubsection()){
524      status = 0;
525      fits_write_comment(fptr,(char *)header_moment0Subsection_comment.c_str(),
526                         &status);
527      status = 0;
528      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
529                     (char *)par.getSubsection().c_str(),
530                     (char *)comment_subsection.c_str(), &status);
531    }
532  }
533
534
535  //---------------------------------------------------------------------------
536
537  OUTCOME Cube::writeBasicHeader(fitsfile *fptr, int bitpix, bool is2D)
538  {
539    char *header, *hptr, keyname[9];
540    int  i, nkeyrec, status = 0;
541   
542    const size_t naxis=this->numDim;
543    long* naxes = new long[this->numDim];
544    for(size_t i=0;i<naxis;i++) naxes[i]=this->axisDim[i];
545    if(is2D) naxes[this->head.WCS().spec]=1;
546    // write the required header keywords
547    fits_write_imghdr(fptr, bitpix, naxis, naxes,  &status);
548
549    // Write beam information
550    this->head.beam().writeToFITS(fptr);
551
552    // Write bunit information
553    status = 0;
554    strcpy(keyname,"BUNIT");
555    if (fits_update_key(fptr, TSTRING, keyname, (char *)this->head.getFluxUnits().c_str(), NULL, &status)){
556      DUCHAMPWARN("saveImage","Error writing bunit info:");
557      fits_report_error(stderr, status);
558      return FAILURE;
559    }
560
561    // convert the wcsprm struct to a set of 80-char keys
562    if ((status = wcshdo(WCSHDO_all, this->head.getWCS(), &nkeyrec, &header))) {
563      DUCHAMPWARN("saveImage","Could not convert WCS information to FITS header. WCS Error Code = "<<status<<": "<<wcs_errmsg[status]);
564      return FAILURE;
565    }
566
567    hptr = header;
568    strncpy(keyname,hptr,8);
569    for (i = 0; i < nkeyrec; i++, hptr += 80) {
570      status=0;
571      if(fits_update_card(fptr,keyname,hptr,&status)){
572        DUCHAMPWARN("saveImage","Error writing header card");
573        fits_report_error(stderr,status);
574        return FAILURE;
575      }
576    }
577   
578    if(bitpix>0){
579      if(this->par.getFlagBlankPix()){
580        strcpy(keyname,"BSCALE");
581        float bscale=this->head.getBscaleKeyword();
582        if(fits_update_key(fptr, TFLOAT, keyname, &bscale, NULL, &status)){
583          duchampFITSerror(status,"saveImage","Error writing BSCALE header:");
584        }
585        strcpy(keyname,"BZERO");
586        float bzero=this->head.getBzeroKeyword();
587        if(fits_update_key(fptr, TFLOAT, keyname, &bzero, NULL, &status)){
588          duchampFITSerror(status,"saveImage","Error writing BZERO header:");
589        }
590        strcpy(keyname,"BLANK");
591        int blank=this->head.getBlankKeyword();
592        if(fits_update_key(fptr, TINT, keyname, &blank, NULL, &status)){
593          duchampFITSerror(status,"saveImage","Error writing BLANK header:");
594        }
595        if(fits_set_imgnull(fptr, blank, &status)){
596          duchampFITSerror(status, "saveImage", "Error setting null value:");
597        }
598        if(fits_set_bscale(fptr, bscale, bzero, &status)){
599          duchampFITSerror(status,"saveImage","Error setting scale:");
600        }
601      }
602    }
603
604    delete [] naxes;
605
606    return SUCCESS;
607
608  }
609
610}
Note: See TracBrowser for help on using the repository browser.