source: tags/release-1.1.4/src/Cubes/cubes.cc

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