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

Last change on this file since 1276 was 1269, checked in by MatthewWhiting, 11 years ago

Ticket #198 - Fixing a bug with the velocity-width measurements. The mask is now properly calculated to enable accurate extraction of the integrated spectrum of a detection. Also in this changeset a few alterations to alternative parameter-measurement functions flowing out of the Selavy development.

File size: 75.2 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/Outputs/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#include <duchamp/FitsIO/DuchampBeam.hh>
54#include <duchamp/FitsIO/WriteReconArray.hh>
55#include <duchamp/FitsIO/WriteSmoothArray.hh>
56#include <duchamp/FitsIO/WriteMaskArray.hh>
57#include <duchamp/FitsIO/WriteMomentMapArray.hh>
58#include <duchamp/FitsIO/WriteMomentMaskArray.hh>
59#include <duchamp/FitsIO/WriteBaselineArray.hh>
60
61using namespace mycpgplot;
62using namespace Statistics;
63using namespace PixelInfo;
64
65#ifdef TEST_DEBUG
66const bool TESTING=true;
67#else
68const bool TESTING=false;
69#endif
70
71namespace duchamp
72{
73
74  using namespace Catalogues;
75
76  /****************************************************************/
77  ///////////////////////////////////////////////////
78  //// Functions for DataArray class:
79  ///////////////////////////////////////////////////
80
81  DataArray::DataArray(){
82    /// Fundamental constructor for DataArray.
83    /// Number of dimensions and pixels are set to 0. Nothing else allocated.
84
85    this->numDim=0;
86    this->numPixels=0;
87    this->objectList = new std::vector<Detection>;
88    this->axisDimAllocated = false;
89    this->arrayAllocated = false;
90  }
91  //--------------------------------------------------------------------
92
93  DataArray::DataArray(const DataArray &d){
94    operator=(d);
95  }
96
97  DataArray& DataArray::operator=(const DataArray &d){
98    if(this==&d) return *this;
99    this->numDim = d.numDim;
100    if(this->axisDimAllocated) delete [] this->axisDim;
101    this->axisDimAllocated = d.axisDimAllocated;
102    if(this->axisDimAllocated){
103      this->axisDim = new size_t[this->numDim];
104      for(size_t i=0;i<size_t(this->numDim);i++) this->axisDim[i] = d.axisDim[i];
105    }
106    this->numPixels = d.numPixels;
107    if(this->arrayAllocated) delete [] this->array;
108    this->arrayAllocated = d.arrayAllocated;
109    if(this->arrayAllocated) {
110      this->array = new float[this->numPixels];
111      for(size_t i=0;i<size_t(this->numPixels);i++) this->array[i] = d.array[i];
112    }
113    this->objectList = d.objectList;
114    this->par = d.par;
115    this->Stats = d.Stats;
116    return *this;
117  }
118
119
120  DataArray::DataArray(short int nDim){
121    /// @details
122    /// N-dimensional constructor for DataArray.
123    /// Number of dimensions defined, and dimension array allocated.
124    /// Number of pixels are set to 0.
125    /// \param nDim Number of dimensions.
126
127    this->axisDimAllocated = false;
128    this->arrayAllocated = false;
129    if(nDim>0){
130      this->axisDim = new size_t[nDim];
131      this->axisDimAllocated = true;
132    }
133    this->numDim=nDim;
134    this->numPixels=0;
135    this->objectList = new std::vector<Detection>;
136  }
137  //--------------------------------------------------------------------
138
139  DataArray::DataArray(short int nDim, size_t size){
140    /// @details
141    /// N-dimensional constructor for DataArray.
142    /// Number of dimensions and number of pixels defined.
143    /// Arrays allocated based on these values.
144    /// \param nDim Number of dimensions.
145    /// \param size Number of pixels.
146    ///
147    /// Note that we can assign values to the dimension array.
148
149    this->axisDimAllocated = false;
150    this->arrayAllocated = false;
151    if(size<0){
152      DUCHAMPERROR("DataArray(nDim,size)", "Negative size -- could not define DataArray");
153    }
154    else if(nDim<0){
155      DUCHAMPERROR("DataArray(nDim,size)", "Negative number of dimensions: could not define DataArray");
156    }
157    else {
158      if(size>0){
159        this->array = new float[size];
160        this->arrayAllocated = true;
161      }
162      this->numPixels = size;
163      if(nDim>0){
164        this->axisDim = new size_t[nDim];
165        this->axisDimAllocated = true;
166      }
167      this->numDim = nDim;
168    }
169    this->objectList = new std::vector<Detection>;
170  }
171  //--------------------------------------------------------------------
172
173  DataArray::DataArray(short int nDim, size_t *dimensions)
174  {
175    /// @details
176    /// Most robust constructor for DataArray.
177    /// Number and sizes of dimensions are defined, and hence the number of
178    /// pixels. Arrays allocated based on these values.
179    /// \param nDim Number of dimensions.
180    /// \param dimensions Array giving sizes of dimensions.
181
182    this->axisDimAllocated = false;
183    this->arrayAllocated = false;
184    if(nDim<0){
185      DUCHAMPERROR("DataArray(nDim,dimArray)", "Negative number of dimensions: could not define DataArray");
186    }
187    else {
188      int size = dimensions[0];
189      for(int i=1;i<nDim;i++) size *= dimensions[i];
190      if(size<0){
191        DUCHAMPERROR("DataArray(nDim,dimArray)", "Negative size: could not define DataArray");
192      }
193      else{
194        this->numPixels = size;
195        if(size>0){
196          this->array = new float[size];
197          this->arrayAllocated = true;
198        }
199        this->numDim=nDim;
200        if(nDim>0){
201          this->axisDim = new size_t[nDim];
202          this->axisDimAllocated = true;
203        }
204        for(int i=0;i<nDim;i++) this->axisDim[i] = dimensions[i];
205      }
206    }
207  }
208  //--------------------------------------------------------------------
209
210  DataArray::~DataArray()
211  {
212    ///  @details
213    ///  Destructor -- arrays deleted if they have been allocated, and the
214    ///   object list is deleted.
215
216    if(this->numPixels>0 && this->arrayAllocated){
217      delete [] this->array;
218      this->arrayAllocated = false;
219    }
220    if(this->numDim>0 && this->axisDimAllocated){
221      delete [] this->axisDim;
222      this->axisDimAllocated = false;
223    }
224    delete this->objectList;
225  }
226  //--------------------------------------------------------------------
227  //--------------------------------------------------------------------
228
229  void DataArray::getDim(size_t &x, size_t &y, size_t &z)
230  {
231    /// @details
232    /// The sizes of the first three dimensions (if they exist) are returned.
233    /// \param x The first dimension. Defaults to 0 if numDim \f$\le\f$ 0.
234    /// \param y The second dimension. Defaults to 1 if numDim \f$\le\f$ 1.
235    /// \param z The third dimension. Defaults to 1 if numDim \f$\le\f$ 2.
236
237    if(this->numDim>0) x=this->axisDim[0];
238    else x=0;
239    if(this->numDim>1) y=this->axisDim[1];
240    else y=1;
241    if(this->numDim>2) z=this->axisDim[2];
242    else z=1;
243  }
244  //--------------------------------------------------------------------
245
246  void DataArray::getDimArray(size_t *output)
247  {
248    /// @details
249    /// The axisDim array is written to output. This needs to be defined
250    ///  beforehand: no checking is done on the memory.
251    /// \param output The array that is written to.
252
253    for(int i=0;i<this->numDim;i++) output[i] = this->axisDim[i];
254  }
255  //--------------------------------------------------------------------
256
257  void DataArray::getArray(float *output)
258  {
259    /// @details
260    /// The pixel value array is written to output. This needs to be defined
261    ///  beforehand: no checking is done on the memory.
262    /// \param output The array that is written to.
263
264    for(size_t i=0;i<this->numPixels;i++) output[i] = this->array[i];
265  }
266  //--------------------------------------------------------------------
267
268  void DataArray::saveArray(float *input, size_t size)
269  {
270    /// @details
271    /// Saves the array in input to the pixel array DataArray::array.
272    /// The size of the array given must be the same as the current number of
273    /// pixels, else an error message is returned and nothing is done.
274    /// \param input The array of values to be saved.
275    /// \param size The size of input.
276
277    if(size != this->numPixels){
278      DUCHAMPERROR("DataArray::saveArray", "Input array different size to existing array. Cannot save.");
279    }
280    else {
281      if(this->numPixels>0 && this->arrayAllocated) delete [] this->array;
282      this->numPixels = size;
283      this->array = new float[size];
284      this->arrayAllocated = true;
285      for(size_t i=0;i<size;i++) this->array[i] = input[i];
286    }
287  }
288  //--------------------------------------------------------------------
289
290  void DataArray::addObject(Detection object)
291  {
292    /// \param object The object to be added to the object list.
293
294    // objectList is a vector, so just use push_back()
295    this->objectList->push_back(object);
296  }
297  //--------------------------------------------------------------------
298
299  void DataArray::addObjectList(std::vector <Detection> newlist)
300  {
301    /// \param newlist The list of objects to be added to the object list.
302
303    std::vector<Detection>::iterator obj;
304    for(obj=newlist.begin();obj<newlist.end();obj++) this->objectList->push_back(*obj);
305  }
306  //--------------------------------------------------------------------
307
308  bool DataArray::isDetection(float value)
309  {
310    ///  @details
311    /// Is a given value a detection, based on the statistics in the
312    /// DataArray's StatsContainer?
313    /// \param value The pixel value to test.
314
315    if(par.isBlank(value)) return false;
316    else return Stats.isDetection(value);
317  }
318  //--------------------------------------------------------------------
319
320  bool DataArray::isDetection(size_t voxel)
321  {
322    ///  @details
323    /// Is a given pixel a detection, based on the statistics in the
324    /// DataArray's StatsContainer?
325    /// If the pixel lies outside the valid range for the data array, return false.
326    /// \param voxel Location of the DataArray's pixel to be tested.
327
328    if((voxel<0)||(voxel>this->numPixels)) return false;
329    else if(par.isBlank(this->array[voxel])) return false;
330    else return Stats.isDetection(this->array[voxel]);
331  } 
332  //--------------------------------------------------------------------
333
334  std::ostream& operator<< ( std::ostream& theStream, DataArray &array)
335  {
336    /// @details
337    /// A way to print out the pixel coordinates & flux values of the
338    /// list of detected objects belonging to the DataArray.
339    /// These are formatted nicely according to the << operator for Detection,
340    ///  with a line indicating the number of detections at the start.
341    /// \param theStream The ostream object to which the output should be sent.
342    /// \param array The DataArray containing the list of objects.
343
344    for(int i=0;i<array.numDim;i++){
345      if(i>0) theStream<<"x";
346      theStream<<array.axisDim[i];
347    }
348    theStream<<std::endl;
349
350    theStream<<"Threshold\tmiddle\tspread\trobust\n" << array.stats().getThreshold() << "\t";
351    if(array.pars().getFlagUserThreshold())
352      theStream << "0.0000\t" << array.stats().getThreshold() << "\t";
353    else
354      theStream << array.stats().getMiddle() << " " << array.stats().getSpread() << "\t";
355    theStream << array.stats().getRobust()<<"\n";
356
357    theStream<<array.objectList->size()<<" detections:\n--------------\n";
358    std::vector<Detection>::iterator obj;
359    for(obj=array.objectList->begin();obj<array.objectList->end();obj++){
360      theStream << "Detection #" << obj->getID()<<std::endl;
361      Detection *newobj = new Detection;
362      *newobj = *obj;
363      newobj->addOffsets();
364      theStream<<*newobj;
365      delete newobj;
366    }
367    theStream<<"--------------\n";
368    return theStream;
369  }
370
371  /****************************************************************/
372  /////////////////////////////////////////////////////////////
373  //// Functions for Cube class
374  /////////////////////////////////////////////////////////////
375
376  Cube::Cube()
377  {
378    /// @details
379    /// Basic Constructor for Cube class.
380    /// numDim set to 3, but numPixels to 0 and all bool flags to false.
381    /// No allocation done.
382
383    numPixels=0; numDim=3;
384    reconExists = false; reconAllocated = false; baselineAllocated = false;
385  }
386  //--------------------------------------------------------------------
387
388  Cube::Cube(size_t size)
389  {
390    /// @details
391    /// Alternative Cube constructor, where size is given but not individual
392    ///  dimensions. Arrays are allocated as appropriate (according to the
393    ///  relevant flags in Param set), but the Cube::axisDim array is not.
394
395    this->reconAllocated = false;
396    this->baselineAllocated = false;
397    this->axisDimAllocated = false;
398    this->arrayAllocated = false;
399    this->numPixels = this->numDim = 0;
400    if(size<0){
401      DUCHAMPERROR("Cube(size)","Negative size -- could not define Cube");
402    }
403    else{
404      if(size>0){
405        this->array = new float[size];
406        this->arrayAllocated = true;
407        if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
408          this->recon = new float[size];
409          this->reconAllocated = true;
410        }
411        if(this->par.getFlagBaseline()){
412          this->baseline = new float[size];
413          this->baselineAllocated = true;
414        }
415      }
416      this->numPixels = size;
417      this->axisDim = new size_t[3];
418      this->axisDimAllocated = true;
419      this->numDim = 3;
420      this->reconExists = false;
421    }
422  }
423  //--------------------------------------------------------------------
424
425  Cube::Cube(size_t *dimensions)
426  {
427    /// Alternative Cube constructor, where sizes of dimensions are given.
428    /// Arrays are allocated as appropriate (according to the
429    ///  relevant flags in Param set), as is the Cube::axisDim array.
430
431    int size   = dimensions[0] * dimensions[1] * dimensions[2];
432    int imsize = dimensions[0] * dimensions[1];
433    this->reconAllocated = false;
434    this->baselineAllocated = false;
435    this->axisDimAllocated = false;
436    this->arrayAllocated = false;
437    this->numPixels = this->numDim = 0;
438    if((size<0) || (imsize<0) ){
439      DUCHAMPERROR("Cube(dimArray)","Negative size -- could not define Cube");
440    }
441    else{
442      this->numPixels = size;
443      if(size>0){
444        this->array      = new float[size];
445        this->arrayAllocated = true;
446        this->detectMap  = new short[imsize];
447        if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
448          this->recon    = new float[size];
449          this->reconAllocated = true;
450        }
451        if(this->par.getFlagBaseline()){
452          this->baseline = new float[size];
453          this->baselineAllocated = true;
454        }
455      }
456      this->numDim  = 3;
457      this->axisDim = new size_t[3];
458      this->axisDimAllocated = true;
459      for(int i=0;i<3     ;i++) this->axisDim[i]   = dimensions[i];
460      for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
461      this->reconExists = false;
462    }
463  }
464  //--------------------------------------------------------------------
465
466  Cube::~Cube()
467  {
468    /// @details
469    ///  The destructor deletes the memory allocated for Cube::detectMap, and,
470    ///  if these have been allocated, Cube::recon and Cube::baseline.
471
472    if(this->numPixels>0 && this->arrayAllocated) delete [] this->detectMap;
473    if(this->reconAllocated)    delete [] this->recon;
474    if(this->baselineAllocated) delete [] this->baseline;
475  }
476  //--------------------------------------------------------------------
477
478  Cube::Cube(const Cube &c):
479    DataArray(c)
480  {
481    this->operator=(c);
482  }
483  //--------------------------------------------------------------------
484
485  Cube& Cube::operator=(const Cube &c)
486  {
487    if(this==&c) return *this;
488    if(this->arrayAllocated) delete [] this->detectMap;
489    ((DataArray &) *this) = c;
490    this->reconExists = c.reconExists;
491    if(this->reconAllocated) delete [] this->recon;
492    this->reconAllocated = c.reconAllocated;
493    if(this->reconAllocated) {
494      this->recon = new float[this->numPixels];
495      for(size_t i=0;i<size_t(this->numPixels);i++) this->recon[i] = c.recon[i];
496    }
497    if(this->arrayAllocated){
498      this->detectMap = new short[this->axisDim[0]*this->axisDim[1]];
499      for(size_t i=0;i<size_t(this->axisDim[0]*this->axisDim[1]);i++) this->detectMap[i] = c.detectMap[i];
500    }
501    if(this->baselineAllocated) delete [] this->baseline;
502    this->baselineAllocated = c.baselineAllocated;
503    if(this->baselineAllocated){
504      this->baseline = new float[this->numPixels];
505      for(size_t i=0;i<size_t(this->numPixels);i++) this->baseline[i] = c.baseline[i];
506    }
507    this->head = c.head;
508    this->fullCols = c.fullCols;
509    return *this;
510  }
511  //--------------------------------------------------------------------
512
513  Cube* Cube::slice(Section subsection)
514  {
515    Cube *output = new Cube;
516    Section thisSection;
517    std::string nullsec=nullSection(this->numDim);
518    if(this->par.section().isParsed()) thisSection=this->par.section();
519    else{
520      thisSection = Section(nullsec);
521      thisSection.parse(this->axisDim, this->numDim);
522    }
523
524    subsection.parse(this->axisDim, this->numDim);
525    if(subsection.isValid()){
526      output->par = this->par;
527      output->par.section() = thisSection * subsection;
528      output->par.setXOffset(output->par.getXOffset()+subsection.getStart(0));
529      output->par.setYOffset(output->par.getYOffset()+subsection.getStart(1));
530      output->par.setZOffset(output->par.getZOffset()+subsection.getStart(2));
531      output->head = this->head;
532      // correct the reference pixel in the WCS
533      output->head.WCS().crpix[output->head.WCS().lng] -= subsection.getStart(output->head.WCS().lng);
534      output->head.WCS().crpix[output->head.WCS().lat] -= subsection.getStart(output->head.WCS().lat);
535      if(output->head.WCS().spec>0)
536        output->head.WCS().crpix[output->head.WCS().spec] -= subsection.getStart(output->head.WCS().spec);
537      output->Stats = this->Stats;
538      output->fullCols = this->fullCols;
539      size_t *dims = new size_t[3];
540      for(size_t i=0;i<3;i++){
541        dims[i] = subsection.getDimList()[i];
542        std::cout << "Dim " << i+1 << " = " << dims[i] << "\n";
543      }
544     
545      output->initialiseCube(dims,true);
546      for(size_t z=0;z<output->axisDim[2];z++){
547        for(size_t y=0;y<output->axisDim[1];y++){
548          for(size_t x=0;x<output->axisDim[0];x++){
549            size_t impos=x+y*output->axisDim[0];
550            size_t pos=impos+z*output->axisDim[0]*output->axisDim[1];
551            if(pos>=output->numPixels) DUCHAMPERROR("cube slicer","Out of bounds in new Cube");
552            size_t imposIn=(x+subsection.getStart(0)) + (y+subsection.getStart(1))*this->axisDim[0];
553            size_t posIn=imposIn + (z+subsection.getStart(2))*this->axisDim[0]*this->axisDim[1];
554            if(posIn>=this->numPixels) DUCHAMPERROR("cube slicer","Out of bounds in new Cube");
555            output->array[pos] = this->array[posIn];
556            output->detectMap[impos] = this->detectMap[imposIn];
557            if(this->reconAllocated) output->recon[pos] = this->recon[posIn];
558            if(this->baselineAllocated) output->baseline[pos] = this->baseline[posIn];
559          }
560        }
561      }
562       std::cout << this->par << "\n"<<output->par <<"\n";
563    }
564    else{
565      DUCHAMPERROR("cube slicer","Subsection does not parse");
566    }
567
568    return output;
569
570  }
571  //--------------------------------------------------------------------
572
573  OUTCOME Cube::initialiseCube(long *dimensions, bool allocateArrays)
574  {
575    int numAxes = this->head.getNumAxes();
576    if(numAxes<=0) numAxes=3;
577    size_t *dim = new size_t[numAxes];
578    for(int i=0;i<numAxes;i++) dim[i]=dimensions[i];
579    OUTCOME outcome=this->initialiseCube(dim,allocateArrays);
580    delete [] dim;
581    return outcome;
582  }
583
584
585  OUTCOME Cube::initialiseCube(size_t *dimensions, bool allocateArrays)
586  {
587    /// @details
588    ///  This function will set the sizes of all arrays that will be used by Cube.
589    ///  It will also define the values of the axis dimensions: this will be done
590    ///   using the WCS in the FitsHeader class, so the WCS needs to be good and
591    ///   have three axes. If this is not the case, the axes are assumed to be
592    ///   ordered in the sense of lng,lat,spc.
593    ///
594    ///  \param dimensions An array of values giving the dimensions (sizes) for
595    ///  all axes. 
596    ///  \param allocateArrays A flag indicating whether to allocate
597    ///  the memory for the data arrays: the default is true. The
598    ///  dimension arrays will be allocated and filled.
599
600    int lng,lat,spc;
601    size_t size,imsize;
602 
603    int numAxes = this->head.getNumAxes();
604    if(numAxes<=0) numAxes=3;
605
606    if(this->head.isWCS() && (numAxes>=3) && (this->head.WCS().spec>=0)){
607      // if there is a WCS and there is at least 3 axes
608      lng = this->head.WCS().lng;
609      lat = this->head.WCS().lat;
610      spc = this->head.WCS().spec;
611    }
612    else{
613      // just take dimensions[] at face value
614      lng = 0;
615      lat = 1;
616      spc = 2;
617    }
618
619    size   = dimensions[lng];
620    if(numAxes>1) size *= dimensions[lat];
621    if(this->head.canUseThirdAxis() && numAxes>spc) size *= dimensions[spc];
622
623    imsize = dimensions[lng];
624    if(numAxes>1) imsize *= dimensions[lat];
625
626    this->reconAllocated = false;
627    this->baselineAllocated = false;
628
629    if(this->axisDimAllocated){
630      delete [] this->axisDim;
631      this->axisDimAllocated = false;
632    }
633
634    if(this->arrayAllocated){
635      delete [] this->array;
636      delete [] this->detectMap;
637      this->arrayAllocated = false;
638    }
639    if(this->reconAllocated){
640      delete [] this->recon;
641      this->reconAllocated = false;
642    }
643    if(this->baselineAllocated){
644      delete [] this->baseline;
645      this->baselineAllocated = false;
646    }
647
648    if((size<0) || (imsize<0) ) {
649      DUCHAMPERROR("Cube::initialiseCube(dimArray)", "Negative size -- could not define Cube.");
650      return FAILURE;
651    }
652    else{
653      this->numPixels = size;
654      this->numDim  = 3;
655
656      this->axisDim = new size_t[this->numDim];
657      this->axisDimAllocated = true;
658      this->axisDim[0] = dimensions[lng];
659      if(numAxes>1) this->axisDim[1] = dimensions[lat];
660      else this->axisDim[1] = 1;
661      if(this->head.canUseThirdAxis() && numAxes>spc) this->axisDim[2] = dimensions[spc];
662      else this->axisDim[2] = 1;
663
664      this->numNondegDim=0;
665      for(int i=0;i<3;i++) if(this->axisDim[i]>1) this->numNondegDim++;
666
667      if(this->numNondegDim == 1){
668        if(!head.isWCS()) std::swap(this->axisDim[0],this->axisDim[2]);
669        imsize=this->axisDim[2];
670      }
671
672      bool haveChanged=false;
673      int change=0;
674      if(this->par.getMinPix() > this->axisDim[0]*this->axisDim[1]){
675        DUCHAMPWARN("Cube::initialiseCube", "The value of minPix ("<<this->par.getMinPix()<<") is greater than the image size. Setting to "<<this->axisDim[0]*this->axisDim[1]);
676        change=this->par.getMinPix() - this->axisDim[0]*this->axisDim[1];
677        haveChanged=true;
678        this->par.setMinPix(this->axisDim[0]*this->axisDim[1]);
679      }
680      if(this->par.getMinChannels() > this->axisDim[2]){
681        DUCHAMPWARN("Cube::initialiseCube", "The value of minChannels ("<<this->par.getMinChannels()<<") is greater than the spectral size. Setting to "<<this->axisDim[2]);
682        change=this->par.getMinChannels() - this->axisDim[2];
683        haveChanged=true;
684        this->par.setMinChannels(this->axisDim[2]);
685      }
686      if(haveChanged){
687        DUCHAMPWARN("Cube::initialiseCube","Reducing minVoxels to "<<this->par.getMinVoxels() - change<<" to accomodate these changes" );
688        this->par.setMinVoxels(this->par.getMinVoxels() - change);
689      }
690     
691      if(this->par.getFlagSmooth()){
692        if(this->par.getSmoothType()=="spectral" && this->numNondegDim==2){
693          DUCHAMPWARN("Cube::initialiseCube", "Spectral smooth requested, but have a 2D image. Setting flagSmooth=false");
694          this->par.setFlagSmooth(false);
695        }
696        if(this->par.getSmoothType()=="spatial" && this->numNondegDim==1){
697          DUCHAMPWARN("Cube::initialiseCube", "Spatial smooth requested, but have a 1D image. Setting flagSmooth=false");
698          this->par.setFlagSmooth(false);
699        }
700      }
701      if(this->par.getFlagATrous()){
702        for(int d=3; d>=1; d--){
703          if(this->par.getReconDim()==d && this->numNondegDim==(d-1)){
704            DUCHAMPWARN("Cube::initialiseCube", d << "D reconstruction requested, but image is " << d-1 <<"D. Setting flagAtrous=false");
705            this->par.setFlagATrous(false);
706          }
707        }
708      }
709
710      if(allocateArrays && this->par.isVerbose()) this->reportMemorySize(std::cout,allocateArrays);
711
712      this->reconExists = false;
713      if(size>0 && allocateArrays){
714        this->array      = new float[size];
715        this->arrayAllocated = true;
716        this->detectMap  = new short[imsize];
717        for(size_t i=0;i<imsize;i++) this->detectMap[i] = 0;
718        if(this->par.getFlagATrous() || this->par.getFlagSmooth()){
719          this->recon    = new float[size];
720          this->reconAllocated = true;
721          for(size_t i=0;i<size;i++) this->recon[i] = 0.;
722        }
723        if(this->par.getFlagBaseline()){
724          this->baseline = new float[size];
725          this->baselineAllocated = true;
726          for(size_t i=0;i<size;i++) this->baseline[i] = 0.;
727        }
728      }
729    }
730    return SUCCESS;
731  }
732  //--------------------------------------------------------------------
733
734  void Cube::reportMemorySize(std::ostream &theStream, bool allocateArrays)
735  {
736    std::string unitlist[4]={"kB","MB","GB","TB"};
737    size_t size=axisDim[0]*axisDim[1]*axisDim[2];
738    size_t imsize=axisDim[0]*axisDim[1];
739   
740      // Calculate and report the total size of memory to be allocated.
741      float allocSize=3*sizeof(size_t);  // axisDim
742      float arrAllocSize=0.;
743      if(size>0 && allocateArrays){
744        allocSize += size * sizeof(float); // array
745        arrAllocSize = size*sizeof(float);
746        allocSize += imsize * sizeof(short); // detectMap
747        if(this->par.getFlagATrous() || this->par.getFlagSmooth())
748          allocSize += size * sizeof(float); // recon
749        if(this->par.getFlagBaseline())
750          allocSize += size * sizeof(float); // baseline
751      }
752      std::string units="bytes";
753      for(int i=0;i<4 && allocSize>1024.;i++){
754        allocSize/=1024.;
755        arrAllocSize /= 1024.;
756        units=unitlist[i];
757      }
758
759      theStream << "\n About to allocate " << allocSize << units;
760      if(arrAllocSize > 0.) theStream << " of which " << arrAllocSize << units << " is for the image\n";
761      else theStream << "\n";
762  }
763
764
765  bool Cube::is2D()
766  {
767    /// @details
768    ///   Check whether the image is 2-dimensional, by counting
769    ///   the number of dimensions that are greater than 1
770
771    if(this->head.WCS().naxis==2) return true;
772    else{
773      int numDim=0;
774      for(int i=0;i<this->numDim;i++) if(axisDim[i]>1) numDim++;
775      return numDim<=2;
776    }
777
778  }
779  //--------------------------------------------------------------------
780
781  OUTCOME Cube::getCube()
782  { 
783    ///  @details
784    /// A front-end to the Cube::getCube() function, that does
785    ///  subsection checks.
786    /// Assumes the Param is set up properly.
787
788    std::string fname = par.getImageFile();
789    if(par.getFlagSubsection()) fname+=par.getSubsection();
790    return getCube(fname);
791  }
792  //--------------------------------------------------------------------
793
794  void Cube::saveArray(float *input, size_t size)
795  {
796    if(size != this->numPixels){
797      DUCHAMPERROR("Cube::saveArray","Input array different size to existing array (" << size << " cf. " << this->numPixels << "). Cannot save.");
798    }
799    else {
800      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
801      this->numPixels = size;
802      this->array = new float[size];
803      this->arrayAllocated = true;
804      for(size_t i=0;i<size;i++) this->array[i] = input[i];
805    }
806  }
807  //--------------------------------------------------------------------
808
809  void Cube::saveArray(std::vector<float> &input)
810  {
811    /// @details
812    /// Saves the array in input to the pixel array Cube::array.
813    /// The size of the array given must be the same as the current number of
814    /// pixels, else an error message is returned and nothing is done.
815    /// \param input The array of values to be saved.
816
817    if(input.size() != this->numPixels){
818      DUCHAMPERROR("Cube::saveArray","Input array different size to existing array (" << input.size() << " cf. " << this->numPixels << "). Cannot save.");
819    }
820    else {
821      if(this->numPixels>0 && this->arrayAllocated) delete [] this->array;
822      this->numPixels = input.size();
823      this->array = new float[input.size()];
824      this->arrayAllocated = true;
825      for(size_t i=0;i<input.size();i++) this->array[i] = input[i];
826    }
827  }
828  //--------------------------------------------------------------------
829
830  void Cube::saveRecon(float *input, size_t size)
831  {
832    /// @details
833    /// Saves the array in input to the reconstructed array Cube::recon
834    /// The size of the array given must be the same as the current number of
835    /// pixels, else an error message is returned and nothing is done.
836    /// If the recon array has already been allocated, it is deleted first, and
837    /// then the space is allocated.
838    /// Afterwards, the appropriate flags are set.
839    /// \param input The array of values to be saved.
840    /// \param size The size of input.
841
842    if(size != this->numPixels){
843      DUCHAMPERROR("Cube::saveRecon","Input array different size to existing array (" << size << " cf. " << this->numPixels << "). Cannot save.");
844    }
845    else {
846      if(this->numPixels>0){
847        if(this->reconAllocated) delete [] this->recon;
848        this->numPixels = size;
849        this->recon = new float[size];
850        this->reconAllocated = true;
851        for(size_t i=0;i<size;i++) this->recon[i] = input[i];
852        this->reconExists = true;
853      }
854    }
855  }
856  //--------------------------------------------------------------------
857
858  void Cube::getRecon(float *output)
859  {
860    /// @details
861    /// The reconstructed array is written to output. The output array needs to
862    ///  be defined beforehand: no checking is done on the memory.
863    /// \param output The array that is written to.
864
865    // Need check for change in number of pixels!
866    for(size_t i=0;i<this->numPixels;i++){
867      if(this->reconExists) output[i] = this->recon[i];
868      else output[i] = 0.;
869    }
870  }
871  //--------------------------------------------------------------------
872
873  void Cube::getBaseline(float *output)
874  {
875    /// @details
876    /// The baseline array is written to output. The output array needs to
877    ///  be defined beforehand: no checking is done on the memory.
878    /// \param output The array that is written to.
879
880    // Need check for change in number of pixels!
881    for(size_t i=0;i<this->numPixels;i++){
882      if(this->baselineAllocated) output[i] = this->baseline[i];
883      else output[i] = 0.;
884    }
885  }
886  //--------------------------------------------------------------------
887  void Cube::setCubeStats()
888  {
889    ///   @details
890    ///   Calculates the full statistics for the cube:
891    ///     mean, rms, median, madfm
892    ///   Only do this if the threshold has not been defined (ie. is still 0.,
893    ///    its default).
894    ///   Also work out the threshold and store it in the par set.
895    ///   
896    ///   Different from Cube::setCubeStatsOld() as it doesn't use the
897    ///    getStats functions but has own versions of them hardcoded to
898    ///    ignore BLANKs and flagged channels. This saves on memory usage -- necessary
899    ///    for dealing with very big files.
900    ///
901    ///   Three cases exist:
902    ///  <ul><li>Simple case, with no reconstruction/smoothing: all stats
903    ///          calculated from the original array.
904    ///      <li>Wavelet reconstruction: mean & median calculated from the
905    ///          original array, and stddev & madfm from the residual.
906    ///      <li>Smoothing: all four stats calculated from the recon array
907    ///          (which holds the smoothed data).
908    ///  </ul>
909
910    if(this->par.getFlagUserThreshold() ){
911      // if the user has defined a threshold, set this in the StatsContainer
912      this->Stats.setThreshold( this->par.getThreshold() );
913    }
914    else{
915      // only work out the stats if we need to.
916      // the only reason we don't is if the user has specified a threshold.
917   
918      this->Stats.setRobust(this->par.getFlagRobustStats());
919
920      if(this->par.isVerbose())
921        std::cout << "Calculating the cube statistics... " << std::flush;
922   
923      // size_t xysize = this->axisDim[0]*this->axisDim[1];
924
925      bool *mask = new bool[this->numPixels];
926      size_t vox=0,goodSize = 0;
927      for(size_t z=0;z<this->axisDim[2];z++){
928        for(size_t y=0;y<this->axisDim[1];y++){
929          for(size_t x=0;x<this->axisDim[0];x++){
930            //      vox = z * xysize + y*this->axisDim[0] + x;
931            bool isBlank=this->isBlank(vox);
932            bool statOK = this->par.isStatOK(x,y,z);
933            bool isFlagged = this->par.isFlaggedChannel(z);
934            mask[vox] = (!isBlank && !isFlagged && statOK );
935            if(mask[vox]) goodSize++;
936            vox++;
937          }
938        }
939      }
940
941      //      float mean,median,stddev,madfm;
942      if( this->par.getFlagATrous() ){
943        // Case #2 -- wavelet reconstruction
944        // just get mean & median from orig array, and rms & madfm from
945        // residual recompute array values to be residuals & then find
946        // stddev & madfm
947        if(!this->reconExists){
948          DUCHAMPERROR("setCubeStats", "Reconstruction not yet done! Cannot calculate stats!");
949        }
950        else{
951          float *tempArray = new float[goodSize];
952
953          goodSize=0;
954          vox=0;
955          for(size_t z=0;z<this->axisDim[2];z++){
956            for(size_t y=0;y<this->axisDim[1];y++){
957              for(size_t x=0;x<this->axisDim[0];x++){
958                //              vox = z * xysize + y*this->axisDim[0] + x;
959                if(mask[vox]) tempArray[goodSize++] = this->array[vox];
960                vox++;
961              }
962            }
963          }
964
965          // First, find the mean of the original array. Store it.
966          float mean = findMean<float>(tempArray, goodSize);
967       
968          // Now sort it and find the median. Store it.
969          float median = findMedian<float>(tempArray, goodSize, true);
970
971          // Now calculate the residuals and find the mean & median of
972          // them. We don't store these, but they are necessary to find
973          // the sttdev & madfm.
974          goodSize = 0;
975          //      for(int p=0;p<xysize;p++){
976          vox=0;
977          for(size_t z=0;z<this->axisDim[2];z++){
978            for(size_t y=0;y<this->axisDim[1];y++){
979              for(size_t x=0;x<this->axisDim[0];x++){
980                //            vox = z * xysize + p;
981              if(mask[vox])
982                tempArray[goodSize++] = this->array[vox] - this->recon[vox];
983              vox++;
984              }
985            }
986          }
987           
988          float stddev = findStddev<float>(tempArray, goodSize);
989
990          // Now find the madfm of the residuals. Store it.
991          float madfm = findMADFM<float>(tempArray, goodSize, true);
992
993          this->Stats.define(mean,median,stddev,madfm);
994
995          delete [] tempArray;
996        }
997      }
998      else if(this->par.getFlagSmooth()) {
999        // Case #3 -- smoothing
1000        // get all four stats from the recon array, which holds the
1001        // smoothed data. This can just be done with the
1002        // StatsContainer::calculate function, using the mask generated
1003        // earlier.
1004        if(!this->reconExists){
1005          DUCHAMPERROR("setCubeStats","Smoothing not yet done! Cannot calculate stats!");
1006        }
1007        else this->Stats.calculate(this->recon,this->numPixels,mask);
1008      }
1009      else{
1010        // Case #1 -- default case, with no smoothing or reconstruction.
1011        // get all four stats from the original array. This can just be
1012        // done with the StatsContainer::calculate function, using the
1013        // mask generated earlier.
1014        this->Stats.calculate(this->array,this->numPixels,mask);
1015      }
1016
1017      this->Stats.setUseFDR( this->par.getFlagFDR() );
1018      // If the FDR method has been requested, define the P-value
1019      // threshold
1020      if(this->par.getFlagFDR())  this->setupFDR();
1021      else{
1022        // otherwise, calculate threshold based on the requested SNR cut
1023        // level, and then set the threshold parameter in the Par set.
1024        this->Stats.setThresholdSNR( this->par.getCut() );
1025        this->par.setThreshold( this->Stats.getThreshold() );
1026      }
1027   
1028      delete [] mask;
1029
1030    }
1031
1032    if(this->par.isVerbose()){
1033      std::cout << "Using ";
1034      if(this->par.getFlagFDR()) std::cout << "effective ";
1035      std::cout << "flux threshold of: ";
1036      float thresh = this->Stats.getThreshold();
1037      if(this->par.getFlagNegative()) thresh *= -1.;
1038      std::cout << thresh;
1039      if(this->par.getFlagGrowth()){
1040        std::cout << " and growing to threshold of: ";
1041        if(this->par.getFlagUserGrowthThreshold()) thresh= this->par.getGrowthThreshold();
1042        else thresh= this->Stats.snrToValue(this->par.getGrowthCut());
1043        if(this->par.getFlagNegative()) thresh *= -1.;
1044        std::cout << thresh;
1045      }
1046      std::cout << std::endl;
1047    }
1048
1049  }
1050  //--------------------------------------------------------------------
1051
1052  void Cube::setupFDR()
1053  {
1054    /// @details
1055    ///  Call the setupFDR(float *) function on the pixel array of the
1056    ///  cube. This is the usual way of running it.
1057    ///
1058    ///  However, if we are in smoothing mode, we calculate the FDR
1059    ///  parameters using the recon array, which holds the smoothed
1060    ///  data. Gives an error message if the reconExists flag is not set.
1061
1062    if(this->par.getFlagSmooth())
1063      if(this->reconExists) this->setupFDR(this->recon);
1064      else{
1065        DUCHAMPERROR("setupFDR", "Smoothing not done properly! Using original array for defining threshold.");
1066        this->setupFDR(this->array);
1067      }
1068    else if( this->par.getFlagATrous() ){
1069      if(this->reconExists) this->setupFDR(this->recon);
1070      else{
1071        DUCHAMPERROR("setupFDR", "Reconstruction not done properly! Using original array for defining threshold.");
1072        this->setupFDR(this->array);
1073      }
1074    }
1075    else{
1076      this->setupFDR(this->array);
1077    }
1078  }
1079  //--------------------------------------------------------------------
1080
1081  void Cube::setupFDR(float *input)
1082  {
1083    ///   @details
1084    ///   Determines the critical Probability value for the False
1085    ///   Discovery Rate detection routine. All pixels in the given arry
1086    ///   with Prob less than this value will be considered detections.
1087    ///
1088    ///   Note that the Stats of the cube need to be calculated first.
1089    ///
1090    ///   The Prob here is the probability, assuming a Normal
1091    ///   distribution, of obtaining a value as high or higher than the
1092    ///   pixel value (ie. only the positive tail of the PDF).
1093    ///
1094    ///   The probabilities are calculated using the
1095    ///   StatsContainer::getPValue(), which calculates the z-statistic,
1096    ///   and then the probability via
1097    ///   \f$0.5\operatorname{erfc}(z/\sqrt{2})\f$ -- giving the positive
1098    ///   tail probability.
1099
1100    // first calculate p-value for each pixel -- assume Gaussian for now.
1101
1102    float *orderedP = new float[this->numPixels];
1103    size_t count = 0;
1104    for(size_t x=0;x<this->axisDim[0];x++){
1105      for(size_t y=0;y<this->axisDim[1];y++){
1106        for(size_t z=0;z<this->axisDim[2];z++){
1107          size_t pix = z * this->axisDim[0]*this->axisDim[1] +
1108            y*this->axisDim[0] + x;
1109
1110          if(!(this->par.isBlank(this->array[pix])) && !this->par.isFlaggedChannel(z)){
1111            // only look at non-blank, valid pixels
1112            //            orderedP[count++] = this->Stats.getPValue(this->array[pix]);
1113            orderedP[count++] = this->Stats.getPValue(input[pix]);
1114          }
1115        }
1116      }
1117    }
1118
1119    // now order them
1120    std::stable_sort(orderedP,orderedP+count);
1121 
1122    // now find the maximum P value.
1123    size_t max = 0;
1124    double cN = 0.;
1125    // Calculate number of correlated pixels. Assume all spatial
1126    // pixels within the beam are correlated, and multiply this by the
1127    // number of correlated pixels as determined by the beam
1128    int numVox;
1129    if(this->head.beam().isDefined()) numVox = int(ceil(this->head.beam().area()));
1130    else  numVox = 1;
1131    if(this->head.canUseThirdAxis()) numVox *= this->par.getFDRnumCorChan();
1132    for(int psfCtr=1;psfCtr<=numVox;psfCtr++) cN += 1./float(psfCtr);
1133
1134    double slope = this->par.getAlpha()/cN;
1135    for(size_t loopCtr=0;loopCtr<count;loopCtr++) {
1136      if( orderedP[loopCtr] < (slope * double(loopCtr+1)/ double(count)) ){
1137        max = loopCtr;
1138      }
1139    }
1140
1141    this->Stats.setPThreshold( orderedP[max] );
1142
1143
1144    // Find real value of the P threshold by finding the inverse of the
1145    //  error function -- root finding with brute force technique
1146    //  (relatively slow, but we only do it once).
1147    double zStat     = 0.;
1148    double deltaZ    = 0.1;
1149    double tolerance = 1.e-6;
1150    double initial   = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1151    do{
1152      zStat+=deltaZ;
1153      double current = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1154      if((initial*current)<0.){
1155        zStat-=deltaZ;
1156        deltaZ/=2.;
1157      }
1158    }while(deltaZ>tolerance);
1159    this->Stats.setThreshold( zStat*this->Stats.getSpread() +
1160                              this->Stats.getMiddle() );
1161
1162    ///////////////////////////
1163    //   if(TESTING){
1164    //     std::stringstream ss;
1165    //     float *xplot = new float[2*max];
1166    //     for(int i=0;i<2*max;i++) xplot[i]=float(i)/float(count);
1167    //     cpgopen("latestFDR.ps/vcps");
1168    //     cpgpap(8.,1.);
1169    //     cpgslw(3);
1170    //     cpgenv(0,float(2*max)/float(count),0,orderedP[2*max],0,0);
1171    //     cpglab("i/N (index)", "p-value","");
1172    //     cpgpt(2*max,xplot,orderedP,DOT);
1173
1174    //     ss.str("");
1175    //     ss << "\\gm = " << this->Stats.getMiddle();
1176    //     cpgtext(max/(4.*count),0.9*orderedP[2*max],ss.str().c_str());
1177    //     ss.str("");
1178    //     ss << "\\gs = " << this->Stats.getSpread();
1179    //     cpgtext(max/(4.*count),0.85*orderedP[2*max],ss.str().c_str());
1180    //     ss.str("");
1181    //     ss << "Slope = " << slope;
1182    //     cpgtext(max/(4.*count),0.8*orderedP[2*max],ss.str().c_str());
1183    //     ss.str("");
1184    //     ss << "Alpha = " << this->par.getAlpha();
1185    //     cpgtext(max/(4.*count),0.75*orderedP[2*max],ss.str().c_str());
1186    //     ss.str("");
1187    //     ss << "c\\dN\\u = " << cN;
1188    //     cpgtext(max/(4.*count),0.7*orderedP[2*max],ss.str().c_str());
1189    //     ss.str("");
1190    //     ss << "max = "<<max << " (out of " << count << ")";
1191    //     cpgtext(max/(4.*count),0.65*orderedP[2*max],ss.str().c_str());
1192    //     ss.str("");
1193    //     ss << "Threshold = "<<zStat*this->Stats.getSpread()+this->Stats.getMiddle();
1194    //     cpgtext(max/(4.*count),0.6*orderedP[2*max],ss.str().c_str());
1195 
1196    //     cpgslw(1);
1197    //     cpgsci(RED);
1198    //     cpgmove(0,0);
1199    //     cpgdraw(1,slope);
1200    //     cpgsci(BLUE);
1201    //     cpgsls(DOTTED);
1202    //     cpgmove(0,orderedP[max]);
1203    //     cpgdraw(2*max/float(count),orderedP[max]);
1204    //     cpgmove(max/float(count),0);
1205    //     cpgdraw(max/float(count),orderedP[2*max]);
1206    //     cpgsci(GREEN);
1207    //     cpgsls(SOLID);
1208    //     for(int i=1;i<=10;i++) {
1209    //       ss.str("");
1210    //       ss << float(i)/2. << "\\gs";
1211    //       float prob = 0.5*erfc((float(i)/2.)/M_SQRT2);
1212    //       cpgtick(0, 0, 0, orderedP[2*max],
1213    //        prob/orderedP[2*max],
1214    //        0, 1, 1.5, 90., ss.str().c_str());
1215    //     }
1216    //     cpgend();
1217    //     delete [] xplot;
1218    //   }
1219    delete [] orderedP;
1220
1221  }
1222  //--------------------------------------------------------------------
1223
1224  void Cube::Search()
1225  {
1226    /// @details
1227    /// This acts as a switching function to select the correct searching function based on the user's parameters.
1228    /// @param verboseFlag If true, text is written to stdout describing the search function being used.
1229    if(this->par.getFlagATrous()){
1230      if(this->par.isVerbose()) std::cout<<"Commencing search in reconstructed cube..."<<std::endl;
1231      this->ReconSearch();
1232    } 
1233    else if(this->par.getFlagSmooth()){
1234      if(this->par.isVerbose()) std::cout<<"Commencing search in smoothed cube..."<<std::endl;
1235      this->SmoothSearch();
1236    }
1237    else{
1238      if(this->par.isVerbose()) std::cout<<"Commencing search in cube..."<<std::endl;
1239      this->CubicSearch();
1240    }
1241
1242  }
1243
1244  bool Cube::isDetection(size_t x, size_t y, size_t z)
1245  {
1246    ///  @details
1247    /// Is a given voxel at position (x,y,z) a detection, based on the statistics
1248    ///  in the Cube's StatsContainer?
1249    /// If the pixel lies outside the valid range for the data array,
1250    /// return false.
1251    /// \param x X-value of the Cube's voxel to be tested.
1252    /// \param y Y-value of the Cube's voxel to be tested.
1253    /// \param z Z-value of the Cube's voxel to be tested.
1254
1255    size_t voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
1256    return DataArray::isDetection(array[voxel]);
1257  }
1258  //--------------------------------------------------------------------
1259
1260  void Cube::calcObjectFluxes()
1261  {
1262    /// @details
1263    ///  A function to calculate the fluxes and centroids for each
1264    ///  object in the Cube's list of detections. Uses
1265    ///  Detection::calcFluxes() for each object.
1266
1267    std::vector<Detection>::iterator obj;
1268    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1269      obj->calcFluxes(this->array, this->axisDim);
1270      if(!this->par.getFlagUserThreshold())
1271          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1272    }
1273  }
1274  //--------------------------------------------------------------------
1275
1276  void Cube::calcObjectWCSparams()
1277  {
1278    ///  @details
1279    ///  A function that calculates the WCS parameters for each object in the
1280    ///  Cube's list of detections.
1281    ///  Each object gets an ID number assigned to it (which is simply its order
1282    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1283
1284    std::vector<Detection>::iterator obj;
1285    int ct=0;
1286    ProgressBar bar;
1287    if(this->par.isVerbose()) bar.init(this->objectList->size());
1288    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1289      //      std::cerr << ct << ' ' << this->array << '\n';
1290      if(this->par.isVerbose()) bar.update(ct);
1291      obj->setID(ct++);
1292      if(!obj->hasParams()){
1293        obj->setCentreType(this->par.getPixelCentre());
1294        obj->calcFluxes(this->array,this->axisDim);
1295        obj->findShape(this->array,this->axisDim,this->head);
1296        //      obj->calcWCSparams(this->array,this->axisDim,this->head);
1297        obj->calcWCSparams(this->head);
1298        obj->calcIntegFlux(this->array,this->axisDim,this->head, this->par);
1299
1300        if(!this->par.getFlagUserThreshold()){
1301           
1302            float peak=obj->getPeakFlux();
1303            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1304                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1305                Detection *newobj = new Detection(*obj);
1306                newobj->calcFluxes(this->recon,this->axisDim);
1307                peak=newobj->getPeakFlux();
1308            }
1309            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1310
1311            if(!this->par.getFlagSmooth()){
1312                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1313                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1314            }
1315
1316            if(!this->head.is2D()){
1317                double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1318                double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1319                obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1320            }
1321            if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1322        }
1323
1324
1325      }
1326    } 
1327    if(this->par.isVerbose()) bar.remove();
1328
1329    if(!this->head.isWCS()){
1330      // if the WCS is bad, set the object names to Obj01 etc
1331      int numspaces = int(log10(this->objectList->size())) + 1;
1332      std::stringstream ss;
1333      for(size_t i=0;i<this->objectList->size();i++){
1334        ss.str("");
1335        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1336        this->objectList->at(i).setName(ss.str());
1337      }
1338    }
1339 
1340  }
1341  //--------------------------------------------------------------------
1342
1343  void Cube::calcObjectWCSparams(std::vector< std::vector<PixelInfo::Voxel> > bigVoxList)
1344  {
1345    ///  @details
1346    ///  A function that calculates the WCS parameters for each object in the
1347    ///  Cube's list of detections.
1348    ///  Each object gets an ID number assigned to it (which is simply its order
1349    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1350    ///
1351    ///  This version uses vectors of Voxels to define the fluxes.
1352    ///
1353    /// \param bigVoxList A vector of vectors of Voxels, with the same
1354    /// number of elements as this->objectList, where each element is a
1355    /// vector of Voxels corresponding to the same voxels in each
1356    /// detection and indicating the flux of each voxel.
1357 
1358    std::vector<Detection>::iterator obj;
1359    int ct=0;
1360    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1361      obj->setID(ct+1);
1362      if(!obj->hasParams()){
1363        obj->setCentreType(this->par.getPixelCentre());
1364        obj->calcFluxes(bigVoxList[ct]);
1365        obj->calcWCSparams(this->head);
1366        obj->calcIntegFlux(this->axisDim[2],bigVoxList[ct],this->head);
1367       
1368        if(!this->par.getFlagUserThreshold()){
1369
1370            float peak=obj->getPeakFlux();
1371            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1372                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1373                Detection *newobj = new Detection(*obj);
1374                newobj->calcFluxes(this->recon,this->axisDim);
1375                peak=newobj->getPeakFlux();
1376            }
1377            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1378
1379            if(!this->par.getFlagSmooth()){
1380                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1381                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1382            }
1383
1384          if(!this->head.is2D()){
1385              double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1386              double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1387              obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1388          }
1389          if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1390        }
1391
1392      }
1393      ct++;
1394    } 
1395
1396    if(!this->head.isWCS()){
1397      // if the WCS is bad, set the object names to Obj01 etc
1398      int numspaces = int(log10(this->objectList->size())) + 1;
1399      std::stringstream ss;
1400      for(size_t i=0;i<this->objectList->size();i++){
1401        ss.str("");
1402        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1403        this->objectList->at(i).setName(ss.str());
1404      }
1405    }
1406 
1407  }
1408  //--------------------------------------------------------------------
1409
1410  void Cube::calcObjectWCSparams(std::map<PixelInfo::Voxel,float> &voxelMap)
1411  {
1412    ///  @details
1413    ///  A function that calculates the WCS parameters for each object in the
1414    ///  Cube's list of detections.
1415    ///  Each object gets an ID number assigned to it (which is simply its order
1416    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1417    ///
1418    ///  This version uses vectors of Voxels to define the fluxes.
1419    ///
1420    /// \param bigVoxList A vector of vectors of Voxels, with the same
1421    /// number of elements as this->objectList, where each element is a
1422    /// vector of Voxels corresponding to the same voxels in each
1423    /// detection and indicating the flux of each voxel.
1424 
1425    std::vector<Detection>::iterator obj;
1426    int ct=0;
1427    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1428      obj->setID(ct+1);
1429      if(!obj->hasParams()){
1430        obj->setCentreType(this->par.getPixelCentre());
1431        obj->calcFluxes(voxelMap);
1432        obj->calcWCSparams(this->head);
1433        obj->calcIntegFlux(this->axisDim[2],voxelMap,this->head);
1434       
1435        if(!this->par.getFlagUserThreshold()){
1436           
1437            float peak=obj->getPeakFlux();
1438            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1439                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1440                Detection *newobj = new Detection(*obj);
1441                newobj->calcFluxes(this->recon,this->axisDim);
1442                peak=newobj->getPeakFlux();
1443            }
1444            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1445
1446            if(!this->par.getFlagSmooth()){
1447                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1448                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1449            }
1450
1451          if(!this->head.is2D()){
1452              double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1453              double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1454              obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1455          }
1456          if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1457        }
1458      }
1459      ct++;
1460    } 
1461
1462    if(!this->head.isWCS()){
1463      // if the WCS is bad, set the object names to Obj01 etc
1464      int numspaces = int(log10(this->objectList->size())) + 1;
1465      std::stringstream ss;
1466      for(size_t i=0;i<this->objectList->size();i++){
1467        ss.str("");
1468        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1469        this->objectList->at(i).setName(ss.str());
1470      }
1471    }
1472 
1473  }
1474  //--------------------------------------------------------------------
1475
1476  void Cube::updateDetectMap()
1477  {
1478    /// @details A function that, for each detected object in the
1479    ///  cube's list, increments the cube's detection map by the
1480    ///  required amount at each pixel. Uses
1481    ///  updateDetectMap(Detection).
1482
1483    std::vector<Detection>::iterator obj;
1484    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1485      this->updateDetectMap(*obj);
1486    }
1487
1488  }
1489  //--------------------------------------------------------------------
1490
1491  void Cube::updateDetectMap(Detection obj)
1492  {
1493    ///  @details
1494    ///  A function that, for the given object, increments the cube's
1495    ///  detection map by the required amount at each pixel.
1496    ///
1497    ///  \param obj A Detection object that is being incorporated into the map.
1498
1499    std::vector<Voxel> vlist = obj.getPixelSet();
1500    for(std::vector<Voxel>::iterator vox=vlist.begin();vox<vlist.end();vox++) {
1501      if(this->numNondegDim==1)
1502        this->detectMap[vox->getZ()]++;
1503      else
1504        this->detectMap[vox->getX()+vox->getY()*this->axisDim[0]]++;
1505    }
1506  }
1507  //--------------------------------------------------------------------
1508
1509  float Cube::enclosedFlux(Detection obj)
1510  {
1511    ///  @details
1512    ///   A function to calculate the flux enclosed by the range
1513    ///    of pixels detected in the object obj (not necessarily all
1514    ///    pixels will have been detected).
1515    ///
1516    ///   \param obj The Detection under consideration.
1517
1518    obj.calcFluxes(this->array, this->axisDim);
1519    int xsize = obj.getXmax()-obj.getXmin()+1;
1520    int ysize = obj.getYmax()-obj.getYmin()+1;
1521    int zsize = obj.getZmax()-obj.getZmin()+1;
1522    std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1523    for(int x=0;x<xsize;x++){
1524      for(int y=0;y<ysize;y++){
1525        for(int z=0;z<zsize;z++){
1526          fluxArray[x+y*xsize+z*ysize*xsize] =
1527            this->getPixValue(x+obj.getXmin(),
1528                              y+obj.getYmin(),
1529                              z+obj.getZmin());
1530          if(this->par.getFlagNegative())
1531            fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1532        }
1533      }
1534    }
1535    float sum = 0.;
1536    for(size_t i=0;i<fluxArray.size();i++)
1537      if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1538    return sum;
1539  }
1540  //--------------------------------------------------------------------
1541
1542  void Cube::setupColumns()
1543  {
1544    /// @details
1545    ///  A front-end to the two setup routines in columns.cc. 
1546    ///
1547    ///  This first gets the starting precisions, which may be from
1548    ///  the input parameters. It then sets up the columns (calculates
1549    ///  their widths and precisions and so on based on the values
1550    ///  within). The precisions are also stored in each Detection
1551    ///  object.
1552    ///
1553    ///  Need to have called calcObjectWCSparams() somewhere
1554    ///  beforehand.
1555
1556    std::vector<Detection>::iterator obj;
1557    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1558      obj->setVelPrec( this->par.getPrecVel() );
1559      obj->setFpeakPrec( this->par.getPrecFlux() );
1560      obj->setXYZPrec( Catalogues::prXYZ );
1561      obj->setPosPrec( Catalogues::prWPOS );
1562      obj->setFintPrec( this->par.getPrecFlux() );
1563      obj->setSNRPrec( this->par.getPrecSNR() );
1564    }
1565 
1566    this->fullCols = getFullColSet(*(this->objectList), this->head);
1567
1568    if(this->par.getFlagUserThreshold()){
1569        this->fullCols.removeColumn("FTOTERR");
1570        this->fullCols.removeColumn("SNRPEAK");
1571        this->fullCols.removeColumn("FINTERR");
1572    }
1573
1574    if(this->par.getFlagSmooth()){
1575        this->fullCols.removeColumn("FTOTERR");
1576        this->fullCols.removeColumn("FINTERR");
1577    }
1578
1579    if(!this->head.isWCS()){
1580        this->fullCols.removeColumn("RA");
1581        this->fullCols.removeColumn("DEC");
1582        this->fullCols.removeColumn("VEL");
1583        this->fullCols.removeColumn("w_RA");
1584        this->fullCols.removeColumn("w_DEC");
1585    }
1586
1587    int vel,fpeak,fint,pos,xyz,snr;
1588    vel = fullCols.column("VEL").getPrecision();
1589    fpeak = fullCols.column("FPEAK").getPrecision();
1590    if(!this->par.getFlagUserThreshold())
1591        snr = fullCols.column("SNRPEAK").getPrecision();
1592    xyz = fullCols.column("X").getPrecision();
1593    xyz = std::max(xyz, fullCols.column("Y").getPrecision());
1594    xyz = std::max(xyz, fullCols.column("Z").getPrecision());
1595    if(this->head.isWCS()) fint = fullCols.column("FINT").getPrecision();
1596    else fint = fullCols.column("FTOT").getPrecision();
1597    pos = fullCols.column("WRA").getPrecision();
1598    pos = std::max(pos, fullCols.column("WDEC").getPrecision());
1599 
1600    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1601      obj->setVelPrec(vel);
1602      obj->setFpeakPrec(fpeak);
1603      obj->setXYZPrec(xyz);
1604      obj->setPosPrec(pos);
1605      obj->setFintPrec(fint);
1606      if(!this->par.getFlagUserThreshold())
1607          obj->setSNRPrec(snr);
1608    }
1609
1610  }
1611  //--------------------------------------------------------------------
1612
1613  bool Cube::objAtSpatialEdge(Detection obj)
1614  {
1615    ///  @details
1616    ///   A function to test whether the object obj
1617    ///    lies at the edge of the cube's spatial field --
1618    ///    either at the boundary, or next to BLANKs.
1619    ///
1620    ///   \param obj The Detection under consideration.
1621
1622    bool atEdge = false;
1623
1624    size_t pix = 0;
1625    std::vector<Voxel> voxlist = obj.getPixelSet();
1626    while(!atEdge && pix<voxlist.size()){
1627      // loop over each pixel in the object, until we find an edge pixel.
1628      for(int dx=-1;dx<=1;dx+=2){
1629        if( ((voxlist[pix].getX()+dx)<0) ||
1630            ((voxlist[pix].getX()+dx)>=int(this->axisDim[0])) )
1631          atEdge = true;
1632        else if(this->isBlank(voxlist[pix].getX()+dx,
1633                              voxlist[pix].getY(),
1634                              voxlist[pix].getZ()))
1635          atEdge = true;
1636      }
1637      for(int dy=-1;dy<=1;dy+=2){
1638        if( ((voxlist[pix].getY()+dy)<0) ||
1639            ((voxlist[pix].getY()+dy)>=int(this->axisDim[1])) )
1640          atEdge = true;
1641        else if(this->isBlank(voxlist[pix].getX(),
1642                              voxlist[pix].getY()+dy,
1643                              voxlist[pix].getZ()))
1644          atEdge = true;
1645      }
1646      pix++;
1647    }
1648
1649    return atEdge;
1650  }
1651  //--------------------------------------------------------------------
1652
1653  bool Cube::objAtSpectralEdge(Detection obj)
1654  {
1655    ///   @details
1656    ///   A function to test whether the object obj
1657    ///    lies at the edge of the cube's spectral extent --
1658    ///    either at the boundary, or next to BLANKs.
1659    ///
1660    ///   \param obj The Detection under consideration.
1661
1662    bool atEdge = false;
1663
1664    size_t pix = 0;
1665    std::vector<Voxel> voxlist = obj.getPixelSet();
1666    while(!atEdge && pix<voxlist.size()){
1667      // loop over each pixel in the object, until we find an edge pixel.
1668      for(int dz=-1;dz<=1;dz+=2){
1669        if( ((voxlist[pix].getZ()+dz)<0) ||
1670            ((voxlist[pix].getZ()+dz)>=int(this->axisDim[2])) )
1671          atEdge = true;
1672        else if(this->isBlank(voxlist[pix].getX(),
1673                              voxlist[pix].getY(),
1674                              voxlist[pix].getZ()+dz))
1675          atEdge = true;
1676      }
1677      pix++;
1678    }
1679
1680    return atEdge;
1681  }
1682  //--------------------------------------------------------------------
1683
1684    bool Cube::objNextToFlaggedChan(Detection &obj)
1685    {
1686   ///   @details A function to test whether the object obj lies
1687    ///   adjacent to a flagged channel or straddles one or more
1688    ///   (conceivably, you could have disconnected channels in your
1689    ///   object that don't touch flagged channels, but lie either side -
1690    ///   in this case we want to flag the object).
1691    ///
1692    ///   We scan across the channel range from one below the 
1693    ///   \param obj The Detection under consideration.
1694
1695        bool isNext=false;
1696        int zstart=std::max(obj.getZmin()-1,0L);
1697        int zend=std::min(obj.getZmax()+1,long(this->axisDim[2]-1));
1698        for(int z=zstart;z<=zend && !isNext; z++)
1699            isNext = isNext || this->par.isFlaggedChannel(z);
1700        return isNext;
1701
1702    }
1703
1704  //--------------------------------------------------------------------
1705
1706  void Cube::setObjectFlags()
1707  {
1708    /// @details
1709    ///   A function to set any warning flags for all the detected objects
1710    ///    associated with the cube.
1711    ///   Flags to be looked for:
1712    ///    <ul><li> Negative enclosed flux (N)
1713    ///        <li> Detection at edge of field (spatially) (E)
1714    ///        <li> Detection at edge of spectral region (S)
1715    ///    </ul>
1716
1717    std::vector<Detection>::iterator obj;
1718    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1719
1720      if( this->enclosedFlux(*obj) < 0. ) 
1721        obj->addToFlagText("N");
1722
1723      if( this->objAtSpatialEdge(*obj) )
1724        obj->addToFlagText("E");
1725
1726      if( this->objAtSpectralEdge(*obj) && (this->axisDim[2] > 2))
1727        obj->addToFlagText("S");
1728
1729      if( this->objNextToFlaggedChan(*obj) )
1730        obj->addToFlagText("F");
1731
1732      if(obj->getFlagText()=="") obj->addToFlagText("-");
1733
1734    }
1735
1736  }
1737  //--------------------------------------------------------------------
1738
1739    OUTCOME Cube::saveReconstructedCube()
1740    {
1741        std::string report;
1742        OUTCOME result=SUCCESS;
1743        if(!this->par.getFlagUsePrevious()){
1744            if(this->par.getFlagATrous()){
1745                if(this->par.getFlagOutputRecon()){
1746                    if(this->par.isVerbose())
1747                        std::cout << "  Saving reconstructed cube to " << this->par.outputReconFile() << "... "<<std::flush;
1748                    WriteReconArray writer(this);
1749                    writer.setFilename(this->par.outputReconFile());
1750                    result = writer.write();
1751                    report=(result==FAILURE)?"Failed!":"done.";
1752                    if(this->par.isVerbose()) std::cout << report << "\n";
1753                }
1754                if(result==SUCCESS && this->par.getFlagOutputResid()){
1755                    if(this->par.isVerbose())
1756                        std::cout << "  Saving reconstruction residual cube to " << this->par.outputResidFile() << "... "<<std::flush;
1757                    WriteReconArray writer(this);
1758                    writer.setFilename(this->par.outputResidFile());
1759                    writer.setIsRecon(false);
1760                    result = writer.write();
1761                    report=(result==FAILURE)?"Failed!":"done.";
1762                    if(this->par.isVerbose()) std::cout << report << "\n";
1763                }
1764            }
1765        }
1766        return result;
1767    }
1768
1769    OUTCOME Cube::saveSmoothedCube()
1770    {
1771        std::string report;
1772        OUTCOME result=SUCCESS;
1773        if(!this->par.getFlagUsePrevious()){
1774            if(this->par.getFlagSmooth() && this->par.getFlagOutputSmooth()){
1775                if(this->par.isVerbose())
1776                    std::cout << "  Saving smoothed cube to " << this->par.outputSmoothFile() << "... "<<std::flush;
1777                WriteSmoothArray writer(this);
1778                writer.setFilename(this->par.outputSmoothFile());
1779                result = writer.write();
1780                report=(result==FAILURE)?"Failed!":"done.";
1781                if(this->par.isVerbose()) std::cout << report << "\n";
1782            }
1783        }
1784        return result;
1785    }
1786
1787    OUTCOME Cube::saveMaskCube()
1788    {
1789        std::string report;
1790        OUTCOME result=SUCCESS;
1791        if(this->par.getFlagOutputMask()){
1792            if(this->par.isVerbose())
1793                std::cout << "  Saving mask cube to " << this->par.outputMaskFile() << "... "<<std::flush;
1794            WriteMaskArray writer(this);
1795            writer.setFilename(this->par.outputMaskFile());
1796            OUTCOME result = writer.write();
1797            report=(result==FAILURE)?"Failed!":"done.";
1798            if(this->par.isVerbose()) std::cout << report << "\n";
1799        }
1800        return result;
1801    }
1802
1803    OUTCOME Cube::saveMomentMapImage()
1804    {
1805        std::string report;
1806        OUTCOME result=SUCCESS;
1807        if(this->par.getFlagOutputMomentMap()){
1808            if(this->par.isVerbose())
1809                std::cout << "  Saving moment map to " << this->par.outputMomentMapFile() << "... "<<std::flush;
1810            WriteMomentMapArray writer(this);
1811            writer.setFilename(this->par.outputMomentMapFile());
1812            OUTCOME result = writer.write();
1813            report=(result==FAILURE)?"Failed!":"done.";
1814            if(this->par.isVerbose()) std::cout << report << "\n";
1815        }
1816        return result;
1817    }
1818
1819    OUTCOME Cube::saveMomentMask()
1820    {
1821        std::string report;
1822        OUTCOME result=SUCCESS;
1823        if(this->par.getFlagOutputMomentMask()){
1824            if(this->par.isVerbose())
1825                std::cout << "  Saving moment-0 mask to " << this->par.outputMomentMaskFile() << "... "<<std::flush;
1826            WriteMomentMaskArray writer(this);
1827            writer.setFilename(this->par.outputMomentMaskFile());
1828            OUTCOME result = writer.write();
1829            report=(result==FAILURE)?"Failed!":"done.";
1830            if(this->par.isVerbose()) std::cout << report << "\n";
1831        }
1832        return result;
1833    }
1834
1835    OUTCOME Cube::saveBaselineCube()
1836    {
1837        std::string report;
1838        OUTCOME result=SUCCESS;
1839        if(this->par.getFlagOutputBaseline()){
1840            if(this->par.isVerbose())
1841                std::cout << "  Saving baseline cube to " << this->par.outputBaselineFile() << "... "<<std::flush;
1842            WriteBaselineArray writer(this);
1843            writer.setFilename(this->par.outputBaselineFile());
1844            OUTCOME result = writer.write();
1845            report=(result==FAILURE)?"Failed!":"done.";
1846            if(this->par.isVerbose()) std::cout << report << "\n";
1847        }
1848        return result;
1849    }
1850   
1851    void Cube::writeToFITS()
1852    {
1853        this->saveReconstructedCube();
1854        this->saveSmoothedCube();
1855        this->saveMomentMapImage();
1856        this->saveMomentMask();
1857        this->saveBaselineCube();
1858        this->saveMaskCube();
1859    }
1860
1861
1862  /****************************************************************/
1863  /////////////////////////////////////////////////////////////
1864  //// Functions for Image class
1865  /////////////////////////////////////////////////////////////
1866
1867  Image::Image(size_t size)
1868  {
1869    // need error handling in case size<0 !!!
1870    this->numPixels = this->numDim = 0;
1871    this->minSize = 2;
1872    if(size<0){
1873      DUCHAMPERROR("Image(size)","Negative size -- could not define Image");
1874    }
1875    else{
1876      if(size>0 && !this->arrayAllocated){
1877        this->array = new float[size];
1878        this->arrayAllocated = true;
1879      }
1880      this->numPixels = size;
1881      this->axisDim = new size_t[2];
1882      this->axisDimAllocated = true;
1883      this->numDim = 2;
1884    }
1885  }
1886  //--------------------------------------------------------------------
1887
1888  Image::Image(size_t *dimensions)
1889  {
1890    this->numPixels = this->numDim = 0;
1891    this->minSize = 2;
1892    int size = dimensions[0] * dimensions[1];
1893    if(size<0){
1894      DUCHAMPERROR("Image(dimArray)","Negative size -- could not define Image");
1895    }
1896    else{
1897      this->numPixels = size;
1898      if(size>0){
1899        this->array = new float[size];
1900        this->arrayAllocated = true;
1901      }
1902      this->numDim=2;
1903      this->axisDim = new size_t[2];
1904      this->axisDimAllocated = true;
1905      for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1906    }
1907  }
1908  //--------------------------------------------------------------------
1909  Image::Image(const Image &i):
1910    DataArray(i)
1911  {
1912    this->operator=(i);
1913  }
1914
1915  Image& Image::operator=(const Image &i)
1916  {
1917    if(this==&i) return *this;
1918    ((DataArray &) *this) = i;
1919    this->minSize = i.minSize;
1920    return *this;
1921  }
1922
1923  //--------------------------------------------------------------------
1924
1925  void Image::saveArray(float *input, size_t size)
1926  {
1927    /// @details
1928    /// Saves the array in input to the pixel array Image::array.
1929    /// The size of the array given must be the same as the current number of
1930    /// pixels, else an error message is returned and nothing is done.
1931    /// \param input The array of values to be saved.
1932    /// \param size The size of input.
1933
1934    if(size != this->numPixels){
1935      DUCHAMPERROR("Image::saveArray", "Input array different size to existing array. Cannot save.");
1936    }
1937    else {
1938      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1939      this->numPixels = size;
1940      if(this->numPixels>0){
1941        this->array = new float[size];
1942        this->arrayAllocated = true;
1943        for(size_t i=0;i<size;i++) this->array[i] = input[i];
1944      }
1945    }
1946  }
1947  //--------------------------------------------------------------------
1948
1949  void Image::extractSpectrum(float *Array, size_t *dim, size_t pixel)
1950  {
1951    /// @details
1952    ///  A function to extract a 1-D spectrum from a 3-D array.
1953    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1954    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1955    ///    by the third argument.
1956    ///  The extracted spectrum is stored in the pixel array Image::array.
1957    /// \param Array The array containing the pixel values, from which
1958    ///               the spectrum is extracted.
1959    /// \param dim The array of dimension values.
1960    /// \param pixel The spatial pixel that contains the desired spectrum.
1961
1962    if((pixel<0)||(pixel>=dim[0]*dim[1])){
1963      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1964    }
1965    else if(dim[2] != this->numPixels){
1966      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1967    }
1968    else {
1969      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1970      this->numPixels = dim[2];
1971      if(this->numPixels>0){
1972        this->array = new float[dim[2]];
1973        this->arrayAllocated = true;
1974        for(size_t z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1975      }
1976    }
1977  }
1978  //--------------------------------------------------------------------
1979
1980  void Image::extractSpectrum(Cube &cube, size_t pixel)
1981  {
1982    /// @details
1983    ///  A function to extract a 1-D spectrum from a Cube class
1984    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1985    ///    by the second argument.
1986    ///  The extracted spectrum is stored in the pixel array Image::array.
1987    /// \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1988    /// \param pixel The spatial pixel that contains the desired spectrum.
1989
1990    size_t zdim = cube.getDimZ();
1991    size_t spatSize = cube.getDimX()*cube.getDimY();
1992    if((pixel<0)||(pixel>=spatSize)){
1993      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1994    }
1995    else if(zdim != this->numPixels){
1996      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1997    }
1998    else {
1999      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
2000      this->numPixels = zdim;
2001      if(this->numPixels>0){
2002        this->array = new float[zdim];
2003        this->arrayAllocated = true;
2004        for(size_t z=0;z<zdim;z++)
2005          this->array[z] = cube.getPixValue(z*spatSize + pixel);
2006      }
2007    }
2008  }
2009  //--------------------------------------------------------------------
2010
2011  void Image::extractImage(float *Array, size_t *dim, size_t channel)
2012  {
2013    /// @details
2014    ///  A function to extract a 2-D image from a 3-D array.
2015    ///  The array is assumed to be 3-D with the third dimension the spectral one.
2016    ///  The dimensions of the array are in the dim[] array.
2017    ///  The image extracted is the one lying in the channel referenced
2018    ///    by the third argument.
2019    ///  The extracted image is stored in the pixel array Image::array.
2020    /// \param Array The array containing the pixel values, from which the image is extracted.
2021    /// \param dim The array of dimension values.
2022    /// \param channel The spectral channel that contains the desired image.
2023
2024    size_t spatSize = dim[0]*dim[1];
2025    if((channel<0)||(channel>=dim[2])){
2026      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
2027    }
2028    else if(spatSize != this->numPixels){
2029      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
2030    }
2031    else {
2032      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
2033      this->numPixels = spatSize;
2034      if(this->numPixels>0){
2035        this->array = new float[spatSize];
2036        this->arrayAllocated = true;
2037        for(size_t npix=0; npix<spatSize; npix++)
2038          this->array[npix] = Array[channel*spatSize + npix];
2039      }
2040    }
2041  }
2042  //--------------------------------------------------------------------
2043
2044  void Image::extractImage(Cube &cube, size_t channel)
2045  {
2046    /// @details
2047    ///  A function to extract a 2-D image from Cube class.
2048    ///  The image extracted is the one lying in the channel referenced
2049    ///    by the second argument.
2050    ///  The extracted image is stored in the pixel array Image::array.
2051    /// \param cube The Cube containing the pixel values, from which the image is extracted.
2052    /// \param channel The spectral channel that contains the desired image.
2053
2054    size_t spatSize = cube.getDimX()*cube.getDimY();
2055    if((channel<0)||(channel>=cube.getDimZ())){
2056      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
2057    }
2058    else if(spatSize != this->numPixels){
2059      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
2060    }
2061    else {
2062      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
2063      this->numPixels = spatSize;
2064      if(this->numPixels>0){
2065        this->array = new float[spatSize];
2066        this->arrayAllocated = true;
2067        for(size_t npix=0; npix<spatSize; npix++)
2068          this->array[npix] = cube.getPixValue(channel*spatSize + npix);
2069      }
2070    }
2071  }
2072  //--------------------------------------------------------------------
2073
2074  void Image::removeFlaggedChannels()
2075  {
2076    /// @details
2077    ///  A function to remove the flagged channels from a 1-D spectrum.
2078    ///  The array in this Image is assumed to be 1-D, with only the first axisDim
2079    ///    equal to 1.
2080    ///  The values of the flagged channels are set to 0, unless they are BLANK.
2081
2082    if(this->axisDim[1]==1) {
2083
2084        std::vector<int> flaggedChans = this->par.getFlaggedChannels();
2085        for(std::vector<int>::iterator chan = flaggedChans.begin();chan!=flaggedChans.end();chan++){
2086            // channels are zero-based
2087            if(!this->isBlank(*chan)) this->array[*chan]=0.;
2088        }
2089
2090    }
2091  }
2092
2093  //--------------------------------------------------------------------
2094
2095  std::vector<Object2D> Image::findSources2D()
2096  {
2097    std::vector<bool> thresholdedArray(this->axisDim[0]*this->axisDim[1]);
2098    for(size_t posY=0;posY<this->axisDim[1];posY++){
2099      for(size_t posX=0;posX<this->axisDim[0];posX++){
2100        size_t loc = posX + this->axisDim[0]*posY;
2101        thresholdedArray[loc] = this->isDetection(posX,posY);
2102      }
2103    }
2104    return lutz_detect(thresholdedArray, this->axisDim[0], this->axisDim[1], this->minSize);
2105  }
2106
2107  std::vector<Scan> Image::findSources1D()
2108  {
2109    std::vector<bool> thresholdedArray(this->axisDim[0]);
2110    for(size_t posX=0;posX<this->axisDim[0];posX++){
2111      thresholdedArray[posX] = this->isDetection(posX,0);
2112    }
2113    return spectrumDetect(thresholdedArray, this->axisDim[0], this->minSize);
2114  }
2115
2116
2117  std::vector< std::vector<PixelInfo::Voxel> > Cube::getObjVoxList()
2118  {
2119   
2120    std::vector< std::vector<PixelInfo::Voxel> > biglist;
2121   
2122    std::vector<Detection>::iterator obj;
2123    for(obj=this->objectList->begin(); obj<this->objectList->end(); obj++) {
2124
2125      Cube *subcube = new Cube;
2126      subcube->pars() = this->par;
2127      subcube->pars().setVerbosity(false);
2128      subcube->pars().setFlagSubsection(true);
2129      duchamp::Section sec = obj->getBoundingSection();
2130      subcube->pars().setSubsection( sec.getSection() );
2131      if(subcube->pars().verifySubsection() == FAILURE)
2132        DUCHAMPERROR("get object voxel list","Unable to verify the subsection - something's wrong!");
2133      if(subcube->getCube() == FAILURE)
2134        DUCHAMPERROR("get object voxel list","Unable to read the FITS file - something's wrong!");
2135      std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
2136      std::vector<PixelInfo::Voxel>::iterator vox;
2137      for(vox=voxlist.begin(); vox<voxlist.end(); vox++){
2138        size_t pix = (vox->getX()-subcube->pars().getXOffset()) +
2139          subcube->getDimX()*(vox->getY()-subcube->pars().getYOffset()) +
2140          subcube->getDimX()*subcube->getDimY()*(vox->getZ()-subcube->pars().getZOffset());
2141        vox->setF( subcube->getPixValue(pix) );
2142      }
2143      biglist.push_back(voxlist);
2144      delete subcube;
2145
2146    }
2147
2148    return biglist;
2149
2150  }
2151
2152}
Note: See TracBrowser for help on using the repository browser.