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

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

Changing the documentation comments to match the askapsoft style. Also have split ChanMap? and Object3D into separate files.

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