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

Last change on this file since 1391 was 309, checked in by Matthew Whiting, 17 years ago

Mostly changes to fix some memory allocation/deallocation issues raised by valgrind. Minor changes to the Guide.

File size: 49.1 KB
Line 
1// -----------------------------------------------------------------------
2// cubes.cc: Member functions for the DataArray, Cube and Image classes.
3// -----------------------------------------------------------------------
4// Copyright (C) 2006, Matthew Whiting, ATNF
5//
6// This program is free software; you can redistribute it and/or modify it
7// under the terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 2 of the License, or (at your
9// option) any later version.
10//
11// Duchamp is distributed in the hope that it will be useful, but WITHOUT
12// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14// for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with Duchamp; if not, write to the Free Software Foundation,
18// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
19//
20// Correspondence concerning Duchamp may be directed to:
21//    Internet email: Matthew.Whiting [at] atnf.csiro.au
22//    Postal address: Dr. Matthew Whiting
23//                    Australia Telescope National Facility, CSIRO
24//                    PO Box 76
25//                    Epping NSW 1710
26//                    AUSTRALIA
27// -----------------------------------------------------------------------
28#include <unistd.h>
29#include <iostream>
30#include <iomanip>
31#include <vector>
32#include <algorithm>
33#include <string>
34#include <math.h>
35
36#include <wcs.h>
37
38#include <cpgplot.h>
39
40#include <duchamp.hh>
41#include <param.hh>
42#include <fitsHeader.hh>
43#include <Cubes/cubes.hh>
44#include <PixelMap/Voxel.hh>
45#include <PixelMap/Object3D.hh>
46#include <Detection/detection.hh>
47#include <Detection/columns.hh>
48#include <Utils/utils.hh>
49#include <Utils/mycpgplot.hh>
50#include <Utils/Statistics.hh>
51using namespace Column;
52using namespace mycpgplot;
53using namespace Statistics;
54using namespace PixelInfo;
55
56#ifdef TEST_DEBUG
57const bool TESTING=true;
58#else
59const bool TESTING=false;
60#endif
61
62/****************************************************************/
63///////////////////////////////////////////////////
64//// Functions for DataArray class:
65///////////////////////////////////////////////////
66
67DataArray::DataArray(){
68  /**
69   * Fundamental constructor for DataArray.
70   * Number of dimensions and pixels are set to 0. Nothing else allocated.
71   */
72  this->numDim=0;
73  this->numPixels=0;
74  this->objectList = new std::vector<Detection>;
75};
76//--------------------------------------------------------------------
77
78DataArray::DataArray(short int nDim){
79  /**
80   * N-dimensional constructor for DataArray.
81   * Number of dimensions defined, and dimension array allocated.
82   * Number of pixels are set to 0.
83   * \param nDim Number of dimensions.
84   */
85  if(nDim>0) this->axisDim = new long[nDim];
86  this->numDim=nDim;
87  this->numPixels=0;
88  this->objectList = new std::vector<Detection>;
89};
90//--------------------------------------------------------------------
91
92DataArray::DataArray(short int nDim, long size){
93  /**
94   * N-dimensional constructor for DataArray.
95   * Number of dimensions and number of pixels defined.
96   * Arrays allocated based on these values.
97   * \param nDim Number of dimensions.
98   * \param size Number of pixels.
99   *
100   * Note that we can assign values to the dimension array.
101   */
102
103  if(size<0)
104    duchampError("DataArray(nDim,size)",
105                 "Negative size -- could not define DataArray");
106  else if(nDim<0)
107    duchampError("DataArray(nDim,size)",
108                 "Negative number of dimensions: could not define DataArray");
109  else {
110    if(size>0) this->array = new float[size];
111    this->numPixels = size;
112    if(nDim>0) this->axisDim = new long[nDim];
113    this->numDim = nDim;
114  }
115  this->objectList = new std::vector<Detection>;
116}
117//--------------------------------------------------------------------
118
119DataArray::DataArray(short int nDim, long *dimensions)
120{
121  /**
122   * Most robust constructor for DataArray.
123   * Number and sizes of dimensions are defined, and hence the number of
124   * pixels. Arrays allocated based on these values.
125   * \param nDim Number of dimensions.
126   * \param dimensions Array giving sizes of dimensions.
127   */
128  if(nDim<0)
129    duchampError("DataArray(nDim,dimArray)",
130                 "Negative number of dimensions: could not define DataArray");
131  else {
132    int size = dimensions[0];
133    for(int i=1;i<nDim;i++) size *= dimensions[i];
134    if(size<0)
135      duchampError("DataArray(nDim,dimArray)",
136                   "Negative size: could not define DataArray");
137    else{
138      this->numPixels = size;
139      if(size>0) this->array = new float[size];
140      this->numDim=nDim;
141      if(nDim>0) this->axisDim = new long[nDim];
142      for(int i=0;i<nDim;i++) this->axisDim[i] = dimensions[i];
143    }
144  }
145}
146//--------------------------------------------------------------------
147
148DataArray::~DataArray()
149{
150  /**
151   *  Destructor -- arrays deleted if they have been allocated, and the
152   *   object list is deleted.
153   */
154  if(this->numPixels>0) delete [] this->array;
155  if(this->numDim>0)    delete [] this->axisDim;
156  delete this->objectList;
157}
158//--------------------------------------------------------------------
159//--------------------------------------------------------------------
160
161void DataArray::getDim(long &x, long &y, long &z){
162  /**
163   * The sizes of the first three dimensions (if they exist) are returned.
164   * \param x The first dimension. Defaults to 0 if numDim \f$\le\f$ 0.
165   * \param y The second dimension. Defaults to 1 if numDim \f$\le\f$ 1.
166   * \param z The third dimension. Defaults to 1 if numDim \f$\le\f$ 2.
167   */
168  if(this->numDim>0) x=this->axisDim[0];
169  else x=0;
170  if(this->numDim>1) y=this->axisDim[1];
171  else y=1;
172  if(this->numDim>2) z=this->axisDim[2];
173  else z=1;
174}
175//--------------------------------------------------------------------
176
177void DataArray::getDimArray(long *output){
178  /**
179   * The axisDim array is written to output. This needs to be defined
180   *  beforehand: no checking is done on the memory.
181   * \param output The array that is written to.
182   */
183  for(int i=0;i<this->numDim;i++) output[i] = this->axisDim[i];
184}
185//--------------------------------------------------------------------
186
187void DataArray::getArray(float *output){
188  /**
189   * The pixel value array is written to output. This needs to be defined
190   *  beforehand: no checking is done on the memory.
191   * \param output The array that is written to.
192   */
193  for(int i=0;i<this->numPixels;i++) output[i] = this->array[i];
194}
195//--------------------------------------------------------------------
196
197void DataArray::saveArray(float *input, long size){
198  /**
199   * Saves the array in input to the pixel array DataArray::array.
200   * The size of the array given must be the same as the current number of
201   * pixels, else an error message is returned and nothing is done.
202   * \param input The array of values to be saved.
203   * \param size The size of input.
204   */
205  if(size != this->numPixels)
206    duchampError("DataArray::saveArray",
207                 "Input array different size to existing array. Cannot save.");
208  else {
209    if(this->numPixels>0) delete [] this->array;
210    this->numPixels = size;
211    this->array = new float[size];
212    for(int i=0;i<size;i++) this->array[i] = input[i];
213  }
214}
215//--------------------------------------------------------------------
216
217void DataArray::addObject(Detection object){
218  /**
219   * \param object The object to be added to the object list.
220   */
221  // objectList is a vector, so just use push_back()
222  this->objectList->push_back(object);
223}
224//--------------------------------------------------------------------
225
226void DataArray::addObjectList(std::vector <Detection> newlist) {
227  /**
228   * \param newlist The list of objects to be added to the object list.
229   */
230  for(int i=0;i<newlist.size();i++) this->objectList->push_back(newlist[i]);
231}
232//--------------------------------------------------------------------
233
234// void DataArray::addObjectOffsets(){
235//   /**
236//    * Add the pixel offsets (that is, offsets from the corner of the cube to the
237//    *  corner of the utilised part) that are stored in the Param set to the
238//    *  coordinate values of each object in the object list.
239//    */
240//   for(int i=0;i<this->objectList->size();i++){
241//     for(int p=0;p<this->objectList->at(i).getSize();p++){
242//       this->objectList->at(i).setX(p,this->objectList->at(i).getX(p)+
243//                             this->par.getXOffset());
244//       this->objectList->at(i).setY(p,this->objectList->at(i).getY(p)+
245//                             this->par.getYOffset());
246//       this->objectList->at(i).setZ(p,this->objectList->at(i).getZ(p)+
247//                             this->par.getZOffset());
248//     }
249//   }
250// }
251// //--------------------------------------------------------------------
252
253bool DataArray::isDetection(float value){
254  /**
255   * Is a given value a detection, based on the statistics in the
256   * DataArray's StatsContainer?
257   * \param value The pixel value to test.
258   */
259  if(par.isBlank(value)) return false;
260  else return Stats.isDetection(value);
261}; 
262//--------------------------------------------------------------------
263
264bool DataArray::isDetection(long voxel){
265  /**
266   * Is a given pixel a detection, based on the statistics in the
267   * DataArray's StatsContainer?
268   * If the pixel lies outside the valid range for the data array, return false.
269   * \param voxel Location of the DataArray's pixel to be tested.
270   */
271  if((voxel<0)||(voxel>this->numPixels)) return false;
272  else if(par.isBlank(this->array[voxel])) return false;
273  else return Stats.isDetection(this->array[voxel]);
274}; 
275//--------------------------------------------------------------------
276
277std::ostream& operator<< ( std::ostream& theStream, DataArray &array)
278{
279  /**
280   * A way to print out the pixel coordinates & flux values of the
281   * list of detected objects belonging to the DataArray.
282   * These are formatted nicely according to the << operator for Detection,
283   *  with a line indicating the number of detections at the start.
284   * \param theStream The ostream object to which the output should be sent.
285   * \param array The DataArray containing the list of objects.
286   */
287  for(int i=0;i<array.numDim;i++){
288    if(i>0) theStream<<"x";
289    theStream<<array.axisDim[i];
290  }
291  theStream<<std::endl;
292  theStream<<array.objectList->size()<<" detections:\n--------------\n";
293  for(int i=0;i<array.objectList->size();i++){
294    theStream << "Detection #" << array.objectList->at(i).getID()<<std::endl;
295    Detection *obj = new Detection;
296    *obj = array.objectList->at(i);
297    obj->addOffsets();
298    theStream<<*obj;
299    delete obj;
300  }
301  theStream<<"--------------\n";
302  return theStream;
303}
304
305/****************************************************************/
306/////////////////////////////////////////////////////////////
307//// Functions for Cube class
308/////////////////////////////////////////////////////////////
309
310Cube::Cube(){
311  /**
312   * Basic Constructor for Cube class.
313   * numDim set to 3, but numPixels to 0 and all bool flags to false.
314   * No allocation done.
315   */
316    numPixels=0; numDim=3;
317    reconExists = false; reconAllocated = false; baselineAllocated = false;
318  };
319//--------------------------------------------------------------------
320
321Cube::Cube(long size){
322  /**
323   * Alternative Cube constructor, where size is given but not individual
324   *  dimensions. Arrays are allocated as appropriate (according to the
325   *  relevant flags in Param set), but the Cube::axisDim array is not.
326   */
327  this->reconAllocated = false;
328  this->baselineAllocated = false;
329  this->numPixels = this->numDim = 0;
330  if(size<0)
331    duchampError("Cube(size)","Negative size -- could not define Cube");
332  else{
333    if(size>0){
334      this->array = new float[size];
335      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
336        this->recon = new float[size];
337        this->reconAllocated = true;
338      }
339      if(this->par.getFlagBaseline()){
340        this->baseline = new float[size];
341        this->baselineAllocated = true;
342      }
343    }
344    this->numPixels = size;
345    this->axisDim = new long[2];
346    this->numDim = 3;
347    this->reconExists = false;
348  }
349}
350//--------------------------------------------------------------------
351
352Cube::Cube(long *dimensions){
353  /**
354   * Alternative Cube constructor, where sizes of dimensions are given.
355   * Arrays are allocated as appropriate (according to the
356   *  relevant flags in Param set), as is the Cube::axisDim array.
357   */
358  int size   = dimensions[0] * dimensions[1] * dimensions[2];
359  int imsize = dimensions[0] * dimensions[1];
360  this->reconAllocated = false;
361  this->baselineAllocated = false;
362  this->numPixels = this->numDim = 0;
363  if((size<0) || (imsize<0) )
364    duchampError("Cube(dimArray)","Negative size -- could not define Cube");
365  else{
366    this->numPixels = size;
367    if(size>0){
368      this->array      = new float[size];
369      this->detectMap  = new short[imsize];
370      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
371        this->recon    = new float[size];
372        this->reconAllocated = true;
373      }
374      if(this->par.getFlagBaseline()){
375        this->baseline = new float[size];
376        this->baselineAllocated = true;
377      }
378    }
379    this->numDim  = 3;
380    this->axisDim = new long[3];
381    for(int i=0;i<3     ;i++) this->axisDim[i]   = dimensions[i];
382    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
383    this->reconExists = false;
384  }
385}
386//--------------------------------------------------------------------
387
388Cube::~Cube()
389{
390  /**
391   *  The destructor deletes the memory allocated for Cube::detectMap, and,
392   *  if these have been allocated, Cube::recon and Cube::baseline.
393   */
394  delete [] this->detectMap;
395  if(this->reconAllocated)    delete [] this->recon;
396  if(this->baselineAllocated) delete [] this->baseline;
397}
398//--------------------------------------------------------------------
399
400void Cube::initialiseCube(long *dimensions)
401{
402  /**
403   *  This function will set the sizes of all arrays that will be used by Cube.
404   *  It will also define the values of the axis dimensions: this will be done
405   *   using the WCS in the FitsHeader class, so the WCS needs to be good and
406   *   have three axes. If this is not the case, the axes are assumed to be
407   *   ordered in the sense of lng,lat,spc.
408   *
409   *  \param dimensions An array of values giving the dimensions (sizes) for
410   *  all axes. 
411   */
412
413  int lng,lat,spc,size,imsize;
414
415  if(this->head.isWCS() && (this->head.getNumAxes()>=3)){
416    // if there is a WCS and there is at least 3 axes
417    lng = this->head.WCS().lng;
418    lat = this->head.WCS().lat;
419    spc = this->head.WCS().spec;
420  }
421  else{
422    // just take dimensions[] at face value
423    lng = 0;
424    lat = 1;
425    spc = 2;
426  }
427
428  size   = dimensions[lng];
429  if(this->head.getNumAxes()>1) size *= dimensions[lat];
430  if(this->head.getNumAxes()>2) size *= dimensions[spc];
431  imsize = dimensions[lng];
432  if(this->head.getNumAxes()>1) imsize *= dimensions[lat];
433
434  this->reconAllocated = false;
435  this->baselineAllocated = false;
436
437  if((size<0) || (imsize<0) )
438    duchampError("Cube::initialiseCube(dimArray)",
439                 "Negative size -- could not define Cube.\n");
440  else{
441    this->numPixels = size;
442    if(size>0){
443      this->array      = new float[size];
444      this->detectMap  = new short[imsize];
445      if(this->par.getFlagATrous() || this->par.getFlagSmooth()){
446        this->recon    = new float[size];
447        this->reconAllocated = true;
448      }
449      if(this->par.getFlagBaseline()){
450        this->baseline = new float[size];
451        this->baselineAllocated = true;
452      }
453    }
454    this->numDim  = 3;
455    this->axisDim = new long[this->numDim];
456    this->axisDim[0] = dimensions[lng];
457    if(this->head.getNumAxes()>1) this->axisDim[1] = dimensions[lat];
458    else this->axisDim[1] = 1;
459    if(this->head.getNumAxes()>2) this->axisDim[2] = dimensions[spc];
460    else this->axisDim[2] = 1;
461    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
462    this->reconExists = false;
463  }
464}
465//--------------------------------------------------------------------
466
467int Cube::getCube(){ 
468    /**
469     * A front-end to the Cube::getCube() function, that does
470     *  subsection checks.
471     * Assumes the Param is set up properly.
472     */
473    std::string fname = par.getImageFile();
474    if(par.getFlagSubsection()) fname+=par.getSubsection();
475    return getCube(fname);
476  };
477//--------------------------------------------------------------------
478
479void Cube::saveArray(float *input, long size){
480  if(size != this->numPixels){
481    std::stringstream errmsg;
482    errmsg << "Input array different size to existing array ("
483           << size << " cf. " << this->numPixels << "). Cannot save.\n";
484    duchampError("Cube::saveArray",errmsg.str());
485  }
486  else {
487    if(this->numPixels>0) delete [] array;
488    this->numPixels = size;
489    this->array = new float[size];
490    for(int i=0;i<size;i++) this->array[i] = input[i];
491  }
492}
493//--------------------------------------------------------------------
494
495void Cube::saveRecon(float *input, long size){
496  /**
497   * Saves the array in input to the reconstructed array Cube::recon
498   * The size of the array given must be the same as the current number of
499   * pixels, else an error message is returned and nothing is done.
500   * If the recon array has already been allocated, it is deleted first, and
501   * then the space is allocated.
502   * Afterwards, the appropriate flags are set.
503   * \param input The array of values to be saved.
504   * \param size The size of input.
505   */
506  if(size != this->numPixels){
507    std::stringstream errmsg;
508    errmsg << "Input array different size to existing array ("
509           << size << " cf. " << this->numPixels << "). Cannot save.\n";
510    duchampError("Cube::saveRecon",errmsg.str());
511  }
512  else {
513    if(this->numPixels>0){
514      if(this->reconAllocated) delete [] this->recon;
515      this->numPixels = size;
516      this->recon = new float[size];
517      this->reconAllocated = true;
518      for(int i=0;i<size;i++) this->recon[i] = input[i];
519      this->reconExists = true;
520    }
521  }
522}
523//--------------------------------------------------------------------
524
525void Cube::getRecon(float *output){
526  /**
527   * The reconstructed array is written to output. The output array needs to
528   *  be defined beforehand: no checking is done on the memory.
529   * \param output The array that is written to.
530   */
531  // Need check for change in number of pixels!
532  for(int i=0;i<this->numPixels;i++){
533    if(this->reconExists) output[i] = this->recon[i];
534    else output[i] = 0.;
535  }
536}
537//--------------------------------------------------------------------
538
539void Cube::removeMW()
540{
541  /**
542   * The channels corresponding to the Milky Way range (as given by the Param
543   *  set) are all set to 0 in the pixel array.
544   * Only done if the appropriate flag is set, and the pixels are not BLANK.
545   * \deprecated
546   */
547  if(this->par.getFlagMW()){
548    for(int pix=0;pix<this->axisDim[0]*this->axisDim[1];pix++){
549      for(int z=0;z<this->axisDim[2];z++){
550        int pos = z*this->axisDim[0]*this->axisDim[1] + pix;
551        if(!this->isBlank(pos) && this->par.isInMW(z)) this->array[pos]=0.;
552      }
553    }
554  }
555}
556//--------------------------------------------------------------------
557
558void Cube::setCubeStatsOld()
559{
560  /** 
561   *   \deprecated
562   *
563   *   Calculates the full statistics for the cube:  mean, rms, median, madfm.
564   *   Only do this if the threshold has not been defined (ie. is still 0.,
565   *    its default).
566   *   Also work out the threshold and store it in the Param set.
567   *   
568   *   For the stats calculations, we ignore BLANKs and MW channels.
569   */
570
571  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
572    // if the user has defined a threshold, set this in the StatsContainer
573    this->Stats.setThreshold( this->par.getThreshold() );
574  }
575  else{
576    // only work out the mean etc if we need to.
577    // the only reason we don't is if the user has specified a threshold.
578   
579    std::cout << "Calculating the cube statistics... " << std::flush;
580   
581    // get number of good pixels;
582    int goodSize = 0;
583    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
584      for(int z=0;z<this->axisDim[2];z++){
585        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
586        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
587      }
588    }
589
590    float *tempArray = new float[goodSize];
591
592    goodSize=0;
593    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
594      for(int z=0;z<this->axisDim[2];z++){
595        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
596        if(!this->isBlank(vox) && !this->par.isInMW(z))
597          tempArray[goodSize++] = this->array[vox];
598      }
599    }
600    if(!this->reconExists){
601      // if there's no recon array, calculate everything from orig array
602      this->Stats.calculate(tempArray,goodSize);
603    }
604    else{
605      // just get mean & median from orig array, and rms & madfm from recon
606      StatsContainer<float> origStats,reconStats;
607      origStats.calculate(tempArray,goodSize);
608      goodSize=0;
609      for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
610        for(int z=0;z<this->axisDim[2];z++){
611          int vox = z * this->axisDim[0] * this->axisDim[1] + p;
612          if(!this->isBlank(vox) && !this->par.isInMW(z))
613            tempArray[goodSize++] = this->array[vox] - this->recon[vox];
614        }
615      }
616      reconStats.calculate(tempArray,goodSize);
617
618      // Get the "middle" estimators from the original array.
619      this->Stats.setMean(origStats.getMean());
620      this->Stats.setMedian(origStats.getMedian());
621      // Get the "spread" estimators from the residual (orig-recon) array
622      this->Stats.setStddev(reconStats.getStddev());
623      this->Stats.setMadfm(reconStats.getMadfm());
624    }
625
626    delete [] tempArray;
627
628    this->Stats.setUseFDR( this->par.getFlagFDR() );
629    // If the FDR method has been requested
630    if(this->par.getFlagFDR())  this->setupFDR();
631    else{
632      // otherwise, calculate one based on the requested SNR cut level, and
633      //   then set the threshold parameter in the Par set.
634      this->Stats.setThresholdSNR( this->par.getCut() );
635      this->par.setThreshold( this->Stats.getThreshold() );
636    }
637   
638   
639  }
640  std::cout << "Using ";
641  if(this->par.getFlagFDR()) std::cout << "effective ";
642  std::cout << "flux threshold of: ";
643  float thresh = this->Stats.getThreshold();
644  if(this->par.getFlagNegative()) thresh *= -1.;
645  std::cout << thresh << std::endl;
646
647}
648//--------------------------------------------------------------------
649
650void Cube::setCubeStats()
651{
652  /** 
653   *   Calculates the full statistics for the cube:
654   *     mean, rms, median, madfm
655   *   Only do this if the threshold has not been defined (ie. is still 0.,
656   *    its default).
657   *   Also work out the threshold and store it in the par set.
658   *   
659   *   Different from Cube::setCubeStatsOld() as it doesn't use the
660   *    getStats functions but has own versions of them hardcoded to
661   *    ignore BLANKs and MW channels. This saves on memory usage -- necessary
662   *    for dealing with very big files.
663   *
664   *   Three cases exist:
665   *  <ul><li>Simple case, with no reconstruction/smoothing: all stats
666   *          calculated from the original array.
667   *      <li>Wavelet reconstruction: mean & median calculated from the
668   *          original array, and stddev & madfm from the residual.
669   *      <li>Smoothing: all four stats calculated from the recon array
670   *          (which holds the smoothed data).
671   *  </ul>
672   */
673
674  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
675    // if the user has defined a threshold, set this in the StatsContainer
676    this->Stats.setThreshold( this->par.getThreshold() );
677  }
678  else{
679    // only work out the stats if we need to.
680    // the only reason we don't is if the user has specified a threshold.
681   
682    if(this->par.isVerbose())
683      std::cout << "Calculating the cube statistics... " << std::flush;
684   
685    long xysize = this->axisDim[0]*this->axisDim[1];
686
687    bool *mask = new bool[this->numPixels];
688    int vox,goodSize = 0;
689    for(int x=0;x<this->axisDim[0];x++){
690      for(int y=0;y<this->axisDim[1];y++){
691        for(int z=0;z<this->axisDim[2];z++){
692          vox = z * xysize + y*this->axisDim[0] + x;
693          mask[vox] = (!this->isBlank(vox) &&
694                       !this->par.isInMW(z) &&
695                       this->par.isStatOK(x,y,z) );
696          if(mask[vox]) goodSize++;
697        }
698      }
699    }
700
701    float mean,median,stddev,madfm;
702    if( this->par.getFlagATrous() ){
703      // Case #2 -- wavelet reconstruction
704      // just get mean & median from orig array, and rms & madfm from
705      // residual recompute array values to be residuals & then find
706      // stddev & madfm
707      if(!this->reconExists)
708        duchampError("setCubeStats",
709                     "Reconstruction not yet done!\nCannot calculate stats!\n");
710      else{
711        float *tempArray = new float[goodSize];
712
713        goodSize=0;
714        for(int x=0;x<this->axisDim[0];x++){
715          for(int y=0;y<this->axisDim[1];y++){
716            for(int z=0;z<this->axisDim[2];z++){
717              vox = z * xysize + y*this->axisDim[0] + x;
718              if(mask[vox]) tempArray[goodSize++] = this->array[vox];
719            }
720          }
721        }
722
723        // First, find the mean of the original array. Store it.
724        mean = tempArray[0];
725        for(int i=1;i<goodSize;i++) mean += tempArray[i];
726        mean /= float(goodSize);
727        mean = findMean(tempArray,goodSize);
728        this->Stats.setMean(mean);
729       
730        // Now sort it and find the median. Store it.
731        std::sort(tempArray,tempArray+goodSize);
732        if((goodSize%2)==0)
733          median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
734        else median = tempArray[goodSize/2];
735        this->Stats.setMedian(median);
736
737        // Now calculate the residuals and find the mean & median of
738        // them. We don't store these, but they are necessary to find
739        // the sttdev & madfm.
740        goodSize = 0;
741        for(int p=0;p<xysize;p++){
742          for(int z=0;z<this->axisDim[2];z++){
743            vox = z * xysize + p;
744            if(mask[vox])
745              tempArray[goodSize++] = this->array[vox] - this->recon[vox];
746          }
747        }
748        mean = tempArray[0];
749        for(int i=1;i<goodSize;i++) mean += tempArray[i];
750        mean /= float(goodSize);
751        std::sort(tempArray,tempArray+goodSize);
752        if((goodSize%2)==0)
753          median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
754        else median = tempArray[goodSize/2];
755
756        // Now find the standard deviation of the residuals. Store it.
757        stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
758        for(int i=1;i<goodSize;i++)
759          stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
760        stddev = sqrt(stddev/float(goodSize-1));
761        this->Stats.setStddev(stddev);
762
763        // Now find the madfm of the residuals. Store it.
764        for(int i=0;i<goodSize;i++){
765          if(tempArray[i]>median) tempArray[i] = tempArray[i]-median;
766          else tempArray[i] = median - tempArray[i];
767        }
768        std::sort(tempArray,tempArray+goodSize);
769        if((goodSize%2)==0)
770          madfm = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
771        else madfm = tempArray[goodSize/2];
772        this->Stats.setMadfm(madfm);
773
774        delete [] tempArray;
775      }
776    }
777    else if(this->par.getFlagSmooth()) {
778      // Case #3 -- smoothing
779      // get all four stats from the recon array, which holds the
780      // smoothed data. This can just be done with the
781      // StatsContainer::calculate function, using the mask generated
782      // earlier.
783      if(!this->reconExists)
784        duchampError("setCubeStats","Smoothing not yet done!\nCannot calculate stats!\n");
785      else this->Stats.calculate(this->recon,this->numPixels,mask);
786    }
787    else{
788      // Case #1 -- default case, with no smoothing or reconstruction.
789      // get all four stats from the original array. This can just be
790      // done with the StatsContainer::calculate function, using the
791      // mask generated earlier.
792      this->Stats.calculate(this->array,this->numPixels,mask);
793
794    }
795
796    this->Stats.setUseFDR( this->par.getFlagFDR() );
797    // If the FDR method has been requested, define the P-value
798    // threshold
799    if(this->par.getFlagFDR())  this->setupFDR();
800    else{
801      // otherwise, calculate threshold based on the requested SNR cut
802      // level, and then set the threshold parameter in the Par set.
803      this->Stats.setThresholdSNR( this->par.getCut() );
804      this->par.setThreshold( this->Stats.getThreshold() );
805    }
806   
807    delete [] mask;
808
809  }
810
811  if(this->par.isVerbose()){
812    std::cout << "Using ";
813    if(this->par.getFlagFDR()) std::cout << "effective ";
814    std::cout << "flux threshold of: ";
815    float thresh = this->Stats.getThreshold();
816    if(this->par.getFlagNegative()) thresh *= -1.;
817    std::cout << thresh << std::endl;
818  }
819
820}
821//--------------------------------------------------------------------
822
823void Cube::setupFDR()
824{
825  /**
826   *  Call the setupFDR(float *) function on the pixel array of the
827   *  cube. This is the usual way of running it.
828   *
829   *  However, if we are in smoothing mode, we calculate the FDR
830   *  parameters using the recon array, which holds the smoothed
831   *  data. Gives an error message if the reconExists flag is not set.
832   *
833   */
834  if(this->par.getFlagSmooth())
835    if(this->reconExists) this->setupFDR(this->recon);
836    else{
837      duchampError("setupFDR",
838"Smoothing not done properly! Using original array for defining threshold.\n");
839      this->setupFDR(this->array);
840    }
841  else if( this->par.getFlagATrous() ){
842    this->setupFDR(this->recon);
843  }
844  else{
845    this->setupFDR(this->array);
846  }
847}
848//--------------------------------------------------------------------
849
850void Cube::setupFDR(float *input)
851{
852  /** 
853   *   Determines the critical Probability value for the False
854   *   Discovery Rate detection routine. All pixels in the given arry
855   *   with Prob less than this value will be considered detections.
856   *
857   *   Note that the Stats of the cube need to be calculated first.
858   *
859   *   The Prob here is the probability, assuming a Normal
860   *   distribution, of obtaining a value as high or higher than the
861   *   pixel value (ie. only the positive tail of the PDF).
862   *
863   *   The probabilities are calculated using the
864   *   StatsContainer::getPValue(), which calculates the z-statistic,
865   *   and then the probability via
866   *   \f$0.5\operatorname{erfc}(z/\sqrt{2})\f$ -- giving the positive
867   *   tail probability.
868   */
869
870  // first calculate p-value for each pixel -- assume Gaussian for now.
871
872  float *orderedP = new float[this->numPixels];
873  int count = 0;
874  for(int x=0;x<this->axisDim[0];x++){
875    for(int y=0;y<this->axisDim[1];y++){
876      for(int z=0;z<this->axisDim[2];z++){
877        int pix = z * this->axisDim[0]*this->axisDim[1] +
878          y*this->axisDim[0] + x;
879
880        if(!(this->par.isBlank(this->array[pix])) && !this->par.isInMW(z)){
881          // only look at non-blank, valid pixels
882//        orderedP[count++] = this->Stats.getPValue(this->array[pix]);
883          orderedP[count++] = this->Stats.getPValue(input[pix]);
884        }
885      }
886    }
887  }
888
889  // now order them
890  std::stable_sort(orderedP,orderedP+count);
891 
892  // now find the maximum P value.
893  int max = 0;
894  float cN = 0.;
895  int numVox = int(ceil(this->par.getBeamSize()));
896  if(this->head.getNumAxes()>2) numVox *= 2;
897  // why beamSize*2? we are doing this in 3D, so spectrally assume just the
898  //  neighbouring channels are correlated, but spatially all those within
899  //  the beam, so total number of voxels is 2*beamSize
900  for(int psfCtr=1;psfCtr<=numVox;psfCtr++) cN += 1./float(psfCtr);
901
902  double slope = this->par.getAlpha()/cN;
903  for(int loopCtr=0;loopCtr<count;loopCtr++) {
904    if( orderedP[loopCtr] < (slope * double(loopCtr+1)/ double(count)) ){
905      max = loopCtr;
906    }
907  }
908
909  this->Stats.setPThreshold( orderedP[max] );
910
911
912  // Find real value of the P threshold by finding the inverse of the
913  //  error function -- root finding with brute force technique
914  //  (relatively slow, but we only do it once).
915  double zStat     = 0.;
916  double deltaZ    = 0.1;
917  double tolerance = 1.e-6;
918  double initial   = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
919  do{
920    zStat+=deltaZ;
921    double current = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
922    if((initial*current)<0.){
923      zStat-=deltaZ;
924      deltaZ/=2.;
925    }
926  }while(deltaZ>tolerance);
927  this->Stats.setThreshold( zStat*this->Stats.getSpread() +
928                            this->Stats.getMiddle() );
929
930  ///////////////////////////
931  if(TESTING){
932    std::stringstream ss;
933    float *xplot = new float[2*max];
934    for(int i=0;i<2*max;i++) xplot[i]=float(i)/float(count);
935    cpgopen("latestFDR.ps/vcps");
936    cpgpap(8.,1.);
937    cpgslw(3);
938    cpgenv(0,float(2*max)/float(count),0,orderedP[2*max],0,0);
939    cpglab("i/N (index)", "p-value","");
940    cpgpt(2*max,xplot,orderedP,DOT);
941
942    ss.str("");
943    ss << "\\gm = " << this->Stats.getMiddle();
944    cpgtext(max/(4.*count),0.9*orderedP[2*max],ss.str().c_str());
945    ss.str("");
946    ss << "\\gs = " << this->Stats.getSpread();
947    cpgtext(max/(4.*count),0.85*orderedP[2*max],ss.str().c_str());
948    ss.str("");
949    ss << "Slope = " << slope;
950    cpgtext(max/(4.*count),0.8*orderedP[2*max],ss.str().c_str());
951    ss.str("");
952    ss << "Alpha = " << this->par.getAlpha();
953    cpgtext(max/(4.*count),0.75*orderedP[2*max],ss.str().c_str());
954    ss.str("");
955    ss << "c\\dN\\u = " << cN;
956    cpgtext(max/(4.*count),0.7*orderedP[2*max],ss.str().c_str());
957    ss.str("");
958    ss << "max = "<<max << " (out of " << count << ")";
959    cpgtext(max/(4.*count),0.65*orderedP[2*max],ss.str().c_str());
960    ss.str("");
961    ss << "Threshold = "<<zStat*this->Stats.getSpread()+this->Stats.getMiddle();
962    cpgtext(max/(4.*count),0.6*orderedP[2*max],ss.str().c_str());
963 
964    cpgslw(1);
965    cpgsci(RED);
966    cpgmove(0,0);
967    cpgdraw(1,slope);
968    cpgsci(BLUE);
969    cpgsls(DOTTED);
970    cpgmove(0,orderedP[max]);
971    cpgdraw(2*max/float(count),orderedP[max]);
972    cpgmove(max/float(count),0);
973    cpgdraw(max/float(count),orderedP[2*max]);
974    cpgsci(GREEN);
975    cpgsls(SOLID);
976    for(int i=1;i<=10;i++) {
977      ss.str("");
978      ss << float(i)/2. << "\\gs";
979      float prob = 0.5*erfc((float(i)/2.)/M_SQRT2);
980      cpgtick(0, 0, 0, orderedP[2*max],
981              prob/orderedP[2*max],
982              0, 1, 1.5, 90., ss.str().c_str());
983    }
984    cpgend();
985    delete [] xplot;
986  }
987  delete [] orderedP;
988
989}
990//--------------------------------------------------------------------
991
992bool Cube::isDetection(long x, long y, long z)
993{
994  /**
995   * Is a given voxel at position (x,y,z) a detection, based on the statistics
996   *  in the Cube's StatsContainer?
997   * If the pixel lies outside the valid range for the data array,
998   * return false.
999   * \param x X-value of the Cube's voxel to be tested.
1000   * \param y Y-value of the Cube's voxel to be tested.
1001   * \param z Z-value of the Cube's voxel to be tested.
1002   */
1003    long voxel = z*axisDim[0]*axisDim[1] + y*axisDim[0] + x;
1004    return DataArray::isDetection(array[voxel]);
1005  };
1006//--------------------------------------------------------------------
1007
1008void Cube::calcObjectWCSparams()
1009{
1010  /**
1011   *  A function that calculates the WCS parameters for each object in the
1012   *  Cube's list of detections.
1013   *  Each object gets an ID number assigned to it (which is simply its order
1014   *   in the list), and if the WCS is good, the WCS paramters are calculated.
1015   */
1016 
1017  for(int i=0;i<this->objectList->size();i++){
1018    this->objectList->at(i).setID(i+1);
1019    this->objectList->at(i).setCentreType(this->par.getPixelCentre());
1020    this->objectList->at(i).calcFluxes(this->array,this->axisDim);
1021    this->objectList->at(i).calcWCSparams(this->array,this->axisDim,this->head);
1022   
1023    if(this->par.getFlagUserThreshold())
1024      this->objectList->at(i).setPeakSNR( this->objectList->at(i).getPeakFlux() / this->Stats.getThreshold() );
1025    else
1026      this->objectList->at(i).setPeakSNR( (this->objectList->at(i).getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
1027
1028  } 
1029
1030  if(!this->head.isWCS()){
1031    // if the WCS is bad, set the object names to Obj01 etc
1032    int numspaces = int(log10(this->objectList->size())) + 1;
1033    std::stringstream ss;
1034    for(int i=0;i<this->objectList->size();i++){
1035      ss.str("");
1036      ss << "Obj" << std::setfill('0') << std::setw(numspaces) << i+1;
1037      this->objectList->at(i).setName(ss.str());
1038    }
1039  }
1040 
1041}
1042//--------------------------------------------------------------------
1043
1044void Cube::updateDetectMap()
1045{
1046  /**
1047   *  A function that, for each detected object in the cube's list, increments
1048   *   the cube's detection map by the required amount at each pixel.
1049   */
1050
1051  Scan temp;
1052  for(int obj=0;obj<this->objectList->size();obj++){
1053    long numZ=this->objectList->at(obj).pixels().getNumChanMap();
1054    for(int iz=0;iz<numZ;iz++){ // for each channel map
1055      Object2D *chanmap = new Object2D;
1056      *chanmap = this->objectList->at(obj).pixels().getChanMap(iz).getObject();
1057      for(int iscan=0;iscan<chanmap->getNumScan();iscan++){
1058        temp = chanmap->getScan(iscan);
1059        for(int x=temp.getX(); x <= temp.getXmax(); x++)
1060          this->detectMap[temp.getY()*this->axisDim[0] + x]++;
1061      } // end of loop over scans
1062      delete chanmap;
1063    } // end of loop over channel maps
1064  } // end of loop over objects.
1065
1066}
1067//--------------------------------------------------------------------
1068
1069void Cube::updateDetectMap(Detection obj)
1070{
1071  /**
1072   *  A function that, for the given object, increments the cube's
1073   *  detection map by the required amount at each pixel.
1074   *
1075   *  \param obj A Detection object that is being incorporated into the map.
1076   */
1077
1078  Scan temp;
1079  long numZ=obj.pixels().getNumChanMap();
1080  for(int iz=0;iz<numZ;iz++){ // for each channel map
1081    Object2D chanmap = obj.pixels().getChanMap(iz).getObject();
1082    for(int iscan=0;iscan<chanmap.getNumScan();iscan++){
1083      temp = chanmap.getScan(iscan);
1084      for(int x=temp.getX(); x <= temp.getXmax(); x++)
1085        this->detectMap[temp.getY()*this->axisDim[0] + x]++;
1086    } // end of loop over scans
1087  } // end of loop over channel maps
1088
1089}
1090//--------------------------------------------------------------------
1091
1092float Cube::enclosedFlux(Detection obj)
1093{
1094  /**
1095   *   A function to calculate the flux enclosed by the range
1096   *    of pixels detected in the object obj (not necessarily all
1097   *    pixels will have been detected).
1098   *
1099   *   \param obj The Detection under consideration.
1100   */
1101  obj.calcFluxes(this->array, this->axisDim);
1102  int xsize = obj.getXmax()-obj.getXmin()+1;
1103  int ysize = obj.getYmax()-obj.getYmin()+1;
1104  int zsize = obj.getZmax()-obj.getZmin()+1;
1105  std::vector <float> fluxArray(xsize*ysize*zsize,0.);
1106  for(int x=0;x<xsize;x++){
1107    for(int y=0;y<ysize;y++){
1108      for(int z=0;z<zsize;z++){
1109        fluxArray[x+y*xsize+z*ysize*xsize] =
1110          this->getPixValue(x+obj.getXmin(),
1111                            y+obj.getYmin(),
1112                            z+obj.getZmin());
1113        if(this->par.getFlagNegative())
1114          fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
1115      }
1116    }
1117  }
1118  float sum = 0.;
1119  for(int i=0;i<fluxArray.size();i++)
1120    if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
1121  return sum;
1122}
1123//--------------------------------------------------------------------
1124
1125void Cube::setupColumns()
1126{
1127  /**
1128   *   A front-end to the two setup routines in columns.cc.
1129   *   This first calculates the WCS parameters for all objects, then
1130   *    sets up the columns (calculates their widths and precisions and so on).
1131   *   The precisions are also stored in each Detection object.
1132   */
1133  this->calcObjectWCSparams(); 
1134  // need this as the colSet functions use vel, RA, Dec, etc...
1135 
1136  this->fullCols.clear();
1137  this->fullCols = getFullColSet(*(this->objectList), this->head);
1138
1139  this->logCols.clear();
1140  this->logCols = getLogColSet(*(this->objectList), this->head);
1141
1142  int vel,fpeak,fint,pos,xyz,snr;
1143  vel = fullCols[VEL].getPrecision();
1144  fpeak = fullCols[FPEAK].getPrecision();
1145  snr = fullCols[SNRPEAK].getPrecision();
1146  xyz = fullCols[X].getPrecision();
1147  xyz = std::max(xyz, fullCols[Y].getPrecision());
1148  xyz = std::max(xyz, fullCols[Z].getPrecision());
1149  if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
1150  else fint = fullCols[FTOT].getPrecision();
1151  pos = fullCols[WRA].getPrecision();
1152  pos = std::max(pos, fullCols[WDEC].getPrecision());
1153 
1154  for(int obj=0;obj<this->objectList->size();obj++){
1155    this->objectList->at(obj).setVelPrec(vel);
1156    this->objectList->at(obj).setFpeakPrec(fpeak);
1157    this->objectList->at(obj).setXYZPrec(xyz);
1158    this->objectList->at(obj).setPosPrec(pos);
1159    this->objectList->at(obj).setFintPrec(fint);
1160    this->objectList->at(obj).setSNRPrec(snr);
1161  }
1162
1163}
1164//--------------------------------------------------------------------
1165
1166bool Cube::objAtSpatialEdge(Detection obj)
1167{
1168  /**
1169   *   A function to test whether the object obj
1170   *    lies at the edge of the cube's spatial field --
1171   *    either at the boundary, or next to BLANKs.
1172   *
1173   *   \param obj The Detection under consideration.
1174   */
1175
1176  bool atEdge = false;
1177
1178  int pix = 0;
1179  std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1180  while(!atEdge && pix<voxlist.size()){
1181    // loop over each pixel in the object, until we find an edge pixel.
1182    for(int dx=-1;dx<=1;dx+=2){
1183      if( ((voxlist[pix].getX()+dx)<0) ||
1184          ((voxlist[pix].getX()+dx)>=this->axisDim[0]) )
1185        atEdge = true;
1186      else if(this->isBlank(voxlist[pix].getX()+dx,
1187                            voxlist[pix].getY(),
1188                            voxlist[pix].getZ()))
1189        atEdge = true;
1190    }
1191    for(int dy=-1;dy<=1;dy+=2){
1192      if( ((voxlist[pix].getY()+dy)<0) ||
1193          ((voxlist[pix].getY()+dy)>=this->axisDim[1]) )
1194        atEdge = true;
1195      else if(this->isBlank(voxlist[pix].getX(),
1196                            voxlist[pix].getY()+dy,
1197                            voxlist[pix].getZ()))
1198        atEdge = true;
1199    }
1200    pix++;
1201  }
1202
1203  return atEdge;
1204}
1205//--------------------------------------------------------------------
1206
1207bool Cube::objAtSpectralEdge(Detection obj)
1208{
1209  /** 
1210   *   A function to test whether the object obj
1211   *    lies at the edge of the cube's spectral extent --
1212   *    either at the boundary, or next to BLANKs.
1213   *
1214   *   /param obj The Detection under consideration.
1215   */
1216
1217  bool atEdge = false;
1218
1219  int pix = 0;
1220  std::vector<Voxel> voxlist = obj.pixels().getPixelSet();
1221  while(!atEdge && pix<voxlist.size()){
1222    // loop over each pixel in the object, until we find an edge pixel.
1223    for(int dz=-1;dz<=1;dz+=2){
1224      if( ((voxlist[pix].getZ()+dz)<0) ||
1225          ((voxlist[pix].getZ()+dz)>=this->axisDim[2]))
1226        atEdge = true;
1227      else if(this->isBlank(voxlist[pix].getX(),
1228                            voxlist[pix].getY(),
1229                            voxlist[pix].getZ()+dz))
1230        atEdge = true;
1231    }
1232    pix++;
1233  }
1234
1235  return atEdge;
1236}
1237//--------------------------------------------------------------------
1238
1239void Cube::setObjectFlags()
1240{
1241  /**   
1242   *   A function to set any warning flags for all the detected objects
1243   *    associated with the cube.
1244   *   Flags to be looked for:
1245   *    <ul><li> Negative enclosed flux (N)
1246   *        <li> Detection at edge of field (spatially) (E)
1247   *        <li> Detection at edge of spectral region (S)
1248   *    </ul>
1249   */
1250
1251  for(int i=0;i<this->objectList->size();i++){
1252
1253    if( this->enclosedFlux(this->objectList->at(i)) < 0. ) 
1254      this->objectList->at(i).addToFlagText("N");
1255
1256    if( this->objAtSpatialEdge(this->objectList->at(i)) )
1257      this->objectList->at(i).addToFlagText("E");
1258
1259    if( this->objAtSpectralEdge(this->objectList->at(i)) &&
1260        (this->axisDim[2] > 2))
1261      this->objectList->at(i).addToFlagText("S");
1262
1263  }
1264
1265}
1266//--------------------------------------------------------------------
1267
1268
1269
1270/****************************************************************/
1271/////////////////////////////////////////////////////////////
1272//// Functions for Image class
1273/////////////////////////////////////////////////////////////
1274
1275Image::Image(long size){
1276  // need error handling in case size<0 !!!
1277  this->numPixels = this->numDim = 0;
1278  if(size<0)
1279    duchampError("Image(size)","Negative size -- could not define Image");
1280  else{
1281    if(size>0) this->array = new float[size];
1282    this->numPixels = size;
1283    this->axisDim = new long[2];
1284    this->numDim = 2;
1285  }
1286}
1287//--------------------------------------------------------------------
1288
1289Image::Image(long *dimensions){
1290  this->numPixels = this->numDim = 0;
1291  int size = dimensions[0] * dimensions[1];
1292  if(size<0)
1293    duchampError("Image(dimArray)","Negative size -- could not define Image");
1294  else{
1295    this->numPixels = size;
1296    if(size>0) this->array = new float[size];
1297    this->numDim=2;
1298    this->axisDim = new long[2];
1299    for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
1300  }
1301}
1302//--------------------------------------------------------------------
1303//--------------------------------------------------------------------
1304
1305void Image::saveArray(float *input, long size)
1306{
1307  /**
1308   * Saves the array in input to the pixel array Image::array.
1309   * The size of the array given must be the same as the current number of
1310   * pixels, else an error message is returned and nothing is done.
1311   * \param input The array of values to be saved.
1312   * \param size The size of input.
1313   */
1314  if(size != this->numPixels)
1315    duchampError("Image::saveArray",
1316                 "Input array different size to existing array. Cannot save.");
1317  else {
1318    if(this->numPixels>0) delete [] array;
1319    this->numPixels = size;
1320    if(this->numPixels>0) this->array = new float[size];
1321    for(int i=0;i<size;i++) this->array[i] = input[i];
1322  }
1323}
1324//--------------------------------------------------------------------
1325
1326void Image::extractSpectrum(float *Array, long *dim, long pixel)
1327{
1328  /**
1329   *  A function to extract a 1-D spectrum from a 3-D array.
1330   *  The array is assumed to be 3-D with the third dimension the spectral one.
1331   *  The spectrum extracted is the one lying in the spatial pixel referenced
1332   *    by the third argument.
1333   *  The extracted spectrum is stored in the pixel array Image::array.
1334   * \param Array The array containing the pixel values, from which
1335   *               the spectrum is extracted.
1336   * \param dim The array of dimension values.
1337   * \param pixel The spatial pixel that contains the desired spectrum.
1338   */
1339  if((pixel<0)||(pixel>=dim[0]*dim[1]))
1340    duchampError("Image::extractSpectrum",
1341                 "Requested spatial pixel outside allowed range. Cannot save.");
1342  else if(dim[2] != this->numPixels)
1343    duchampError("Image::extractSpectrum",
1344                 "Input array different size to existing array. Cannot save.");
1345  else {
1346    if(this->numPixels>0) delete [] array;
1347    this->numPixels = dim[2];
1348    if(this->numPixels>0) this->array = new float[dim[2]];
1349    for(int z=0;z<dim[2];z++) this->array[z] = Array[z*dim[0]*dim[1] + pixel];
1350  }
1351}
1352//--------------------------------------------------------------------
1353
1354void Image::extractSpectrum(Cube &cube, long pixel)
1355{
1356  /**
1357   *  A function to extract a 1-D spectrum from a Cube class
1358   *  The spectrum extracted is the one lying in the spatial pixel referenced
1359   *    by the second argument.
1360   *  The extracted spectrum is stored in the pixel array Image::array.
1361   * \param cube The Cube containing the pixel values, from which the spectrum is extracted.
1362   * \param pixel The spatial pixel that contains the desired spectrum.
1363   */
1364  long zdim = cube.getDimZ();
1365  long spatSize = cube.getDimX()*cube.getDimY();
1366  if((pixel<0)||(pixel>=spatSize))
1367    duchampError("Image::extractSpectrum",
1368                 "Requested spatial pixel outside allowed range. Cannot save.");
1369  else if(zdim != this->numPixels)
1370    duchampError("Image::extractSpectrum",
1371                 "Input array different size to existing array. Cannot save.");
1372  else {
1373    if(this->numPixels>0) delete [] array;
1374    this->numPixels = zdim;
1375    if(this->numPixels>0) this->array = new float[zdim];
1376    for(int z=0;z<zdim;z++)
1377      this->array[z] = cube.getPixValue(z*spatSize + pixel);
1378  }
1379}
1380//--------------------------------------------------------------------
1381
1382void Image::extractImage(float *Array, long *dim, long channel)
1383{
1384  /**
1385   *  A function to extract a 2-D image from a 3-D array.
1386   *  The array is assumed to be 3-D with the third dimension the spectral one.
1387   *  The dimensions of the array are in the dim[] array.
1388   *  The image extracted is the one lying in the channel referenced
1389   *    by the third argument.
1390   *  The extracted image is stored in the pixel array Image::array.
1391   * \param Array The array containing the pixel values, from which the image is extracted.
1392   * \param dim The array of dimension values.
1393   * \param channel The spectral channel that contains the desired image.
1394   */
1395
1396  long spatSize = dim[0]*dim[1];
1397  if((channel<0)||(channel>=dim[2]))
1398    duchampError("Image::extractImage",
1399                 "Requested channel outside allowed range. Cannot save.");
1400  else if(spatSize != this->numPixels)
1401    duchampError("Image::extractImage",
1402                 "Input array different size to existing array. Cannot save.");
1403  else {
1404    if(this->numPixels>0) delete [] array;
1405    this->numPixels = spatSize;
1406    if(this->numPixels>0) this->array = new float[spatSize];
1407    for(int npix=0; npix<spatSize; npix++)
1408      this->array[npix] = Array[channel*spatSize + npix];
1409  }
1410}
1411//--------------------------------------------------------------------
1412
1413void Image::extractImage(Cube &cube, long channel)
1414{
1415  /**
1416   *  A function to extract a 2-D image from Cube class.
1417   *  The image extracted is the one lying in the channel referenced
1418   *    by the second argument.
1419   *  The extracted image is stored in the pixel array Image::array.
1420   * \param cube The Cube containing the pixel values, from which the image is extracted.
1421   * \param channel The spectral channel that contains the desired image.
1422   */
1423  long spatSize = cube.getDimX()*cube.getDimY();
1424  if((channel<0)||(channel>=cube.getDimZ()))
1425    duchampError("Image::extractImage",
1426                 "Requested channel outside allowed range. Cannot save.");
1427  else if(spatSize != this->numPixels)
1428    duchampError("Image::extractImage",
1429                 "Input array different size to existing array. Cannot save.");
1430  else {
1431    if(this->numPixels>0) delete [] array;
1432    this->numPixels = spatSize;
1433    if(this->numPixels>0) this->array = new float[spatSize];
1434    for(int npix=0; npix<spatSize; npix++)
1435      this->array[npix] = cube.getPixValue(channel*spatSize + npix);
1436  }
1437}
1438//--------------------------------------------------------------------
1439
1440void Image::removeMW()
1441{
1442  /**
1443   *  A function to remove the Milky Way range of channels from a 1-D spectrum.
1444   *  The array in this Image is assumed to be 1-D, with only the first axisDim
1445   *    equal to 1.
1446   *  The values of the MW channels are set to 0, unless they are BLANK.
1447   */
1448  if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
1449    for(int z=0;z<this->axisDim[0];z++){
1450      if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
1451    }
1452  }
1453}
1454
Note: See TracBrowser for help on using the repository browser.