source: tags/release-1.1.5/src/Cubes/cubes.cc @ 1441

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

Added another allocation flag, this time for the pixel array itself.

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