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

Last change on this file since 1361 was 1361, checked in by MatthewWhiting, 10 years ago

Ticket #219 - largely addressing the problem. When doing negative + baseline modes, need to undo the inversion & baseline removal in two steps, so that we get the right sequencing of parameterisation and inversion of values. Also removing the reInvert() function, as it is no longer used.

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