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

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

Ticket #131: Enabling the writing of the moment-0 mask image, with updated documentation. Also removing the code that wrote the BLANK/BSCALE etc parameters to integer FITS files - the only integer files we write are the mask ones which don't need them.

File size: 71.7 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.getThreshold() );
1290      else
1291        obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1292    }
1293  }
1294  //--------------------------------------------------------------------
1295
1296  void Cube::calcObjectWCSparams()
1297  {
1298    ///  @details
1299    ///  A function that calculates the WCS parameters for each object in the
1300    ///  Cube's list of detections.
1301    ///  Each object gets an ID number assigned to it (which is simply its order
1302    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1303
1304    std::vector<Detection>::iterator obj;
1305    int ct=0;
1306    ProgressBar bar;
1307    if(this->par.isVerbose()) bar.init(this->objectList->size());
1308    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1309      //      std::cerr << ct << ' ' << this->array << '\n';
1310      if(this->par.isVerbose()) bar.update(ct);
1311      obj->setID(ct++);
1312      if(!obj->hasParams()){
1313        obj->setCentreType(this->par.getPixelCentre());
1314        obj->calcFluxes(this->array,this->axisDim);
1315        //      obj->calcWCSparams(this->array,this->axisDim,this->head);
1316        obj->calcWCSparams(this->head);
1317        obj->calcIntegFlux(this->array,this->axisDim,this->head);
1318
1319        if(this->par.getFlagUserThreshold())
1320          obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1321        else
1322          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1323      }
1324    } 
1325    if(this->par.isVerbose()) bar.remove();
1326
1327    if(!this->head.isWCS()){
1328      // if the WCS is bad, set the object names to Obj01 etc
1329      int numspaces = int(log10(this->objectList->size())) + 1;
1330      std::stringstream ss;
1331      for(size_t i=0;i<this->objectList->size();i++){
1332        ss.str("");
1333        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1334        this->objectList->at(i).setName(ss.str());
1335      }
1336    }
1337 
1338  }
1339  //--------------------------------------------------------------------
1340
1341  void Cube::calcObjectWCSparams(std::vector< std::vector<PixelInfo::Voxel> > bigVoxList)
1342  {
1343    ///  @details
1344    ///  A function that calculates the WCS parameters for each object in the
1345    ///  Cube's list of detections.
1346    ///  Each object gets an ID number assigned to it (which is simply its order
1347    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1348    ///
1349    ///  This version uses vectors of Voxels to define the fluxes.
1350    ///
1351    /// \param bigVoxList A vector of vectors of Voxels, with the same
1352    /// number of elements as this->objectList, where each element is a
1353    /// vector of Voxels corresponding to the same voxels in each
1354    /// detection and indicating the flux of each voxel.
1355 
1356    std::vector<Detection>::iterator obj;
1357    int ct=0;
1358    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1359      obj->setID(ct+1);
1360      if(!obj->hasParams()){
1361        obj->setCentreType(this->par.getPixelCentre());
1362        obj->calcFluxes(bigVoxList[ct]);
1363        obj->calcWCSparams(this->head);
1364        obj->calcIntegFlux(this->axisDim[2],bigVoxList[ct],this->head);
1365       
1366        if(this->par.getFlagUserThreshold())
1367          obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1368        else
1369          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1370      }
1371      ct++;
1372    } 
1373
1374    if(!this->head.isWCS()){
1375      // if the WCS is bad, set the object names to Obj01 etc
1376      int numspaces = int(log10(this->objectList->size())) + 1;
1377      std::stringstream ss;
1378      for(size_t i=0;i<this->objectList->size();i++){
1379        ss.str("");
1380        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1381        this->objectList->at(i).setName(ss.str());
1382      }
1383    }
1384 
1385  }
1386  //--------------------------------------------------------------------
1387
1388  void Cube::calcObjectWCSparams(std::map<PixelInfo::Voxel,float> &voxelMap)
1389  {
1390    ///  @details
1391    ///  A function that calculates the WCS parameters for each object in the
1392    ///  Cube's list of detections.
1393    ///  Each object gets an ID number assigned to it (which is simply its order
1394    ///   in the list), and if the WCS is good, the WCS paramters are calculated.
1395    ///
1396    ///  This version uses vectors of Voxels to define the fluxes.
1397    ///
1398    /// \param bigVoxList A vector of vectors of Voxels, with the same
1399    /// number of elements as this->objectList, where each element is a
1400    /// vector of Voxels corresponding to the same voxels in each
1401    /// detection and indicating the flux of each voxel.
1402 
1403    std::vector<Detection>::iterator obj;
1404    int ct=0;
1405    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1406      obj->setID(ct+1);
1407      if(!obj->hasParams()){
1408        obj->setCentreType(this->par.getPixelCentre());
1409        obj->calcFluxes(voxelMap);
1410        obj->calcWCSparams(this->head);
1411        obj->calcIntegFlux(this->axisDim[2],voxelMap,this->head);
1412       
1413        if(this->par.getFlagUserThreshold())
1414          obj->setPeakSNR( obj->getPeakFlux() / this->Stats.getThreshold() );
1415        else
1416          obj->setPeakSNR( (obj->getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1417      }
1418      ct++;
1419    } 
1420
1421    if(!this->head.isWCS()){
1422      // if the WCS is bad, set the object names to Obj01 etc
1423      int numspaces = int(log10(this->objectList->size())) + 1;
1424      std::stringstream ss;
1425      for(size_t i=0;i<this->objectList->size();i++){
1426        ss.str("");
1427        ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1428        this->objectList->at(i).setName(ss.str());
1429      }
1430    }
1431 
1432  }
1433  //--------------------------------------------------------------------
1434
1435  void Cube::updateDetectMap()
1436  {
1437    /// @details A function that, for each detected object in the
1438    ///  cube's list, increments the cube's detection map by the
1439    ///  required amount at each pixel. Uses
1440    ///  updateDetectMap(Detection).
1441
1442    std::vector<Detection>::iterator obj;
1443    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1444      this->updateDetectMap(*obj);
1445    }
1446
1447  }
1448  //--------------------------------------------------------------------
1449
1450  void Cube::updateDetectMap(Detection obj)
1451  {
1452    ///  @details
1453    ///  A function that, for the given object, increments the cube's
1454    ///  detection map by the required amount at each pixel.
1455    ///
1456    ///  \param obj A Detection object that is being incorporated into the map.
1457
1458    std::vector<Voxel> vlist = obj.getPixelSet();
1459    for(std::vector<Voxel>::iterator vox=vlist.begin();vox<vlist.end();vox++) {
1460      if(this->numNondegDim==1)
1461        this->detectMap[vox->getZ()]++;
1462      else
1463        this->detectMap[vox->getX()+vox->getY()*this->axisDim[0]]++;
1464    }
1465  }
1466  //--------------------------------------------------------------------
1467
1468  float Cube::enclosedFlux(Detection obj)
1469  {
1470    ///  @details
1471    ///   A function to calculate the flux enclosed by the range
1472    ///    of pixels detected in the object obj (not necessarily all
1473    ///    pixels will have been detected).
1474    ///
1475    ///   \param obj The Detection under consideration.
1476
1477    obj.calcFluxes(this->array, this->axisDim);
1478    int xsize = obj.getXmax()-obj.getXmin()+1;
1479    int ysize = obj.getYmax()-obj.getYmin()+1;
1480    int zsize = obj.getZmax()-obj.getZmin()+1;
1481    std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1482    for(int x=0;x<xsize;x++){
1483      for(int y=0;y<ysize;y++){
1484        for(int z=0;z<zsize;z++){
1485          fluxArray[x+y*xsize+z*ysize*xsize] =
1486            this->getPixValue(x+obj.getXmin(),
1487                              y+obj.getYmin(),
1488                              z+obj.getZmin());
1489          if(this->par.getFlagNegative())
1490            fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1491        }
1492      }
1493    }
1494    float sum = 0.;
1495    for(size_t i=0;i<fluxArray.size();i++)
1496      if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1497    return sum;
1498  }
1499  //--------------------------------------------------------------------
1500
1501  void Cube::setupColumns()
1502  {
1503    /// @details
1504    ///  A front-end to the two setup routines in columns.cc. 
1505    ///
1506    ///  This first gets the starting precisions, which may be from
1507    ///  the input parameters. It then sets up the columns (calculates
1508    ///  their widths and precisions and so on based on the values
1509    ///  within). The precisions are also stored in each Detection
1510    ///  object.
1511    ///
1512    ///  Need to have called calcObjectWCSparams() somewhere
1513    ///  beforehand.
1514
1515    std::vector<Detection>::iterator obj;
1516    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1517      obj->setVelPrec( this->par.getPrecVel() );
1518      obj->setFpeakPrec( this->par.getPrecFlux() );
1519      obj->setXYZPrec( Catalogues::prXYZ );
1520      obj->setPosPrec( Catalogues::prWPOS );
1521      obj->setFintPrec( this->par.getPrecFlux() );
1522      obj->setSNRPrec( this->par.getPrecSNR() );
1523    }
1524 
1525    this->fullCols = getFullColSet(*(this->objectList), this->head);
1526
1527    int vel,fpeak,fint,pos,xyz,snr;
1528    vel = fullCols.column("VEL").getPrecision();
1529    fpeak = fullCols.column("FPEAK").getPrecision();
1530    snr = fullCols.column("SNRPEAK").getPrecision();
1531    xyz = fullCols.column("X").getPrecision();
1532    xyz = std::max(xyz, fullCols.column("Y").getPrecision());
1533    xyz = std::max(xyz, fullCols.column("Z").getPrecision());
1534    if(this->head.isWCS()) fint = fullCols.column("FINT").getPrecision();
1535    else fint = fullCols.column("FTOT").getPrecision();
1536    pos = fullCols.column("WRA").getPrecision();
1537    pos = std::max(pos, fullCols.column("WDEC").getPrecision());
1538 
1539    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1540      obj->setVelPrec(vel);
1541      obj->setFpeakPrec(fpeak);
1542      obj->setXYZPrec(xyz);
1543      obj->setPosPrec(pos);
1544      obj->setFintPrec(fint);
1545      obj->setSNRPrec(snr);
1546    }
1547
1548  }
1549  //--------------------------------------------------------------------
1550
1551  bool Cube::objAtSpatialEdge(Detection obj)
1552  {
1553    ///  @details
1554    ///   A function to test whether the object obj
1555    ///    lies at the edge of the cube's spatial field --
1556    ///    either at the boundary, or next to BLANKs.
1557    ///
1558    ///   \param obj The Detection under consideration.
1559
1560    bool atEdge = false;
1561
1562    size_t pix = 0;
1563    std::vector<Voxel> voxlist = obj.getPixelSet();
1564    while(!atEdge && pix<voxlist.size()){
1565      // loop over each pixel in the object, until we find an edge pixel.
1566      for(int dx=-1;dx<=1;dx+=2){
1567        if( ((voxlist[pix].getX()+dx)<0) ||
1568            ((voxlist[pix].getX()+dx)>=int(this->axisDim[0])) )
1569          atEdge = true;
1570        else if(this->isBlank(voxlist[pix].getX()+dx,
1571                              voxlist[pix].getY(),
1572                              voxlist[pix].getZ()))
1573          atEdge = true;
1574      }
1575      for(int dy=-1;dy<=1;dy+=2){
1576        if( ((voxlist[pix].getY()+dy)<0) ||
1577            ((voxlist[pix].getY()+dy)>=int(this->axisDim[1])) )
1578          atEdge = true;
1579        else if(this->isBlank(voxlist[pix].getX(),
1580                              voxlist[pix].getY()+dy,
1581                              voxlist[pix].getZ()))
1582          atEdge = true;
1583      }
1584      pix++;
1585    }
1586
1587    return atEdge;
1588  }
1589  //--------------------------------------------------------------------
1590
1591  bool Cube::objAtSpectralEdge(Detection obj)
1592  {
1593    ///   @details
1594    ///   A function to test whether the object obj
1595    ///    lies at the edge of the cube's spectral extent --
1596    ///    either at the boundary, or next to BLANKs.
1597    ///
1598    ///   \param obj The Detection under consideration.
1599
1600    bool atEdge = false;
1601
1602    size_t pix = 0;
1603    std::vector<Voxel> voxlist = obj.getPixelSet();
1604    while(!atEdge && pix<voxlist.size()){
1605      // loop over each pixel in the object, until we find an edge pixel.
1606      for(int dz=-1;dz<=1;dz+=2){
1607        if( ((voxlist[pix].getZ()+dz)<0) ||
1608            ((voxlist[pix].getZ()+dz)>=int(this->axisDim[2])) )
1609          atEdge = true;
1610        else if(this->isBlank(voxlist[pix].getX(),
1611                              voxlist[pix].getY(),
1612                              voxlist[pix].getZ()+dz))
1613          atEdge = true;
1614      }
1615      pix++;
1616    }
1617
1618    return atEdge;
1619  }
1620  //--------------------------------------------------------------------
1621
1622  bool Cube::objNextToMW(Detection obj)
1623  {
1624    ///   @details A function to test whether the object obj lies
1625    ///   adjacent to the MW range or straddles it (conceivably, you
1626    ///   could have disconnected channels in your object that don't
1627    ///   touch the MW range, but lie either side - in this case we
1628    ///   want the flag). If flagMW=false we will always return false.
1629    ///
1630    ///   \param obj The Detection under consideration.
1631
1632    bool isNext = this->par.getFlagMW() &&
1633      ((obj.getZmin() <= this->par.getMaxMW()+1) && (obj.getZmax() >= this->par.getMinMW()-1));
1634   
1635    return isNext;
1636
1637  }
1638  //--------------------------------------------------------------------
1639
1640  void Cube::setObjectFlags()
1641  {
1642    /// @details
1643    ///   A function to set any warning flags for all the detected objects
1644    ///    associated with the cube.
1645    ///   Flags to be looked for:
1646    ///    <ul><li> Negative enclosed flux (N)
1647    ///        <li> Detection at edge of field (spatially) (E)
1648    ///        <li> Detection at edge of spectral region (S)
1649    ///    </ul>
1650
1651    std::vector<Detection>::iterator obj;
1652    for(obj=this->objectList->begin();obj<this->objectList->end();obj++){
1653
1654      if( this->enclosedFlux(*obj) < 0. ) 
1655        obj->addToFlagText("N");
1656
1657      if( this->objAtSpatialEdge(*obj) )
1658        obj->addToFlagText("E");
1659
1660      if( this->objAtSpectralEdge(*obj) && (this->axisDim[2] > 2))
1661        obj->addToFlagText("S");
1662
1663      if( this->objNextToMW(*obj) )
1664        obj->addToFlagText("M");
1665
1666      if(obj->getFlagText()=="") obj->addToFlagText("-");
1667
1668    }
1669
1670  }
1671  //--------------------------------------------------------------------
1672
1673  void Cube::writeToFITS()
1674  {
1675    std::string report;
1676    if(!this->par.getFlagUsePrevious()){
1677      if(this->par.getFlagATrous()){
1678        if(this->par.getFlagOutputRecon()){
1679          if(this->par.isVerbose())
1680            std::cout << "  Saving reconstructed cube to " << this->par.outputReconFile() << "... "<<std::flush;
1681          WriteReconArray writer(this);
1682          writer.setFilename(this->par.outputReconFile());
1683          OUTCOME result = writer.write();
1684          report=(result==FAILURE)?"Failed!":"done.";
1685          if(this->par.isVerbose()) std::cout << report << "\n";
1686        }
1687        if(this->par.getFlagOutputResid()){
1688          if(this->par.isVerbose())
1689            std::cout << "  Saving reconstruction residual cube to " << this->par.outputResidFile() << "... "<<std::flush;
1690          WriteReconArray writer(this);
1691          writer.setFilename(this->par.outputResidFile());
1692          writer.setIsRecon(false);
1693          OUTCOME result = writer.write();
1694          report=(result==FAILURE)?"Failed!":"done.";
1695          if(this->par.isVerbose()) std::cout << report << "\n";
1696        }
1697      }
1698      if(this->par.getFlagSmooth()&& this->par.getFlagOutputSmooth()){
1699        if(this->par.isVerbose())
1700          std::cout << "  Saving smoothed cube to " << this->par.outputSmoothFile() << "... "<<std::flush;
1701        WriteSmoothArray writer(this);
1702        writer.setFilename(this->par.outputSmoothFile());
1703        OUTCOME result = writer.write();
1704        report=(result==FAILURE)?"Failed!":"done.";
1705        if(this->par.isVerbose()) std::cout << report << "\n";
1706      }
1707    }
1708    if(this->par.getFlagOutputMomentMap()){
1709      if(this->par.isVerbose())
1710        std::cout << "  Saving moment map to " << this->par.outputMomentMapFile() << "... "<<std::flush;
1711      WriteMomentMapArray writer(this);
1712      writer.setFilename(this->par.outputMomentMapFile());
1713      OUTCOME result = writer.write();
1714      report=(result==FAILURE)?"Failed!":"done.";
1715      if(this->par.isVerbose()) std::cout << report << "\n";
1716    }
1717    if(this->par.getFlagOutputMomentMask()){
1718      if(this->par.isVerbose())
1719        std::cout << "  Saving moment-0 mask to " << this->par.outputMomentMaskFile() << "... "<<std::flush;
1720      WriteMomentMaskArray writer(this);
1721      writer.setFilename(this->par.outputMomentMaskFile());
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.getFlagOutputBaseline()){
1727      if(this->par.isVerbose())
1728        std::cout << "  Saving baseline cube to " << this->par.outputBaselineFile() << "... "<<std::flush;
1729      WriteBaselineArray writer(this);
1730      writer.setFilename(this->par.outputBaselineFile());
1731      OUTCOME result = writer.write();
1732      report=(result==FAILURE)?"Failed!":"done.";
1733      if(this->par.isVerbose()) std::cout << report << "\n";
1734    }
1735    if(this->par.getFlagOutputMask()){
1736      if(this->par.isVerbose())
1737        std::cout << "  Saving mask cube to " << this->par.outputMaskFile() << "... "<<std::flush;
1738      WriteMaskArray writer(this);
1739      writer.setFilename(this->par.outputMaskFile());
1740      OUTCOME result = writer.write();
1741      report=(result==FAILURE)?"Failed!":"done.";
1742      if(this->par.isVerbose()) std::cout << report << "\n";
1743    }
1744
1745  }
1746
1747
1748  /****************************************************************/
1749  /////////////////////////////////////////////////////////////
1750  //// Functions for Image class
1751  /////////////////////////////////////////////////////////////
1752
1753  Image::Image(size_t size)
1754  {
1755    // need error handling in case size<0 !!!
1756    this->numPixels = this->numDim = 0;
1757    this->minSize = 2;
1758    if(size<0){
1759      DUCHAMPERROR("Image(size)","Negative size -- could not define Image");
1760    }
1761    else{
1762      if(size>0 && !this->arrayAllocated){
1763        this->array = new float[size];
1764        this->arrayAllocated = true;
1765      }
1766      this->numPixels = size;
1767      this->axisDim = new size_t[2];
1768      this->axisDimAllocated = true;
1769      this->numDim = 2;
1770    }
1771  }
1772  //--------------------------------------------------------------------
1773
1774  Image::Image(size_t *dimensions)
1775  {
1776    this->numPixels = this->numDim = 0;
1777    this->minSize = 2;
1778    int size = dimensions[0] * dimensions[1];
1779    if(size<0){
1780      DUCHAMPERROR("Image(dimArray)","Negative size -- could not define Image");
1781    }
1782    else{
1783      this->numPixels = size;
1784      if(size>0){
1785        this->array = new float[size];
1786        this->arrayAllocated = true;
1787      }
1788      this->numDim=2;
1789      this->axisDim = new size_t[2];
1790      this->axisDimAllocated = true;
1791      for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1792    }
1793  }
1794  //--------------------------------------------------------------------
1795  Image::Image(const Image &i):
1796    DataArray(i)
1797  {
1798    this->operator=(i);
1799  }
1800
1801  Image& Image::operator=(const Image &i)
1802  {
1803    if(this==&i) return *this;
1804    ((DataArray &) *this) = i;
1805    this->minSize = i.minSize;
1806    return *this;
1807  }
1808
1809  //--------------------------------------------------------------------
1810
1811  void Image::saveArray(float *input, size_t size)
1812  {
1813    /// @details
1814    /// Saves the array in input to the pixel array Image::array.
1815    /// The size of the array given must be the same as the current number of
1816    /// pixels, else an error message is returned and nothing is done.
1817    /// \param input The array of values to be saved.
1818    /// \param size The size of input.
1819
1820    if(size != this->numPixels){
1821      DUCHAMPERROR("Image::saveArray", "Input array different size to existing array. Cannot save.");
1822    }
1823    else {
1824      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1825      this->numPixels = size;
1826      if(this->numPixels>0){
1827        this->array = new float[size];
1828        this->arrayAllocated = true;
1829        for(size_t i=0;i<size;i++) this->array[i] = input[i];
1830      }
1831    }
1832  }
1833  //--------------------------------------------------------------------
1834
1835  void Image::extractSpectrum(float *Array, size_t *dim, size_t pixel)
1836  {
1837    /// @details
1838    ///  A function to extract a 1-D spectrum from a 3-D array.
1839    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1840    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1841    ///    by the third argument.
1842    ///  The extracted spectrum is stored in the pixel array Image::array.
1843    /// \param Array The array containing the pixel values, from which
1844    ///               the spectrum is extracted.
1845    /// \param dim The array of dimension values.
1846    /// \param pixel The spatial pixel that contains the desired spectrum.
1847
1848    if((pixel<0)||(pixel>=dim[0]*dim[1])){
1849      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1850    }
1851    else if(dim[2] != this->numPixels){
1852      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1853    }
1854    else {
1855      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1856      this->numPixels = dim[2];
1857      if(this->numPixels>0){
1858        this->array = new float[dim[2]];
1859        this->arrayAllocated = true;
1860        for(size_t z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1861      }
1862    }
1863  }
1864  //--------------------------------------------------------------------
1865
1866  void Image::extractSpectrum(Cube &cube, size_t pixel)
1867  {
1868    /// @details
1869    ///  A function to extract a 1-D spectrum from a Cube class
1870    ///  The spectrum extracted is the one lying in the spatial pixel referenced
1871    ///    by the second argument.
1872    ///  The extracted spectrum is stored in the pixel array Image::array.
1873    /// \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1874    /// \param pixel The spatial pixel that contains the desired spectrum.
1875
1876    size_t zdim = cube.getDimZ();
1877    size_t spatSize = cube.getDimX()*cube.getDimY();
1878    if((pixel<0)||(pixel>=spatSize)){
1879      DUCHAMPERROR("Image::extractSpectrum", "Requested spatial pixel outside allowed range. Cannot save.");
1880    }
1881    else if(zdim != this->numPixels){
1882      DUCHAMPERROR("Image::extractSpectrum", "Input array different size to existing array. Cannot save.");
1883    }
1884    else {
1885      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1886      this->numPixels = zdim;
1887      if(this->numPixels>0){
1888        this->array = new float[zdim];
1889        this->arrayAllocated = true;
1890        for(size_t z=0;z<zdim;z++)
1891          this->array[z] = cube.getPixValue(z*spatSize + pixel);
1892      }
1893    }
1894  }
1895  //--------------------------------------------------------------------
1896
1897  void Image::extractImage(float *Array, size_t *dim, size_t channel)
1898  {
1899    /// @details
1900    ///  A function to extract a 2-D image from a 3-D array.
1901    ///  The array is assumed to be 3-D with the third dimension the spectral one.
1902    ///  The dimensions of the array are in the dim[] array.
1903    ///  The image extracted is the one lying in the channel referenced
1904    ///    by the third argument.
1905    ///  The extracted image is stored in the pixel array Image::array.
1906    /// \param Array The array containing the pixel values, from which the image is extracted.
1907    /// \param dim The array of dimension values.
1908    /// \param channel The spectral channel that contains the desired image.
1909
1910    size_t spatSize = dim[0]*dim[1];
1911    if((channel<0)||(channel>=dim[2])){
1912      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
1913    }
1914    else if(spatSize != this->numPixels){
1915      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
1916    }
1917    else {
1918      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1919      this->numPixels = spatSize;
1920      if(this->numPixels>0){
1921        this->array = new float[spatSize];
1922        this->arrayAllocated = true;
1923        for(size_t npix=0; npix<spatSize; npix++)
1924          this->array[npix] = Array[channel*spatSize + npix];
1925      }
1926    }
1927  }
1928  //--------------------------------------------------------------------
1929
1930  void Image::extractImage(Cube &cube, size_t channel)
1931  {
1932    /// @details
1933    ///  A function to extract a 2-D image from Cube class.
1934    ///  The image extracted is the one lying in the channel referenced
1935    ///    by the second argument.
1936    ///  The extracted image is stored in the pixel array Image::array.
1937    /// \param cube The Cube containing the pixel values, from which the image is extracted.
1938    /// \param channel The spectral channel that contains the desired image.
1939
1940    size_t spatSize = cube.getDimX()*cube.getDimY();
1941    if((channel<0)||(channel>=cube.getDimZ())){
1942      DUCHAMPERROR("Image::extractImage", "Requested channel outside allowed range. Cannot save.");
1943    }
1944    else if(spatSize != this->numPixels){
1945      DUCHAMPERROR("Image::extractImage", "Input array different size to existing array. Cannot save.");
1946    }
1947    else {
1948      if(this->numPixels>0 && this->arrayAllocated) delete [] array;
1949      this->numPixels = spatSize;
1950      if(this->numPixels>0){
1951        this->array = new float[spatSize];
1952        this->arrayAllocated = true;
1953        for(size_t npix=0; npix<spatSize; npix++)
1954          this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1955      }
1956    }
1957  }
1958  //--------------------------------------------------------------------
1959
1960  void Image::removeMW()
1961  {
1962    /// @details
1963    ///  A function to remove the Milky Way range of channels from a 1-D spectrum.
1964    ///  The array in this Image is assumed to be 1-D, with only the first axisDim
1965    ///    equal to 1.
1966    ///  The values of the MW channels are set to 0, unless they are BLANK.
1967
1968    if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1969      for(size_t z=0;z<this->axisDim[0];z++){
1970        if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1971      }
1972    }
1973  }
1974  //--------------------------------------------------------------------
1975
1976  std::vector<Object2D> Image::findSources2D()
1977  {
1978    std::vector<bool> thresholdedArray(this->axisDim[0]*this->axisDim[1]);
1979    for(size_t posY=0;posY<this->axisDim[1];posY++){
1980      for(size_t posX=0;posX<this->axisDim[0];posX++){
1981        size_t loc = posX + this->axisDim[0]*posY;
1982        thresholdedArray[loc] = this->isDetection(posX,posY);
1983      }
1984    }
1985    return lutz_detect(thresholdedArray, this->axisDim[0], this->axisDim[1], this->minSize);
1986  }
1987
1988  std::vector<Scan> Image::findSources1D()
1989  {
1990    std::vector<bool> thresholdedArray(this->axisDim[0]);
1991    for(size_t posX=0;posX<this->axisDim[0];posX++){
1992      thresholdedArray[posX] = this->isDetection(posX,0);
1993    }
1994    return spectrumDetect(thresholdedArray, this->axisDim[0], this->minSize);
1995  }
1996
1997
1998  std::vector< std::vector<PixelInfo::Voxel> > Cube::getObjVoxList()
1999  {
2000   
2001    std::vector< std::vector<PixelInfo::Voxel> > biglist;
2002   
2003    std::vector<Detection>::iterator obj;
2004    for(obj=this->objectList->begin(); obj<this->objectList->end(); obj++) {
2005
2006      Cube *subcube = new Cube;
2007      subcube->pars() = this->par;
2008      subcube->pars().setVerbosity(false);
2009      subcube->pars().setFlagSubsection(true);
2010      duchamp::Section sec = obj->getBoundingSection();
2011      subcube->pars().setSubsection( sec.getSection() );
2012      if(subcube->pars().verifySubsection() == FAILURE)
2013        DUCHAMPERROR("get object voxel list","Unable to verify the subsection - something's wrong!");
2014      if(subcube->getCube() == FAILURE)
2015        DUCHAMPERROR("get object voxel list","Unable to read the FITS file - something's wrong!");
2016      std::vector<PixelInfo::Voxel> voxlist = obj->getPixelSet();
2017      std::vector<PixelInfo::Voxel>::iterator vox;
2018      for(vox=voxlist.begin(); vox<voxlist.end(); vox++){
2019        size_t pix = (vox->getX()-subcube->pars().getXOffset()) +
2020          subcube->getDimX()*(vox->getY()-subcube->pars().getYOffset()) +
2021          subcube->getDimX()*subcube->getDimY()*(vox->getZ()-subcube->pars().getZOffset());
2022        vox->setF( subcube->getPixValue(pix) );
2023      }
2024      biglist.push_back(voxlist);
2025      delete subcube;
2026
2027    }
2028
2029    return biglist;
2030
2031  }
2032
2033}
Note: See TracBrowser for help on using the repository browser.