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

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

In addressing Andrew's problem #208, I'm looking at only generating the mask when we need it. If it isn't required because there are no blank pixels or flagged channels, and no stats section, then we don't make it or use it.

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