source: trunk/src/Cubes/detectionIO.cc @ 987

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

Making the karma output robust against a bad/missing WCS.

File size: 15.5 KB
Line 
1// -----------------------------------------------------------------------
2// detectionIO.cc: Screen and File output of the detected objects.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <iostream>
29#include <sstream>
30#include <fstream>
31#include <iomanip>
32#include <string>
33#include <vector>
34#include <time.h>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Cubes/cubes.hh>
38#include <duchamp/PixelMap/Object3D.hh>
39#include <duchamp/Detection/detection.hh>
40#include <duchamp/Detection/columns.hh>
41#include <duchamp/Utils/utils.hh>
42#include <duchamp/Utils/Statistics.hh>
43 
44using std::endl;
45using std::setw;
46using std::setprecision;
47using namespace PixelInfo;
48using namespace Statistics;
49
50namespace duchamp
51{
52
53  void Cube::outputDetectionsKarma(std::ostream &stream)
54  {
55    /// @details
56    ///  Prints to a stream (provided) the list of detected objects in the cube
57    ///   in the format of an annotation file for the Karma suite of programs.
58    ///  Annotation file draws a box enclosing the detection, and writes the
59    ///   ID number of the detection to the right of the box.
60
61    std::string fname = this->par.getImageFile();
62    if(this->par.getFlagSubsection()) fname+=this->par.getSubsection();
63    stream << "# Duchamp Source Finder v."<< VERSION << endl;
64    stream << "# Results for FITS file: " << fname << endl;
65    if(this->par.getFlagFDR())
66      stream<<"# FDR Significance = " << this->par.getAlpha() << endl;
67    else
68      stream<<"# Threshold = " << this->par.getCut() << endl;
69    if(this->par.getFlagATrous()){
70      stream<<"# The a trous reconstruction method was used, with the following parameters." << endl;
71      stream<<"#  Dimension = " << this->par.getReconDim() << endl;
72      stream<<"#  Threshold = " << this->par.getAtrousCut() << endl;
73      stream<<"#  Minimum Scale =" << this->par.getMinScale() << endl;
74      stream<<"#  Filter = " << this->par.getFilterName() << endl;
75    }
76    else if(this->par.getFlagSmooth()){
77      stream<<"# The data was smoothed prior to searching, with the following parameters." << endl;
78      stream<<"#  Smoothing type = " << this->par.getSmoothType() << endl;
79      if(this->par.getSmoothType()=="spectral"){
80        stream << "#  Hanning width = " << this->par.getHanningWidth() << endl;
81      }
82      else{
83        stream << "#  Kernel Major axis = " << this->par.getKernMaj() << endl;
84        if(this->par.getKernMin()>0)
85          stream << "#  Kernel Minor axis = " << this->par.getKernMin() << endl;
86        else
87          stream << "#  Kernel Minor axis = " << this->par.getKernMaj() << endl;
88        stream << "#  Kernel Major axis = " << this->par.getKernPA() << endl;
89      }
90    }
91    stream << "#\n";
92    stream << "COLOR RED" << endl;
93    if(this->head.isWCS()) stream << "COORD W" << endl;
94    else stream << "COORD P" << endl;
95    stream << std::setprecision(6);
96    stream.setf(std::ios::fixed);
97    double *pix = new double[3];
98    double *wld = new double[3];
99    std::vector<Detection>::iterator obj;
100    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
101
102      if(this->par.getAnnotationType()=="borders"){
103        std::vector<int> vertexSet = obj->getVertexSet();
104        for(size_t i=0;i<vertexSet.size()/4;i++){
105          pix[0] = vertexSet[i*4]-0.5;
106          pix[1] = vertexSet[i*4+1]-0.5;
107          pix[2] = obj->getZcentre();
108          if(this->head.isWCS()){
109            this->head.pixToWCS(pix,wld);
110            stream << "LINE " << wld[0] << " " << wld[1];
111          }
112          else
113            stream << "LINE " << pix[0] << " " << pix[1];
114          pix[0] = vertexSet[i*4+2]-0.5;
115          pix[1] = vertexSet[i*4+3]-0.5;
116          if(this->head.isWCS()){
117            this->head.pixToWCS(pix,wld);
118            stream << " " << wld[0] << " " << wld[1] << "\n";
119          }
120          else
121            stream << " " << pix[0] << " " << pix[1] << "\n";
122        }
123      }
124      else if(this->par.getAnnotationType()=="circles"){
125        float radius = obj->getRAWidth()/120.;
126        if(obj->getDecWidth()/120.>radius)
127          radius = obj->getDecWidth()/120.;
128        stream << "CIRCLE "
129               << obj->getRA() << " "
130               << obj->getDec() << " "
131               << radius << "\n";
132      }
133
134      stream << "TEXT "
135             << obj->getRA() << " "
136             << obj->getDec() << " "
137             << obj->getID() << "\n\n";
138    }
139
140    delete [] pix;
141    delete [] wld;
142
143  }
144
145  void Cube::prepareOutputFile()
146  {
147    ///  @details
148    ///  A function to write the paramters, time of execution, and
149    ///  statistical information to the output file.
150
151    std::string outfile;
152    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
153    else outfile = this->par.getOutFile();
154    std::ofstream output(outfile.c_str());
155    output<<"Results of the Duchamp source finder v."<<VERSION<<": ";
156    time_t now = time(NULL);
157    output << asctime( localtime(&now) );
158    this->showParam(output);
159    output<<"--------------------\n";
160    output.close();
161    this->outputStats();
162 
163  }
164
165  void Cube::outputStats()
166  {
167    ///  @details
168    ///  A function to write the statistical information to the output
169    ///  file. This writes the threshold, its equivalent S/N ratio, and
170    ///  the noise level and spread.
171    ///
172    ///  If smoothing has been done, the noise level & spread for the
173    ///  original array are calculated and printed as well.
174
175    std::string outfile;
176    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
177    else outfile = this->par.getOutFile();
178    std::ofstream output(outfile.c_str(),std::ios::app);
179    output<<"Summary of statistics:\n";
180    output<<"Detection threshold = " << this->Stats.getThreshold()
181          <<" " << this->head.getFluxUnits();
182    if(this->par.getFlagFDR())
183      output<<" (or S/N=" << this->Stats.getThresholdSNR()<<")";
184    if(this->par.getFlagSmooth()){
185      output << " in smoothed cube.";
186      if(!this->par.getFlagUserThreshold())
187        output<<"\nNoise level = " << this->Stats.getMiddle()
188              <<", Noise spread = " << this->Stats.getSpread()
189              <<" in smoothed cube.";
190   
191      // calculate the stats for the original array, so that we can
192      // quote S/N values correctly.
193      this->par.setFlagSmooth(false);
194      bool verb=this->par.isVerbose();
195      bool fdrflag=this->par.getFlagFDR();
196      this->par.setVerbosity(false);
197      this->par.setFlagFDR(false);
198      this->setCubeStats();
199      this->par.setVerbosity(verb);
200      this->par.setFlagFDR(fdrflag);
201      this->par.setFlagSmooth(true);
202     
203      output << "\nNoise properties for the original cube are:";
204    }
205     
206    if(!this->par.getFlagUserThreshold())
207      output<<"\nNoise level = " << this->Stats.getMiddle()
208            <<", Noise spread = " << this->Stats.getSpread();
209
210    if(this->par.getFlagGrowth()){
211      StatsContainer<float> growthStats = this->Stats;
212      if(this->par.getFlagUserGrowthThreshold())
213        growthStats.setThreshold(this->par.getGrowthThreshold());
214      else
215        growthStats.setThresholdSNR(this->par.getGrowthCut());
216      growthStats.setUseFDR(false);
217      output<<"\nDetections grown down to threshold of "
218            << growthStats.getThreshold() << " "
219            << this->head.getFluxUnits();
220    }
221
222    if(!this->par.getFlagUserThreshold())
223      output << "\nFull stats:\n" << this->Stats;
224    else
225      output << "\n\nNot calculating full stats since threshold was provided directly.\n";
226
227    output<<"--------------------\n";
228    output.close();
229  }
230
231  void Cube::outputDetectionList()
232  {
233    ///  @details
234    ///  A front-end to writing the full list of detected objects to a results
235    ///   file and to cout.
236    ///  Leaves the testing of whether the WCS parameters for each object
237    ///   have been calculated to the printing function.
238
239    std::string outfile;
240    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
241    else outfile = this->par.getOutFile();
242    std::ofstream output(outfile.c_str(),std::ios::app);
243    output<<"Total number of detections = "<<this->objectList->size()<<endl;
244    output<<"--------------------\n";
245    output.close();
246
247    if(this->par.getFlagSeparateHeader())
248      output.open(this->par.getOutFile().c_str());
249    else
250      output.open(this->par.getOutFile().c_str(),std::ios::app);
251
252    if(this->objectList->size()>0){
253      this->setupColumns();
254      outputTableHeader(output,this->fullCols,"file",this->head.isWCS());
255      outputTableHeader(std::cout,this->fullCols,"screen",this->head.isWCS());
256      std::vector<Detection>::iterator obj;
257      for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
258        obj->printTableRow(output,this->fullCols,"file");
259        obj->printTableRow(std::cout,this->fullCols,"screen");
260      }
261    }
262
263    output.close();
264  }
265
266  void Cube::prepareLogFile(int argc, char *argv[])
267  {
268    /// @details
269    ///  Opens the log file so that it can be written to, and writes
270    ///  the parameter summary as well as the time of execution to the
271    ///  file.
272    ///
273    ///  It also writes the command-line statement, hence the need for
274    ///  argv and argc.
275
276    // Open the logfile and write the time on the first line
277    std::ofstream logfile(this->par.getLogFile().c_str());
278    logfile << "New run of the Duchamp source finder v."<<VERSION<<": ";
279    time_t now = time(NULL);
280    logfile << asctime( localtime(&now) );
281    // Write out the command-line statement
282    logfile << "Executing statement : ";
283    for(int i=0;i<argc;i++) logfile << argv[i] << " ";
284    logfile << std::endl;
285    logfile << this->par;
286    logfile.close();
287
288  }
289
290
291  void Cube::logDetectionList(bool calcFluxes)
292  {
293    /// @details
294    ///  A front-end to writing a list of detected objects to the log file.
295    ///  Does not assume WCS is present.
296    ///  Designed to be used by searching routines before returning their
297    ///   final list.
298    ///  @param[in] calcFluxes If true (the default), calculate the various flux parameters for each object.
299
300    if(this->objectList->size()>0){
301
302      long left = this->par.getBorderLeft();
303      long bottom = this->par.getBorderBottom();
304
305      std::ofstream fout(this->par.getLogFile().c_str(),std::ios::app);
306      if(calcFluxes) this->calcObjectFluxes();
307      this->setupColumns();
308      outputTableHeader(fout,this->fullCols,"log",this->head.isWCS());
309
310      if(this->par.getFlagBaseline()){
311        for(size_t i=0;i<this->axisDim[0]*this->axisDim[1]*this->axisDim[2];i++)
312          this->array[i] += this->baseline[i];
313      }
314
315      for(size_t objCtr=0;objCtr<this->objectList->size();objCtr++){
316        Detection obj = this->objectList->at(objCtr);
317        obj.setOffsets(par);
318        if(this->par.getFlagCubeTrimmed()){
319          obj.addOffsets(left,bottom,0);
320        }
321        if(calcFluxes) obj.calcFluxes(this->array, this->axisDim);
322        obj.setID(objCtr+1);
323        obj.printTableRow(fout,this->fullCols,"log");
324      }
325
326      if(this->par.getFlagBaseline()){
327        for(size_t i=0;i<this->axisDim[0]*this->axisDim[1]*this->axisDim[2];i++)
328          this->array[i] -= this->baseline[i];
329      }
330      fout.close();
331 
332    }
333
334  }
335
336  void Cube::logDetection(Detection obj, int counter)
337  {
338    /// @details
339    ///  A front-end to writing a detected object to the log file.
340    ///  Does not assume WCS is present.
341    ///  Corrects for changes to positions of pixels and removal of baselines.
342    ///  Designed to be used by searching routines before returning their final
343    ///   list.
344    ///  \param obj Detection object to be written : passed by value, as we want
345    ///    to potentially change positions etc, but not for the object in the
346    ///    calling function.
347    ///  \param counter The number to assign to the object : ideally its number
348    ///    in a list of some kind.
349
350    std::ofstream fout(this->par.getLogFile().c_str(),std::ios::app);
351    // Need to deal with possibility of trimmed array
352    size_t left = this->par.getBorderLeft();
353    size_t right = this->par.getBorderRight();
354    size_t top = this->par.getBorderTop();
355    size_t bottom = this->par.getBorderBottom();
356    size_t *tempDim = new size_t[3];
357    tempDim[0] = (this->axisDim[0] + left + right);
358    tempDim[1] = (this->axisDim[1] + bottom + top);
359    tempDim[2] = this->axisDim[2];
360    size_t tempsize = tempDim[0] * tempDim[1] * tempDim[2];
361    float *temparray = new float[tempsize];
362    //  for(int i=0;i<this->numPixels;i++){ // loop over this->array
363    for(size_t z=0;z<tempDim[2];z++){
364      for(size_t y=0;y<tempDim[1];y++){
365        for(size_t x=0;x<tempDim[0];x++){
366
367          bool isDud = (x<left) || (x>=this->axisDim[0]+left) ||
368            (y<bottom) || (y>=this->axisDim[1]+bottom);
369       
370          size_t temppos = x + tempDim[0]*y + tempDim[1]*tempDim[0]*z;
371
372          size_t pos = (x-left) + (y-bottom)*this->axisDim[0] +
373            z*this->axisDim[0]*this->axisDim[1];
374
375          if(isDud) temparray[temppos] = this->par.getBlankPixVal();
376          else temparray[temppos] = this->array[pos];
377 
378          if(this->par.getFlagBaseline() && !isDud)
379            temparray[temppos] += this->baseline[pos];
380
381        }
382      }
383    }
384
385    if(this->par.getFlagCubeTrimmed()){
386      obj.addOffsets(left,bottom,0);
387    }
388    obj.calcFluxes(temparray, this->axisDim);
389    obj.printTableRow(fout,this->fullCols,"log");
390    delete [] temparray;
391    delete [] tempDim;
392    fout.close();
393  }
394
395
396  void Cube::writeSpectralData()
397  {
398    /// @details
399    ///  A function to write, in ascii form, the spectra of each
400    ///  detected object to a file. The file consists of a column for
401    ///  the spectral coordinates, and one column for each object
402    ///  showing the flux at that spectral position. The units are the
403    ///  same as those shown in the graphical output. The filename is
404    ///  given by the Param::spectraTextFile parameter in the Cube::par
405    ///  parameter set.
406
407    const int zdim = this->axisDim[2];
408    const int numObj = this->objectList->size();
409    float *specxOut = new float[zdim];
410    float *spectra = new float[numObj*zdim];
411   
412    for(int obj=0; obj<numObj; obj++){
413      float *temp = new float[zdim];
414      float *specx = new float[zdim];
415      float *recon = new float[zdim];
416      float *base = new float[zdim];
417      this->getSpectralArrays(obj, specx, temp, recon, base);
418      for(int z=0;z<zdim;z++) spectra[obj*zdim+z] = temp[z];
419      if(obj==0) for(int z=0;z<zdim;z++) specxOut[z] = specx[z];
420      delete [] specx;
421      delete [] recon;
422      delete [] base;
423      delete [] temp;
424    }
425   
426    std::ofstream fspec(this->par.getSpectraTextFile().c_str());
427    fspec.setf(std::ios::fixed);
428
429    for(int z=0;z<zdim;z++){
430     
431      fspec << std::setprecision(8);
432      fspec << specxOut[z] << "  ";
433      for(int obj=0;obj<numObj; obj++) {
434        fspec << spectra[obj*zdim+z] << "  ";
435      }
436      fspec << "\n";
437
438    }
439    fspec.close();
440
441    delete [] spectra;
442    delete [] specxOut;
443
444  }
445  //--------------------------------------------------------------------
446
447
448}
Note: See TracBrowser for help on using the repository browser.