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

Last change on this file since 978 was 978, checked in by MatthewWhiting, 12 years ago

Ticket #148: For a 1D case, moving the non-degenerate axis to the spectral z-axis. Fixing an output string.

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