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

Last change on this file was 700, checked in by MatthewWhiting, 14 years ago

Making use of the returned OUTCOME values.

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