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

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

Making sure we don't calculate or print the peak SNR when there is no noise measurement.

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