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

Last change on this file since 438 was 438, checked in by MatthewWhiting, 16 years ago

Enabled the user to specify desired decimal precisions for the flux, velocity and SNR columns via new input parameters precFlux, precVel and precSNR (ticket #34).

File size: 54.4 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     *
1202     *   This first gets the starting precisions, which may be from
1203     *   the input parameters. It then sets up the columns (calculates
1204     *   their widths and precisions and so on based on the values
1205     *   within). The precisions are also stored in each Detection
1206     *   object.
1207     *
1208     *   Need to have called calcObjectWCSparams() somewhere
1209     *   beforehand.
1210     */
1211
1212    std::vector<Detection>::iterator obj;
1213    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1214      obj->setVelPrec( this->par.getPrecVel() );
1215      obj->setFpeakPrec( this->par.getPrecFlux() );
1216      obj->setXYZPrec( Column::prec[Column::prXYZ] );
1217      obj->setPosPrec( Column::prec[Column::prWPOS] );
1218      obj->setFintPrec( this->par.getPrecFlux() );
1219      obj->setSNRPrec( this->par.getPrecSNR() );
1220    }
1221 
1222    this->fullCols.clear();
1223    this->fullCols = getFullColSet(*(this->objectList), this->head);
1224
1225    this->logCols.clear();
1226    this->logCols = getLogColSet(*(this->objectList), this->head);
1227
1228    int vel,fpeak,fint,pos,xyz,snr;
1229    vel = fullCols[VEL].getPrecision();
1230    fpeak = fullCols[FPEAK].getPrecision();
1231    snr = fullCols[SNRPEAK].getPrecision();
1232    xyz = fullCols[X].getPrecision();
1233    xyz = std::max(xyz, fullCols[Y].getPrecision());
1234    xyz = std::max(xyz, fullCols[Z].getPrecision());
1235    if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1236    else fint = fullCols[FTOT].getPrecision();
1237    pos = fullCols[WRA].getPrecision();
1238    pos = std::max(pos, fullCols[WDEC].getPrecision());
1239 
1240    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1241      obj->setVelPrec(vel);
1242      obj->setFpeakPrec(fpeak);
1243      obj->setXYZPrec(xyz);
1244      obj->setPosPrec(pos);
1245      obj->setFintPrec(fint);
1246      obj->setSNRPrec(snr);
1247    }
1248
1249  }
1250  //--------------------------------------------------------------------
1251
1252  bool Cube::objAtSpatialEdge(Detection obj)
1253  {
1254    /**
1255     *   A function to test whether the object obj
1256     *    lies at the edge of the cube's spatial field --
1257     *    either at the boundary, or next to BLANKs.
1258     *
1259     *   \param obj The Detection under consideration.
1260     */
1261
1262    bool atEdge = false;
1263
1264    int pix = 0;
1265    std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1266    while(!atEdge && pix<voxlist.size()){
1267      // loop over each pixel in the object, until we find an edge pixel.
1268      for(int dx=-1;dx<=1;dx+=2){
1269        if( ((voxlist[pix].getX()+dx)<0) ||
1270            ((voxlist[pix].getX()+dx)>=this->axisDim[0]) )
1271          atEdge = true;
1272        else if(this->isBlank(voxlist[pix].getX()+dx,
1273                              voxlist[pix].getY(),
1274                              voxlist[pix].getZ()))
1275          atEdge = true;
1276      }
1277      for(int dy=-1;dy<=1;dy+=2){
1278        if( ((voxlist[pix].getY()+dy)<0) ||
1279            ((voxlist[pix].getY()+dy)>=this->axisDim[1]) )
1280          atEdge = true;
1281        else if(this->isBlank(voxlist[pix].getX(),
1282                              voxlist[pix].getY()+dy,
1283                              voxlist[pix].getZ()))
1284          atEdge = true;
1285      }
1286      pix++;
1287    }
1288
1289    return atEdge;
1290  }
1291  //--------------------------------------------------------------------
1292
1293  bool Cube::objAtSpectralEdge(Detection obj)
1294  {
1295    /** 
1296     *   A function to test whether the object obj
1297     *    lies at the edge of the cube's spectral extent --
1298     *    either at the boundary, or next to BLANKs.
1299     *
1300     *   /param obj The Detection under consideration.
1301     */
1302
1303    bool atEdge = false;
1304
1305    int pix = 0;
1306    std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1307    while(!atEdge && pix<voxlist.size()){
1308      // loop over each pixel in the object, until we find an edge pixel.
1309      for(int dz=-1;dz<=1;dz+=2){
1310        if( ((voxlist[pix].getZ()+dz)<0) ||
1311            ((voxlist[pix].getZ()+dz)>=this->axisDim[2]))
1312          atEdge = true;
1313        else if(this->isBlank(voxlist[pix].getX(),
1314                              voxlist[pix].getY(),
1315                              voxlist[pix].getZ()+dz))
1316          atEdge = true;
1317      }
1318      pix++;
1319    }
1320
1321    return atEdge;
1322  }
1323  //--------------------------------------------------------------------
1324
1325  void Cube::setObjectFlags()
1326  {
1327    /**   
1328     *   A function to set any warning flags for all the detected objects
1329     *    associated with the cube.
1330     *   Flags to be looked for:
1331     *    <ul><li> Negative enclosed flux (N)
1332     *        <li> Detection at edge of field (spatially) (E)
1333     *        <li> Detection at edge of spectral region (S)
1334     *    </ul>
1335     */
1336
1337    for(int i=0;i<this->objectList->size();i++){
1338
1339      if( this->enclosedFlux(this->objectList->at(i)) < 0. ) 
1340        this->objectList->at(i).addToFlagText("N");
1341
1342      if( this->objAtSpatialEdge(this->objectList->at(i)) )
1343        this->objectList->at(i).addToFlagText("E");
1344
1345      if( this->objAtSpectralEdge(this->objectList->at(i)) &&
1346          (this->axisDim[2] > 2))
1347        this->objectList->at(i).addToFlagText("S");
1348
1349    }
1350
1351  }
1352  //--------------------------------------------------------------------
1353
1354
1355
1356  /****************************************************************/
1357  /////////////////////////////////////////////////////////////
1358  //// Functions for Image class
1359  /////////////////////////////////////////////////////////////
1360
1361  Image::Image(long size){
1362    // need error handling in case size<0 !!!
1363    this->numPixels = this->numDim = 0;
1364    if(size<0)
1365      duchampError("Image(size)","Negative size -- could not define Image");
1366    else{
1367      if(size>0) this->array = new float[size];
1368      this->numPixels = size;
1369      this->axisDim = new long[2];
1370      this->numDim = 2;
1371    }
1372  }
1373  //--------------------------------------------------------------------
1374
1375  Image::Image(long *dimensions){
1376    this->numPixels = this->numDim = 0;
1377    int size = dimensions[0] * dimensions[1];
1378    if(size<0)
1379      duchampError("Image(dimArray)","Negative size -- could not define Image");
1380    else{
1381      this->numPixels = size;
1382      if(size>0) this->array = new float[size];
1383      this->numDim=2;
1384      this->axisDim = new long[2];
1385      for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1386    }
1387  }
1388  //--------------------------------------------------------------------
1389  //--------------------------------------------------------------------
1390
1391  void Image::saveArray(float *input, long size)
1392  {
1393    /**
1394     * Saves the array in input to the pixel array Image::array.
1395     * The size of the array given must be the same as the current number of
1396     * pixels, else an error message is returned and nothing is done.
1397     * \param input The array of values to be saved.
1398     * \param size The size of input.
1399     */
1400    if(size != this->numPixels)
1401      duchampError("Image::saveArray",
1402                   "Input array different size to existing array. Cannot save.");
1403    else {
1404      if(this->numPixels>0) delete [] array;
1405      this->numPixels = size;
1406      if(this->numPixels>0) this->array = new float[size];
1407      for(int i=0;i<size;i++) this->array[i] = input[i];
1408    }
1409  }
1410  //--------------------------------------------------------------------
1411
1412  void Image::extractSpectrum(float *Array, long *dim, long pixel)
1413  {
1414    /**
1415     *  A function to extract a 1-D spectrum from a 3-D array.
1416     *  The array is assumed to be 3-D with the third dimension the spectral one.
1417     *  The spectrum extracted is the one lying in the spatial pixel referenced
1418     *    by the third argument.
1419     *  The extracted spectrum is stored in the pixel array Image::array.
1420     * \param Array The array containing the pixel values, from which
1421     *               the spectrum is extracted.
1422     * \param dim The array of dimension values.
1423     * \param pixel The spatial pixel that contains the desired spectrum.
1424     */
1425    if((pixel<0)||(pixel>=dim[0]*dim[1]))
1426      duchampError("Image::extractSpectrum",
1427                   "Requested spatial pixel outside allowed range. Cannot save.");
1428    else if(dim[2] != this->numPixels)
1429      duchampError("Image::extractSpectrum",
1430                   "Input array different size to existing array. Cannot save.");
1431    else {
1432      if(this->numPixels>0) delete [] array;
1433      this->numPixels = dim[2];
1434      if(this->numPixels>0) this->array = new float[dim[2]];
1435      for(int z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1436    }
1437  }
1438  //--------------------------------------------------------------------
1439
1440  void Image::extractSpectrum(Cube &cube, long pixel)
1441  {
1442    /**
1443     *  A function to extract a 1-D spectrum from a Cube class
1444     *  The spectrum extracted is the one lying in the spatial pixel referenced
1445     *    by the second argument.
1446     *  The extracted spectrum is stored in the pixel array Image::array.
1447     * \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1448     * \param pixel The spatial pixel that contains the desired spectrum.
1449     */
1450    long zdim = cube.getDimZ();
1451    long spatSize = cube.getDimX()*cube.getDimY();
1452    if((pixel<0)||(pixel>=spatSize))
1453      duchampError("Image::extractSpectrum",
1454                   "Requested spatial pixel outside allowed range. Cannot save.");
1455    else if(zdim != this->numPixels)
1456      duchampError("Image::extractSpectrum",
1457                   "Input array different size to existing array. Cannot save.");
1458    else {
1459      if(this->numPixels>0) delete [] array;
1460      this->numPixels = zdim;
1461      if(this->numPixels>0) this->array = new float[zdim];
1462      for(int z=0;z<zdim;z++)
1463        this->array[z] = cube.getPixValue(z*spatSize + pixel);
1464    }
1465  }
1466  //--------------------------------------------------------------------
1467
1468  void Image::extractImage(float *Array, long *dim, long channel)
1469  {
1470    /**
1471     *  A function to extract a 2-D image from a 3-D array.
1472     *  The array is assumed to be 3-D with the third dimension the spectral one.
1473     *  The dimensions of the array are in the dim[] array.
1474     *  The image extracted is the one lying in the channel referenced
1475     *    by the third argument.
1476     *  The extracted image is stored in the pixel array Image::array.
1477     * \param Array The array containing the pixel values, from which the image is extracted.
1478     * \param dim The array of dimension values.
1479     * \param channel The spectral channel that contains the desired image.
1480     */
1481
1482    long spatSize = dim[0]*dim[1];
1483    if((channel<0)||(channel>=dim[2]))
1484      duchampError("Image::extractImage",
1485                   "Requested channel outside allowed range. Cannot save.");
1486    else if(spatSize != this->numPixels)
1487      duchampError("Image::extractImage",
1488                   "Input array different size to existing array. Cannot save.");
1489    else {
1490      if(this->numPixels>0) delete [] array;
1491      this->numPixels = spatSize;
1492      if(this->numPixels>0) this->array = new float[spatSize];
1493      for(int npix=0; npix<spatSize; npix++)
1494        this->array[npix] = Array[channel*spatSize + npix];
1495    }
1496  }
1497  //--------------------------------------------------------------------
1498
1499  void Image::extractImage(Cube &cube, long channel)
1500  {
1501    /**
1502     *  A function to extract a 2-D image from Cube class.
1503     *  The image extracted is the one lying in the channel referenced
1504     *    by the second argument.
1505     *  The extracted image is stored in the pixel array Image::array.
1506     * \param cube The Cube containing the pixel values, from which the image is extracted.
1507     * \param channel The spectral channel that contains the desired image.
1508     */
1509    long spatSize = cube.getDimX()*cube.getDimY();
1510    if((channel<0)||(channel>=cube.getDimZ()))
1511      duchampError("Image::extractImage",
1512                   "Requested channel outside allowed range. Cannot save.");
1513    else if(spatSize != this->numPixels)
1514      duchampError("Image::extractImage",
1515                   "Input array different size to existing array. Cannot save.");
1516    else {
1517      if(this->numPixels>0) delete [] array;
1518      this->numPixels = spatSize;
1519      if(this->numPixels>0) this->array = new float[spatSize];
1520      for(int npix=0; npix<spatSize; npix++)
1521        this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1522    }
1523  }
1524  //--------------------------------------------------------------------
1525
1526  void Image::removeMW()
1527  {
1528    /**
1529     *  A function to remove the Milky Way range of channels from a 1-D spectrum.
1530     *  The array in this Image is assumed to be 1-D, with only the first axisDim
1531     *    equal to 1.
1532     *  The values of the MW channels are set to 0, unless they are BLANK.
1533     */
1534    if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1535      for(int z=0;z<this->axisDim[0];z++){
1536        if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1537      }
1538    }
1539  }
1540
1541}
Note: See TracBrowser for help on using the repository browser.