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

Last change on this file since 521 was 519, checked in by MatthewWhiting, 16 years ago

Minor changes.

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