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

Last change on this file since 754 was 751, checked in by MatthewWhiting, 14 years ago

Getting initialisation of vox right.

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