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

Last change on this file since 1447 was 1447, checked in by MatthewWhiting, 4 years ago

Changing a couple of cubes functions to use references rather than direct copies.
Also adding a mergeIntoList function to the DataArray? class

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