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

Last change on this file since 1430 was 1430, checked in by MatthewWhiting, 7 years ago

Undoing previous commit, as I didn't mean to commit everything :)

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