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

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

Getting the Cube::slice command working right. Still some issues with the WCS positions of the final objects.

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