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

Last change on this file since 582 was 582, checked in by MatthewWhiting, 15 years ago

Separating out the functionality of the searching from the Image classes, making the search functions more generic. They now accept just a vector of bools, indicating "detection" or not. The calling functions in the classes have been renamed to findSources1D (was spectrumDetect()) and findSources2D (was lutz_detect()).

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