source: tags/release-1.3/src/Cubes/cubes.cc @ 1391

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

Reinstating the saveMask() etc functions - just breaking out the functionality into individual functions that can be called easily from elsewhere. This keeps the interface from 1.2.2 to minimise disruption.

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