source: tags/release-1.1.8/src/Cubes/cubes.cc @ 1391

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

Didn't include [539] in the previous commit -- done so now.

File size: 54.4 KB
Line 
1// -----------------------------------------------------------------------
2// cubes.cc: Member functions for the DataArray, Cube and Image classes.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <unistd.h>
29#include <iostream>
30#include <iomanip>
31#include <vector>
32#include <algorithm>
33#include <string>
34#include <math.h>
35
36#include <wcslib/wcs.h>
37
38#include <duchamp/pgheader.hh>
39
40#include <duchamp/duchamp.hh>
41#include <duchamp/param.hh>
42#include <duchamp/fitsHeader.hh>
43#include <duchamp/Cubes/cubes.hh>
44#include <duchamp/PixelMap/Voxel.hh>
45#include <duchamp/PixelMap/Object3D.hh>
46#include <duchamp/Detection/detection.hh>
47#include <duchamp/Detection/columns.hh>
48#include <duchamp/Utils/utils.hh>
49#include <duchamp/Utils/mycpgplot.hh>
50#include <duchamp/Utils/Statistics.hh>
51
52using namespace mycpgplot;
53using namespace Statistics;
54using namespace PixelInfo;
55
56#ifdef TEST_DEBUG
57const bool TESTING=true;
58#else
59const bool TESTING=false;
60#endif
61
62namespace duchamp
63{
64
65  using namespace Column;
66
67  /****************************************************************/
68  ///////////////////////////////////////////////////
69  //// Functions for DataArray class:
70  ///////////////////////////////////////////////////
71
72  DataArray::DataArray(){
73    /// 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(unsigned 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(unsigned 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(long(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(unsigned 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;
827      if(this->par.getFlagGrowth()){
828        std::cout << " and growing to threshold of: ";
829        if(this->par.getFlagUserGrowthThreshold()) std::cout << this->par.getGrowthThreshold();
830        else std::cout << this->Stats.snrToValue(this->par.getGrowthCut());
831      }
832      std::cout << std::endl;
833    }
834
835  }
836  //--------------------------------------------------------------------
837
838  void Cube::setupFDR()
839  {
840    /// @details
841    ///  Call the setupFDR(float *) function on the pixel array of the
842    ///  cube. This is the usual way of running it.
843    ///
844    ///  However, if we are in smoothing mode, we calculate the FDR
845    ///  parameters using the recon array, which holds the smoothed
846    ///  data. Gives an error message if the reconExists flag is not set.
847
848    if(this->par.getFlagSmooth())
849      if(this->reconExists) this->setupFDR(this->recon);
850      else{
851        duchampError("setupFDR",
852                     "Smoothing not done properly! Using original array for defining threshold.\n");
853        this->setupFDR(this->array);
854      }
855    else if( this->par.getFlagATrous() ){
856      this->setupFDR(this->recon);
857    }
858    else{
859      this->setupFDR(this->array);
860    }
861  }
862  //--------------------------------------------------------------------
863
864  void Cube::setupFDR(float *input)
865  {
866    ///   @details
867    ///   Determines the critical Probability value for the False
868    ///   Discovery Rate detection routine. All pixels in the given arry
869    ///   with Prob less than this value will be considered detections.
870    ///
871    ///   Note that the Stats of the cube need to be calculated first.
872    ///
873    ///   The Prob here is the probability, assuming a Normal
874    ///   distribution, of obtaining a value as high or higher than the
875    ///   pixel value (ie. only the positive tail of the PDF).
876    ///
877    ///   The probabilities are calculated using the
878    ///   StatsContainer::getPValue(), which calculates the z-statistic,
879    ///   and then the probability via
880    ///   \f$0.5\operatorname{erfc}(z/\sqrt{2})\f$ -- giving the positive
881    ///   tail probability.
882
883    // first calculate p-value for each pixel -- assume Gaussian for now.
884
885    float *orderedP = new float[this->numPixels];
886    int count = 0;
887    for(int x=0;x<this->axisDim[0];x++){
888      for(int y=0;y<this->axisDim[1];y++){
889        for(int z=0;z<this->axisDim[2];z++){
890          int pix = z * this->axisDim[0]*this->axisDim[1] +
891            y*this->axisDim[0] + x;
892
893          if(!(this->par.isBlank(this->array[pix])) && !this->par.isInMW(z)){
894            // only look at non-blank, valid pixels
895            //            orderedP[count++] = this->Stats.getPValue(this->array[pix]);
896            orderedP[count++] = this->Stats.getPValue(input[pix]);
897          }
898        }
899      }
900    }
901
902    // now order them
903    std::stable_sort(orderedP,orderedP+count);
904 
905    // now find the maximum P value.
906    int max = 0;
907    float cN = 0.;
908    // Calculate number of correlated pixels. Assume all spatial
909    // pixels within the beam are correlated, and multiply this by the
910    // number of correlated pixels as determined by the parameter set.
911    int numVox = int(ceil(this->par.getBeamSize()));
912    if(this->head.canUseThirdAxis()) numVox *= this->par.getFDRnumCorChan();
913    for(int psfCtr=1;psfCtr<=numVox;psfCtr++) cN += 1./float(psfCtr);
914
915    double slope = this->par.getAlpha()/cN;
916    for(int loopCtr=0;loopCtr<count;loopCtr++) {
917      if( orderedP[loopCtr] < (slope * double(loopCtr+1)/ double(count)) ){
918        max = loopCtr;
919      }
920    }
921
922    this->Stats.setPThreshold( orderedP[max] );
923
924
925    // Find real value of the P threshold by finding the inverse of the
926    //  error function -- root finding with brute force technique
927    //  (relatively slow, but we only do it once).
928    double zStat     = 0.;
929    double deltaZ    = 0.1;
930    double tolerance = 1.e-6;
931    double initial   = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
932    do{
933      zStat+=deltaZ;
934      double current = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
935      if((initial*current)<0.){
936        zStat-=deltaZ;
937        deltaZ/=2.;
938      }
939    }while(deltaZ>tolerance);
940    this->Stats.setThreshold( zStat*this->Stats.getSpread() +
941                              this->Stats.getMiddle() );
942
943    ///////////////////////////
944    //   if(TESTING){
945    //     std::stringstream ss;
946    //     float *xplot = new float[2*max];
947    //     for(int i=0;i<2*max;i++) xplot[i]=float(i)/float(count);
948    //     cpgopen("latestFDR.ps/vcps");
949    //     cpgpap(8.,1.);
950    //     cpgslw(3);
951    //     cpgenv(0,float(2*max)/float(count),0,orderedP[2*max],0,0);
952    //     cpglab("i/N (index)", "p-value","");
953    //     cpgpt(2*max,xplot,orderedP,DOT);
954
955    //     ss.str("");
956    //     ss << "\\gm = " << this->Stats.getMiddle();
957    //     cpgtext(max/(4.*count),0.9*orderedP[2*max],ss.str().c_str());
958    //     ss.str("");
959    //     ss << "\\gs = " << this->Stats.getSpread();
960    //     cpgtext(max/(4.*count),0.85*orderedP[2*max],ss.str().c_str());
961    //     ss.str("");
962    //     ss << "Slope = " << slope;
963    //     cpgtext(max/(4.*count),0.8*orderedP[2*max],ss.str().c_str());
964    //     ss.str("");
965    //     ss << "Alpha = " << this->par.getAlpha();
966    //     cpgtext(max/(4.*count),0.75*orderedP[2*max],ss.str().c_str());
967    //     ss.str("");
968    //     ss << "c\\dN\\u = " << cN;
969    //     cpgtext(max/(4.*count),0.7*orderedP[2*max],ss.str().c_str());
970    //     ss.str("");
971    //     ss << "max = "<<max << " (out of " << count << ")";
972    //     cpgtext(max/(4.*count),0.65*orderedP[2*max],ss.str().c_str());
973    //     ss.str("");
974    //     ss << "Threshold = "<<zStat*this->Stats.getSpread()+this->Stats.getMiddle();
975    //     cpgtext(max/(4.*count),0.6*orderedP[2*max],ss.str().c_str());
976 
977    //     cpgslw(1);
978    //     cpgsci(RED);
979    //     cpgmove(0,0);
980    //     cpgdraw(1,slope);
981    //     cpgsci(BLUE);
982    //     cpgsls(DOTTED);
983    //     cpgmove(0,orderedP[max]);
984    //     cpgdraw(2*max/float(count),orderedP[max]);
985    //     cpgmove(max/float(count),0);
986    //     cpgdraw(max/float(count),orderedP[2*max]);
987    //     cpgsci(GREEN);
988    //     cpgsls(SOLID);
989    //     for(int i=1;i<=10;i++) {
990    //       ss.str("");
991    //       ss << float(i)/2. << "\\gs";
992    //       float prob = 0.5*erfc((float(i)/2.)/M_SQRT2);
993    //       cpgtick(0, 0, 0, orderedP[2*max],
994    //        prob/orderedP[2*max],
995    //        0, 1, 1.5, 90., ss.str().c_str());
996    //     }
997    //     cpgend();
998    //     delete [] xplot;
999    //   }
1000    delete [] orderedP;
1001
1002  }
1003  //--------------------------------------------------------------------
1004
1005  bool Cube::isDetection(long x, long y, long z)
1006  {
1007    ///  @details
1008    /// Is a given voxel at position (x,y,z) a detection, based on the statistics
1009    ///  in the Cube's StatsContainer?
1010    /// If the pixel lies outside the valid range for the data array,
1011    /// return false.
1012    /// \param x X-value of the Cube's voxel to be tested.
1013    /// \param y Y-value of the Cube's voxel to be tested.
1014    /// \param z Z-value of the Cube's voxel to be tested.
1015
1016    long voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
1017    return DataArray::isDetection(array[voxel]);
1018  }
1019  //--------------------------------------------------------------------
1020
1021  void Cube::calcObjectFluxes()
1022  {
1023    /// @details
1024    ///  A function to calculate the fluxes and centroids for each
1025    ///  object in the Cube's list of detections. Uses
1026    ///  Detection::calcFluxes() for each object.
1027
1028    std::vector<Detection>::iterator obj;
1029    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1030      obj->calcFluxes(this->array, this->axisDim);
1031      if(this->par.getFlagUserThreshold())
1032        obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1033      else
1034        obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1035    }
1036  }
1037  //--------------------------------------------------------------------
1038
1039  void Cube::calcObjectWCSparams()
1040  {
1041    ///  @details
1042    ///  A function that calculates the WCS parameters for each object in the
1043    ///  Cube's list of detections.
1044    ///  Each object gets an ID number assigned to it (which is simply its order
1045    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1046
1047    std::vector<Detection>::iterator obj;
1048    int ct=0;
1049    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1050      obj->setID(ct++);
1051      obj->setCentreType(this->par.getPixelCentre());
1052      obj->calcFluxes(this->array,this->axisDim);
1053      //      obj->calcWCSparams(this->array,this->axisDim,this->head);
1054      obj->calcWCSparams(this->head);
1055      obj->calcIntegFlux(this->array,this->axisDim,this->head);
1056   
1057      if(this->par.getFlagUserThreshold())
1058        obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1059      else
1060        obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1061
1062    } 
1063
1064    if(!this->head.isWCS()){
1065      // if the WCS is bad, set the object names to Obj01 etc
1066      int numspaces = int(log10(this->objectList->size())) + 1;
1067      std::stringstream ss;
1068      for(unsigned int i=0;i<this->objectList->size();i++){
1069        ss.str("");
1070        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1071        obj->setName(ss.str());
1072      }
1073    }
1074 
1075  }
1076  //--------------------------------------------------------------------
1077
1078  void Cube::calcObjectWCSparams(std::vector< std::vector<PixelInfo::Voxel> > bigVoxList)
1079  {
1080    ///  @details
1081    ///  A function that calculates the WCS parameters for each object in the
1082    ///  Cube's list of detections.
1083    ///  Each object gets an ID number assigned to it (which is simply its order
1084    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1085    ///
1086    ///  This version uses vectors of Voxels to define the fluxes.
1087    ///
1088    /// \param bigVoxList A vector of vectors of Voxels, with the same
1089    /// number of elements as this->objectList, where each element is a
1090    /// vector of Voxels corresponding to the same voxels in each
1091    /// detection and indicating the flux of each voxel.
1092 
1093    std::vector<Detection>::iterator obj;
1094    int ct=0;
1095    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1096      obj->setID(ct+1);
1097      obj->setCentreType(this->par.getPixelCentre());
1098      obj->calcFluxes(bigVoxList[ct]);
1099      obj->calcWCSparams(this->head);
1100      obj->calcIntegFlux(bigVoxList[ct],this->head);
1101   
1102      if(this->par.getFlagUserThreshold())
1103        obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1104      else
1105        obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1106
1107      ct++;
1108    } 
1109
1110    if(!this->head.isWCS()){
1111      // if the WCS is bad, set the object names to Obj01 etc
1112      int numspaces = int(log10(this->objectList->size())) + 1;
1113      std::stringstream ss;
1114      for(unsigned int i=0;i<this->objectList->size();i++){
1115        ss.str("");
1116        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1117        obj->setName(ss.str());
1118      }
1119    }
1120 
1121  }
1122  //--------------------------------------------------------------------
1123
1124  void Cube::updateDetectMap()
1125  {
1126    /// @details
1127    ///  A function that, for each detected object in the cube's list, increments
1128    ///   the cube's detection map by the required amount at each pixel.
1129
1130    Scan temp;
1131    for(unsigned int obj=0;obj<this->objectList->size();obj++){
1132      long numZ=this->objectList->at(obj).pixels().getNumChanMap();
1133      for(int iz=0;iz<numZ;iz++){ // for each channel map
1134        Object2D *chanmap = new Object2D;
1135        *chanmap = this->objectList->at(obj).pixels().getChanMap(iz).getObject();
1136        for(int iscan=0;iscan<chanmap->getNumScan();iscan++){
1137          temp = chanmap->getScan(iscan);
1138          for(int x=temp.getX(); x <= temp.getXmax(); x++)
1139            this->detectMap[temp.getY()*this->axisDim[0] + x]++;
1140        } // end of loop over scans
1141        delete chanmap;
1142      } // end of loop over channel maps
1143    } // end of loop over objects.
1144
1145  }
1146  //--------------------------------------------------------------------
1147
1148  void Cube::updateDetectMap(Detection obj)
1149  {
1150    ///  @details
1151    ///  A function that, for the given object, increments the cube's
1152    ///  detection map by the required amount at each pixel.
1153    ///
1154    ///  \param obj A Detection object that is being incorporated into the map.
1155
1156    Scan temp;
1157    long numZ=obj.pixels().getNumChanMap();
1158    for(int iz=0;iz<numZ;iz++){ // for each channel map
1159      Object2D chanmap = obj.pixels().getChanMap(iz).getObject();
1160      for(int iscan=0;iscan<chanmap.getNumScan();iscan++){
1161        temp = chanmap.getScan(iscan);
1162        for(int x=temp.getX(); x <= temp.getXmax(); x++)
1163          this->detectMap[temp.getY()*this->axisDim[0] + x]++;
1164      } // end of loop over scans
1165    } // end of loop over channel maps
1166
1167  }
1168  //--------------------------------------------------------------------
1169
1170  float Cube::enclosedFlux(Detection obj)
1171  {
1172    ///  @details
1173    ///   A function to calculate the flux enclosed by the range
1174    ///    of pixels detected in the object obj (not necessarily all
1175    ///    pixels will have been detected).
1176    ///
1177    ///   \param obj The Detection under consideration.
1178
1179    obj.calcFluxes(this->array, this->axisDim);
1180    int xsize = obj.getXmax()-obj.getXmin()+1;
1181    int ysize = obj.getYmax()-obj.getYmin()+1;
1182    int zsize = obj.getZmax()-obj.getZmin()+1;
1183    std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1184    for(int x=0;x<xsize;x++){
1185      for(int y=0;y<ysize;y++){
1186        for(int z=0;z<zsize;z++){
1187          fluxArray[x+y*xsize+z*ysize*xsize] =
1188            this->getPixValue(x+obj.getXmin(),
1189                              y+obj.getYmin(),
1190                              z+obj.getZmin());
1191          if(this->par.getFlagNegative())
1192            fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1193        }
1194      }
1195    }
1196    float sum = 0.;
1197    for(unsigned int i=0;i<fluxArray.size();i++)
1198      if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1199    return sum;
1200  }
1201  //--------------------------------------------------------------------
1202
1203  void Cube::setupColumns()
1204  {
1205    /// @details
1206    ///  A front-end to the two setup routines in columns.cc. 
1207    ///
1208    ///  This first gets the starting precisions, which may be from
1209    ///  the input parameters. It then sets up the columns (calculates
1210    ///  their widths and precisions and so on based on the values
1211    ///  within). The precisions are also stored in each Detection
1212    ///  object.
1213    ///
1214    ///  Need to have called calcObjectWCSparams() somewhere
1215    ///  beforehand.
1216
1217    std::vector<Detection>::iterator obj;
1218    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1219      obj->setVelPrec( this->par.getPrecVel() );
1220      obj->setFpeakPrec( this->par.getPrecFlux() );
1221      obj->setXYZPrec( Column::prXYZ );
1222      obj->setPosPrec( Column::prWPOS );
1223      obj->setFintPrec( this->par.getPrecFlux() );
1224      obj->setSNRPrec( this->par.getPrecSNR() );
1225    }
1226 
1227    this->fullCols.clear();
1228    this->fullCols = getFullColSet(*(this->objectList), this->head);
1229
1230    this->logCols.clear();
1231    this->logCols = getLogColSet(*(this->objectList), this->head);
1232
1233    int vel,fpeak,fint,pos,xyz,snr;
1234    vel = fullCols[VEL].getPrecision();
1235    fpeak = fullCols[FPEAK].getPrecision();
1236    snr = fullCols[SNRPEAK].getPrecision();
1237    xyz = fullCols[X].getPrecision();
1238    xyz = std::max(xyz, fullCols[Y].getPrecision());
1239    xyz = std::max(xyz, fullCols[Z].getPrecision());
1240    if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1241    else fint = fullCols[FTOT].getPrecision();
1242    pos = fullCols[WRA].getPrecision();
1243    pos = std::max(pos, fullCols[WDEC].getPrecision());
1244 
1245    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1246      obj->setVelPrec(vel);
1247      obj->setFpeakPrec(fpeak);
1248      obj->setXYZPrec(xyz);
1249      obj->setPosPrec(pos);
1250      obj->setFintPrec(fint);
1251      obj->setSNRPrec(snr);
1252    }
1253
1254  }
1255  //--------------------------------------------------------------------
1256
1257  bool Cube::objAtSpatialEdge(Detection obj)
1258  {
1259    ///  @details
1260    ///   A function to test whether the object obj
1261    ///    lies at the edge of the cube's spatial field --
1262    ///    either at the boundary, or next to BLANKs.
1263    ///
1264    ///   \param obj The Detection under consideration.
1265
1266    bool atEdge = false;
1267
1268    unsigned int pix = 0;
1269    std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1270    while(!atEdge && pix<voxlist.size()){
1271      // loop over each pixel in the object, until we find an edge pixel.
1272      for(int dx=-1;dx<=1;dx+=2){
1273        if( ((voxlist[pix].getX()+dx)<0) ||
1274            ((voxlist[pix].getX()+dx)>=this->axisDim[0]) )
1275          atEdge = true;
1276        else if(this->isBlank(voxlist[pix].getX()+dx,
1277                              voxlist[pix].getY(),
1278                              voxlist[pix].getZ()))
1279          atEdge = true;
1280      }
1281      for(int dy=-1;dy<=1;dy+=2){
1282        if( ((voxlist[pix].getY()+dy)<0) ||
1283            ((voxlist[pix].getY()+dy)>=this->axisDim[1]) )
1284          atEdge = true;
1285        else if(this->isBlank(voxlist[pix].getX(),
1286                              voxlist[pix].getY()+dy,
1287                              voxlist[pix].getZ()))
1288          atEdge = true;
1289      }
1290      pix++;
1291    }
1292
1293    return atEdge;
1294  }
1295  //--------------------------------------------------------------------
1296
1297  bool Cube::objAtSpectralEdge(Detection obj)
1298  {
1299    ///   @details
1300    ///   A function to test whether the object obj
1301    ///    lies at the edge of the cube's spectral extent --
1302    ///    either at the boundary, or next to BLANKs.
1303    ///
1304    ///   \param obj The Detection under consideration.
1305
1306    bool atEdge = false;
1307
1308    unsigned int pix = 0;
1309    std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1310    while(!atEdge && pix<voxlist.size()){
1311      // loop over each pixel in the object, until we find an edge pixel.
1312      for(int dz=-1;dz<=1;dz+=2){
1313        if( ((voxlist[pix].getZ()+dz)<0) ||
1314            ((voxlist[pix].getZ()+dz)>=this->axisDim[2]))
1315          atEdge = true;
1316        else if(this->isBlank(voxlist[pix].getX(),
1317                              voxlist[pix].getY(),
1318                              voxlist[pix].getZ()+dz))
1319          atEdge = true;
1320      }
1321      pix++;
1322    }
1323
1324    return atEdge;
1325  }
1326  //--------------------------------------------------------------------
1327
1328  void Cube::setObjectFlags()
1329  {
1330    /// @details
1331    ///   A function to set any warning flags for all the detected objects
1332    ///    associated with the cube.
1333    ///   Flags to be looked for:
1334    ///    <ul><li> Negative enclosed flux (N)
1335    ///        <li> Detection at edge of field (spatially) (E)
1336    ///        <li> Detection at edge of spectral region (S)
1337    ///    </ul>
1338
1339    std::vector<Detection>::iterator obj;
1340    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1341
1342      if( this->enclosedFlux(*obj) < 0. ) 
1343        obj->addToFlagText("N");
1344
1345      if( this->objAtSpatialEdge(*obj) )
1346        obj->addToFlagText("E");
1347
1348      if( this->objAtSpectralEdge(*obj) && (this->axisDim[2] > 2))
1349        obj->addToFlagText("S");
1350
1351      if(obj->getFlagText()=="") obj->addToFlagText("-");
1352
1353    }
1354
1355  }
1356  //--------------------------------------------------------------------
1357
1358
1359
1360  /****************************************************************/
1361  /////////////////////////////////////////////////////////////
1362  //// Functions for Image class
1363  /////////////////////////////////////////////////////////////
1364
1365  Image::Image(long size)
1366  {
1367    // need error handling in case size<0 !!!
1368    this->numPixels = this->numDim = 0;
1369    if(size<0)
1370      duchampError("Image(size)","Negative size -- could not define Image");
1371    else{
1372      if(size>0 && !this->arrayAllocated){
1373        this->array = new float[size];
1374        this->arrayAllocated = true;
1375      }
1376      this->numPixels = size;
1377      this->axisDim = new long[2];
1378      this->axisDimAllocated = true;
1379      this->numDim = 2;
1380    }
1381  }
1382  //--------------------------------------------------------------------
1383
1384  Image::Image(long *dimensions)
1385  {
1386    this->numPixels = this->numDim = 0;
1387    int size = dimensions[0] * dimensions[1];
1388    if(size<0)
1389      duchampError("Image(dimArray)","Negative size -- could not define Image");
1390    else{
1391      this->numPixels = size;
1392      if(size>0){
1393        this->array = new float[size];
1394        this->arrayAllocated = true;
1395      }
1396      this->numDim=2;
1397      this->axisDim = new long[2];
1398      this->axisDimAllocated = true;
1399      for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1400    }
1401  }
1402  //--------------------------------------------------------------------
1403  //--------------------------------------------------------------------
1404
1405  void Image::saveArray(float *input, long size)
1406  {
1407    /// @details
1408    /// Saves the array in input to the pixel array Image::array.
1409    /// The size of the array given must be the same as the current number of
1410    /// pixels, else an error message is returned and nothing is done.
1411    /// \param input The array of values to be saved.
1412    /// \param size The size of input.
1413
1414    if(size != this->numPixels)
1415      duchampError("Image::saveArray",
1416                   "Input array different size to existing array. Cannot save.");
1417    else {
1418      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1419      this->numPixels = size;
1420      if(this->numPixels>0){
1421        this->array = new float[size];
1422        this->arrayAllocated = true;
1423        for(int i=0;i<size;i++) this->array[i] = input[i];
1424      }
1425    }
1426  }
1427  //--------------------------------------------------------------------
1428
1429  void Image::extractSpectrum(float *Array, long *dim, long pixel)
1430  {
1431    /// @details
1432    ///  A function to extract a 1-D spectrum from a 3-D array.
1433    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1434    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1435    ///    by the third argument.
1436    ///  The extracted spectrum is stored in the pixel array Image::array.
1437    /// \param Array The array containing the pixel values, from which
1438    ///               the spectrum is extracted.
1439    /// \param dim The array of dimension values.
1440    /// \param pixel The spatial pixel that contains the desired spectrum.
1441
1442    if((pixel<0)||(pixel>=dim[0]*dim[1]))
1443      duchampError("Image::extractSpectrum",
1444                   "Requested spatial pixel outside allowed range. Cannot save.");
1445    else if(dim[2] != this->numPixels)
1446      duchampError("Image::extractSpectrum",
1447                   "Input array different size to existing array. Cannot save.");
1448    else {
1449      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1450      this->numPixels = dim[2];
1451      if(this->numPixels>0){
1452        this->array = new float[dim[2]];
1453        this->arrayAllocated = true;
1454        for(int z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1455      }
1456    }
1457  }
1458  //--------------------------------------------------------------------
1459
1460  void Image::extractSpectrum(Cube &cube, long pixel)
1461  {
1462    /// @details
1463    ///  A function to extract a 1-D spectrum from a Cube class
1464    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1465    ///    by the second argument.
1466    ///  The extracted spectrum is stored in the pixel array Image::array.
1467    /// \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1468    /// \param pixel The spatial pixel that contains the desired spectrum.
1469
1470    long zdim = cube.getDimZ();
1471    long spatSize = cube.getDimX()*cube.getDimY();
1472    if((pixel<0)||(pixel>=spatSize))
1473      duchampError("Image::extractSpectrum",
1474                   "Requested spatial pixel outside allowed range. Cannot save.");
1475    else if(zdim != this->numPixels)
1476      duchampError("Image::extractSpectrum",
1477                   "Input array different size to existing array. Cannot save.");
1478    else {
1479      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1480      this->numPixels = zdim;
1481      if(this->numPixels>0){
1482        this->array = new float[zdim];
1483        this->arrayAllocated = true;
1484        for(int z=0;z<zdim;z++)
1485          this->array[z] = cube.getPixValue(z*spatSize + pixel);
1486      }
1487    }
1488  }
1489  //--------------------------------------------------------------------
1490
1491  void Image::extractImage(float *Array, long *dim, long channel)
1492  {
1493    /// @details
1494    ///  A function to extract a 2-D image from a 3-D array.
1495    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1496    ///  The dimensions of the array are in the dim[] array.
1497    ///  The image extracted is the one lying in the channel referenced
1498    ///    by the third argument.
1499    ///  The extracted image is stored in the pixel array Image::array.
1500    /// \param Array The array containing the pixel values, from which the image is extracted.
1501    /// \param dim The array of dimension values.
1502    /// \param channel The spectral channel that contains the desired image.
1503
1504    long spatSize = dim[0]*dim[1];
1505    if((channel<0)||(channel>=dim[2]))
1506      duchampError("Image::extractImage",
1507                   "Requested channel outside allowed range. Cannot save.");
1508    else if(spatSize != this->numPixels)
1509      duchampError("Image::extractImage",
1510                   "Input array different size to existing array. Cannot save.");
1511    else {
1512      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1513      this->numPixels = spatSize;
1514      if(this->numPixels>0){
1515        this->array = new float[spatSize];
1516        this->arrayAllocated = true;
1517        for(int npix=0; npix<spatSize; npix++)
1518          this->array[npix] = Array[channel*spatSize + npix];
1519      }
1520    }
1521  }
1522  //--------------------------------------------------------------------
1523
1524  void Image::extractImage(Cube &cube, long channel)
1525  {
1526    /// @details
1527    ///  A function to extract a 2-D image from Cube class.
1528    ///  The image extracted is the one lying in the channel referenced
1529    ///    by the second argument.
1530    ///  The extracted image is stored in the pixel array Image::array.
1531    /// \param cube The Cube containing the pixel values, from which the image is extracted.
1532    /// \param channel The spectral channel that contains the desired image.
1533
1534    long spatSize = cube.getDimX()*cube.getDimY();
1535    if((channel<0)||(channel>=cube.getDimZ()))
1536      duchampError("Image::extractImage",
1537                   "Requested channel outside allowed range. Cannot save.");
1538    else if(spatSize != this->numPixels)
1539      duchampError("Image::extractImage",
1540                   "Input array different size to existing array. Cannot save.");
1541    else {
1542      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1543      this->numPixels = spatSize;
1544      if(this->numPixels>0){
1545        this->array = new float[spatSize];
1546        this->arrayAllocated = true;
1547        for(int npix=0; npix<spatSize; npix++)
1548          this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1549      }
1550    }
1551  }
1552  //--------------------------------------------------------------------
1553
1554  void Image::removeMW()
1555  {
1556    /// @details
1557    ///  A function to remove the Milky Way range of channels from a 1-D spectrum.
1558    ///  The array in this Image is assumed to be 1-D, with only the first axisDim
1559    ///    equal to 1.
1560    ///  The values of the MW channels are set to 0, unless they are BLANK.
1561
1562    if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1563      for(int z=0;z<this->axisDim[0];z++){
1564        if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1565      }
1566    }
1567  }
1568
1569}
Note: See TracBrowser for help on using the repository browser.