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

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

Another int --> size_t conversion.

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;
104      this->getMomentMap(momentMap,detectionMap);
105      status=0;
106      if(fits_write_pix(fptr, TFLOAT, fpixel, size, momentMap, &status)){
107        duchampFITSerror(status,"saveMomentMapImage","Error writing data");
108      }
109      status = 0;
110      if(fits_close_file(fptr, &status)){
111        duchampFITSerror(status,"saveMomentMapImage","Error closing file");
112      }
113
114      delete [] momentMap;
115
116    }
117
118    delete [] fpixel;
119
120    return SUCCESS;
121
122  }
123
124  OUTCOME Cube::saveMaskCube()
125  {
126    /// @details
127    ///  A function to save a mask to a FITS file, indicating where the
128    ///  detections where made. The value of the detected pixels is
129    ///  determined by the flagMaskWithObjectNum parameter: if true,
130    ///  the value of the pixels is given by the corresponding object
131    ///  ID number; if false, they take the value 1 for all
132    ///  objects. Pixels not in a detected object have the value 0.
133
134    int newbitpix = SHORT_IMG;
135    char *comment = new char[FLEN_COMMENT];
136    strcpy(comment,"");
137    long *fpixel = new long[this->head.WCS().naxis];
138    for(int i=0;i<this->header().WCS().naxis;i++) fpixel[i]=1;
139    int status = 0;  /* MUST initialize status */
140    fitsfile *fptrNew;         
141
142    std::string fileout = "!" + this->par.outputMaskFile();
143    // the ! is there so that it writes over an existing file.
144
145    status = 0;
146    if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
147      duchampFITSerror(status,"saveMask","Error creating file:");
148      return FAILURE;
149    }
150    else {
151      if(this->writeBasicHeader(fptrNew, newbitpix)==FAILURE){
152        DUCHAMPWARN("write Recon Cube", "Failure writing to header");
153        return FAILURE;
154      }
155
156      writeMaskHeaderInfo(fptrNew, this->par);
157
158      char *keyword = new char[FLEN_KEYWORD];
159      std::string newunits;
160      if(this->par.getFlagMaskWithObjectNum())
161        newunits = "Object ID";
162      else
163        newunits = "Detection flag";
164      strcpy(keyword,"BUNIT");
165      if(fits_update_key(fptrNew, TSTRING, keyword, (char *)newunits.c_str(), comment, &status)){
166        duchampFITSerror(status,"saveMask","Error writing BUNIT header:");
167      }
168
169      delete [] comment;
170      delete [] keyword;
171       
172      short *mask = new short[this->numPixels];
173      for(size_t i=0;i<this->numPixels;i++) mask[i]=0;
174      std::vector<Detection>::iterator obj;
175      for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
176        std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
177        std::vector<PixelInfo::Voxel>::iterator vox;
178        for(vox=voxlist.begin();vox<voxlist.end();vox++){
179          size_t pixelpos = vox->getX() + this->axisDim[0]*vox->getY() +
180            this->axisDim[0]*this->axisDim[1]*vox->getZ();
181          if(this->par.getFlagMaskWithObjectNum()) mask[pixelpos] = obj->getID();
182          else mask[pixelpos] = 1;
183        }
184      }
185      status=0;
186      if(fits_write_pix(fptrNew, TSHORT, fpixel, this->numPixels, mask, &status)){
187        duchampFITSerror(status,"saveMask","Error writing mask array:");
188      }
189      status = 0;
190      if(fits_close_file(fptrNew, &status)){
191        duchampFITSerror(status,"saveMask","Error closing file:");
192      }
193
194      delete [] mask;
195
196    }
197
198    delete [] fpixel;
199
200    return SUCCESS;
201  }
202 
203  //---------------------------------------------------------------------------
204
205  OUTCOME Cube::saveSmoothedCube()
206  {
207    /// @brief
208    ///   A function to save the smoothed arrays to a FITS file.
209    ///   Additional header keywords are written as well, indicating the
210    ///   width of the Hanning filter or the dimensions of the Gaussian
211    ///   kernel.
212    ///   The file is always written -- if the filename (as calculated
213    ///    based on the parameters) exists, then it is overwritten.
214 
215    float blankval = this->par.getBlankPixVal();
216
217    int status = 0;  /* MUST initialize status */
218    fitsfile *fptrNew;         
219
220    if(this->par.getFlagOutputSmooth()){
221      std::string fileout = "!" + this->par.outputSmoothFile();
222      // the ! is there so that it writes over an existing file.
223
224      status = 0;
225      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
226        duchampFITSerror(status,"saveSmoothedCube","Error creating smoothed FITS file:");
227        return FAILURE;
228      }
229      else {
230
231          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
232            DUCHAMPWARN("write Smoothed Cube", "Failure writing to header");
233            return FAILURE;
234          }
235
236        writeSmoothHeaderInfo(fptrNew, this->par);
237
238        if(this->par.getFlagBlankPix())
239          fits_write_imgnull(fptrNew, TFLOAT, 1, this->numPixels, this->recon, &blankval, &status);
240        else
241          fits_write_img(fptrNew, TFLOAT, 1, this->numPixels, this->recon, &status);
242        if(status){
243          duchampFITSerror(status,"saveSmothedCube","Error writing smoothed array:");
244          return FAILURE;
245        }
246
247        status = 0;
248        if(fits_close_file(fptrNew, &status)){
249          duchampFITSerror(status,"saveSmoothedCube","Error closing file:");
250        }
251      }
252    }
253
254    return SUCCESS;
255
256  }
257
258  //---------------------------------------------------------------------------
259
260  OUTCOME Cube::saveReconstructedCube()
261  {
262    /// @details
263    ///  A function to save the reconstructed and/or residual arrays.
264    ///   A number of header keywords are written as well, indicating the
265    ///    nature of the reconstruction that has been done.
266    ///   The file is always written -- if the filename (as calculated
267    ///    based on the recon parameters) exists, then it is overwritten.
268 
269    float blankval = this->par.getBlankPixVal();
270
271    if(!this->reconAllocated){
272      DUCHAMPERROR("saveReconCube","Have not allocated reconstructed array, so cannot save");
273      return FAILURE;
274    }
275
276    int status = 0;  /* MUST initialize status */
277    fitsfile *fptrNew;         
278 
279    if(this->par.getFlagOutputRecon()){
280      std::string fileout = "!" + this->par.outputReconFile();
281      // the ! is there so that it writes over an existing file.
282
283      status = 0;
284      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
285        duchampFITSerror(status,"saveReconCube","Error creating file:");
286        return FAILURE;
287      }
288      else
289        {
290
291          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
292            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
293            return FAILURE;
294          }
295         
296          writeReconHeaderInfo(fptrNew, this->par, "recon");
297
298          status=0;
299          long *fpixel = new long[this->header().WCS().naxis];
300          for(int i=0;i<this->numDim;i++) fpixel[i]=1;
301          long group=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          delete [] fpixel;
311
312          status = 0;
313          if(fits_close_file(fptrNew, &status)){
314            duchampFITSerror(status,"saveReconCube","Error closing file:");
315          }
316        }
317    }
318
319
320    if(this->par.getFlagOutputResid()){
321      float *resid = new float[this->numPixels];
322      for(size_t i=0;i<this->numPixels;i++)
323        resid[i] = this->array[i] - this->recon[i];
324
325      std::string fileout = "!" + this->par.outputResidFile();
326      // the ! is there so that it writes over an existing file.
327      status = 0;
328      if(fits_create_file(&fptrNew,fileout.c_str(),&status)){
329        duchampFITSerror(status,"saveResidualCube","Error creating new file:");
330        return FAILURE;
331      }
332      else
333        {
334
335          if(this->writeBasicHeader(fptrNew, -32)==FAILURE){
336            DUCHAMPWARN("write Recon Cube", "Failure writing to header");
337            return FAILURE;
338          }
339
340          writeReconHeaderInfo(fptrNew, this->par, "resid");
341
342          if(this->par.getFlagBlankPix())
343            fits_write_imgnull(fptrNew, TFLOAT, 1, this->numPixels, resid, &blankval, &status);
344          else 
345            fits_write_img(fptrNew, TFLOAT, 1, this->numPixels, resid, &status);
346          if(status){
347            duchampFITSerror(status,"saveResidualCube","Error writing reconstructed array:");
348            return FAILURE;
349          }
350
351          status=0;
352          if(fits_close_file(fptrNew, &status)){
353            duchampFITSerror(status,"saveResidualCube","Error closing file:");
354          }
355        }
356      delete [] resid;
357    }
358
359    return SUCCESS;
360  }
361
362  //---------------------------------------------------------------------------
363
364  void writeReconHeaderInfo(fitsfile *fptr, Param &par, std::string nature)
365  {
366    /// @details
367    ///   A simple function that writes all the necessary keywords and comments
368    ///    to the FITS header pointed to by fptr.
369    ///   The keyword names and comments are taken from duchamp.hh
370    ///   The parameter "nature" indicates what type of file is being written:
371    ///    should be either "recon" or "resid".
372
373    int status = 0;
374    std::string explanation = "",ReconResid="";
375
376    fits_write_history(fptr, (char *)header_reconHistory1.c_str(), &status);
377                                   
378    fits_write_history(fptr, (char *)header_reconHistory2.c_str(), &status);
379
380    fits_write_history(fptr, (char *)header_reconHistory_input.c_str(), &status);
381
382    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
383
384    if(par.getFlagSubsection()){
385      fits_write_comment(fptr,(char *)header_reconSubsection_comment.c_str(),
386                         &status);
387      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
388                     (char *)par.getSubsection().c_str(),
389                     (char *)comment_subsection.c_str(), &status);
390    }
391   
392    fits_write_comment(fptr, (char *)header_atrous_comment.c_str(), &status);
393
394    float valf = par.getAtrousCut();
395    fits_write_key(fptr, TFLOAT, (char *)keyword_snrRecon.c_str(), &valf,
396                   (char *)comment_snrRecon.c_str(), &status);
397
398    int vali = par.getReconDim();
399    fits_write_key(fptr, TINT, (char *)keyword_reconDim.c_str(), &vali,
400                   (char *)comment_reconDim.c_str(), &status);
401
402    vali = par.getMinScale();
403    fits_write_key(fptr, TINT, (char *)keyword_scaleMin.c_str(), &vali,
404                   (char *)comment_scaleMin.c_str(), &status);
405
406    vali = par.getFilterCode();
407    fits_write_key(fptr, TINT, (char *)keyword_filterCode.c_str(), &vali,
408                   (char *)comment_filterCode.c_str(), &status);
409
410    if(nature == "recon"){
411      explanation = "Duchamp: This is the RECONSTRUCTED cube";
412      ReconResid = "RECON";
413    }
414    else if(nature == "resid"){
415      explanation = "Duchamp: This is the RESIDUAL cube";
416      ReconResid = "RESID";
417    }
418    else DUCHAMPWARN("write_header_info","explanation not present");
419    fits_write_comment(fptr, (char *)explanation.c_str(), &status);
420    fits_write_key(fptr, TSTRING, (char *)keyword_ReconResid.c_str(),
421                   (char *)ReconResid.c_str(),
422                   (char *)comment_ReconResid.c_str(), &status);
423
424  }
425
426  //---------------------------------------------------------------------------
427
428  void writeSmoothHeaderInfo(fitsfile *fptr, Param &par)
429  {
430    /// @details
431    ///   A simple function that writes all the necessary keywords and comments
432    ///    to the FITS header pointed to by fptr.
433    ///   The keyword names and comments are taken from duchamp.hh
434
435    int status = 0;
436
437    fits_write_history(fptr, (char *)header_smoothHistory.c_str(), &status);
438    status = 0;
439    fits_write_history(fptr, (char *)header_smoothHistory_input.c_str(),&status);
440    status = 0;
441    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
442
443    if(par.getFlagSubsection()){
444      status = 0;
445      fits_write_comment(fptr,(char *)header_smoothSubsection_comment.c_str(),
446                         &status);
447      status = 0;
448      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
449                     (char *)par.getSubsection().c_str(),
450                     (char *)comment_subsection.c_str(), &status);
451    }
452   
453    if(par.getSmoothType()=="spatial"){
454      // if kernMin is negative (not defined), make it equal to kernMaj
455      if(par.getKernMin() < 0) par.setKernMin(par.getKernMaj());
456
457      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
458                     (char *)header_smoothSpatial.c_str(),
459                     (char *)comment_smoothtype.c_str(), &status);
460      float valf = par.getKernMaj();
461      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmaj.c_str(), &valf,
462                     (char *)comment_kernmaj.c_str(), &status);
463      valf = par.getKernMin();
464      fits_write_key(fptr, TFLOAT, (char *)keyword_kernmin.c_str(), &valf,
465                     (char *)comment_kernmin.c_str(), &status);
466      valf = par.getKernPA();
467      fits_write_key(fptr, TFLOAT, (char *)keyword_kernpa.c_str(), &valf,
468                     (char *)comment_kernpa.c_str(), &status);
469    }
470    else if(par.getSmoothType()=="spectral"){
471      fits_write_key(fptr, TSTRING, (char *)keyword_smoothtype.c_str(),
472                     (char *)header_smoothSpectral.c_str(),
473                     (char *)comment_smoothtype.c_str(), &status);
474      int vali = par.getHanningWidth();
475      fits_write_key(fptr, TINT, (char *)keyword_hanningwidth.c_str(), &vali,
476                     (char *)comment_hanningwidth.c_str(), &status);
477    }
478  }
479
480  //---------------------------------------------------------------------------
481
482  void writeMaskHeaderInfo(fitsfile *fptr, Param &par)
483  {
484    /// @details
485    ///   A simple function that writes all the necessary keywords and comments
486    ///    to the FITS header pointed to by fptr.
487    ///   The keyword names and comments are taken from duchamp.hh
488
489    int status = 0;
490
491    fits_write_history(fptr, (char *)header_maskHistory.c_str(), &status);
492    status = 0;
493    fits_write_history(fptr, (char *)header_maskHistory_input.c_str(),&status);
494    status = 0;
495    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
496
497    if(par.getFlagSubsection()){
498      status = 0;
499      fits_write_comment(fptr,(char *)header_maskSubsection_comment.c_str(),
500                         &status);
501      status = 0;
502      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
503                     (char *)par.getSubsection().c_str(),
504                     (char *)comment_subsection.c_str(), &status);
505    }
506  }
507
508  //---------------------------------------------------------------------------
509
510  void writeMomentMapHeaderInfo(fitsfile *fptr, Param &par)
511  {
512    /// @details
513    ///   A simple function that writes all the necessary keywords and comments
514    ///    to the FITS header pointed to by fptr.
515    ///   The keyword names and comments are taken from duchamp.hh
516
517    int status = 0;
518
519    fits_write_history(fptr, (char *)header_moment0History.c_str(), &status);
520    status = 0;
521    fits_write_history(fptr, (char *)header_moment0History_input.c_str(),&status);
522    status = 0;
523    fits_write_history(fptr, (char *)par.getImageFile().c_str(), &status);
524
525    if(par.getFlagSubsection()){
526      status = 0;
527      fits_write_comment(fptr,(char *)header_moment0Subsection_comment.c_str(),
528                         &status);
529      status = 0;
530      fits_write_key(fptr, TSTRING, (char *)keyword_subsection.c_str(),
531                     (char *)par.getSubsection().c_str(),
532                     (char *)comment_subsection.c_str(), &status);
533    }
534  }
535
536
537  //---------------------------------------------------------------------------
538
539  OUTCOME Cube::writeBasicHeader(fitsfile *fptr, int bitpix, bool is2D)
540  {
541    char *header, *hptr, keyname[9];
542    int  i, nkeyrec, status = 0;
543   
544    long naxis=this->numDim;
545    long naxes[this->numDim];
546    for(int i=0;i<naxis;i++) naxes[i]=this->axisDim[i];
547    if(is2D) naxes[this->head.WCS().spec]=1;
548    // write the required header keywords
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    return SUCCESS;
607
608  }
609
610}
Note: See TracBrowser for help on using the repository browser.