source: trunk/src/Cubes/cubes.cc @ 220

Last change on this file since 220 was 220, checked in by Matthew Whiting, 18 years ago
  • Two new files: plots.cc and feedback.cc. Introduced to separate the declarations and definitions for various classes.
  • Mostly just improving the documentation for use with Doxygen.
File size: 43.0 KB
Line 
1#include <unistd.h>
2#include <iostream>
3#include <iomanip>
4#include <vector>
5#include <algorithm>
6#include <string>
7#include <math.h>
8
9#include <wcs.h>
10
11#include <duchamp.hh>
12#include <param.hh>
13#include <Cubes/cubes.hh>
14#include <Detection/detection.hh>
15#include <Detection/columns.hh>
16#include <Utils/utils.hh>
17#include <Utils/mycpgplot.hh>
18#include <Utils/Statistics.hh>
19using namespace Column;
20using namespace mycpgplot;
21using namespace Statistics;
22
23/****************************************************************/
24///////////////////////////////////////////////////
25//// Functions for DataArray class:
26///////////////////////////////////////////////////
27
28DataArray::DataArray(){
29  /**
30   * Fundamental constructor for DataArray.
31   * Number of dimensions and pixels are set to 0. Nothing else allocated.
32   */
33  this->numDim=0;
34  this->numPixels=0;
35};
36//--------------------------------------------------------------------
37
38DataArray::DataArray(short int nDim){
39  /**
40   * N-dimensional constructor for DataArray.
41   * Number of dimensions defined, and dimension array allocated.
42   * Number of pixels are set to 0.
43   * \param nDim Number of dimensions.
44   */
45  if(nDim>0) this->axisDim = new long[nDim];
46  this->numDim=nDim;
47  this->numPixels=0;
48};
49//--------------------------------------------------------------------
50
51DataArray::DataArray(short int nDim, long size){
52  /**
53   * N-dimensional constructor for DataArray.
54   * Number of dimensions and number of pixels defined.
55   * Arrays allocated based on these values.
56   * \param nDim Number of dimensions.
57   * \param size Number of pixels.
58   *
59   * Note that we can assign values to the dimension array.
60   */
61
62  if(size<0)
63    duchampError("DataArray(nDim,size)",
64                 "Negative size -- could not define DataArray");
65  else if(nDim<0)
66    duchampError("DataArray(nDim,size)",
67                 "Negative number of dimensions: could not define DataArray");
68  else {
69    if(size>0) this->array = new float[size];
70    this->numPixels = size;
71    if(nDim>0) this->axisDim = new long[nDim];
72    this->numDim = nDim;
73  }
74}
75//--------------------------------------------------------------------
76
77DataArray::DataArray(short int nDim, long *dimensions)
78{
79  /**
80   * Most robust constructor for DataArray.
81   * Number and sizes of dimensions are defined, and hence the number of
82   * pixels. Arrays allocated based on these values.
83   * \param nDim Number of dimensions.
84   * \param dimensions Array giving sizes of dimensions.
85   */
86  if(nDim<0)
87    duchampError("DataArray(nDim,dimArray)",
88                 "Negative number of dimensions: could not define DataArray");
89  else {
90    int size = dimensions[0];
91    for(int i=1;i<nDim;i++) size *= dimensions[i];
92    if(size<0)
93      duchampError("DataArray(nDim,dimArray)",
94                   "Negative size: could not define DataArray");
95    else{
96      this->numPixels = size;
97      if(size>0) this->array = new float[size];
98      this->numDim=nDim;
99      if(nDim>0) this->axisDim = new long[nDim];
100      for(int i=0;i<nDim;i++) this->axisDim[i] = dimensions[i];
101    }
102  }
103}
104//--------------------------------------------------------------------
105
106DataArray::~DataArray()
107{
108  /**
109   *  Destructor -- arrays deleted if they have been allocated, and the
110   *   object list is cleared.
111   */
112  if(this->numPixels>0) delete [] this->array;
113  if(this->numDim>0)    delete [] this->axisDim;
114  this->objectList.clear();
115}
116//--------------------------------------------------------------------
117//--------------------------------------------------------------------
118
119void DataArray::getDim(long &x, long &y, long &z){
120  /**
121   * The sizes of the first three dimensions (if they exist) are returned.
122   * \param x The first dimension. Defaults to 0 if numDim \f$\le\f$ 0.
123   * \param y The second dimension. Defaults to 1 if numDim \f$\le\f$ 1.
124   * \param z The third dimension. Defaults to 1 if numDim \f$\le\f$ 2.
125   */
126  if(this->numDim>0) x=this->axisDim[0];
127  else x=0;
128  if(this->numDim>1) y=this->axisDim[1];
129  else y=1;
130  if(this->numDim>2) z=this->axisDim[2];
131  else z=1;
132}
133//--------------------------------------------------------------------
134
135void DataArray::getDimArray(long *output){
136  /**
137   * The axisDim array is written to output. This needs to be defined
138   *  beforehand: no checking is done on the memory.
139   * \param output The array that is written to.
140   */
141  for(int i=0;i<this->numDim;i++) output[i] = this->axisDim[i];
142}
143//--------------------------------------------------------------------
144
145void DataArray::getArray(float *output){
146  /**
147   * The pixel value array is written to output. This needs to be defined
148   *  beforehand: no checking is done on the memory.
149   * \param output The array that is written to.
150   */
151  for(int i=0;i<this->numPixels;i++) output[i] = this->array[i];
152}
153//--------------------------------------------------------------------
154
155void DataArray::saveArray(float *input, long size){
156  /**
157   * Saves the array in input to the pixel array DataArray::array.
158   * The size of the array given must be the same as the current number of
159   * pixels, else an error message is returned and nothing is done.
160   * \param input The array of values to be saved.
161   * \param size The size of input.
162   */
163  if(size != this->numPixels)
164    duchampError("DataArray::saveArray",
165                 "Input array different size to existing array. Cannot save.");
166  else {
167    if(this->numPixels>0) delete [] this->array;
168    this->numPixels = size;
169    this->array = new float[size];
170    for(int i=0;i<size;i++) this->array[i] = input[i];
171  }
172}
173//--------------------------------------------------------------------
174
175void DataArray::addObject(Detection object){
176  /**
177   * \param object The object to be added to the object list.
178   */
179  // objectList is a vector, so just use push_back()
180  this->objectList.push_back(object);
181}
182//--------------------------------------------------------------------
183
184void DataArray::addObjectList(vector <Detection> newlist) {
185  /**
186   * \param newlist The list of objects to be added to the object list.
187   */
188  for(int i=0;i<newlist.size();i++) this->objectList.push_back(newlist[i]);
189}
190//--------------------------------------------------------------------
191
192void DataArray::addObjectOffsets(){
193  /**
194   * Add the pixel offsets (that is, offsets from the corner of the cube to the
195   *  corner of the utilised part) that are stored in the Param set to the
196   *  coordinate values of each object in the object list.
197   */
198  for(int i=0;i<this->objectList.size();i++){
199    for(int p=0;p<this->objectList[i].getSize();p++){
200      this->objectList[i].setX(p,this->objectList[i].getX(p)+
201                               this->par.getXOffset());
202      this->objectList[i].setY(p,this->objectList[i].getY(p)+
203                               this->par.getYOffset());
204      this->objectList[i].setZ(p,this->objectList[i].getZ(p)+
205                               this->par.getZOffset());
206    }
207  }
208}
209//--------------------------------------------------------------------
210
211bool DataArray::isDetection(float value){
212  /**
213   * Is a given value a detection, based on the statistics in the
214   * DataArray's StatsContainer?
215   * \param value The pixel value to test.
216   */
217  if(par.isBlank(value)) return false;
218  else return Stats.isDetection(value);
219}; 
220//--------------------------------------------------------------------
221
222bool DataArray::isDetection(long voxel){
223  /**
224   * Is a given pixel a detection, based on the statistics in the
225   * DataArray's StatsContainer?
226   * If the pixel lies outside the valid range for the data array, return false.
227   * \param voxel Location of the DataArray's pixel to be tested.
228   */
229  if((voxel<0)||(voxel>this->numPixels)) return false;
230  else if(par.isBlank(this->array[voxel])) return false;
231  else return Stats.isDetection(this->array[voxel]);
232}; 
233//--------------------------------------------------------------------
234
235std::ostream& operator<< ( std::ostream& theStream, DataArray &array)
236{
237  /**
238   * A way to print out the pixel coordinates & flux values of the
239   * list of detected objects belonging to the DataArray.
240   * These are formatted nicely according to the << operator for Detection,
241   *  with a line indicating the number of detections at the start.
242   * \param theStream The ostream object to which the output should be sent.
243   * \param array The DataArray containing the list of objects.
244   */
245  for(int i=0;i<array.numDim;i++){
246    if(i>0) theStream<<"x";
247    theStream<<array.axisDim[i];
248  }
249  theStream<<std::endl;
250  theStream<<array.objectList.size()<<" detections:\n--------------\n";
251  for(int i=0;i<array.objectList.size();i++){
252    theStream << "Detection #" << array.objectList[i].getID()<<std::endl;
253    Detection *obj = new Detection;
254    *obj = array.objectList[i];
255    for(int j=0;j<obj->getSize();j++){
256      obj->setX(j,obj->getX(j)+obj->getXOffset());
257      obj->setY(j,obj->getY(j)+obj->getYOffset());
258      obj->setZ(j,obj->getZ(j)+obj->getZOffset());
259    }
260    theStream<<*obj;
261    delete obj;
262  }
263  theStream<<"--------------\n";
264}
265
266/****************************************************************/
267/////////////////////////////////////////////////////////////
268//// Functions for Cube class
269/////////////////////////////////////////////////////////////
270
271Cube::Cube(){
272  /**
273   * Basic Constructor for Cube class.
274   * numDim set to 3, but numPixels to 0 and all bool flags to false.
275   * No allocation done.
276   */
277    numPixels=0; numDim=3;
278    reconExists = false; reconAllocated = false; baselineAllocated = false;
279  };
280//--------------------------------------------------------------------
281
282Cube::Cube(long size){
283  /**
284   * Alternative Cube constructor, where size is given but not individual
285   *  dimensions. Arrays are allocated as appropriate (according to the
286   *  relevant flags in Param set), but the Cube::axisDim array is not.
287   */
288  this->reconAllocated = false;
289  this->baselineAllocated = false;
290  this->numPixels = this->numDim = 0;
291  if(size<0)
292    duchampError("Cube(size)","Negative size -- could not define Cube");
293  else{
294    if(size>0){
295      this->array = new float[size];
296      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
297        this->recon = new float[size];
298        this->reconAllocated = true;
299      }
300      if(this->par.getFlagBaseline()){
301        this->baseline = new float[size];
302        this->baselineAllocated = true;
303      }
304    }
305    this->numPixels = size;
306    this->axisDim = new long[2];
307    this->numDim = 3;
308    this->reconExists = false;
309  }
310}
311//--------------------------------------------------------------------
312
313Cube::Cube(long *dimensions){
314  /**
315   * Alternative Cube constructor, where sizes of dimensions are given.
316   * Arrays are allocated as appropriate (according to the
317   *  relevant flags in Param set), as is the Cube::axisDim array.
318   */
319  int size   = dimensions[0] * dimensions[1] * dimensions[2];
320  int imsize = dimensions[0] * dimensions[1];
321  this->reconAllocated = false;
322  this->baselineAllocated = false;
323  this->numPixels = this->numDim = 0;
324  if((size<0) || (imsize<0) )
325    duchampError("Cube(dimArray)","Negative size -- could not define Cube");
326  else{
327    this->numPixels = size;
328    if(size>0){
329      this->array      = new float[size];
330      this->detectMap  = new short[imsize];
331      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
332        this->recon    = new float[size];
333        this->reconAllocated = true;
334      }
335      if(this->par.getFlagBaseline()){
336        this->baseline = new float[size];
337        this->baselineAllocated = true;
338      }
339    }
340    this->numDim  = 3;
341    this->axisDim = new long[3];
342    for(int i=0;i<3     ;i++) this->axisDim[i]   = dimensions[i];
343    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
344    this->reconExists = false;
345  }
346}
347//--------------------------------------------------------------------
348
349Cube::~Cube()
350{
351  /**
352   *  The destructor deletes the memory allocated for Cube::detectMap, and,
353   *  if these have been allocated, Cube::recon and Cube::baseline.
354   */
355  delete [] this->detectMap;
356  if(this->reconAllocated)    delete [] this->recon;
357  if(this->baselineAllocated) delete [] this->baseline;
358}
359//--------------------------------------------------------------------
360
361void Cube::initialiseCube(long *dimensions)
362{
363  /**
364   *  This function will set the sizes of all arrays that will be used by Cube.
365   *  It will also define the values of the axis dimensions: this will be done
366   *   using the WCS in the FitsHeader class, so the WCS needs to be good and
367   *   have three axes. If this is not the case, the axes are assumed to be
368   *   ordered in the sense of lng,lat,spc.
369   *
370   *  \param dimensions An array of values giving the dimensions (sizes) for
371   *  all axes. 
372   */
373
374  int lng,lat,spc,size,imsize;
375
376  if(this->head.isWCS() && (this->head.getWCS()->naxis>=3)){
377    // if there is a WCS and there is at least 3 axes
378    lng = this->head.getWCS()->lng;
379    lat = this->head.getWCS()->lat;
380    spc = this->head.getWCS()->spec;
381  }
382  else{
383    // just take dimensions[] at face value
384    lng = 0;
385    lat = 1;
386    spc = 2;
387  }
388  size   = dimensions[lng] * dimensions[lat] * dimensions[spc];
389  imsize = dimensions[lng] * dimensions[lat];
390
391  this->reconAllocated = false;
392  this->baselineAllocated = false;
393
394  if((size<0) || (imsize<0) )
395    duchampError("Cube::initialiseCube(dimArray)",
396                 "Negative size -- could not define Cube.\n");
397  else{
398    this->numPixels = size;
399    if(size>0){
400      this->array      = new float[size];
401      this->detectMap  = new short[imsize];
402      if(this->par.getFlagATrous() || this->par.getFlagSmooth()){
403        this->recon    = new float[size];
404        this->reconAllocated = true;
405      }
406      if(this->par.getFlagBaseline()){
407        this->baseline = new float[size];
408        this->baselineAllocated = true;
409      }
410    }
411    this->numDim  = 3;
412    this->axisDim = new long[3];
413    this->axisDim[0] = dimensions[lng];
414    this->axisDim[1] = dimensions[lat];
415    this->axisDim[2] = dimensions[spc];
416    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
417    this->reconExists = false;
418  }
419}
420//--------------------------------------------------------------------
421
422int Cube::getCube(){ 
423    /**
424     * A front-end to the Cube::getCube() function, that does
425     *  subsection checks.
426     * Assumes the Param is set up properly.
427     */
428    string fname = par.getImageFile();
429    if(par.getFlagSubsection()) fname+=par.getSubsection();
430    return getCube(fname);
431  };
432//--------------------------------------------------------------------
433
434int Cube::getopts(int argc, char ** argv)
435{
436  /** 
437   *   A function that reads in the command-line options, in a manner
438   *    tailored for use with the main Duchamp program.
439   *   Based on the options given, the appropriate Param set will be read
440   *    in to the Cube class.
441   *
442   *   \param argc The number of command line arguments.
443   *   \param argv The array of command line arguments.
444   */
445
446  int returnValue;
447  if(argc==1){
448    std::cout << ERR_USAGE_MSG;
449    returnValue = FAILURE;
450  }
451  else {
452    string file;
453    Param *par = new Param;
454    char c;
455    while( ( c = getopt(argc,argv,"p:f:hv") )!=-1){
456      switch(c) {
457      case 'p':
458        file = optarg;
459        if(this->readParam(file)==FAILURE){
460          stringstream errmsg;
461          errmsg << "Could not open parameter file " << file << ".\n";
462          duchampError("Duchamp",errmsg.str());
463          returnValue = FAILURE;
464        }
465        else returnValue = SUCCESS;
466        break;
467      case 'f':
468        file = optarg;
469        par->setImageFile(file);
470        this->saveParam(*par);
471        returnValue = SUCCESS;
472        break;
473      case 'v':
474        std::cout << PROGNAME << " version " << VERSION << std::endl;
475        returnValue = FAILURE;
476        break;
477      case 'h':
478      default :
479        std::cout << ERR_USAGE_MSG;
480        returnValue = FAILURE;
481        break;
482      }
483    }
484    delete par;
485  }
486  return returnValue;
487}
488//--------------------------------------------------------------------
489
490void Cube::readSavedArrays()
491{
492  /**
493   *  This function reads in reconstructed and/or smoothed arrays that have
494   *   been saved on disk in FITS files.
495   *  To do this it calls the functions Cube::readReconCube() and
496   *   Cube::readSmoothCube().
497   *  The Param set is consulted to determine which of these arrays are needed.
498   */
499
500  // If the reconstructed array is to be read in from disk
501  if( this->par.getFlagReconExists() && this->par.getFlagATrous() ){
502    std::cout << "Reading reconstructed array: "<<std::endl;
503    if( this->readReconCube() == FAILURE){
504      std::stringstream errmsg;
505      errmsg <<"Could not read in existing reconstructed array.\n"
506             <<"Will perform reconstruction using assigned parameters.\n";
507      duchampWarning("Duchamp", errmsg.str());
508      this->par.setFlagReconExists(false);
509    }
510    else std::cout << "Reconstructed array available.\n";
511  }
512
513  if( this->par.getFlagSmoothExists() && this->par.getFlagSmooth() ){
514    std::cout << "Reading Hanning-smoothed array: "<<std::endl;
515    if( this->readSmoothCube() == FAILURE){
516      std::stringstream errmsg;
517      errmsg <<"Could not read in existing smoothed array.\n"
518             <<"Will smooth the cube using assigned parameters.\n";
519      duchampWarning("Duchamp", errmsg.str());
520      this->par.setFlagSmoothExists(false);
521    }
522    else std::cout << "Smoothed array available.\n";
523  }
524   
525}
526
527//--------------------------------------------------------------------
528
529void Cube::saveArray(float *input, long size){
530  if(size != this->numPixels){
531    stringstream errmsg;
532    errmsg << "Input array different size to existing array ("
533           << size << " cf. " << this->numPixels << "). Cannot save.\n";
534    duchampError("Cube::saveArray",errmsg.str());
535  }
536  else {
537    if(this->numPixels>0) delete [] array;
538    this->numPixels = size;
539    this->array = new float[size];
540    for(int i=0;i<size;i++) this->array[i] = input[i];
541  }
542}
543//--------------------------------------------------------------------
544
545void Cube::saveRecon(float *input, long size){
546  /**
547   * Saves the array in input to the reconstructed array Cube::recon
548   * The size of the array given must be the same as the current number of
549   * pixels, else an error message is returned and nothing is done.
550   * If the recon array has already been allocated, it is deleted first, and
551   * then the space is allocated.
552   * Afterwards, the appropriate flags are set.
553   * \param input The array of values to be saved.
554   * \param size The size of input.
555   */
556  if(size != this->numPixels){
557    stringstream errmsg;
558    errmsg << "Input array different size to existing array ("
559           << size << " cf. " << this->numPixels << "). Cannot save.\n";
560    duchampError("Cube::saveRecon",errmsg.str());
561  }
562  else {
563    if(this->numPixels>0){
564      if(this->reconAllocated) delete [] this->recon;
565      this->numPixels = size;
566      this->recon = new float[size];
567      this->reconAllocated = true;
568      for(int i=0;i<size;i++) this->recon[i] = input[i];
569      this->reconExists = true;
570    }
571  }
572}
573//--------------------------------------------------------------------
574
575void Cube::getRecon(float *output){
576  /**
577   * The reconstructed array is written to output. The output array needs to
578   *  be defined beforehand: no checking is done on the memory.
579   * \param output The array that is written to.
580   */
581  // Need check for change in number of pixels!
582  for(int i=0;i<this->numPixels;i++){
583    if(this->reconExists) output[i] = this->recon[i];
584    else output[i] = 0.;
585  }
586}
587//--------------------------------------------------------------------
588
589void Cube::removeMW()
590{
591  /**
592   * The channels corresponding to the Milky Way range (as given by the Param
593   *  set) are all set to 0 in the pixel array.
594   * Only done if the appropriate flag is set, and the pixels are not BLANK.
595   * \deprecated
596   */
597  if(this->par.getFlagMW()){
598    for(int pix=0;pix<this->axisDim[0]*this->axisDim[1];pix++){
599      for(int z=0;z<this->axisDim[2];z++){
600        int pos = z*this->axisDim[0]*this->axisDim[1] + pix;
601        if(!this->isBlank(pos) && this->par.isInMW(z)) this->array[pos]=0.;
602      }
603    }
604  }
605}
606//--------------------------------------------------------------------
607
608void Cube::setCubeStatsOld()
609{
610  /** 
611   *   \deprecated
612   *
613   *   Calculates the full statistics for the cube:  mean, rms, median, madfm.
614   *   Only do this if the threshold has not been defined (ie. is still 0.,
615   *    its default).
616   *   Also work out the threshold and store it in the Param set.
617   *   
618   *   For the stats calculations, we ignore BLANKs and MW channels.
619   */
620
621  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
622    // if the user has defined a threshold, set this in the StatsContainer
623    this->Stats.setThreshold( this->par.getThreshold() );
624  }
625  else{
626    // only work out the mean etc if we need to.
627    // the only reason we don't is if the user has specified a threshold.
628   
629    std::cout << "Calculating the cube statistics... " << std::flush;
630   
631    // get number of good pixels;
632    int goodSize = 0;
633    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
634      for(int z=0;z<this->axisDim[2];z++){
635        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
636        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
637      }
638    }
639
640    float *tempArray = new float[goodSize];
641
642    goodSize=0;
643    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
644      for(int z=0;z<this->axisDim[2];z++){
645        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
646        if(!this->isBlank(vox) && !this->par.isInMW(z))
647          tempArray[goodSize++] = this->array[vox];
648      }
649    }
650    if(!this->reconExists){
651      // if there's no recon array, calculate everything from orig array
652      this->Stats.calculate(tempArray,goodSize);
653    }
654    else{
655      // just get mean & median from orig array, and rms & madfm from recon
656      StatsContainer<float> origStats,reconStats;
657      origStats.calculate(tempArray,goodSize);
658      goodSize=0;
659      for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
660        for(int z=0;z<this->axisDim[2];z++){
661          int vox = z * this->axisDim[0] * this->axisDim[1] + p;
662          if(!this->isBlank(vox) && !this->par.isInMW(z))
663            tempArray[goodSize++] = this->array[vox] - this->recon[vox];
664        }
665      }
666      reconStats.calculate(tempArray,goodSize);
667
668      // Get the "middle" estimators from the original array.
669      this->Stats.setMean(origStats.getMean());
670      this->Stats.setMedian(origStats.getMedian());
671      // Get the "spread" estimators from the residual (orig-recon) array
672      this->Stats.setStddev(reconStats.getStddev());
673      this->Stats.setMadfm(reconStats.getMadfm());
674    }
675
676    delete [] tempArray;
677
678    this->Stats.setUseFDR( this->par.getFlagFDR() );
679    // If the FDR method has been requested
680    if(this->par.getFlagFDR())  this->setupFDR();
681    else{
682      // otherwise, calculate one based on the requested SNR cut level, and
683      //   then set the threshold parameter in the Par set.
684      this->Stats.setThresholdSNR( this->par.getCut() );
685      this->par.setThreshold( this->Stats.getThreshold() );
686    }
687   
688   
689  }
690  std::cout << "Using ";
691  if(this->par.getFlagFDR()) std::cout << "effective ";
692  std::cout << "flux threshold of: ";
693  float thresh = this->Stats.getThreshold();
694  if(this->par.getFlagNegative()) thresh *= -1.;
695  std::cout << thresh << std::endl;
696
697}
698//--------------------------------------------------------------------
699
700void Cube::setCubeStats()
701{
702  /** 
703   *   Calculates the full statistics for the cube:
704   *     mean, rms, median, madfm
705   *   Only do this if the threshold has not been defined (ie. is still 0.,
706   *    its default).
707   *   Also work out the threshold and store it in the par set.
708   *   
709   *   Different from Cube::setCubeStatsOld() as it doesn't use the
710   *    getStats functions but has own versions of them hardcoded to
711   *    ignore BLANKs and MW channels. This saves on memory usage -- necessary
712   *    for dealing with very big files.
713   */
714
715  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
716    // if the user has defined a threshold, set this in the StatsContainer
717    this->Stats.setThreshold( this->par.getThreshold() );
718  }
719  else{
720    // only work out the mean etc if we need to.
721    // the only reason we don't is if the user has specified a threshold.
722   
723    std::cout << "Calculating the cube statistics... " << std::flush;
724   
725    long xysize = this->axisDim[0]*this->axisDim[1];
726
727    // get number of good pixels;
728    int vox,goodSize = 0;
729    for(int p=0;p<xysize;p++){
730      for(int z=0;z<this->axisDim[2];z++){
731        vox = z*xysize+p;
732        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
733      }
734    }
735
736    float *tempArray = new float[goodSize];
737   
738    goodSize=0;
739    for(int p=0;p<xysize;p++){
740      for(int z=0;z<this->axisDim[2];z++){
741        vox = z * xysize + p;
742        if(!this->isBlank(vox) && !this->par.isInMW(z)){
743          tempArray[goodSize] = this->array[vox];
744          goodSize++;
745        }
746      }
747    }
748
749    float mean,median,stddev,madfm;
750      mean = tempArray[0];
751      for(int i=1;i<goodSize;i++) mean += tempArray[i];
752      mean /= float(goodSize);
753      mean = findMean(tempArray,goodSize);
754      this->Stats.setMean(mean);
755
756      std::sort(tempArray,tempArray+goodSize);
757      if((goodSize%2)==0)
758        median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
759      else median = tempArray[goodSize/2];
760      this->Stats.setMedian(median);
761
762   
763    if(!this->reconExists){
764      // if there's no recon array, calculate everything from orig array
765      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
766      for(int i=1;i<goodSize;i++)
767        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
768      stddev = sqrt(stddev/float(goodSize-1));
769      this->Stats.setStddev(stddev);
770
771      for(int i=0;i<goodSize;i++)// tempArray[i] = absval(tempArray[i]-median);
772        {
773          if(tempArray[i]>median) tempArray[i] -= median;
774          else tempArray[i] = median - tempArray[i];
775        }
776      std::sort(tempArray,tempArray+goodSize);
777      if((goodSize%2)==0)
778        madfm = (tempArray[goodSize/2-1]+tempArray[goodSize/2])/2;
779      else madfm = tempArray[goodSize/2];
780      this->Stats.setMadfm(madfm);
781    }
782    else{
783      // just get mean & median from orig array, and rms & madfm from residual
784      // recompute array values to be residuals & then find stddev & madfm
785      goodSize = 0;
786      for(int p=0;p<xysize;p++){
787        for(int z=0;z<this->axisDim[2];z++){
788          vox = z * xysize + p;
789          if(!this->isBlank(vox) && !this->par.isInMW(z)){
790            tempArray[goodSize] = this->array[vox] - this->recon[vox];
791            goodSize++;
792          }
793        }
794      }
795      mean = tempArray[0];
796      for(int i=1;i<goodSize;i++) mean += tempArray[i];
797      mean /= float(goodSize);
798      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
799      for(int i=1;i<goodSize;i++)
800        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
801      stddev = sqrt(stddev/float(goodSize-1));
802      this->Stats.setStddev(stddev);
803
804      std::sort(tempArray,tempArray+goodSize);
805      if((goodSize%2)==0)
806        median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
807      else median = tempArray[goodSize/2];
808      for(int i=0;i<goodSize;i++){
809        if(tempArray[i]>median) tempArray[i] = tempArray[i]-median;
810        else tempArray[i] = median - tempArray[i];
811      }
812      std::sort(tempArray,tempArray+goodSize);
813      if((goodSize%2)==0)
814        madfm = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
815      else madfm = tempArray[goodSize/2];
816      this->Stats.setMadfm(madfm);
817    }
818
819    delete [] tempArray;
820
821    this->Stats.setUseFDR( this->par.getFlagFDR() );
822    // If the FDR method has been requested
823    if(this->par.getFlagFDR())  this->setupFDR();
824    else{
825      // otherwise, calculate threshold based on the requested SNR cut level,
826      //  and then set the threshold parameter in the Par set.
827      this->Stats.setThresholdSNR( this->par.getCut() );
828      this->par.setThreshold( this->Stats.getThreshold() );
829    }
830   
831  }
832
833  std::cout << "Using ";
834  if(this->par.getFlagFDR()) std::cout << "effective ";
835  std::cout << "flux threshold of: ";
836  float thresh = this->Stats.getThreshold();
837  if(this->par.getFlagNegative()) thresh *= -1.;
838  std::cout << thresh << std::endl;
839
840}
841//--------------------------------------------------------------------
842
843int Cube::setupFDR()
844{
845  /** 
846   *   Determines the critical Probability value for the False Discovery Rate
847   *    detection routine. All pixels with Prob less than this value will
848   *    be considered detections.
849   *
850   *   The Prob here is the probability, assuming a Normal distribution, of
851   *    obtaining a value as high or higher than the pixel value (ie. only the
852   *    positive tail of the PDF)
853   */
854
855  // first calculate p-value for each pixel -- assume Gaussian for now.
856
857  float *orderedP = new float[this->numPixels];
858  int count = 0;
859  float zStat;
860  for(int pix=0; pix<this->numPixels; pix++){
861
862    if( !(this->par.isBlank(this->array[pix])) ){
863      // only look at non-blank pixels
864      zStat = (this->array[pix] - this->Stats.getMiddle()) /
865        this->Stats.getSpread();
866     
867      orderedP[count++] = 0.5 * erfc(zStat/M_SQRT2);
868      // Need the factor of 0.5 here, as we are only considering the positive
869      //  tail of the distribution. Don't care about negative detections.
870    }
871  }
872
873  // now order them
874  std::sort(orderedP,orderedP+count);
875 
876  // now find the maximum P value.
877  int max = 0;
878  float cN = 0.;
879  int psfCtr;
880  int numVox = int(this->par.getBeamSize()) * 2;
881  // why beamSize*2? we are doing this in 3D, so spectrally assume just the
882  //  neighbouring channels are correlated, but spatially all those within
883  //  the beam, so total number of voxels is 2*beamSize
884  for(psfCtr=1;psfCtr<=numVox;(psfCtr)++)
885    cN += 1./float(psfCtr);
886
887  for(int loopCtr=0;loopCtr<count;loopCtr++) {
888    if( orderedP[loopCtr] <
889        (double(loopCtr+1)*this->par.getAlpha()/(cN * double(count))) ) {
890      max = loopCtr;
891    }
892  }
893
894  this->Stats.setPThreshold( orderedP[max] );
895
896  delete [] orderedP;
897
898  // Find real value of the P threshold by finding the inverse of the
899  //  error function -- root finding with brute force technique
900  //  (relatively slow, but we only do it once).
901  zStat = 0;
902  float deltaZ = 0.1;
903  float tolerance = 1.e-6;
904  float zeroP = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
905  do{
906    zStat+=deltaZ;
907    if((zeroP * (erfc(zStat/M_SQRT2)-this->Stats.getPThreshold()))<0.){
908      zStat-=deltaZ;
909      deltaZ/=2.;
910    }
911  }while(deltaZ>tolerance);
912  this->Stats.setThreshold( zStat*this->Stats.getSpread() +
913                            this->Stats.getMiddle() );
914
915}
916//--------------------------------------------------------------------
917
918bool Cube::isDetection(long x, long y, long z)
919{
920  /**
921   * Is a given voxel at position (x,y,z) a detection, based on the statistics
922   *  in the Cube's StatsContainer?
923   * If the pixel lies outside the valid range for the data array, return false.
924   * \param x X-value of the Cube's voxel to be tested.
925   * \param y Y-value of the Cube's voxel to be tested.
926   * \param z Z-value of the Cube's voxel to be tested.
927   */
928    long voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
929    return DataArray::isDetection(array[voxel]);
930  };
931//--------------------------------------------------------------------
932
933void Cube::calcObjectWCSparams()
934{
935  /**
936   *  A function that calculates the WCS parameters for each object in the
937   *  Cube's list of detections.
938   *  Each object gets an ID number assigned to it (which is simply its order
939   *   in the list), and if the WCS is good, the WCS paramters are calculated.
940   */
941 
942  for(int i=0;i<this->objectList.size();i++){
943    this->objectList[i].setID(i+1);
944    this->objectList[i].calcWCSparams(this->head);
945    this->objectList[i].setPeakSNR( (this->objectList[i].getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
946  } 
947
948  if(!this->head.isWCS()){
949    // if the WCS is bad, set the object names to Obj01 etc
950    int numspaces = int(log10(this->objectList.size())) + 1;
951    stringstream ss;
952    for(int i=0;i<this->objectList.size();i++){
953      ss.str("");
954      ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
955      this->objectList[i].setName(ss.str());
956    }
957  }
958 
959}
960//--------------------------------------------------------------------
961
962void Cube::sortDetections()
963{
964  /**
965   *  A front end to the sort-by functions.
966   *  If there is a good WCS, the detection list is sorted by velocity.
967   *  Otherwise, it is sorted by increasing z-pixel value.
968   *  The ID numbers are then re-calculated.
969   */
970 
971  if(this->head.isWCS()) SortByVel(this->objectList);
972  else SortByZ(this->objectList);
973  for(int i=0; i<this->objectList.size();i++) this->objectList[i].setID(i+1);
974
975}
976//--------------------------------------------------------------------
977
978void Cube::updateDetectMap()
979{
980  /**
981   *  A function that, for each detected object in the cube's list, increments
982   *   the cube's detection map by the required amount at each pixel.
983   */
984
985  for(int obj=0;obj<this->objectList.size();obj++){
986    for(int pix=0;pix<this->objectList[obj].getSize();pix++) {
987      int spatialPos = this->objectList[obj].getX(pix)+
988        this->objectList[obj].getY(pix)*this->axisDim[0];
989      this->detectMap[spatialPos]++;
990    }
991  }
992}
993//--------------------------------------------------------------------
994
995void Cube::updateDetectMap(Detection obj)
996{
997  /**
998   *  A function that, for the given object, increments the cube's
999   *  detection map by the required amount at each pixel.
1000   *
1001   *  \param obj A Detection object that is being incorporated into the map.
1002   */
1003  for(int pix=0;pix<obj.getSize();pix++) {
1004    int spatialPos = obj.getX(pix)+obj.getY(pix)*this->axisDim[0];
1005    this->detectMap[spatialPos]++;
1006  }
1007}
1008//--------------------------------------------------------------------
1009
1010float Cube::enclosedFlux(Detection obj)
1011{
1012  /**
1013   *   A function to calculate the flux enclosed by the range
1014   *    of pixels detected in the object obj (not necessarily all
1015   *    pixels will have been detected).
1016   *
1017   *   \param obj The Detection under consideration.
1018   */
1019  obj.calcParams();
1020  int xsize = obj.getXmax()-obj.getXmin()+1;
1021  int ysize = obj.getYmax()-obj.getYmin()+1;
1022  int zsize = obj.getZmax()-obj.getZmin()+1;
1023  vector <float> fluxArray(xsize*ysize*zsize,0.);
1024  for(int x=0;x<xsize;x++){
1025    for(int y=0;y<ysize;y++){
1026      for(int z=0;z<zsize;z++){
1027        fluxArray[x+y*xsize+z*ysize*xsize] =
1028          this->getPixValue(x+obj.getXmin(),
1029                            y+obj.getYmin(),
1030                            z+obj.getZmin());
1031        if(this->par.getFlagNegative())
1032          fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1033      }
1034    }
1035  }
1036  float sum = 0.;
1037  for(int i=0;i<fluxArray.size();i++)
1038    if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1039  return sum;
1040}
1041//--------------------------------------------------------------------
1042
1043void Cube::setupColumns()
1044{
1045  /**
1046   *   A front-end to the two setup routines in columns.cc.
1047   *   This first calculates the WCS parameters for all objects, then
1048   *    sets up the columns (calculates their widths and precisions and so on).
1049   *   The precisions are also stored in each Detection object.
1050   */
1051  this->calcObjectWCSparams(); 
1052  // need this as the colSet functions use vel, RA, Dec, etc...
1053 
1054  this->fullCols.clear();
1055  this->fullCols = getFullColSet(this->objectList, this->head);
1056
1057  this->logCols.clear();
1058  this->logCols = getLogColSet(this->objectList, this->head);
1059
1060  int vel,fpeak,fint,pos,xyz,temp,snr;
1061  vel = fullCols[VEL].getPrecision();
1062  fpeak = fullCols[FPEAK].getPrecision();
1063  snr = fullCols[SNRPEAK].getPrecision();
1064  xyz = fullCols[X].getPrecision();
1065  if(temp=fullCols[Y].getPrecision() > xyz) xyz = temp;
1066  if(temp=fullCols[Z].getPrecision() > xyz) xyz = temp;
1067  if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1068  else fint = fullCols[FTOT].getPrecision();
1069  pos = fullCols[WRA].getPrecision();
1070  if(temp=fullCols[WDEC].getPrecision() > pos) pos = temp;
1071 
1072  for(int obj=0;obj<this->objectList.size();obj++){
1073    this->objectList[obj].setVelPrec(vel);
1074    this->objectList[obj].setFpeakPrec(fpeak);
1075    this->objectList[obj].setXYZPrec(xyz);
1076    this->objectList[obj].setPosPrec(pos);
1077    this->objectList[obj].setFintPrec(fint);
1078    this->objectList[obj].setSNRPrec(snr);
1079  }
1080
1081}
1082//--------------------------------------------------------------------
1083
1084bool Cube::objAtSpatialEdge(Detection obj)
1085{
1086  /**
1087   *   A function to test whether the object obj
1088   *    lies at the edge of the cube's spatial field --
1089   *    either at the boundary, or next to BLANKs.
1090   *
1091   *   /param obj The Detection under consideration.
1092   */
1093
1094  bool atEdge = false;
1095
1096  int pix = 0;
1097  while(!atEdge && pix<obj.getSize()){
1098    // loop over each pixel in the object, until we find an edge pixel.
1099    Voxel vox = obj.getPixel(pix);
1100    for(int dx=-1;dx<=1;dx+=2){
1101      if(((vox.getX()+dx)<0) || ((vox.getX()+dx)>=this->axisDim[0]))
1102        atEdge = true;
1103      else if(this->isBlank(vox.getX()+dx,vox.getY(),vox.getZ()))
1104        atEdge = true;
1105    }
1106    for(int dy=-1;dy<=1;dy+=2){
1107      if(((vox.getY()+dy)<0) || ((vox.getY()+dy)>=this->axisDim[1]))
1108        atEdge = true;
1109      else if(this->isBlank(vox.getX(),vox.getY()+dy,vox.getZ()))
1110        atEdge = true;
1111    }
1112    pix++;
1113  }
1114
1115  return atEdge;
1116}
1117//--------------------------------------------------------------------
1118
1119bool Cube::objAtSpectralEdge(Detection obj)
1120{
1121  /** 
1122   *   A function to test whether the object obj
1123   *    lies at the edge of the cube's spectral extent --
1124   *    either at the boundary, or next to BLANKs.
1125   *
1126   *   /param obj The Detection under consideration.
1127   */
1128
1129  bool atEdge = false;
1130
1131  int pix = 0;
1132  while(!atEdge && pix<obj.getSize()){
1133    // loop over each pixel in the object, until we find an edge pixel.
1134    Voxel vox = obj.getPixel(pix);
1135    for(int dz=-1;dz<=1;dz+=2){
1136      if(((vox.getZ()+dz)<0) || ((vox.getZ()+dz)>=this->axisDim[2]))
1137        atEdge = true;
1138      else if(this->isBlank(vox.getX(),vox.getY(),vox.getZ()+dz))
1139        atEdge = true;
1140    }
1141    pix++;
1142  }
1143
1144  return atEdge;
1145}
1146//--------------------------------------------------------------------
1147
1148void Cube::setObjectFlags()
1149{
1150  /**   
1151   *   A function to set any warning flags for all the detected objects
1152   *    associated with the cube.
1153   *   Flags to be looked for:
1154   *    <ul><li> Negative enclosed flux (N)
1155   *        <li> Detection at edge of field (spatially) (E)
1156   *        <li> Detection at edge of spectral region (S)
1157   *    </ul>
1158   */
1159
1160  for(int i=0;i<this->objectList.size();i++){
1161
1162    if( this->enclosedFlux(this->objectList[i]) < 0. ) 
1163      this->objectList[i].addToFlagText("N");
1164
1165    if( this->objAtSpatialEdge(this->objectList[i]) )
1166      this->objectList[i].addToFlagText("E");
1167
1168    if( this->objAtSpectralEdge(this->objectList[i]) )
1169      this->objectList[i].addToFlagText("S");
1170
1171  }
1172
1173}
1174//--------------------------------------------------------------------
1175
1176void Cube::plotBlankEdges()
1177{
1178  /**
1179   *  A front end to the drawBlankEdges() function. This draws the lines
1180   *   indicating the extent of the non-BLANK region of the cube in the
1181   *   PGPLOT colour MAGENTA (from the namespace mycpgplot), using the Cube's
1182   *   arrays and dimensions.
1183   *
1184   *  Note that a PGPLOT device needs to be open. This is only done if the
1185   *   appropriate Param parameter is set.
1186   */
1187  if(this->par.drawBlankEdge()){
1188    int colour;
1189    cpgqci(&colour);
1190    cpgsci(MAGENTA);
1191    drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
1192    cpgsci(colour);
1193  }
1194}
1195//--------------------------------------------------------------------
1196
1197
1198
1199/****************************************************************/
1200/////////////////////////////////////////////////////////////
1201//// Functions for Image class
1202/////////////////////////////////////////////////////////////
1203
1204Image::Image(long size){
1205  // need error handling in case size<0 !!!
1206  this->numPixels = this->numDim = 0;
1207  if(size<0)
1208    duchampError("Image(size)","Negative size -- could not define Image");
1209  else{
1210    if(size>0) this->array = new float[size];
1211    this->numPixels = size;
1212    this->axisDim = new long[2];
1213    this->numDim = 2;
1214  }
1215}
1216//--------------------------------------------------------------------
1217
1218Image::Image(long *dimensions){
1219  this->numPixels = this->numDim = 0;
1220  int size = dimensions[0] * dimensions[1];
1221  if(size<0)
1222    duchampError("Image(dimArray)","Negative size -- could not define Image");
1223  else{
1224    this->numPixels = size;
1225    if(size>0) this->array = new float[size];
1226    this->numDim=2;
1227    this->axisDim = new long[2];
1228    for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1229  }
1230}
1231//--------------------------------------------------------------------
1232//--------------------------------------------------------------------
1233
1234void Image::saveArray(float *input, long size){
1235  if(size != this->numPixels)
1236    duchampError("Image::saveArray",
1237                 "Input array different size to existing array. Cannot save.");
1238  else {
1239    if(this->numPixels>0) delete [] array;
1240    this->numPixels = size;
1241    if(this->numPixels>0) this->array = new float[size];
1242    for(int i=0;i<size;i++) this->array[i] = input[i];
1243  }
1244}
1245//--------------------------------------------------------------------
1246
1247void Image::extractSpectrum(float *Array, long *dim, long pixel)
1248{
1249  /**
1250   * Image::extractSpectrum(float *, long *, int)
1251   *  A function to extract a 1-D spectrum from a 3-D array.
1252   *  The array is assumed to be 3-D with the third dimension the spectral one.
1253   *  The dimensions of the array are in the dim[] array.
1254   *  The spectrum extracted is the one lying in the spatial pixel referenced
1255   *    by the third argument.
1256   */
1257  float *spec = new float[dim[2]];
1258  for(int z=0;z<dim[2];z++) spec[z] = Array[z*dim[0]*dim[1] + pixel];
1259  this->saveArray(spec,dim[2]);
1260  delete [] spec;
1261}
1262//--------------------------------------------------------------------
1263
1264void Image::extractSpectrum(Cube &cube, long pixel)
1265{
1266  /**
1267   * Image::extractSpectrum(Cube &, int)
1268   *  A function to extract a 1-D spectrum from a Cube class
1269   *  The spectrum extracted is the one lying in the spatial pixel referenced
1270   *    by the second argument.
1271   */
1272  long zdim = cube.getDimZ();
1273  long spatSize = cube.getDimX()*cube.getDimY();
1274  float *spec = new float[zdim];
1275  for(int z=0;z<zdim;z++) spec[z] = cube.getPixValue(z*spatSize + pixel);
1276  this->saveArray(spec,zdim);
1277  delete [] spec;
1278}
1279//--------------------------------------------------------------------
1280
1281void Image::extractImage(float *Array, long *dim, long channel)
1282{
1283  /**
1284   * Image::extractImage(float *, long *, int)
1285   *  A function to extract a 2-D image from a 3-D array.
1286   *  The array is assumed to be 3-D with the third dimension the spectral one.
1287   *  The dimensions of the array are in the dim[] array.
1288   *  The image extracted is the one lying in the channel referenced
1289   *    by the third argument.
1290   */
1291  float *image = new float[dim[0]*dim[1]];
1292  for(int npix=0; npix<dim[0]*dim[1]; npix++){
1293    image[npix] = Array[channel*dim[0]*dim[1] + npix];
1294  }
1295  this->saveArray(image,dim[0]*dim[1]);
1296  delete [] image;
1297}
1298//--------------------------------------------------------------------
1299
1300void Image::extractImage(Cube &cube, long channel)
1301{
1302  /**
1303   * Image::extractImage(Cube &, int)
1304   *  A function to extract a 2-D image from Cube class.
1305   *  The image extracted is the one lying in the channel referenced
1306   *    by the second argument.
1307   */
1308  long spatSize = cube.getDimX()*cube.getDimY();
1309  float *image = new float[spatSize];
1310  for(int npix=0; npix<spatSize; npix++)
1311    image[npix] = cube.getPixValue(channel*spatSize + npix);
1312  this->saveArray(image,spatSize);
1313  delete [] image;
1314}
1315//--------------------------------------------------------------------
1316
1317void Image::removeMW()
1318{
1319  /**
1320   * Image::removeMW()
1321   *  A function to remove the Milky Way range of channels from a 1-D spectrum.
1322   *  The array in this Image is assumed to be 1-D, with only the first axisDim
1323   *    equal to 1.
1324   *  The values of the MW channels are set to 0, unless they are BLANK.
1325   */
1326  if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1327    for(int z=0;z<this->axisDim[0];z++){
1328      if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1329    }
1330  }
1331}
1332
Note: See TracBrowser for help on using the repository browser.