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

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

Ticket #162 - A large refactoring of the Column-related code. Columns have moved to Outputs, and in a new namespace Catalogues. The interface has changed, using strings to record the type rather
than the enum. Also included is a new class CatalogueSpecification?, that is designed to hold a collection of Columns. This is not yet implemented - everything still uses the full & log column
vectors, and the code still passes the verification script.

File size: 17.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/Outputs/columns.hh>
41#include <duchamp/Utils/utils.hh>
42#include <duchamp/Utils/Statistics.hh>
43#include <duchamp/Outputs/ASCIICatalogueWriter.hh>
44 
45using std::endl;
46using std::setw;
47using std::setprecision;
48using namespace PixelInfo;
49using namespace Statistics;
50
51namespace duchamp
52{
53
54  void Cube::outputDetectionsKarma(std::ostream &stream)
55  {
56    /// @details
57    ///  Prints to a stream (provided) the list of detected objects in the cube
58    ///   in the format of an annotation file for the Karma suite of programs.
59    ///  Annotation file draws a box enclosing the detection, and writes the
60    ///   ID number of the detection to the right of the box.
61
62    std::string fname = this->par.getImageFile();
63    if(this->par.getFlagSubsection()) fname+=this->par.getSubsection();
64    stream << "# Duchamp Source Finder v."<< VERSION << endl;
65    stream << "# Results for FITS file: " << fname << endl;
66    if(this->par.getFlagFDR())
67      stream<<"# FDR Significance = " << this->par.getAlpha() << endl;
68    else
69      stream<<"# Threshold = " << this->par.getCut() << endl;
70    if(this->par.getFlagATrous()){
71      stream<<"# The a trous reconstruction method was used, with the following parameters." << endl;
72      stream<<"#  Dimension = " << this->par.getReconDim() << endl;
73      stream<<"#  Threshold = " << this->par.getAtrousCut() << endl;
74      stream<<"#  Minimum Scale =" << this->par.getMinScale() << endl;
75      stream<<"#  Filter = " << this->par.getFilterName() << endl;
76    }
77    else if(this->par.getFlagSmooth()){
78      stream<<"# The data was smoothed prior to searching, with the following parameters." << endl;
79      stream<<"#  Smoothing type = " << this->par.getSmoothType() << endl;
80      if(this->par.getSmoothType()=="spectral"){
81        stream << "#  Hanning width = " << this->par.getHanningWidth() << endl;
82      }
83      else{
84        stream << "#  Kernel Major axis = " << this->par.getKernMaj() << endl;
85        if(this->par.getKernMin()>0)
86          stream << "#  Kernel Minor axis = " << this->par.getKernMin() << endl;
87        else
88          stream << "#  Kernel Minor axis = " << this->par.getKernMaj() << endl;
89        stream << "#  Kernel Major axis = " << this->par.getKernPA() << endl;
90      }
91    }
92    stream << "#\n";
93    stream << "COLOR RED" << endl;
94    if(this->head.isWCS()) stream << "COORD W" << endl;
95    else stream << "COORD P" << endl;
96    stream << std::setprecision(6);
97    stream.setf(std::ios::fixed);
98    double *pix = new double[3];
99    double *wld = new double[3];
100    std::vector<Detection>::iterator obj;
101    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
102
103      if(this->par.getAnnotationType()=="borders"){
104        std::vector<int> vertexSet = obj->getVertexSet();
105        for(size_t i=0;i<vertexSet.size()/4;i++){
106          pix[0] = vertexSet[i*4]-0.5;
107          pix[1] = vertexSet[i*4+1]-0.5;
108          pix[2] = obj->getZcentre();
109          if(this->head.isWCS()){
110            this->head.pixToWCS(pix,wld);
111            stream << "LINE " << wld[0] << " " << wld[1];
112          }
113          else
114            stream << "LINE " << pix[0] << " " << pix[1];
115          pix[0] = vertexSet[i*4+2]-0.5;
116          pix[1] = vertexSet[i*4+3]-0.5;
117          if(this->head.isWCS()){
118            this->head.pixToWCS(pix,wld);
119            stream << " " << wld[0] << " " << wld[1] << "\n";
120          }
121          else
122            stream << " " << pix[0] << " " << pix[1] << "\n";
123        }
124      }
125      else if(this->par.getAnnotationType()=="circles"){
126        float radius = obj->getRAWidth()/120.;
127        if(obj->getDecWidth()/120.>radius)
128          radius = obj->getDecWidth()/120.;
129        stream << "CIRCLE "
130               << obj->getRA() << " "
131               << obj->getDec() << " "
132               << radius << "\n";
133      }
134
135      stream << "TEXT "
136             << obj->getRA() << " "
137             << obj->getDec() << " "
138             << obj->getID() << "\n\n";
139    }
140
141    delete [] pix;
142    delete [] wld;
143
144  }
145
146  void Cube::outputCatalogue()
147  {
148    this->setupColumns();
149
150    ASCIICatalogueWriter catWriter(this->par.getOutFile(),Catalogues::FILE);
151    catWriter.setup(this);
152    ASCIICatalogueWriter headerWriter(this->par.getHeaderFile(),Catalogues::FILE);
153    headerWriter.setup(this);
154    ASCIICatalogueWriter screenWriter(Catalogues::SCREEN);
155    screenWriter.setup(this);
156    ASCIICatalogueWriter *writer;
157
158    // write the header information
159    if(this->par.getFlagSeparateHeader()) writer = &headerWriter;
160    else writer=&catWriter;
161
162    writer->openCatalogue();
163    writer->writeHeader();
164    writer->writeParameters();
165    writer->writeStats();
166
167    if(this->par.getFlagSeparateHeader()){
168      writer->closeCatalogue();
169      writer = &catWriter;
170      writer->openCatalogue();
171    }
172
173    // write the catalogue
174    writer->writeTableHeader();
175    writer->writeEntries();
176    writer->closeCatalogue();
177
178    screenWriter.openCatalogue();
179    screenWriter.writeTableHeader();
180    screenWriter.writeEntries();
181    screenWriter.closeCatalogue();
182
183  }
184
185  void Cube::prepareOutputFile()
186  {
187    ///  @details
188    ///  A function to write the paramters, time of execution, and
189    ///  statistical information to the output file.
190
191    std::string outfile;
192    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
193    else outfile = this->par.getOutFile();
194    std::ofstream output(outfile.c_str());
195    output<<"Results of the Duchamp source finder v."<<VERSION<<": ";
196    time_t now = time(NULL);
197    output << asctime( localtime(&now) );
198    this->showParam(output);
199    output<<"--------------------\n";
200    output.close();
201    this->outputStats();
202 
203  }
204
205  void Cube::outputStats()
206  {
207    ///  @details
208    ///  A function to write the statistical information to the output
209    ///  file. This writes the threshold, its equivalent S/N ratio, and
210    ///  the noise level and spread.
211    ///
212    ///  If smoothing has been done, the noise level & spread for the
213    ///  original array are calculated and printed as well.
214
215    std::string outfile;
216    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
217    else outfile = this->par.getOutFile();
218    std::ofstream output(outfile.c_str(),std::ios::app);
219    output<<"Summary of statistics:\n";
220    output<<"Detection threshold = " << this->Stats.getThreshold()
221          <<" " << this->head.getFluxUnits();
222    if(this->par.getFlagFDR())
223      output<<" (or S/N=" << this->Stats.getThresholdSNR()<<")";
224    if(this->par.getFlagSmooth()){
225      output << " in smoothed cube.";
226      if(!this->par.getFlagUserThreshold())
227        output<<"\nNoise level = " << this->Stats.getMiddle()
228              <<", Noise spread = " << this->Stats.getSpread()
229              <<" in smoothed cube.";
230   
231      // calculate the stats for the original array, so that we can
232      // quote S/N values correctly.
233      this->par.setFlagSmooth(false);
234      bool verb=this->par.isVerbose();
235      bool fdrflag=this->par.getFlagFDR();
236      this->par.setVerbosity(false);
237      this->par.setFlagFDR(false);
238      this->setCubeStats();
239      this->par.setVerbosity(verb);
240      this->par.setFlagFDR(fdrflag);
241      this->par.setFlagSmooth(true);
242     
243      output << "\nNoise properties for the original cube are:";
244    }
245     
246    if(!this->par.getFlagUserThreshold())
247      output<<"\nNoise level = " << this->Stats.getMiddle()
248            <<", Noise spread = " << this->Stats.getSpread();
249
250    if(this->par.getFlagGrowth()){
251      StatsContainer<float> growthStats = this->Stats;
252      if(this->par.getFlagUserGrowthThreshold())
253        growthStats.setThreshold(this->par.getGrowthThreshold());
254      else
255        growthStats.setThresholdSNR(this->par.getGrowthCut());
256      growthStats.setUseFDR(false);
257      output<<"\nDetections grown down to threshold of "
258            << growthStats.getThreshold() << " "
259            << this->head.getFluxUnits();
260    }
261
262    if(!this->par.getFlagUserThreshold())
263      output << "\nFull stats:\n" << this->Stats;
264    else
265      output << "\n\nNot calculating full stats since threshold was provided directly.\n";
266
267    output<<"--------------------\n";
268    output.close();
269  }
270
271  void Cube::outputDetectionList()
272  {
273    ///  @details
274    ///  A front-end to writing the full list of detected objects to a results
275    ///   file and to cout.
276    ///  Leaves the testing of whether the WCS parameters for each object
277    ///   have been calculated to the printing function.
278
279    std::string outfile;
280    if(this->par.getFlagSeparateHeader()) outfile = this->par.getHeaderFile();
281    else outfile = this->par.getOutFile();
282    std::ofstream output(outfile.c_str(),std::ios::app);
283    output<<"Total number of detections = "<<this->objectList->size()<<endl;
284    output<<"--------------------\n";
285    output.close();
286
287    if(this->par.getFlagSeparateHeader())
288      output.open(this->par.getOutFile().c_str());
289    else
290      output.open(this->par.getOutFile().c_str(),std::ios::app);
291
292    if(this->objectList->size()>0){
293      this->setupColumns();
294      outputTableHeader(output,this->fullCols,Catalogues::FILE,this->head.isWCS());
295      outputTableHeader(std::cout,this->fullCols,Catalogues::SCREEN,this->head.isWCS());
296      std::vector<Detection>::iterator obj;
297      for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
298        obj->printTableRow(output,this->fullCols,Catalogues::FILE);
299        obj->printTableRow(std::cout,this->fullCols,Catalogues::SCREEN);
300      }
301    }
302
303    output.close();
304  }
305
306  void Cube::prepareLogFile(int argc, char *argv[])
307  {
308    /// @details
309    ///  Opens the log file so that it can be written to, and writes
310    ///  the parameter summary as well as the time of execution to the
311    ///  file.
312    ///
313    ///  It also writes the command-line statement, hence the need for
314    ///  argv and argc.
315
316    // // Open the logfile and write the time on the first line
317    // std::ofstream logfile(this->par.getLogFile().c_str());
318    // logfile << "New run of the Duchamp source finder v."<<VERSION<<": ";
319    // time_t now = time(NULL);
320    // logfile << asctime( localtime(&now) );
321    // // Write out the command-line statement
322    // logfile << "Executing statement : ";
323    // for(int i=0;i<argc;i++) logfile << argv[i] << " ";
324    // logfile << std::endl;
325    // logfile << this->par;
326    // logfile.close();
327
328    ASCIICatalogueWriter logwriter(this->par.getLogFile(),Catalogues::LOG);
329    logwriter.setup(this);
330    logwriter.openCatalogue();
331    logwriter.writeHeader();
332    logwriter.writeCommandLineEntry(argc,argv);
333    logwriter.writeParameters();
334    logwriter.closeCatalogue();
335  }
336
337
338  void Cube::logDetectionList(bool calcFluxes)
339  {
340    /// @details
341    ///  A front-end to writing a list of detected objects to the log file.
342    ///  Does not assume WCS is present.
343    ///  Designed to be used by searching routines before returning their
344    ///   final list.
345    ///  @param[in] calcFluxes If true (the default), calculate the various flux parameters for each object.
346
347    if(this->objectList->size()>0){
348
349      ASCIICatalogueWriter logwriter(this->par.getLogFile(),Catalogues::LOG);
350      logwriter.setup(this);
351      logwriter.openCatalogue(std::ios::app);
352
353      long left = this->par.getBorderLeft();
354      long bottom = this->par.getBorderBottom();
355
356      // std::ofstream fout(this->par.getLogFile().c_str(),std::ios::app);
357      if(calcFluxes) this->calcObjectFluxes();
358      this->setupColumns();
359      // outputTableHeader(fout,this->fullCols,Catalogues::LOG,this->head.isWCS());
360      logwriter.writeTableHeader();
361
362      if(this->par.getFlagBaseline()){
363        for(size_t i=0;i<this->axisDim[0]*this->axisDim[1]*this->axisDim[2];i++)
364          this->array[i] += this->baseline[i];
365      }
366
367      for(size_t objCtr=0;objCtr<this->objectList->size();objCtr++){
368        Detection obj = this->objectList->at(objCtr);
369        obj.setOffsets(par);
370        if(this->par.getFlagCubeTrimmed()){
371          obj.addOffsets(left,bottom,0);
372        }
373        if(calcFluxes) obj.calcFluxes(this->array, this->axisDim);
374        obj.setID(objCtr+1);
375        //      obj.printTableRow(fout,this->fullCols,Catalogues::LOG);
376        logwriter.writeEntry(&obj);
377      }
378
379      if(this->par.getFlagBaseline()){
380        for(size_t i=0;i<this->axisDim[0]*this->axisDim[1]*this->axisDim[2];i++)
381          this->array[i] -= this->baseline[i];
382      }
383      //      fout.close();
384      logwriter.closeCatalogue();
385 
386    }
387
388  }
389
390  void Cube::logDetection(Detection obj, int counter)
391  {
392    /// @details
393    ///  A front-end to writing a detected object to the log file.
394    ///  Does not assume WCS is present.
395    ///  Corrects for changes to positions of pixels and removal of baselines.
396    ///  Designed to be used by searching routines before returning their final
397    ///   list.
398    ///  \param obj Detection object to be written : passed by value, as we want
399    ///    to potentially change positions etc, but not for the object in the
400    ///    calling function.
401    ///  \param counter The number to assign to the object : ideally its number
402    ///    in a list of some kind.
403
404    std::ofstream fout(this->par.getLogFile().c_str(),std::ios::app);
405    // Need to deal with possibility of trimmed array
406    size_t left = this->par.getBorderLeft();
407    size_t right = this->par.getBorderRight();
408    size_t top = this->par.getBorderTop();
409    size_t bottom = this->par.getBorderBottom();
410    size_t *tempDim = new size_t[3];
411    tempDim[0] = (this->axisDim[0] + left + right);
412    tempDim[1] = (this->axisDim[1] + bottom + top);
413    tempDim[2] = this->axisDim[2];
414    size_t tempsize = tempDim[0] * tempDim[1] * tempDim[2];
415    float *temparray = new float[tempsize];
416    //  for(int i=0;i<this->numPixels;i++){ // loop over this->array
417    for(size_t z=0;z<tempDim[2];z++){
418      for(size_t y=0;y<tempDim[1];y++){
419        for(size_t x=0;x<tempDim[0];x++){
420
421          bool isDud = (x<left) || (x>=this->axisDim[0]+left) ||
422            (y<bottom) || (y>=this->axisDim[1]+bottom);
423       
424          size_t temppos = x + tempDim[0]*y + tempDim[1]*tempDim[0]*z;
425
426          size_t pos = (x-left) + (y-bottom)*this->axisDim[0] +
427            z*this->axisDim[0]*this->axisDim[1];
428
429          if(isDud) temparray[temppos] = this->par.getBlankPixVal();
430          else temparray[temppos] = this->array[pos];
431 
432          if(this->par.getFlagBaseline() && !isDud)
433            temparray[temppos] += this->baseline[pos];
434
435        }
436      }
437    }
438
439    if(this->par.getFlagCubeTrimmed()){
440      obj.addOffsets(left,bottom,0);
441    }
442    obj.calcFluxes(temparray, this->axisDim);
443    obj.printTableRow(fout,this->fullCols,Catalogues::LOG);
444    delete [] temparray;
445    delete [] tempDim;
446    fout.close();
447  }
448
449  void Cube::logSummary()
450  {
451    ASCIICatalogueWriter logwriter(this->par.getLogFile(),Catalogues::LOG);
452    logwriter.setup(this);
453    logwriter.openCatalogue(std::ios::app);
454    logwriter.writeCubeSummary();
455    logwriter.closeCatalogue();
456  }
457
458  void Cube::writeSpectralData()
459  {
460    /// @details
461    ///  A function to write, in ascii form, the spectra of each
462    ///  detected object to a file. The file consists of a column for
463    ///  the spectral coordinates, and one column for each object
464    ///  showing the flux at that spectral position. The units are the
465    ///  same as those shown in the graphical output. The filename is
466    ///  given by the Param::spectraTextFile parameter in the Cube::par
467    ///  parameter set.
468
469    const int zdim = this->axisDim[2];
470    const int numObj = this->objectList->size();
471    float *specxOut = new float[zdim];
472    float *spectra = new float[numObj*zdim];
473   
474    for(int obj=0; obj<numObj; obj++){
475      float *temp = new float[zdim];
476      float *specx = new float[zdim];
477      float *recon = new float[zdim];
478      float *base = new float[zdim];
479      this->getSpectralArrays(obj, specx, temp, recon, base);
480      for(int z=0;z<zdim;z++) spectra[obj*zdim+z] = temp[z];
481      if(obj==0) for(int z=0;z<zdim;z++) specxOut[z] = specx[z];
482      delete [] specx;
483      delete [] recon;
484      delete [] base;
485      delete [] temp;
486    }
487   
488    std::ofstream fspec(this->par.getSpectraTextFile().c_str());
489    fspec.setf(std::ios::fixed);
490
491    for(int z=0;z<zdim;z++){
492     
493      fspec << std::setprecision(8);
494      fspec << specxOut[z] << "  ";
495      for(int obj=0;obj<numObj; obj++) {
496        fspec << spectra[obj*zdim+z] << "  ";
497      }
498      fspec << "\n";
499
500    }
501    fspec.close();
502
503    delete [] spectra;
504    delete [] specxOut;
505
506  }
507  //--------------------------------------------------------------------
508
509
510}
Note: See TracBrowser for help on using the repository browser.