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

Last change on this file since 782 was 777, checked in by MatthewWhiting, 14 years ago

Improved reporting via a progress bar for parameter calcs, and getting the threshold signs correct when negative.

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