source: tags/release-1.6.1/src/Cubes/cubes.cc @ 1441

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

A large changeset that moves all use of bool arrays (ie. bool *) to std::vector<bool>. This will be a bit safer, plus potentially cheaper in memory usage. Interfaces to all stats functions with masks have changed accordingly, as well as the Gaussian smoothing functions and some spectral utility functions.

File size: 75.2 KB
Line 
1// -----------------------------------------------------------------------
2// cubes.cc: Member functions for the DataArray, Cube and Image classes.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <unistd.h>
29#include <iostream>
30#include <iomanip>
31#include <vector>
32#include <algorithm>
33#include <string>
34#include <math.h>
35
36#include <wcslib/wcs.h>
37
38#include <duchamp/pgheader.hh>
39
40#include <duchamp/duchamp.hh>
41#include <duchamp/param.hh>
42#include <duchamp/fitsHeader.hh>
43#include <duchamp/Cubes/cubes.hh>
44#include <duchamp/PixelMap/Voxel.hh>
45#include <duchamp/PixelMap/Object3D.hh>
46#include <duchamp/Detection/detection.hh>
47#include <duchamp/Outputs/columns.hh>
48#include <duchamp/Detection/finders.hh>
49#include <duchamp/Utils/utils.hh>
50#include <duchamp/Utils/feedback.hh>
51#include <duchamp/Utils/mycpgplot.hh>
52#include <duchamp/Utils/Statistics.hh>
53#include <duchamp/FitsIO/DuchampBeam.hh>
54#include <duchamp/FitsIO/WriteReconArray.hh>
55#include <duchamp/FitsIO/WriteSmoothArray.hh>
56#include <duchamp/FitsIO/WriteMaskArray.hh>
57#include <duchamp/FitsIO/WriteMomentMapArray.hh>
58#include <duchamp/FitsIO/WriteMomentMaskArray.hh>
59#include <duchamp/FitsIO/WriteBaselineArray.hh>
60
61using namespace mycpgplot;
62using namespace Statistics;
63using namespace PixelInfo;
64
65#ifdef TEST_DEBUG
66const bool TESTING=true;
67#else
68const bool TESTING=false;
69#endif
70
71namespace duchamp
72{
73
74  using namespace Catalogues;
75
76  /****************************************************************/
77  ///////////////////////////////////////////////////
78  //// Functions for DataArray class:
79  ///////////////////////////////////////////////////
80
81    DataArray::DataArray():
82        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 needMask=true;
911      if (!this->par.getFlagBlankPix() && !this->par.getFlagStatSec() && (this->par.getFlaggedChannels().size()==0) )
912        needMask=false;
913
914      std::vector<bool> mask;
915      size_t vox=0,goodSize=this->numPixels;
916      if (needMask) {
917        mask = std::vector<bool>(this->numPixels,false);
918        goodSize = 0;
919        for(size_t z=0;z<this->axisDim[2];z++){
920          for(size_t y=0;y<this->axisDim[1];y++){
921            for(size_t x=0;x<this->axisDim[0];x++){
922              //            vox = z * xysize + y*this->axisDim[0] + x;
923              bool isBlank=this->isBlank(vox);
924              bool statOK = this->par.isStatOK(x,y,z);
925              bool isFlagged = this->par.isFlaggedChannel(z);
926              mask[vox] = (!isBlank && !isFlagged && statOK );
927              if(mask[vox]) goodSize++;
928              vox++;
929            }
930          }
931        }
932      }
933
934      //      float mean,median,stddev,madfm;
935      if( this->par.getFlagATrous() ){
936        // Case #2 -- wavelet reconstruction
937        // just get mean & median from orig array, and rms & madfm from
938        // residual recompute array values to be residuals & then find
939        // stddev & madfm
940        if(!this->reconExists){
941          DUCHAMPERROR("setCubeStats", "Reconstruction not yet done! Cannot calculate stats!");
942        }
943        else{
944          float *tempArray = new float[goodSize];
945
946          goodSize=0;
947          vox=0;
948          for(size_t z=0;z<this->axisDim[2];z++){
949            for(size_t y=0;y<this->axisDim[1];y++){
950              for(size_t x=0;x<this->axisDim[0];x++){
951                //              vox = z * xysize + y*this->axisDim[0] + x;
952                if(!needMask || mask[vox]) tempArray[goodSize++] = this->array[vox];
953                vox++;
954              }
955            }
956          }
957
958          // First, find the mean of the original array. Store it.
959          float mean = findMean<float>(tempArray, goodSize);
960       
961          // Now sort it and find the median. Store it.
962          float median = findMedian<float>(tempArray, goodSize, true);
963
964          // Now calculate the residuals and find the mean & median of
965          // them. We don't store these, but they are necessary to find
966          // the sttdev & madfm.
967          goodSize = 0;
968          //      for(int p=0;p<xysize;p++){
969          vox=0;
970          for(size_t z=0;z<this->axisDim[2];z++){
971            for(size_t y=0;y<this->axisDim[1];y++){
972              for(size_t x=0;x<this->axisDim[0];x++){
973                //            vox = z * xysize + p;
974              if(!needMask || mask[vox])
975                tempArray[goodSize++] = this->array[vox] - this->recon[vox];
976              vox++;
977              }
978            }
979          }
980           
981          float stddev = findStddev<float>(tempArray, goodSize);
982
983          // Now find the madfm of the residuals. Store it.
984          float madfm = findMADFM<float>(tempArray, goodSize, true);
985
986          this->Stats.define(mean,median,stddev,madfm);
987
988          delete [] tempArray;
989        }
990      }
991      else if(this->par.getFlagSmooth()) {
992        // Case #3 -- smoothing
993        // get all four stats from the recon array, which holds the
994        // smoothed data. This can just be done with the
995        // StatsContainer::calculate function, using the mask generated
996        // earlier.
997        if(!this->reconExists){
998          DUCHAMPERROR("setCubeStats","Smoothing not yet done! Cannot calculate stats!");
999        }
1000        else{
1001          if(needMask) this->Stats.calculate(this->recon,this->numPixels,mask);
1002          else         this->Stats.calculate(this->recon,this->numPixels);
1003        }
1004      }
1005      else{
1006        // Case #1 -- default case, with no smoothing or reconstruction.
1007        // get all four stats from the original array. This can just be
1008        // done with the StatsContainer::calculate function, using the
1009        // mask generated earlier.
1010        if(needMask) this->Stats.calculate(this->array,this->numPixels,mask);
1011        else         this->Stats.calculate(this->array,this->numPixels);
1012      }
1013
1014      this->Stats.setUseFDR( this->par.getFlagFDR() );
1015      // If the FDR method has been requested, define the P-value
1016      // threshold
1017      if(this->par.getFlagFDR())  this->setupFDR();
1018      else{
1019        // otherwise, calculate threshold based on the requested SNR cut
1020        // level, and then set the threshold parameter in the Par set.
1021        this->Stats.setThresholdSNR( this->par.getCut() );
1022        this->par.setThreshold( this->Stats.getThreshold() );
1023      }
1024   
1025    }
1026
1027    if(this->par.isVerbose()){
1028      std::cout << "Using ";
1029      if(this->par.getFlagFDR()) std::cout << "effective ";
1030      std::cout << "flux threshold of: ";
1031      float thresh = this->Stats.getThreshold();
1032      if(this->par.getFlagNegative()) thresh *= -1.;
1033      std::cout << thresh;
1034      if(this->par.getFlagGrowth()){
1035        std::cout << " and growing to threshold of: ";
1036        if(this->par.getFlagUserGrowthThreshold()) thresh= this->par.getGrowthThreshold();
1037        else thresh= this->Stats.snrToValue(this->par.getGrowthCut());
1038        if(this->par.getFlagNegative()) thresh *= -1.;
1039        std::cout << thresh;
1040      }
1041      std::cout << std::endl;
1042    }
1043
1044  }
1045  //--------------------------------------------------------------------
1046
1047  void Cube::setupFDR()
1048  {
1049    /// @details
1050    ///  Call the setupFDR(float *) function on the pixel array of the
1051    ///  cube. This is the usual way of running it.
1052    ///
1053    ///  However, if we are in smoothing mode, we calculate the FDR
1054    ///  parameters using the recon array, which holds the smoothed
1055    ///  data. Gives an error message if the reconExists flag is not set.
1056
1057    if(this->par.getFlagSmooth())
1058      if(this->reconExists) this->setupFDR(this->recon);
1059      else{
1060        DUCHAMPERROR("setupFDR", "Smoothing not done properly! Using original array for defining threshold.");
1061        this->setupFDR(this->array);
1062      }
1063    else if( this->par.getFlagATrous() ){
1064      if(this->reconExists) this->setupFDR(this->recon);
1065      else{
1066        DUCHAMPERROR("setupFDR", "Reconstruction not done properly! Using original array for defining threshold.");
1067        this->setupFDR(this->array);
1068      }
1069    }
1070    else{
1071      this->setupFDR(this->array);
1072    }
1073  }
1074  //--------------------------------------------------------------------
1075
1076  void Cube::setupFDR(float *input)
1077  {
1078    ///   @details
1079    ///   Determines the critical Probability value for the False
1080    ///   Discovery Rate detection routine. All pixels in the given arry
1081    ///   with Prob less than this value will be considered detections.
1082    ///
1083    ///   Note that the Stats of the cube need to be calculated first.
1084    ///
1085    ///   The Prob here is the probability, assuming a Normal
1086    ///   distribution, of obtaining a value as high or higher than the
1087    ///   pixel value (ie. only the positive tail of the PDF).
1088    ///
1089    ///   The probabilities are calculated using the
1090    ///   StatsContainer::getPValue(), which calculates the z-statistic,
1091    ///   and then the probability via
1092    ///   \f$0.5\operatorname{erfc}(z/\sqrt{2})\f$ -- giving the positive
1093    ///   tail probability.
1094
1095    // first calculate p-value for each pixel -- assume Gaussian for now.
1096
1097    float *orderedP = new float[this->numPixels];
1098    size_t count = 0;
1099    for(size_t x=0;x<this->axisDim[0];x++){
1100      for(size_t y=0;y<this->axisDim[1];y++){
1101        for(size_t z=0;z<this->axisDim[2];z++){
1102          size_t pix = z * this->axisDim[0]*this->axisDim[1] +
1103            y*this->axisDim[0] + x;
1104
1105          if(!(this->par.isBlank(this->array[pix])) && !this->par.isFlaggedChannel(z)){
1106            // only look at non-blank, valid pixels
1107            //            orderedP[count++] = this->Stats.getPValue(this->array[pix]);
1108            orderedP[count++] = this->Stats.getPValue(input[pix]);
1109          }
1110        }
1111      }
1112    }
1113
1114    // now order them
1115    std::stable_sort(orderedP,orderedP+count);
1116 
1117    // now find the maximum P value.
1118    size_t max = 0;
1119    double cN = 0.;
1120    // Calculate number of correlated pixels. Assume all spatial
1121    // pixels within the beam are correlated, and multiply this by the
1122    // number of correlated pixels as determined by the beam
1123    int numVox;
1124    if(this->head.beam().isDefined()) numVox = int(ceil(this->head.beam().area()));
1125    else  numVox = 1;
1126    if(this->head.canUseThirdAxis()) numVox *= this->par.getFDRnumCorChan();
1127    for(int psfCtr=1;psfCtr<=numVox;psfCtr++) cN += 1./float(psfCtr);
1128
1129    double slope = this->par.getAlpha()/cN;
1130    for(size_t loopCtr=0;loopCtr<count;loopCtr++) {
1131      if( orderedP[loopCtr] < (slope * double(loopCtr+1)/ double(count)) ){
1132        max = loopCtr;
1133      }
1134    }
1135
1136    this->Stats.setPThreshold( orderedP[max] );
1137
1138
1139    // Find real value of the P threshold by finding the inverse of the
1140    //  error function -- root finding with brute force technique
1141    //  (relatively slow, but we only do it once).
1142    double zStat     = 0.;
1143    double deltaZ    = 0.1;
1144    double tolerance = 1.e-6;
1145    double initial   = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1146    do{
1147      zStat+=deltaZ;
1148      double current = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
1149      if((initial*current)<0.){
1150        zStat-=deltaZ;
1151        deltaZ/=2.;
1152      }
1153    }while(deltaZ>tolerance);
1154    this->Stats.setThreshold( zStat*this->Stats.getSpread() +
1155                              this->Stats.getMiddle() );
1156
1157    ///////////////////////////
1158    //   if(TESTING){
1159    //     std::stringstream ss;
1160    //     float *xplot = new float[2*max];
1161    //     for(int i=0;i<2*max;i++) xplot[i]=float(i)/float(count);
1162    //     cpgopen("latestFDR.ps/vcps");
1163    //     cpgpap(8.,1.);
1164    //     cpgslw(3);
1165    //     cpgenv(0,float(2*max)/float(count),0,orderedP[2*max],0,0);
1166    //     cpglab("i/N (index)", "p-value","");
1167    //     cpgpt(2*max,xplot,orderedP,DOT);
1168
1169    //     ss.str("");
1170    //     ss << "\\gm = " << this->Stats.getMiddle();
1171    //     cpgtext(max/(4.*count),0.9*orderedP[2*max],ss.str().c_str());
1172    //     ss.str("");
1173    //     ss << "\\gs = " << this->Stats.getSpread();
1174    //     cpgtext(max/(4.*count),0.85*orderedP[2*max],ss.str().c_str());
1175    //     ss.str("");
1176    //     ss << "Slope = " << slope;
1177    //     cpgtext(max/(4.*count),0.8*orderedP[2*max],ss.str().c_str());
1178    //     ss.str("");
1179    //     ss << "Alpha = " << this->par.getAlpha();
1180    //     cpgtext(max/(4.*count),0.75*orderedP[2*max],ss.str().c_str());
1181    //     ss.str("");
1182    //     ss << "c\\dN\\u = " << cN;
1183    //     cpgtext(max/(4.*count),0.7*orderedP[2*max],ss.str().c_str());
1184    //     ss.str("");
1185    //     ss << "max = "<<max << " (out of " << count << ")";
1186    //     cpgtext(max/(4.*count),0.65*orderedP[2*max],ss.str().c_str());
1187    //     ss.str("");
1188    //     ss << "Threshold = "<<zStat*this->Stats.getSpread()+this->Stats.getMiddle();
1189    //     cpgtext(max/(4.*count),0.6*orderedP[2*max],ss.str().c_str());
1190 
1191    //     cpgslw(1);
1192    //     cpgsci(RED);
1193    //     cpgmove(0,0);
1194    //     cpgdraw(1,slope);
1195    //     cpgsci(BLUE);
1196    //     cpgsls(DOTTED);
1197    //     cpgmove(0,orderedP[max]);
1198    //     cpgdraw(2*max/float(count),orderedP[max]);
1199    //     cpgmove(max/float(count),0);
1200    //     cpgdraw(max/float(count),orderedP[2*max]);
1201    //     cpgsci(GREEN);
1202    //     cpgsls(SOLID);
1203    //     for(int i=1;i<=10;i++) {
1204    //       ss.str("");
1205    //       ss << float(i)/2. << "\\gs";
1206    //       float prob = 0.5*erfc((float(i)/2.)/M_SQRT2);
1207    //       cpgtick(0, 0, 0, orderedP[2*max],
1208    //        prob/orderedP[2*max],
1209    //        0, 1, 1.5, 90., ss.str().c_str());
1210    //     }
1211    //     cpgend();
1212    //     delete [] xplot;
1213    //   }
1214    delete [] orderedP;
1215
1216  }
1217  //--------------------------------------------------------------------
1218
1219  void Cube::Search()
1220  {
1221    /// @details
1222    /// This acts as a switching function to select the correct searching function based on the user's parameters.
1223    /// @param verboseFlag If true, text is written to stdout describing the search function being used.
1224    if(this->par.getFlagATrous()){
1225      if(this->par.isVerbose()) std::cout<<"Commencing search in reconstructed cube..."<<std::endl;
1226      this->ReconSearch();
1227    } 
1228    else if(this->par.getFlagSmooth()){
1229      if(this->par.isVerbose()) std::cout<<"Commencing search in smoothed cube..."<<std::endl;
1230      this->SmoothSearch();
1231    }
1232    else{
1233      if(this->par.isVerbose()) std::cout<<"Commencing search in cube..."<<std::endl;
1234      this->CubicSearch();
1235    }
1236
1237  }
1238
1239  bool Cube::isDetection(size_t x, size_t y, size_t z)
1240  {
1241    ///  @details
1242    /// Is a given voxel at position (x,y,z) a detection, based on the statistics
1243    ///  in the Cube's StatsContainer?
1244    /// If the pixel lies outside the valid range for the data array,
1245    /// return false.
1246    /// \param x X-value of the Cube's voxel to be tested.
1247    /// \param y Y-value of the Cube's voxel to be tested.
1248    /// \param z Z-value of the Cube's voxel to be tested.
1249
1250    size_t voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
1251    return DataArray::isDetection(array[voxel]);
1252  }
1253  //--------------------------------------------------------------------
1254
1255  void Cube::calcObjectFluxes()
1256  {
1257    /// @details
1258    ///  A function to calculate the fluxes and centroids for each
1259    ///  object in the Cube's list of detections. Uses
1260    ///  Detection::calcFluxes() for each object.
1261
1262    std::vector<Detection>::iterator obj;
1263    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1264      obj->calcFluxes(this->array, this->axisDim);
1265      if(!this->par.getFlagUserThreshold())
1266          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1267    }
1268  }
1269  //--------------------------------------------------------------------
1270
1271  void Cube::calcObjectWCSparams()
1272  {
1273    ///  @details
1274    ///  A function that calculates the WCS parameters for each object in the
1275    ///  Cube's list of detections.
1276    ///  Each object gets an ID number assigned to it (which is simply its order
1277    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1278
1279    std::vector<Detection>::iterator obj;
1280    int ct=0;
1281    ProgressBar bar;
1282    if(this->par.isVerbose()) bar.init(this->objectList->size());
1283    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1284      //      std::cerr << ct << ' ' << this->array << '\n';
1285      if(this->par.isVerbose()) bar.update(ct);
1286      obj->setID(ct++);
1287      if(!obj->hasParams()){
1288        obj->setCentreType(this->par.getPixelCentre());
1289        obj->calcFluxes(this->array,this->axisDim);
1290        obj->findShape(this->array,this->axisDim,this->head);
1291        //      obj->calcWCSparams(this->array,this->axisDim,this->head);
1292        obj->calcWCSparams(this->head);
1293        obj->calcIntegFlux(this->array,this->axisDim,this->head, this->par);
1294
1295        if(!this->par.getFlagUserThreshold()){
1296           
1297            float peak=obj->getPeakFlux();
1298            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1299                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1300                Detection *newobj = new Detection(*obj);
1301                newobj->calcFluxes(this->recon,this->axisDim);
1302                peak=newobj->getPeakFlux();
1303                delete newobj;
1304            }
1305            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1306
1307            if(!this->par.getFlagSmooth()){
1308                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1309                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1310            }
1311
1312            if(!this->head.is2D()){
1313                double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1314                double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1315                obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1316            }
1317            if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1318        }
1319
1320
1321      }
1322    } 
1323    if(this->par.isVerbose()) bar.remove();
1324
1325    if(!this->head.isWCS()){
1326      // if the WCS is bad, set the object names to Obj01 etc
1327      int numspaces = int(log10(this->objectList->size())) + 1;
1328      std::stringstream ss;
1329      for(size_t i=0;i<this->objectList->size();i++){
1330        ss.str("");
1331        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1332        this->objectList->at(i).setName(ss.str());
1333      }
1334    }
1335 
1336  }
1337  //--------------------------------------------------------------------
1338
1339  void Cube::calcObjectWCSparams(std::vector< std::vector<PixelInfo::Voxel> > bigVoxList)
1340  {
1341    ///  @details
1342    ///  A function that calculates the WCS parameters for each object in the
1343    ///  Cube's list of detections.
1344    ///  Each object gets an ID number assigned to it (which is simply its order
1345    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1346    ///
1347    ///  This version uses vectors of Voxels to define the fluxes.
1348    ///
1349    /// \param bigVoxList A vector of vectors of Voxels, with the same
1350    /// number of elements as this->objectList, where each element is a
1351    /// vector of Voxels corresponding to the same voxels in each
1352    /// detection and indicating the flux of each voxel.
1353 
1354    std::vector<Detection>::iterator obj;
1355    int ct=0;
1356    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1357      obj->setID(ct+1);
1358      if(!obj->hasParams()){
1359        obj->setCentreType(this->par.getPixelCentre());
1360        obj->calcFluxes(bigVoxList[ct]);
1361        obj->calcWCSparams(this->head);
1362        obj->calcIntegFlux(this->axisDim[2],bigVoxList[ct],this->head);
1363       
1364        if(!this->par.getFlagUserThreshold()){
1365
1366            float peak=obj->getPeakFlux();
1367            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1368                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1369                Detection *newobj = new Detection(*obj);
1370                newobj->calcFluxes(this->recon,this->axisDim);
1371                peak=newobj->getPeakFlux();
1372            }
1373            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1374
1375            if(!this->par.getFlagSmooth()){
1376                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1377                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1378            }
1379
1380          if(!this->head.is2D()){
1381              double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1382              double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1383              obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1384          }
1385          if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1386        }
1387
1388      }
1389      ct++;
1390    } 
1391
1392    if(!this->head.isWCS()){
1393      // if the WCS is bad, set the object names to Obj01 etc
1394      int numspaces = int(log10(this->objectList->size())) + 1;
1395      std::stringstream ss;
1396      for(size_t i=0;i<this->objectList->size();i++){
1397        ss.str("");
1398        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1399        this->objectList->at(i).setName(ss.str());
1400      }
1401    }
1402 
1403  }
1404  //--------------------------------------------------------------------
1405
1406  void Cube::calcObjectWCSparams(std::map<PixelInfo::Voxel,float> &voxelMap)
1407  {
1408    ///  @details
1409    ///  A function that calculates the WCS parameters for each object in the
1410    ///  Cube's list of detections.
1411    ///  Each object gets an ID number assigned to it (which is simply its order
1412    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1413    ///
1414    ///  This version uses vectors of Voxels to define the fluxes.
1415    ///
1416    /// \param bigVoxList A vector of vectors of Voxels, with the same
1417    /// number of elements as this->objectList, where each element is a
1418    /// vector of Voxels corresponding to the same voxels in each
1419    /// detection and indicating the flux of each voxel.
1420 
1421    std::vector<Detection>::iterator obj;
1422    int ct=0;
1423    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1424      obj->setID(ct+1);
1425      if(!obj->hasParams()){
1426        obj->setCentreType(this->par.getPixelCentre());
1427        obj->calcFluxes(voxelMap);
1428        obj->calcWCSparams(this->head);
1429        obj->calcIntegFlux(this->axisDim[2],voxelMap,this->head);
1430       
1431        if(!this->par.getFlagUserThreshold()){
1432           
1433            float peak=obj->getPeakFlux();
1434            if(this->par.getFlagATrous() || this->par.getFlagSmooth()) {
1435                // for these situations, need to measure peak flux in the reconstructed array, where we do the searching
1436                Detection *newobj = new Detection(*obj);
1437                newobj->calcFluxes(this->recon,this->axisDim);
1438                peak=newobj->getPeakFlux();
1439            }
1440            obj->setPeakSNR( (peak - this->Stats.getMiddle()) / this->Stats.getSpread() );
1441
1442            if(!this->par.getFlagSmooth()){
1443                obj->setTotalFluxError( sqrt(float(obj->getSize())) * this->Stats.getSpread() );
1444                obj->setIntegFluxError( sqrt(double(obj->getSize())) * this->Stats.getSpread() );
1445            }
1446
1447          if(!this->head.is2D()){
1448              double x=obj->getXcentre(),y=obj->getYcentre(),z1=obj->getZcentre(),z2=z1+1;
1449              double dz=this->head.pixToVel(x,y,z1)-this->head.pixToVel(x,y,z2);
1450              obj->setIntegFluxError( obj->getIntegFluxError() * fabs(dz));
1451          }
1452          if(head.needBeamSize()) obj->setIntegFluxError( obj->getIntegFluxError()  / head.beam().area() );
1453        }
1454      }
1455      ct++;
1456    } 
1457
1458    if(!this->head.isWCS()){
1459      // if the WCS is bad, set the object names to Obj01 etc
1460      int numspaces = int(log10(this->objectList->size())) + 1;
1461      std::stringstream ss;
1462      for(size_t i=0;i<this->objectList->size();i++){
1463        ss.str("");
1464        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1465        this->objectList->at(i).setName(ss.str());
1466      }
1467    }
1468 
1469  }
1470  //--------------------------------------------------------------------
1471
1472  void Cube::updateDetectMap()
1473  {
1474    /// @details A function that, for each detected object in the
1475    ///  cube's list, increments the cube's detection map by the
1476    ///  required amount at each pixel. Uses
1477    ///  updateDetectMap(Detection).
1478
1479    std::vector<Detection>::iterator obj;
1480    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1481      this->updateDetectMap(*obj);
1482    }
1483
1484  }
1485  //--------------------------------------------------------------------
1486
1487  void Cube::updateDetectMap(Detection obj)
1488  {
1489    ///  @details
1490    ///  A function that, for the given object, increments the cube's
1491    ///  detection map by the required amount at each pixel.
1492    ///
1493    ///  \param obj A Detection object that is being incorporated into the map.
1494
1495    std::vector<Voxel> vlist = obj.getPixelSet();
1496    for(std::vector<Voxel>::iterator vox=vlist.begin();vox<vlist.end();vox++) {
1497      if(this->numNondegDim==1)
1498        this->detectMap[vox->getZ()]++;
1499      else
1500        this->detectMap[vox->getX()+vox->getY()*this->axisDim[0]]++;
1501    }
1502  }
1503  //--------------------------------------------------------------------
1504
1505  float Cube::enclosedFlux(Detection obj)
1506  {
1507    ///  @details
1508    ///   A function to calculate the flux enclosed by the range
1509    ///    of pixels detected in the object obj (not necessarily all
1510    ///    pixels will have been detected).
1511    ///
1512    ///   \param obj The Detection under consideration.
1513
1514    obj.calcFluxes(this->array, this->axisDim);
1515    int xsize = obj.getXmax()-obj.getXmin()+1;
1516    int ysize = obj.getYmax()-obj.getYmin()+1;
1517    int zsize = obj.getZmax()-obj.getZmin()+1;
1518    std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1519    for(int x=0;x<xsize;x++){
1520      for(int y=0;y<ysize;y++){
1521        for(int z=0;z<zsize;z++){
1522          fluxArray[x+y*xsize+z*ysize*xsize] =
1523            this->getPixValue(x+obj.getXmin(),
1524                              y+obj.getYmin(),
1525                              z+obj.getZmin());
1526          if(this->par.getFlagNegative())
1527            fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1528        }
1529      }
1530    }
1531    float sum = 0.;
1532    for(size_t i=0;i<fluxArray.size();i++)
1533      if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1534    return sum;
1535  }
1536  //--------------------------------------------------------------------
1537
1538  void Cube::setupColumns()
1539  {
1540    /// @details
1541    ///  A front-end to the two setup routines in columns.cc. 
1542    ///
1543    ///  This first gets the starting precisions, which may be from
1544    ///  the input parameters. It then sets up the columns (calculates
1545    ///  their widths and precisions and so on based on the values
1546    ///  within). The precisions are also stored in each Detection
1547    ///  object.
1548    ///
1549    ///  Need to have called calcObjectWCSparams() somewhere
1550    ///  beforehand.
1551
1552    std::vector<Detection>::iterator obj;
1553    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1554      obj->setVelPrec( this->par.getPrecVel() );
1555      obj->setFpeakPrec( this->par.getPrecFlux() );
1556      obj->setXYZPrec( Catalogues::prXYZ );
1557      obj->setPosPrec( Catalogues::prWPOS );
1558      obj->setFintPrec( this->par.getPrecFlux() );
1559      obj->setSNRPrec( this->par.getPrecSNR() );
1560    }
1561 
1562    this->fullCols = getFullColSet(*(this->objectList), this->head);
1563
1564    if(this->par.getFlagUserThreshold()){
1565        this->fullCols.removeColumn("FTOTERR");
1566        this->fullCols.removeColumn("SNRPEAK");
1567        this->fullCols.removeColumn("FINTERR");
1568    }
1569
1570    if(this->par.getFlagSmooth()){
1571        this->fullCols.removeColumn("FTOTERR");
1572        this->fullCols.removeColumn("FINTERR");
1573    }
1574
1575    if(!this->head.isWCS()){
1576        this->fullCols.removeColumn("RA");
1577        this->fullCols.removeColumn("DEC");
1578        this->fullCols.removeColumn("VEL");
1579        this->fullCols.removeColumn("w_RA");
1580        this->fullCols.removeColumn("w_DEC");
1581    }
1582
1583    int vel,fpeak,fint,pos,xyz,snr;
1584    vel = fullCols.column("VEL").getPrecision();
1585    fpeak = fullCols.column("FPEAK").getPrecision();
1586    if(!this->par.getFlagUserThreshold())
1587        snr = fullCols.column("SNRPEAK").getPrecision();
1588    xyz = fullCols.column("X").getPrecision();
1589    xyz = std::max(xyz, fullCols.column("Y").getPrecision());
1590    xyz = std::max(xyz, fullCols.column("Z").getPrecision());
1591    if(this->head.isWCS()) fint = fullCols.column("FINT").getPrecision();
1592    else fint = fullCols.column("FTOT").getPrecision();
1593    pos = fullCols.column("WRA").getPrecision();
1594    pos = std::max(pos, fullCols.column("WDEC").getPrecision());
1595 
1596    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1597      obj->setVelPrec(vel);
1598      obj->setFpeakPrec(fpeak);
1599      obj->setXYZPrec(xyz);
1600      obj->setPosPrec(pos);
1601      obj->setFintPrec(fint);
1602      if(!this->par.getFlagUserThreshold())
1603          obj->setSNRPrec(snr);
1604    }
1605
1606  }
1607  //--------------------------------------------------------------------
1608
1609  bool Cube::objAtSpatialEdge(Detection obj)
1610  {
1611    ///  @details
1612    ///   A function to test whether the object obj
1613    ///    lies at the edge of the cube's spatial field --
1614    ///    either at the boundary, or next to BLANKs.
1615    ///
1616    ///   \param obj The Detection under consideration.
1617
1618    bool atEdge = false;
1619
1620    size_t pix = 0;
1621    std::vector<Voxel> voxlist = obj.getPixelSet();
1622    while(!atEdge && pix<voxlist.size()){
1623      // loop over each pixel in the object, until we find an edge pixel.
1624      for(int dx=-1;dx<=1;dx+=2){
1625        if( ((voxlist[pix].getX()+dx)<0) ||
1626            ((voxlist[pix].getX()+dx)>=int(this->axisDim[0])) )
1627          atEdge = true;
1628        else if(this->isBlank(voxlist[pix].getX()+dx,
1629                              voxlist[pix].getY(),
1630                              voxlist[pix].getZ()))
1631          atEdge = true;
1632      }
1633      for(int dy=-1;dy<=1;dy+=2){
1634        if( ((voxlist[pix].getY()+dy)<0) ||
1635            ((voxlist[pix].getY()+dy)>=int(this->axisDim[1])) )
1636          atEdge = true;
1637        else if(this->isBlank(voxlist[pix].getX(),
1638                              voxlist[pix].getY()+dy,
1639                              voxlist[pix].getZ()))
1640          atEdge = true;
1641      }
1642      pix++;
1643    }
1644
1645    return atEdge;
1646  }
1647  //--------------------------------------------------------------------
1648
1649  bool Cube::objAtSpectralEdge(Detection obj)
1650  {
1651    ///   @details
1652    ///   A function to test whether the object obj
1653    ///    lies at the edge of the cube's spectral extent --
1654    ///    either at the boundary, or next to BLANKs.
1655    ///
1656    ///   \param obj The Detection under consideration.
1657
1658    bool atEdge = false;
1659
1660    size_t pix = 0;
1661    std::vector<Voxel> voxlist = obj.getPixelSet();
1662    while(!atEdge && pix<voxlist.size()){
1663      // loop over each pixel in the object, until we find an edge pixel.
1664      for(int dz=-1;dz<=1;dz+=2){
1665        if( ((voxlist[pix].getZ()+dz)<0) ||
1666            ((voxlist[pix].getZ()+dz)>=int(this->axisDim[2])) )
1667          atEdge = true;
1668        else if(this->isBlank(voxlist[pix].getX(),
1669                              voxlist[pix].getY(),
1670                              voxlist[pix].getZ()+dz))
1671          atEdge = true;
1672      }
1673      pix++;
1674    }
1675
1676    return atEdge;
1677  }
1678  //--------------------------------------------------------------------
1679
1680    bool Cube::objNextToFlaggedChan(Detection &obj)
1681    {
1682   ///   @details A function to test whether the object obj lies
1683    ///   adjacent to a flagged channel or straddles one or more
1684    ///   (conceivably, you could have disconnected channels in your
1685    ///   object that don't touch flagged channels, but lie either side -
1686    ///   in this case we want to flag the object).
1687    ///
1688    ///   We scan across the channel range from one below the 
1689    ///   \param obj The Detection under consideration.
1690
1691        bool isNext=false;
1692        int zstart=std::max(obj.getZmin()-1,0L);
1693        int zend=std::min(obj.getZmax()+1,long(this->axisDim[2]-1));
1694        for(int z=zstart;z<=zend && !isNext; z++)
1695            isNext = isNext || this->par.isFlaggedChannel(z);
1696        return isNext;
1697
1698    }
1699
1700  //--------------------------------------------------------------------
1701
1702  void Cube::setObjectFlags()
1703  {
1704    /// @details
1705    ///   A function to set any warning flags for all the detected objects
1706    ///    associated with the cube.
1707    ///   Flags to be looked for:
1708    ///    <ul><li> Negative enclosed flux (N)
1709    ///        <li> Detection at edge of field (spatially) (E)
1710    ///        <li> Detection at edge of spectral region (S)
1711    ///    </ul>
1712
1713    std::vector<Detection>::iterator obj;
1714    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1715
1716      if( (!this->par.getFlagNegative() &&this->enclosedFlux(*obj) < 0.) ||
1717          (this->par.getFlagNegative() && this->enclosedFlux(*obj)>0.)) 
1718        obj->addToFlagText("N");
1719
1720      if( this->objAtSpatialEdge(*obj) )
1721        obj->addToFlagText("E");
1722
1723      if( this->objAtSpectralEdge(*obj) && (this->axisDim[2] > 2))
1724        obj->addToFlagText("S");
1725
1726      if( this->objNextToFlaggedChan(*obj) )
1727        obj->addToFlagText("F");
1728
1729      if(obj->getFlagText()=="") obj->addToFlagText("-");
1730
1731    }
1732
1733  }
1734  //--------------------------------------------------------------------
1735
1736    OUTCOME Cube::saveReconstructedCube()
1737    {
1738        std::string report;
1739        OUTCOME result=SUCCESS;
1740        if(!this->par.getFlagUsePrevious()){
1741            if(this->par.getFlagATrous()){
1742                if(this->par.getFlagOutputRecon()){
1743                    if(this->par.isVerbose())
1744                        std::cout << "  Saving reconstructed cube to " << this->par.outputReconFile() << "... "<<std::flush;
1745                    WriteReconArray writer(this);
1746                    writer.setFilename(this->par.outputReconFile());
1747                    result = writer.write();
1748                    report=(result==FAILURE)?"Failed!":"done.";
1749                    if(this->par.isVerbose()) std::cout << report << "\n";
1750                }
1751                if(result==SUCCESS && this->par.getFlagOutputResid()){
1752                    if(this->par.isVerbose())
1753                        std::cout << "  Saving reconstruction residual cube to " << this->par.outputResidFile() << "... "<<std::flush;
1754                    WriteReconArray writer(this);
1755                    writer.setFilename(this->par.outputResidFile());
1756                    writer.setIsRecon(false);
1757                    result = writer.write();
1758                    report=(result==FAILURE)?"Failed!":"done.";
1759                    if(this->par.isVerbose()) std::cout << report << "\n";
1760                }
1761            }
1762        }
1763        return result;
1764    }
1765
1766    OUTCOME Cube::saveSmoothedCube()
1767    {
1768        std::string report;
1769        OUTCOME result=SUCCESS;
1770        if(!this->par.getFlagUsePrevious()){
1771            if(this->par.getFlagSmooth() && this->par.getFlagOutputSmooth()){
1772                if(this->par.isVerbose())
1773                    std::cout << "  Saving smoothed cube to " << this->par.outputSmoothFile() << "... "<<std::flush;
1774                WriteSmoothArray writer(this);
1775                writer.setFilename(this->par.outputSmoothFile());
1776                result = writer.write();
1777                report=(result==FAILURE)?"Failed!":"done.";
1778                if(this->par.isVerbose()) std::cout << report << "\n";
1779            }
1780        }
1781        return result;
1782    }
1783
1784    OUTCOME Cube::saveMaskCube()
1785    {
1786        std::string report;
1787        OUTCOME result=SUCCESS;
1788        if(this->par.getFlagOutputMask()){
1789            if(this->par.isVerbose())
1790                std::cout << "  Saving mask cube to " << this->par.outputMaskFile() << "... "<<std::flush;
1791            WriteMaskArray writer(this);
1792            writer.setFilename(this->par.outputMaskFile());
1793            OUTCOME result = writer.write();
1794            report=(result==FAILURE)?"Failed!":"done.";
1795            if(this->par.isVerbose()) std::cout << report << "\n";
1796        }
1797        return result;
1798    }
1799
1800    OUTCOME Cube::saveMomentMapImage()
1801    {
1802        std::string report;
1803        OUTCOME result=SUCCESS;
1804        if(this->par.getFlagOutputMomentMap()){
1805            if(this->par.isVerbose())
1806                std::cout << "  Saving moment map to " << this->par.outputMomentMapFile() << "... "<<std::flush;
1807            WriteMomentMapArray writer(this);
1808            writer.setFilename(this->par.outputMomentMapFile());
1809            OUTCOME result = writer.write();
1810            report=(result==FAILURE)?"Failed!":"done.";
1811            if(this->par.isVerbose()) std::cout << report << "\n";
1812        }
1813        return result;
1814    }
1815
1816    OUTCOME Cube::saveMomentMask()
1817    {
1818        std::string report;
1819        OUTCOME result=SUCCESS;
1820        if(this->par.getFlagOutputMomentMask()){
1821            if(this->par.isVerbose())
1822                std::cout << "  Saving moment-0 mask to " << this->par.outputMomentMaskFile() << "... "<<std::flush;
1823            WriteMomentMaskArray writer(this);
1824            writer.setFilename(this->par.outputMomentMaskFile());
1825            OUTCOME result = writer.write();
1826            report=(result==FAILURE)?"Failed!":"done.";
1827            if(this->par.isVerbose()) std::cout << report << "\n";
1828        }
1829        return result;
1830    }
1831
1832    OUTCOME Cube::saveBaselineCube()
1833    {
1834        std::string report;
1835        OUTCOME result=SUCCESS;
1836        if(this->par.getFlagOutputBaseline()){
1837            if(this->par.isVerbose())
1838                std::cout << "  Saving baseline cube to " << this->par.outputBaselineFile() << "... "<<std::flush;
1839            WriteBaselineArray writer(this);
1840            writer.setFilename(this->par.outputBaselineFile());
1841            OUTCOME result = writer.write();
1842            report=(result==FAILURE)?"Failed!":"done.";
1843            if(this->par.isVerbose()) std::cout << report << "\n";
1844        }
1845        return result;
1846    }
1847   
1848    void Cube::writeToFITS()
1849    {
1850        this->saveReconstructedCube();
1851        this->saveSmoothedCube();
1852        this->saveMomentMapImage();
1853        this->saveMomentMask();
1854        this->saveBaselineCube();
1855        this->saveMaskCube();
1856    }
1857
1858
1859  /****************************************************************/
1860  /////////////////////////////////////////////////////////////
1861  //// Functions for Image class
1862  /////////////////////////////////////////////////////////////
1863
1864  Image::Image(size_t size)
1865  {
1866    this->numPixels = this->numDim = 0;
1867    this->minSize = 2;
1868    if(!this->arrayAllocated){
1869        this->array = new float[size];
1870        this->arrayAllocated = true;
1871    }
1872    this->numPixels = size;
1873    this->axisDim = new size_t[2];
1874    this->axisDimAllocated = true;
1875    this->numDim = 2;
1876  }
1877  //--------------------------------------------------------------------
1878
1879  Image::Image(size_t *dimensions)
1880  {
1881    this->numPixels = this->numDim = 0;
1882    this->minSize = 2;
1883    size_t size = dimensions[0] * dimensions[1];
1884    this->numPixels = size;
1885    this->array = new float[size];
1886    this->arrayAllocated = true;
1887    this->numDim=2;
1888    this->axisDim = new size_t[2];
1889    this->axisDimAllocated = true;
1890    for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1891  }
1892  //--------------------------------------------------------------------
1893  Image::Image(const Image &i):
1894    DataArray(i)
1895  {
1896    this->operator=(i);
1897  }
1898
1899  Image& Image::operator=(const Image &i)
1900  {
1901    if(this==&i) return *this;
1902    ((DataArray &) *this) = i;
1903    this->minSize = i.minSize;
1904    return *this;
1905  }
1906
1907  //--------------------------------------------------------------------
1908
1909  void Image::saveArray(float *input, size_t size)
1910  {
1911    /// @details
1912    /// Saves the array in input to the pixel array Image::array.
1913    /// The size of the array given must be the same as the current number of
1914    /// pixels, else an error message is returned and nothing is done.
1915    /// \param input The array of values to be saved.
1916    /// \param size The size of input.
1917
1918    if(size != this->numPixels){
1919      DUCHAMPERROR("Image::saveArray", "Input array different size to existing array. Cannot save.");
1920    }
1921    else {
1922      if(this->numPixels>0 && this->arrayAllocated){
1923        delete [] array;
1924        this->arrayAllocated=false;
1925      }
1926      this->numPixels = size;
1927      if(this->numPixels>0){
1928        this->array = new float[size];
1929        this->arrayAllocated = true;
1930        for(size_t i=0;i<size;i++) this->array[i] = input[i];
1931      }
1932    }
1933  }
1934  //--------------------------------------------------------------------
1935
1936  void Image::extractSpectrum(float *Array, size_t *dim, size_t pixel)
1937  {
1938    /// @details
1939    ///  A function to extract a 1-D spectrum from a 3-D array.
1940    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1941    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1942    ///    by the third argument.
1943    ///  The extracted spectrum is stored in the pixel array Image::array.
1944    /// \param Array The array containing the pixel values, from which
1945    ///               the spectrum is extracted.
1946    /// \param dim The array of dimension values.
1947    /// \param pixel The spatial pixel that contains the desired spectrum.
1948
1949    if(pixel>=dim[0]*dim[1]){
1950      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1951    }
1952    else if(dim[2] != this->numPixels){
1953      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1954    }
1955    else {
1956      if(this->numPixels>0 && this->arrayAllocated){
1957        delete [] array;
1958        this->arrayAllocated=false;
1959      }
1960      this->numPixels = dim[2];
1961      if(this->numPixels>0){
1962        this->array = new float[dim[2]];
1963        this->arrayAllocated = true;
1964        for(size_t z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1965      }
1966    }
1967  }
1968  //--------------------------------------------------------------------
1969
1970  void Image::extractSpectrum(Cube &cube, size_t pixel)
1971  {
1972    /// @details
1973    ///  A function to extract a 1-D spectrum from a Cube class
1974    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1975    ///    by the second argument.
1976    ///  The extracted spectrum is stored in the pixel array Image::array.
1977    /// \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1978    /// \param pixel The spatial pixel that contains the desired spectrum.
1979
1980    size_t zdim = cube.getDimZ();
1981    size_t spatSize = cube.getDimX()*cube.getDimY();
1982    if(pixel>=spatSize){
1983      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1984    }
1985    else if(zdim != this->numPixels){
1986      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1987    }
1988    else {
1989      if(this->numPixels>0 && this->arrayAllocated){
1990        delete [] array;
1991        this->arrayAllocated=false;
1992      }
1993      this->numPixels = zdim;
1994      if(this->numPixels>0){
1995        this->array = new float[zdim];
1996        this->arrayAllocated = true;
1997        for(size_t z=0;z<zdim;z++)
1998          this->array[z] = cube.getPixValue(z*spatSize + pixel);
1999      }
2000    }
2001  }
2002  //--------------------------------------------------------------------
2003
2004  void Image::extractImage(float *Array, size_t *dim, size_t channel)
2005  {
2006    /// @details
2007    ///  A function to extract a 2-D image from a 3-D array.
2008    ///  The array is assumed to be 3-D with the third dimension the spectral one.
2009    ///  The dimensions of the array are in the dim[] array.
2010    ///  The image extracted is the one lying in the channel referenced
2011    ///    by the third argument.
2012    ///  The extracted image is stored in the pixel array Image::array.
2013    /// \param Array The array containing the pixel values, from which the image is extracted.
2014    /// \param dim The array of dimension values.
2015    /// \param channel The spectral channel that contains the desired image.
2016
2017    size_t spatSize = dim[0]*dim[1];
2018    if(channel>=dim[2]){
2019      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
2020    }
2021    else if(spatSize != this->numPixels){
2022      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
2023    }
2024    else {
2025      if(this->numPixels>0 && this->arrayAllocated){
2026        delete [] array;
2027        this->arrayAllocated = false;
2028      }
2029      this->numPixels = spatSize;
2030      if(this->numPixels>0){
2031        this->array = new float[spatSize];
2032        this->arrayAllocated = true;
2033        for(size_t npix=0; npix<spatSize; npix++)
2034          this->array[npix] = Array[channel*spatSize + npix];
2035      }
2036    }
2037  }
2038  //--------------------------------------------------------------------
2039
2040  void Image::extractImage(Cube &cube, size_t channel)
2041  {
2042    /// @details
2043    ///  A function to extract a 2-D image from Cube class.
2044    ///  The image extracted is the one lying in the channel referenced
2045    ///    by the second argument.
2046    ///  The extracted image is stored in the pixel array Image::array.
2047    /// \param cube The Cube containing the pixel values, from which the image is extracted.
2048    /// \param channel The spectral channel that contains the desired image.
2049
2050    size_t spatSize = cube.getDimX()*cube.getDimY();
2051    if(channel>=cube.getDimZ()){
2052      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
2053    }
2054    else if(spatSize != this->numPixels){
2055      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
2056    }
2057    else {
2058      if(this->numPixels>0 && this->arrayAllocated){
2059        delete [] array;
2060        this->arrayAllocated=false;
2061      }
2062      this->numPixels = spatSize;
2063      if(this->numPixels>0){
2064        this->array = new float[spatSize];
2065        this->arrayAllocated = true;
2066        for(size_t npix=0; npix<spatSize; npix++)
2067          this->array[npix] = cube.getPixValue(channel*spatSize + npix);
2068      }
2069    }
2070  }
2071  //--------------------------------------------------------------------
2072
2073  void Image::removeFlaggedChannels()
2074  {
2075    /// @details
2076    ///  A function to remove the flagged channels from a 1-D spectrum.
2077    ///  The array in this Image is assumed to be 1-D, with only the first axisDim
2078    ///    equal to 1.
2079    ///  The values of the flagged channels are set to 0, unless they are BLANK.
2080
2081    if(this->axisDim[1]==1) {
2082
2083        std::vector<int> flaggedChans = this->par.getFlaggedChannels();
2084        for(std::vector<int>::iterator chan = flaggedChans.begin();chan!=flaggedChans.end();chan++){
2085            // channels are zero-based
2086            if(!this->isBlank(*chan)) this->array[*chan]=0.;
2087        }
2088
2089    }
2090  }
2091
2092  //--------------------------------------------------------------------
2093
2094  std::vector<Object2D> Image::findSources2D()
2095  {
2096    std::vector<bool> thresholdedArray(this->axisDim[0]*this->axisDim[1]);
2097    for(size_t posY=0;posY<this->axisDim[1];posY++){
2098      for(size_t posX=0;posX<this->axisDim[0];posX++){
2099        size_t loc = posX + this->axisDim[0]*posY;
2100        thresholdedArray[loc] = this->isDetection(posX,posY);
2101      }
2102    }
2103    return lutz_detect(thresholdedArray, this->axisDim[0], this->axisDim[1], this->minSize);
2104  }
2105
2106  std::vector<Scan> Image::findSources1D()
2107  {
2108    std::vector<bool> thresholdedArray(this->axisDim[0]);
2109    for(size_t posX=0;posX<this->axisDim[0];posX++){
2110      thresholdedArray[posX] = this->isDetection(posX,0);
2111    }
2112    return spectrumDetect(thresholdedArray, this->axisDim[0], this->minSize);
2113  }
2114
2115
2116  std::vector< std::vector<PixelInfo::Voxel> > Cube::getObjVoxList()
2117  {
2118   
2119    std::vector< std::vector<PixelInfo::Voxel> > biglist;
2120   
2121    std::vector<Detection>::iterator obj;
2122    for(obj=this->objectList->begin(); obj<this->objectList->end(); obj++) {
2123
2124      Cube *subcube = new Cube;
2125      subcube->pars() = this->par;
2126      subcube->pars().setVerbosity(false);
2127      subcube->pars().setFlagSubsection(true);
2128      duchamp::Section sec = obj->getBoundingSection();
2129      subcube->pars().setSubsection( sec.getSection() );
2130      if(subcube->pars().verifySubsection() == FAILURE)
2131        DUCHAMPERROR("get object voxel list","Unable to verify the subsection - something's wrong!");
2132      if(subcube->getCube() == FAILURE)
2133        DUCHAMPERROR("get object voxel list","Unable to read the FITS file - something's wrong!");
2134      std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
2135      std::vector<PixelInfo::Voxel>::iterator vox;
2136      for(vox=voxlist.begin(); vox<voxlist.end(); vox++){
2137        size_t pix = (vox->getX()-subcube->pars().getXOffset()) +
2138          subcube->getDimX()*(vox->getY()-subcube->pars().getYOffset()) +
2139          subcube->getDimX()*subcube->getDimY()*(vox->getZ()-subcube->pars().getZOffset());
2140        vox->setF( subcube->getPixValue(pix) );
2141      }
2142      biglist.push_back(voxlist);
2143      delete subcube;
2144
2145    }
2146
2147    return biglist;
2148
2149  }
2150
2151}
Note: See TracBrowser for help on using the repository browser.