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

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

Ticket #193 - Removing all the MW-related code. Most of it was commented out, but Param now no longer has anything referring to MW. The flag array in ObjectGrower? has also changed to FLAG from MW.

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