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

Last change on this file since 788 was 788, checked in by MatthewWhiting, 13 years ago

First part of dealing with #110. Have defined a Beam & DuchampBeam? class and use these to hold the beam information. FitsHeader? holds the one that we work with, and copies it to Param for use with outputs. Parameters will be taken into account if no header information is present. Still need to add code to deal with the case of neither being present (the beam being EMPTY) and how that affects the integrated flux calculations.

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 beam
1009    int numVox = int(ceil(this->head.beam().area()));
1010    // int numVox = int(ceil(this->par.getBeamSize()));
1011    if(this->head.canUseThirdAxis()) numVox *= this->par.getFDRnumCorChan();
1012    for(int psfCtr=1;psfCtr<=numVox;psfCtr++) cN += 1./float(psfCtr);
1013
1014    double slope = this->par.getAlpha()/cN;
1015    for(int loopCtr=0;loopCtr<count;loopCtr++) {
1016      if( orderedP[loopCtr] < (slope * double(loopCtr+1)/ double(count)) ){
1017        max = loopCtr;
1018      }
1019    }
1020
1021    this->Stats.setPThreshold( orderedP[max] );
1022
1023
1024    // Find real value of the P threshold by finding the inverse of the
1025    //  error function -- root finding with brute force technique
1026    //  (relatively slow, but we only do it once).
1027    double zStat     = 0.;
1028    double deltaZ    = 0.1;
1029    double tolerance = 1.e-6;
1030    double initial   = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1031    do{
1032      zStat+=deltaZ;
1033      double current = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1034      if((initial*current)<0.){
1035        zStat-=deltaZ;
1036        deltaZ/=2.;
1037      }
1038    }while(deltaZ>tolerance);
1039    this->Stats.setThreshold( zStat*this->Stats.getSpread() +
1040                              this->Stats.getMiddle() );
1041
1042    ///////////////////////////
1043    //   if(TESTING){
1044    //     std::stringstream ss;
1045    //     float *xplot = new float[2*max];
1046    //     for(int i=0;i<2*max;i++) xplot[i]=float(i)/float(count);
1047    //     cpgopen("latestFDR.ps/vcps");
1048    //     cpgpap(8.,1.);
1049    //     cpgslw(3);
1050    //     cpgenv(0,float(2*max)/float(count),0,orderedP[2*max],0,0);
1051    //     cpglab("i/N (index)", "p-value","");
1052    //     cpgpt(2*max,xplot,orderedP,DOT);
1053
1054    //     ss.str("");
1055    //     ss << "\\gm = " << this->Stats.getMiddle();
1056    //     cpgtext(max/(4.*count),0.9*orderedP[2*max],ss.str().c_str());
1057    //     ss.str("");
1058    //     ss << "\\gs = " << this->Stats.getSpread();
1059    //     cpgtext(max/(4.*count),0.85*orderedP[2*max],ss.str().c_str());
1060    //     ss.str("");
1061    //     ss << "Slope = " << slope;
1062    //     cpgtext(max/(4.*count),0.8*orderedP[2*max],ss.str().c_str());
1063    //     ss.str("");
1064    //     ss << "Alpha = " << this->par.getAlpha();
1065    //     cpgtext(max/(4.*count),0.75*orderedP[2*max],ss.str().c_str());
1066    //     ss.str("");
1067    //     ss << "c\\dN\\u = " << cN;
1068    //     cpgtext(max/(4.*count),0.7*orderedP[2*max],ss.str().c_str());
1069    //     ss.str("");
1070    //     ss << "max = "<<max << " (out of " << count << ")";
1071    //     cpgtext(max/(4.*count),0.65*orderedP[2*max],ss.str().c_str());
1072    //     ss.str("");
1073    //     ss << "Threshold = "<<zStat*this->Stats.getSpread()+this->Stats.getMiddle();
1074    //     cpgtext(max/(4.*count),0.6*orderedP[2*max],ss.str().c_str());
1075 
1076    //     cpgslw(1);
1077    //     cpgsci(RED);
1078    //     cpgmove(0,0);
1079    //     cpgdraw(1,slope);
1080    //     cpgsci(BLUE);
1081    //     cpgsls(DOTTED);
1082    //     cpgmove(0,orderedP[max]);
1083    //     cpgdraw(2*max/float(count),orderedP[max]);
1084    //     cpgmove(max/float(count),0);
1085    //     cpgdraw(max/float(count),orderedP[2*max]);
1086    //     cpgsci(GREEN);
1087    //     cpgsls(SOLID);
1088    //     for(int i=1;i<=10;i++) {
1089    //       ss.str("");
1090    //       ss << float(i)/2. << "\\gs";
1091    //       float prob = 0.5*erfc((float(i)/2.)/M_SQRT2);
1092    //       cpgtick(0, 0, 0, orderedP[2*max],
1093    //        prob/orderedP[2*max],
1094    //        0, 1, 1.5, 90., ss.str().c_str());
1095    //     }
1096    //     cpgend();
1097    //     delete [] xplot;
1098    //   }
1099    delete [] orderedP;
1100
1101  }
1102  //--------------------------------------------------------------------
1103
1104  void Cube::Search(bool verboseFlag)
1105  {
1106    /// @details
1107    /// This acts as a switching function to select the correct searching function based on the user's parameters.
1108    /// @param verboseFlag If true, text is written to stdout describing the search function being used.
1109    if(this->par.getFlagATrous()){
1110      if(verboseFlag) std::cout<<"Commencing search in reconstructed cube..."<<std::endl;
1111      this->ReconSearch();
1112    } 
1113    else if(this->par.getFlagSmooth()){
1114      if(verboseFlag) std::cout<<"Commencing search in smoothed cube..."<<std::endl;
1115      this->SmoothSearch();
1116    }
1117    else{
1118      if(verboseFlag) std::cout<<"Commencing search in cube..."<<std::endl;
1119      this->CubicSearch();
1120    }
1121
1122  }
1123
1124  bool Cube::isDetection(long x, long y, long z)
1125  {
1126    ///  @details
1127    /// Is a given voxel at position (x,y,z) a detection, based on the statistics
1128    ///  in the Cube's StatsContainer?
1129    /// If the pixel lies outside the valid range for the data array,
1130    /// return false.
1131    /// \param x X-value of the Cube's voxel to be tested.
1132    /// \param y Y-value of the Cube's voxel to be tested.
1133    /// \param z Z-value of the Cube's voxel to be tested.
1134
1135    long voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
1136    return DataArray::isDetection(array[voxel]);
1137  }
1138  //--------------------------------------------------------------------
1139
1140  void Cube::calcObjectFluxes()
1141  {
1142    /// @details
1143    ///  A function to calculate the fluxes and centroids for each
1144    ///  object in the Cube's list of detections. Uses
1145    ///  Detection::calcFluxes() for each object.
1146
1147    std::vector<Detection>::iterator obj;
1148    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1149      obj->calcFluxes(this->array, this->axisDim);
1150      if(this->par.getFlagUserThreshold())
1151        obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1152      else
1153        obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1154    }
1155  }
1156  //--------------------------------------------------------------------
1157
1158  void Cube::calcObjectWCSparams()
1159  {
1160    ///  @details
1161    ///  A function that calculates the WCS parameters for each object in the
1162    ///  Cube's list of detections.
1163    ///  Each object gets an ID number assigned to it (which is simply its order
1164    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1165
1166    std::vector<Detection>::iterator obj;
1167    int ct=0;
1168    ProgressBar bar;
1169    if(this->par.isVerbose()) bar.init(this->objectList->size());
1170    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1171      //      std::cerr << ct << ' ' << this->array << '\n';
1172      if(this->par.isVerbose()) bar.update(ct);
1173      obj->setID(ct++);
1174      if(!obj->hasParams()){
1175        obj->setCentreType(this->par.getPixelCentre());
1176        obj->calcFluxes(this->array,this->axisDim);
1177        //      obj->calcWCSparams(this->array,this->axisDim,this->head);
1178        obj->calcWCSparams(this->head);
1179        obj->calcIntegFlux(this->array,this->axisDim,this->head);
1180       
1181        if(this->par.getFlagUserThreshold())
1182          obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1183        else
1184          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1185      }
1186    } 
1187    if(this->par.isVerbose()) bar.remove();
1188
1189    if(!this->head.isWCS()){
1190      // if the WCS is bad, set the object names to Obj01 etc
1191      int numspaces = int(log10(this->objectList->size())) + 1;
1192      std::stringstream ss;
1193      for(size_t i=0;i<this->objectList->size();i++){
1194        ss.str("");
1195        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1196        this->objectList->at(i).setName(ss.str());
1197      }
1198    }
1199 
1200  }
1201  //--------------------------------------------------------------------
1202
1203  void Cube::calcObjectWCSparams(std::vector< std::vector<PixelInfo::Voxel> > bigVoxList)
1204  {
1205    ///  @details
1206    ///  A function that calculates the WCS parameters for each object in the
1207    ///  Cube's list of detections.
1208    ///  Each object gets an ID number assigned to it (which is simply its order
1209    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1210    ///
1211    ///  This version uses vectors of Voxels to define the fluxes.
1212    ///
1213    /// \param bigVoxList A vector of vectors of Voxels, with the same
1214    /// number of elements as this->objectList, where each element is a
1215    /// vector of Voxels corresponding to the same voxels in each
1216    /// detection and indicating the flux of each voxel.
1217 
1218    std::vector<Detection>::iterator obj;
1219    int ct=0;
1220    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1221      obj->setID(ct+1);
1222      if(!obj->hasParams()){
1223        obj->setCentreType(this->par.getPixelCentre());
1224        obj->calcFluxes(bigVoxList[ct]);
1225        obj->calcWCSparams(this->head);
1226        obj->calcIntegFlux(this->axisDim[2],bigVoxList[ct],this->head);
1227       
1228        if(this->par.getFlagUserThreshold())
1229          obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1230        else
1231          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1232      }
1233      ct++;
1234    } 
1235
1236    if(!this->head.isWCS()){
1237      // if the WCS is bad, set the object names to Obj01 etc
1238      int numspaces = int(log10(this->objectList->size())) + 1;
1239      std::stringstream ss;
1240      for(size_t i=0;i<this->objectList->size();i++){
1241        ss.str("");
1242        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1243        this->objectList->at(i).setName(ss.str());
1244      }
1245    }
1246 
1247  }
1248  //--------------------------------------------------------------------
1249
1250  void Cube::updateDetectMap()
1251  {
1252    /// @details A function that, for each detected object in the
1253    ///  cube's list, increments the cube's detection map by the
1254    ///  required amount at each pixel. Uses
1255    ///  updateDetectMap(Detection).
1256
1257    std::vector<Detection>::iterator obj;
1258    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1259      this->updateDetectMap(*obj);
1260    }
1261
1262  }
1263  //--------------------------------------------------------------------
1264
1265  void Cube::updateDetectMap(Detection obj)
1266  {
1267    ///  @details
1268    ///  A function that, for the given object, increments the cube's
1269    ///  detection map by the required amount at each pixel.
1270    ///
1271    ///  \param obj A Detection object that is being incorporated into the map.
1272
1273    std::vector<Voxel> vlist = obj.getPixelSet();
1274    for(std::vector<Voxel>::iterator vox=vlist.begin();vox<vlist.end();vox++)
1275      this->detectMap[vox->getX()+vox->getY()*this->axisDim[0]]++;
1276
1277  }
1278  //--------------------------------------------------------------------
1279
1280  float Cube::enclosedFlux(Detection obj)
1281  {
1282    ///  @details
1283    ///   A function to calculate the flux enclosed by the range
1284    ///    of pixels detected in the object obj (not necessarily all
1285    ///    pixels will have been detected).
1286    ///
1287    ///   \param obj The Detection under consideration.
1288
1289    obj.calcFluxes(this->array, this->axisDim);
1290    int xsize = obj.getXmax()-obj.getXmin()+1;
1291    int ysize = obj.getYmax()-obj.getYmin()+1;
1292    int zsize = obj.getZmax()-obj.getZmin()+1;
1293    std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1294    for(int x=0;x<xsize;x++){
1295      for(int y=0;y<ysize;y++){
1296        for(int z=0;z<zsize;z++){
1297          fluxArray[x+y*xsize+z*ysize*xsize] =
1298            this->getPixValue(x+obj.getXmin(),
1299                              y+obj.getYmin(),
1300                              z+obj.getZmin());
1301          if(this->par.getFlagNegative())
1302            fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1303        }
1304      }
1305    }
1306    float sum = 0.;
1307    for(size_t i=0;i<fluxArray.size();i++)
1308      if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1309    return sum;
1310  }
1311  //--------------------------------------------------------------------
1312
1313  void Cube::setupColumns()
1314  {
1315    /// @details
1316    ///  A front-end to the two setup routines in columns.cc. 
1317    ///
1318    ///  This first gets the starting precisions, which may be from
1319    ///  the input parameters. It then sets up the columns (calculates
1320    ///  their widths and precisions and so on based on the values
1321    ///  within). The precisions are also stored in each Detection
1322    ///  object.
1323    ///
1324    ///  Need to have called calcObjectWCSparams() somewhere
1325    ///  beforehand.
1326
1327    std::vector<Detection>::iterator obj;
1328    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1329      obj->setVelPrec( this->par.getPrecVel() );
1330      obj->setFpeakPrec( this->par.getPrecFlux() );
1331      obj->setXYZPrec( Column::prXYZ );
1332      obj->setPosPrec( Column::prWPOS );
1333      obj->setFintPrec( this->par.getPrecFlux() );
1334      obj->setSNRPrec( this->par.getPrecSNR() );
1335    }
1336 
1337    this->fullCols.clear();
1338    this->fullCols = getFullColSet(*(this->objectList), this->head);
1339
1340    this->logCols.clear();
1341    this->logCols = getLogColSet(*(this->objectList), this->head);
1342
1343    int vel,fpeak,fint,pos,xyz,snr;
1344    vel = fullCols[VEL].getPrecision();
1345    fpeak = fullCols[FPEAK].getPrecision();
1346    snr = fullCols[SNRPEAK].getPrecision();
1347    xyz = fullCols[X].getPrecision();
1348    xyz = std::max(xyz, fullCols[Y].getPrecision());
1349    xyz = std::max(xyz, fullCols[Z].getPrecision());
1350    if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1351    else fint = fullCols[FTOT].getPrecision();
1352    pos = fullCols[WRA].getPrecision();
1353    pos = std::max(pos, fullCols[WDEC].getPrecision());
1354 
1355    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1356      obj->setVelPrec(vel);
1357      obj->setFpeakPrec(fpeak);
1358      obj->setXYZPrec(xyz);
1359      obj->setPosPrec(pos);
1360      obj->setFintPrec(fint);
1361      obj->setSNRPrec(snr);
1362    }
1363
1364  }
1365  //--------------------------------------------------------------------
1366
1367  bool Cube::objAtSpatialEdge(Detection obj)
1368  {
1369    ///  @details
1370    ///   A function to test whether the object obj
1371    ///    lies at the edge of the cube's spatial field --
1372    ///    either at the boundary, or next to BLANKs.
1373    ///
1374    ///   \param obj The Detection under consideration.
1375
1376    bool atEdge = false;
1377
1378    size_t pix = 0;
1379    std::vector<Voxel> voxlist = obj.getPixelSet();
1380    while(!atEdge && pix<voxlist.size()){
1381      // loop over each pixel in the object, until we find an edge pixel.
1382      for(int dx=-1;dx<=1;dx+=2){
1383        if( ((voxlist[pix].getX()+dx)<0) ||
1384            ((voxlist[pix].getX()+dx)>=this->axisDim[0]) )
1385          atEdge = true;
1386        else if(this->isBlank(voxlist[pix].getX()+dx,
1387                              voxlist[pix].getY(),
1388                              voxlist[pix].getZ()))
1389          atEdge = true;
1390      }
1391      for(int dy=-1;dy<=1;dy+=2){
1392        if( ((voxlist[pix].getY()+dy)<0) ||
1393            ((voxlist[pix].getY()+dy)>=this->axisDim[1]) )
1394          atEdge = true;
1395        else if(this->isBlank(voxlist[pix].getX(),
1396                              voxlist[pix].getY()+dy,
1397                              voxlist[pix].getZ()))
1398          atEdge = true;
1399      }
1400      pix++;
1401    }
1402
1403    return atEdge;
1404  }
1405  //--------------------------------------------------------------------
1406
1407  bool Cube::objAtSpectralEdge(Detection obj)
1408  {
1409    ///   @details
1410    ///   A function to test whether the object obj
1411    ///    lies at the edge of the cube's spectral extent --
1412    ///    either at the boundary, or next to BLANKs.
1413    ///
1414    ///   \param obj The Detection under consideration.
1415
1416    bool atEdge = false;
1417
1418    size_t pix = 0;
1419    std::vector<Voxel> voxlist = obj.getPixelSet();
1420    while(!atEdge && pix<voxlist.size()){
1421      // loop over each pixel in the object, until we find an edge pixel.
1422      for(int dz=-1;dz<=1;dz+=2){
1423        if( ((voxlist[pix].getZ()+dz)<0) ||
1424            ((voxlist[pix].getZ()+dz)>=this->axisDim[2]))
1425          atEdge = true;
1426        else if(this->isBlank(voxlist[pix].getX(),
1427                              voxlist[pix].getY(),
1428                              voxlist[pix].getZ()+dz))
1429          atEdge = true;
1430      }
1431      pix++;
1432    }
1433
1434    return atEdge;
1435  }
1436  //--------------------------------------------------------------------
1437
1438  void Cube::setObjectFlags()
1439  {
1440    /// @details
1441    ///   A function to set any warning flags for all the detected objects
1442    ///    associated with the cube.
1443    ///   Flags to be looked for:
1444    ///    <ul><li> Negative enclosed flux (N)
1445    ///        <li> Detection at edge of field (spatially) (E)
1446    ///        <li> Detection at edge of spectral region (S)
1447    ///    </ul>
1448
1449    std::vector<Detection>::iterator obj;
1450    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1451
1452      if( this->enclosedFlux(*obj) < 0. ) 
1453        obj->addToFlagText("N");
1454
1455      if( this->objAtSpatialEdge(*obj) )
1456        obj->addToFlagText("E");
1457
1458      if( this->objAtSpectralEdge(*obj) && (this->axisDim[2] > 2))
1459        obj->addToFlagText("S");
1460
1461      if(obj->getFlagText()=="") obj->addToFlagText("-");
1462
1463    }
1464
1465  }
1466  //--------------------------------------------------------------------
1467
1468
1469
1470  /****************************************************************/
1471  /////////////////////////////////////////////////////////////
1472  //// Functions for Image class
1473  /////////////////////////////////////////////////////////////
1474
1475  Image::Image(long size)
1476  {
1477    // need error handling in case size<0 !!!
1478    this->numPixels = this->numDim = 0;
1479    this->minSize = 2;
1480    if(size<0)
1481      duchampError("Image(size)","Negative size -- could not define Image");
1482    else{
1483      if(size>0 && !this->arrayAllocated){
1484        this->array = new float[size];
1485        this->arrayAllocated = true;
1486      }
1487      this->numPixels = size;
1488      this->axisDim = new long[2];
1489      this->axisDimAllocated = true;
1490      this->numDim = 2;
1491    }
1492  }
1493  //--------------------------------------------------------------------
1494
1495  Image::Image(long *dimensions)
1496  {
1497    this->numPixels = this->numDim = 0;
1498    this->minSize = 2;
1499    int size = dimensions[0] * dimensions[1];
1500    if(size<0)
1501      duchampError("Image(dimArray)","Negative size -- could not define Image");
1502    else{
1503      this->numPixels = size;
1504      if(size>0){
1505        this->array = new float[size];
1506        this->arrayAllocated = true;
1507      }
1508      this->numDim=2;
1509      this->axisDim = new long[2];
1510      this->axisDimAllocated = true;
1511      for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1512    }
1513  }
1514  //--------------------------------------------------------------------
1515  Image::Image(const Image &i):
1516    DataArray(i)
1517  {
1518    this->operator=(i);
1519  }
1520
1521  Image& Image::operator=(const Image &i)
1522  {
1523    if(this==&i) return *this;
1524    ((DataArray &) *this) = i;
1525    this->minSize = i.minSize;
1526    return *this;
1527  }
1528
1529  //--------------------------------------------------------------------
1530
1531  void Image::saveArray(float *input, long size)
1532  {
1533    /// @details
1534    /// Saves the array in input to the pixel array Image::array.
1535    /// The size of the array given must be the same as the current number of
1536    /// pixels, else an error message is returned and nothing is done.
1537    /// \param input The array of values to be saved.
1538    /// \param size The size of input.
1539
1540    if(size != this->numPixels)
1541      duchampError("Image::saveArray",
1542                   "Input array different size to existing array. Cannot save.");
1543    else {
1544      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1545      this->numPixels = size;
1546      if(this->numPixels>0){
1547        this->array = new float[size];
1548        this->arrayAllocated = true;
1549        for(int i=0;i<size;i++) this->array[i] = input[i];
1550      }
1551    }
1552  }
1553  //--------------------------------------------------------------------
1554
1555  void Image::extractSpectrum(float *Array, long *dim, long pixel)
1556  {
1557    /// @details
1558    ///  A function to extract a 1-D spectrum from a 3-D array.
1559    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1560    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1561    ///    by the third argument.
1562    ///  The extracted spectrum is stored in the pixel array Image::array.
1563    /// \param Array The array containing the pixel values, from which
1564    ///               the spectrum is extracted.
1565    /// \param dim The array of dimension values.
1566    /// \param pixel The spatial pixel that contains the desired spectrum.
1567
1568    if((pixel<0)||(pixel>=dim[0]*dim[1]))
1569      duchampError("Image::extractSpectrum",
1570                   "Requested spatial pixel outside allowed range. Cannot save.");
1571    else if(dim[2] != this->numPixels)
1572      duchampError("Image::extractSpectrum",
1573                   "Input array different size to existing array. Cannot save.");
1574    else {
1575      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1576      this->numPixels = dim[2];
1577      if(this->numPixels>0){
1578        this->array = new float[dim[2]];
1579        this->arrayAllocated = true;
1580        for(int z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1581      }
1582    }
1583  }
1584  //--------------------------------------------------------------------
1585
1586  void Image::extractSpectrum(Cube &cube, long pixel)
1587  {
1588    /// @details
1589    ///  A function to extract a 1-D spectrum from a Cube class
1590    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1591    ///    by the second argument.
1592    ///  The extracted spectrum is stored in the pixel array Image::array.
1593    /// \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1594    /// \param pixel The spatial pixel that contains the desired spectrum.
1595
1596    long zdim = cube.getDimZ();
1597    long spatSize = cube.getDimX()*cube.getDimY();
1598    if((pixel<0)||(pixel>=spatSize))
1599      duchampError("Image::extractSpectrum",
1600                   "Requested spatial pixel outside allowed range. Cannot save.");
1601    else if(zdim != this->numPixels)
1602      duchampError("Image::extractSpectrum",
1603                   "Input array different size to existing array. Cannot save.");
1604    else {
1605      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1606      this->numPixels = zdim;
1607      if(this->numPixels>0){
1608        this->array = new float[zdim];
1609        this->arrayAllocated = true;
1610        for(int z=0;z<zdim;z++)
1611          this->array[z] = cube.getPixValue(z*spatSize + pixel);
1612      }
1613    }
1614  }
1615  //--------------------------------------------------------------------
1616
1617  void Image::extractImage(float *Array, long *dim, long channel)
1618  {
1619    /// @details
1620    ///  A function to extract a 2-D image from a 3-D array.
1621    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1622    ///  The dimensions of the array are in the dim[] array.
1623    ///  The image extracted is the one lying in the channel referenced
1624    ///    by the third argument.
1625    ///  The extracted image is stored in the pixel array Image::array.
1626    /// \param Array The array containing the pixel values, from which the image is extracted.
1627    /// \param dim The array of dimension values.
1628    /// \param channel The spectral channel that contains the desired image.
1629
1630    long spatSize = dim[0]*dim[1];
1631    if((channel<0)||(channel>=dim[2]))
1632      duchampError("Image::extractImage",
1633                   "Requested channel outside allowed range. Cannot save.");
1634    else if(spatSize != this->numPixels)
1635      duchampError("Image::extractImage",
1636                   "Input array different size to existing array. Cannot save.");
1637    else {
1638      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1639      this->numPixels = spatSize;
1640      if(this->numPixels>0){
1641        this->array = new float[spatSize];
1642        this->arrayAllocated = true;
1643        for(int npix=0; npix<spatSize; npix++)
1644          this->array[npix] = Array[channel*spatSize + npix];
1645      }
1646    }
1647  }
1648  //--------------------------------------------------------------------
1649
1650  void Image::extractImage(Cube &cube, long channel)
1651  {
1652    /// @details
1653    ///  A function to extract a 2-D image from Cube class.
1654    ///  The image extracted is the one lying in the channel referenced
1655    ///    by the second argument.
1656    ///  The extracted image is stored in the pixel array Image::array.
1657    /// \param cube The Cube containing the pixel values, from which the image is extracted.
1658    /// \param channel The spectral channel that contains the desired image.
1659
1660    long spatSize = cube.getDimX()*cube.getDimY();
1661    if((channel<0)||(channel>=cube.getDimZ()))
1662      duchampError("Image::extractImage",
1663                   "Requested channel outside allowed range. Cannot save.");
1664    else if(spatSize != this->numPixels)
1665      duchampError("Image::extractImage",
1666                   "Input array different size to existing array. Cannot save.");
1667    else {
1668      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1669      this->numPixels = spatSize;
1670      if(this->numPixels>0){
1671        this->array = new float[spatSize];
1672        this->arrayAllocated = true;
1673        for(int npix=0; npix<spatSize; npix++)
1674          this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1675      }
1676    }
1677  }
1678  //--------------------------------------------------------------------
1679
1680  void Image::removeMW()
1681  {
1682    /// @details
1683    ///  A function to remove the Milky Way range of channels from a 1-D spectrum.
1684    ///  The array in this Image is assumed to be 1-D, with only the first axisDim
1685    ///    equal to 1.
1686    ///  The values of the MW channels are set to 0, unless they are BLANK.
1687
1688    if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1689      for(int z=0;z<this->axisDim[0];z++){
1690        if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1691      }
1692    }
1693  }
1694  //--------------------------------------------------------------------
1695
1696  std::vector<Object2D> Image::findSources2D()
1697  {
1698    std::vector<bool> thresholdedArray(this->axisDim[0]*this->axisDim[1]);
1699    for(int posY=0;posY<this->axisDim[1];posY++){
1700      for(int posX=0;posX<this->axisDim[0];posX++){
1701        int loc = posX + this->axisDim[0]*posY;
1702        thresholdedArray[loc] = this->isDetection(posX,posY);
1703      }
1704    }
1705    return lutz_detect(thresholdedArray, this->axisDim[0], this->axisDim[1], this->minSize);
1706  }
1707
1708  std::vector<Scan> Image::findSources1D()
1709  {
1710    std::vector<bool> thresholdedArray(this->axisDim[0]);
1711    for(int posX=0;posX<this->axisDim[0];posX++){
1712      thresholdedArray[posX] = this->isDetection(posX,0);
1713    }
1714    return spectrumDetect(thresholdedArray, this->axisDim[0], this->minSize);
1715  }
1716
1717
1718  std::vector< std::vector<PixelInfo::Voxel> > Cube::getObjVoxList()
1719  {
1720   
1721    std::vector< std::vector<PixelInfo::Voxel> > biglist;
1722   
1723    std::vector<Detection>::iterator obj;
1724    for(obj=this->objectList->begin(); obj<this->objectList->end(); obj++) {
1725
1726      Cube *subcube = new Cube;
1727      subcube->pars() = this->par;
1728      subcube->pars().setVerbosity(false);
1729      subcube->pars().setFlagSubsection(true);
1730      duchamp::Section sec = obj->getBoundingSection();
1731      subcube->pars().setSubsection( sec.getSection() );
1732      if(subcube->pars().verifySubsection() == FAILURE)
1733        duchampError("get object voxel list","Unable to verify the subsection - something's wrong!");
1734      if(subcube->getCube() == FAILURE)
1735        duchampError("get object voxel list","Unable to read the FITS file - something's wrong!");
1736      std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
1737      std::vector<PixelInfo::Voxel>::iterator vox;
1738      for(vox=voxlist.begin(); vox<voxlist.end(); vox++){
1739        long pix = (vox->getX()-subcube->pars().getXOffset()) +
1740          subcube->getDimX()*(vox->getY()-subcube->pars().getYOffset()) +
1741          subcube->getDimX()*subcube->getDimY()*(vox->getZ()-subcube->pars().getZOffset());
1742        vox->setF( subcube->getPixValue(pix) );
1743      }
1744      biglist.push_back(voxlist);
1745      delete subcube;
1746
1747    }
1748
1749    return biglist;
1750
1751  }
1752
1753}
Note: See TracBrowser for help on using the repository browser.