source: tags/release-1.1.10/src/Cubes/cubes.cc @ 1112

Last change on this file since 1112 was 762, checked in by MatthewWhiting, 14 years ago

Changing this back - need to calculate both median & MADFM as we're using residuals to measure spread, but original array values to measure mean/median.

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