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

Last change on this file since 208 was 208, checked in by Matthew Whiting, 18 years ago
  • Enabled saving and reading in of a smoothed array, in manner directly analogous to that for the recon array.
    • New file : src/Cubes/readSmooth.cc
    • The other new functions go in existing files eg. saveImage.cc
    • Renamed some functions (like writeHeader...) to be more obvious what they do.
    • The reading in is taken care of by new function Cube::readSavedArrays() -- handles both smoothed and recon'd arrays.
    • Associated parameters in Param class
    • Clarified names of FITS header strings in duchamp.hh.
  • Updated the documentation to describe the ability to smooth a cube.
  • Added description of feedback mechanisms in the Install appendix.
  • Also, Hanning class improved to guard against memory leaks.


File size: 33.9 KB
Line 
1#include <unistd.h>
2#include <iostream>
3#include <iomanip>
4#include <vector>
5#include <string>
6
7#include <wcs.h>
8
9#include <duchamp.hh>
10#include <param.hh>
11#include <Cubes/cubes.hh>
12#include <Detection/detection.hh>
13#include <Detection/columns.hh>
14#include <Utils/utils.hh>
15#include <Utils/mycpgplot.hh>
16#include <Utils/Statistics.hh>
17using namespace Column;
18using namespace mycpgplot;
19using namespace Statistics;
20
21/****************************************************************/
22///////////////////////////////////////////////////
23//// Functions for DataArray class:
24///////////////////////////////////////////////////
25
26DataArray::DataArray(short int nDim, long size){
27
28  if(size<0)
29    duchampError("DataArray(nDim,size)",
30                 "Negative size -- could not define DataArray");
31  else if(nDim<0)
32    duchampError("DataArray(nDim,size)",
33                 "Negative number of dimensions: could not define DataArray");
34  else {
35    if(size>0) this->array = new float[size];
36    this->numPixels = size;
37    if(nDim>0) this->axisDim = new long[nDim];
38    this->numDim = nDim;
39  }
40}
41//--------------------------------------------------------------------
42
43DataArray::DataArray(short int nDim, long *dimensions){
44  if(nDim<0)
45    duchampError("DataArray(nDim,dimArray)",
46                 "Negative number of dimensions: could not define DataArray");
47  else {
48    int size = dimensions[0];
49    for(int i=1;i<nDim;i++) size *= dimensions[i];
50    if(size<0)
51      duchampError("DataArray(nDim,dimArray)",
52                   "Negative size: could not define DataArray");
53    else{
54      this->numPixels = size;
55      if(size>0) this->array = new float[size];
56      this->numDim=nDim;
57      if(nDim>0) this->axisDim = new long[nDim];
58      for(int i=0;i<nDim;i++) this->axisDim[i] = dimensions[i];
59    }
60  }
61}
62//--------------------------------------------------------------------
63
64DataArray::~DataArray()
65{
66  delete [] this->array;
67  delete [] this->axisDim;
68  this->objectList.clear();
69}
70//--------------------------------------------------------------------
71
72void DataArray::getDimArray(long *output){
73  for(int i=0;i<this->numDim;i++) output[i] = this->axisDim[i];
74}
75//--------------------------------------------------------------------
76
77void DataArray::getArray(float *output){
78  for(int i=0;i<this->numPixels;i++) output[i] = this->array[i];
79}
80//--------------------------------------------------------------------
81
82void DataArray::saveArray(float *input, long size){
83  if(size != this->numPixels)
84    duchampError("DataArray::saveArray",
85                 "Input array different size to existing array. Cannot save.");
86  else {
87    if(this->numPixels>0) delete [] this->array;
88    this->numPixels = size;
89    this->array = new float[size];
90    for(int i=0;i<size;i++) this->array[i] = input[i];
91  }
92}
93//--------------------------------------------------------------------
94
95void DataArray::getDim(long &x, long &y, long &z){
96  if(numDim>0) x=axisDim[0];
97  else x=0;
98  if(numDim>1) y=axisDim[1];
99  else y=0;
100  if(numDim>2) z=axisDim[2];
101  else z=0;
102}
103//--------------------------------------------------------------------
104
105void DataArray::addObject(Detection object){
106  // adds a single detection to the object list
107  // objectList is a vector, so just use push_back()
108  this->objectList.push_back(object);
109}
110//--------------------------------------------------------------------
111
112void DataArray::addObjectList(vector <Detection> newlist) {
113  for(int i=0;i<newlist.size();i++) this->objectList.push_back(newlist[i]);
114}
115//--------------------------------------------------------------------
116
117void DataArray::addObjectOffsets(){
118  for(int i=0;i<this->objectList.size();i++){
119    for(int p=0;p<this->objectList[i].getSize();p++){
120      this->objectList[i].setX(p,this->objectList[i].getX(p)+
121                               this->par.getXOffset());
122      this->objectList[i].setY(p,this->objectList[i].getY(p)+
123                               this->par.getYOffset());
124      this->objectList[i].setZ(p,this->objectList[i].getZ(p)+
125                               this->par.getZOffset());
126    }
127  }
128}
129//--------------------------------------------------------------------
130
131std::ostream& operator<< ( std::ostream& theStream, DataArray &array)
132{
133  for(int i=0;i<array.numDim;i++){
134    if(i>0) theStream<<"x";
135    theStream<<array.axisDim[i];
136  }
137  theStream<<std::endl;
138  theStream<<array.objectList.size()<<" detections:\n--------------\n";
139  for(int i=0;i<array.objectList.size();i++){
140    theStream << "Detection #" << array.objectList[i].getID()<<std::endl;
141    Detection *obj = new Detection;
142    *obj = array.objectList[i];
143    for(int j=0;j<obj->getSize();j++){
144      obj->setX(j,obj->getX(j)+obj->getXOffset());
145      obj->setY(j,obj->getY(j)+obj->getYOffset());
146      obj->setZ(j,obj->getZ(j)+obj->getZOffset());
147    }
148    theStream<<*obj;
149    delete obj;
150  }
151  theStream<<"--------------\n";
152}
153
154/****************************************************************/
155/////////////////////////////////////////////////////////////
156//// Functions for Image class
157/////////////////////////////////////////////////////////////
158
159Image::Image(long size){
160  // need error handling in case size<0 !!!
161  if(size<0)
162    duchampError("Image(size)","Negative size -- could not define Image");
163  else{
164    if(size>0) this->array = new float[size];
165    this->numPixels = size;
166    this->axisDim = new long[2];
167    this->numDim = 2;
168  }
169}
170//--------------------------------------------------------------------
171
172Image::Image(long *dimensions){
173  int size = dimensions[0] * dimensions[1];
174  if(size<0)
175    duchampError("Image(dimArray)","Negative size -- could not define Image");
176  else{
177    this->numPixels = size;
178    if(size>0) this->array = new float[size];
179    this->numDim=2;
180    this->axisDim = new long[2];
181    for(int i=0;i<2;i++) this->axisDim[i] = dimensions[i];
182  }
183}
184//--------------------------------------------------------------------
185//--------------------------------------------------------------------
186
187void Image::saveArray(float *input, long size){
188  if(size != this->numPixels)
189    duchampError("Image::saveArray",
190                 "Input array different size to existing array. Cannot save.");
191  else {
192    if(this->numPixels>0) delete [] array;
193    this->numPixels = size;
194    if(this->numPixels>0) this->array = new float[size];
195    for(int i=0;i<size;i++) this->array[i] = input[i];
196  }
197}
198//--------------------------------------------------------------------
199
200void Image::extractSpectrum(float *Array, long *dim, long pixel)
201{
202  /**
203   * Image::extractSpectrum(float *, long *, int)
204   *  A function to extract a 1-D spectrum from a 3-D array.
205   *  The array is assumed to be 3-D with the third dimension the spectral one.
206   *  The dimensions of the array are in the dim[] array.
207   *  The spectrum extracted is the one lying in the spatial pixel referenced
208   *    by the third argument.
209   */
210  float *spec = new float[dim[2]];
211  for(int z=0;z<dim[2];z++) spec[z] = Array[z*dim[0]*dim[1] + pixel];
212  this->saveArray(spec,dim[2]);
213  delete [] spec;
214}
215//--------------------------------------------------------------------
216
217void Image::extractSpectrum(Cube &cube, long pixel)
218{
219  /**
220   * Image::extractSpectrum(Cube &, int)
221   *  A function to extract a 1-D spectrum from a Cube class
222   *  The spectrum extracted is the one lying in the spatial pixel referenced
223   *    by the second argument.
224   */
225  long zdim = cube.getDimZ();
226  long spatSize = cube.getDimX()*cube.getDimY();
227  float *spec = new float[zdim];
228  for(int z=0;z<zdim;z++) spec[z] = cube.getPixValue(z*spatSize + pixel);
229  this->saveArray(spec,zdim);
230  delete [] spec;
231}
232//--------------------------------------------------------------------
233
234void Image::extractImage(float *Array, long *dim, long channel)
235{
236  /**
237   * Image::extractImage(float *, long *, int)
238   *  A function to extract a 2-D image from a 3-D array.
239   *  The array is assumed to be 3-D with the third dimension the spectral one.
240   *  The dimensions of the array are in the dim[] array.
241   *  The image extracted is the one lying in the channel referenced
242   *    by the third argument.
243   */
244  float *image = new float[dim[0]*dim[1]];
245  for(int npix=0; npix<dim[0]*dim[1]; npix++){
246    image[npix] = Array[channel*dim[0]*dim[1] + npix];
247  }
248  this->saveArray(image,dim[0]*dim[1]);
249  delete [] image;
250}
251//--------------------------------------------------------------------
252
253void Image::extractImage(Cube &cube, long channel)
254{
255  /**
256   * Image::extractImage(Cube &, int)
257   *  A function to extract a 2-D image from Cube class.
258   *  The image extracted is the one lying in the channel referenced
259   *    by the second argument.
260   */
261  long spatSize = cube.getDimX()*cube.getDimY();
262  float *image = new float[spatSize];
263  for(int npix=0; npix<spatSize; npix++)
264    image[npix] = cube.getPixValue(channel*spatSize + npix);
265  this->saveArray(image,spatSize);
266  delete [] image;
267}
268//--------------------------------------------------------------------
269
270void Image::removeMW()
271{
272  /**
273   * Image::removeMW()
274   *  A function to remove the Milky Way range of channels from a 1-D spectrum.
275   *  The array in this Image is assumed to be 1-D, with only the first axisDim
276   *    equal to 1.
277   *  The values of the MW channels are set to 0, unless they are BLANK.
278   */
279  if(this->par.getFlagMW() && (this->axisDim[1]==1) ){
280    for(int z=0;z<this->axisDim[0];z++){
281      if(!this->isBlank(z) && this->par.isInMW(z)) this->array[z]=0.;
282    }
283  }
284}
285
286/****************************************************************/
287/////////////////////////////////////////////////////////////
288//// Functions for Cube class
289/////////////////////////////////////////////////////////////
290
291Cube::Cube(long size){
292  // need error handling in case size<0 !!!
293  this->reconAllocated = false;
294  this->baselineAllocated = false;
295  if(size<0)
296    duchampError("Cube(size)","Negative size -- could not define Cube");
297  else{
298    if(size>0){
299      this->array = new float[size];
300      this->recon = new float[size];
301      this->reconAllocated = true;
302    }
303    this->numPixels = size;
304    this->axisDim = new long[2];
305    this->numDim = 3;
306    this->reconExists = false;
307  }
308}
309//--------------------------------------------------------------------
310
311Cube::Cube(long *dimensions){
312  int size   = dimensions[0] * dimensions[1] * dimensions[2];
313  int imsize = dimensions[0] * dimensions[1];
314  this->reconAllocated = false;
315  this->baselineAllocated = false;
316  if((size<0) || (imsize<0) )
317    duchampError("Cube(dimArray)","Negative size -- could not define Cube");
318  else{
319    this->numPixels = size;
320    if(size>0){
321      this->array      = new float[size];
322      this->detectMap  = new short[imsize];
323      if(this->par.getFlagATrous()||this->par.getFlagSmooth()){
324        this->recon    = new float[size];
325        this->reconAllocated = true;
326      }
327      if(this->par.getFlagBaseline()){
328        this->baseline = new float[size];
329        this->baselineAllocated = true;
330      }
331    }
332    this->numDim  = 3;
333    this->axisDim = new long[3];
334    for(int i=0;i<3     ;i++) this->axisDim[i]   = dimensions[i];
335    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
336    this->reconExists = false;
337  }
338}
339//--------------------------------------------------------------------
340
341Cube::~Cube()
342{
343//   delete [] array;
344//   delete [] axisDim;
345//   objectList.clear();
346
347  delete [] this->detectMap;
348  if(this->reconAllocated)    delete [] this->recon;
349  if(this->baselineAllocated) delete [] this->baseline;
350}
351//--------------------------------------------------------------------
352
353void Cube::initialiseCube(long *dimensions)
354{
355  /**
356   *  Cube::initialiseCube(long *dim)
357   *   A function that defines the sizes of all the necessary
358   *    arrays in the Cube class.
359   *   It also defines the values of the axis dimensions.
360   *   This is done with the WCS in the FitsHeader class, so the
361   *    WCS needs to be good and have three axes.
362   */
363
364  int lng,lat,spc,size,imsize;
365
366  if(this->head.isWCS() && (this->head.getWCS()->naxis>=3)){
367    // if there is a WCS and there is at least 3 axes
368    lng = this->head.getWCS()->lng;
369    lat = this->head.getWCS()->lat;
370    spc = this->head.getWCS()->spec;
371  }
372  else{
373    // just take dimensions[] at face value
374    lng = 0;
375    lat = 1;
376    spc = 2;
377  }
378  size   = dimensions[lng] * dimensions[lat] * dimensions[spc];
379  imsize = dimensions[lng] * dimensions[lat];
380
381  this->reconAllocated = false;
382  this->baselineAllocated = false;
383
384  if((size<0) || (imsize<0) )
385    duchampError("Cube::initialiseCube(dimArray)",
386                 "Negative size -- could not define Cube.\n");
387  else{
388    this->numPixels = size;
389    if(size>0){
390      this->array      = new float[size];
391      this->detectMap  = new short[imsize];
392      if(this->par.getFlagATrous() || this->par.getFlagSmooth()){
393        this->recon    = new float[size];
394        this->reconAllocated = true;
395      }
396      if(this->par.getFlagBaseline()){
397        this->baseline = new float[size];
398        this->baselineAllocated = true;
399      }
400    }
401    this->numDim  = 3;
402    this->axisDim = new long[3];
403    this->axisDim[0] = dimensions[lng];
404    this->axisDim[1] = dimensions[lat];
405    this->axisDim[2] = dimensions[spc];
406    for(int i=0;i<imsize;i++) this->detectMap[i] = 0;
407    this->reconExists = false;
408  }
409}
410//--------------------------------------------------------------------
411
412int Cube::getopts(int argc, char ** argv)
413{
414  /**
415   *   Cube::getopt
416   *    A front-end to read in the command-line options,
417   *     and then read in the correct parameters to the cube->par
418   */
419
420  int returnValue;
421  if(argc==1){
422    std::cout << ERR_USAGE_MSG;
423    returnValue = FAILURE;
424  }
425  else {
426    string file;
427    Param *par = new Param;
428    char c;
429    while( ( c = getopt(argc,argv,"p:f:hv") )!=-1){
430      switch(c) {
431      case 'p':
432        file = optarg;
433        if(this->readParam(file)==FAILURE){
434          stringstream errmsg;
435          errmsg << "Could not open parameter file " << file << ".\n";
436          duchampError("Duchamp",errmsg.str());
437          returnValue = FAILURE;
438        }
439        else returnValue = SUCCESS;
440        break;
441      case 'f':
442        file = optarg;
443        par->setImageFile(file);
444        this->saveParam(*par);
445        returnValue = SUCCESS;
446        break;
447      case 'v':
448        std::cout << PROGNAME << " version " << VERSION << std::endl;
449        returnValue = FAILURE;
450        break;
451      case 'h':
452      default :
453        std::cout << ERR_USAGE_MSG;
454        returnValue = FAILURE;
455        break;
456      }
457    }
458    delete par;
459  }
460  return returnValue;
461}
462//--------------------------------------------------------------------
463
464void Cube::readSavedArrays()
465{
466  // If the reconstructed array is to be read in from disk
467  if( this->par.getFlagReconExists() && this->par.getFlagATrous() ){
468    std::cout << "Reading reconstructed array: "<<std::endl;
469    if( this->readReconCube() == FAILURE){
470      std::stringstream errmsg;
471      errmsg <<"Could not read in existing reconstructed array.\n"
472             <<"Will perform reconstruction using assigned parameters.\n";
473      duchampWarning("Duchamp", errmsg.str());
474      this->par.setFlagReconExists(false);
475    }
476    else std::cout << "Reconstructed array available.\n";
477  }
478
479  if( this->par.getFlagSmoothExists() && this->par.getFlagSmooth() ){
480    std::cout << "Reading Hanning-smoothed array: "<<std::endl;
481    if( this->readSmoothCube() == FAILURE){
482      std::stringstream errmsg;
483      errmsg <<"Could not read in existing smoothed array.\n"
484             <<"Will smooth the cube using assigned parameters.\n";
485      duchampWarning("Duchamp", errmsg.str());
486      this->par.setFlagSmoothExists(false);
487    }
488    else std::cout << "Smoothed array available.\n";
489  }
490   
491}
492
493//--------------------------------------------------------------------
494
495void Cube::saveArray(float *input, long size){
496  if(size != this->numPixels){
497    stringstream errmsg;
498    errmsg << "Input array different size to existing array ("
499           << size << " cf. " << this->numPixels << "). Cannot save.\n";
500    duchampError("Cube::saveArray",errmsg.str());
501  }
502  else {
503    if(this->numPixels>0) delete [] array;
504    this->numPixels = size;
505    this->array = new float[size];
506    for(int i=0;i<size;i++) this->array[i] = input[i];
507  }
508}
509//--------------------------------------------------------------------
510
511void Cube::saveRecon(float *input, long size){
512  if(size != this->numPixels){
513    stringstream errmsg;
514    errmsg << "Input array different size to existing array ("
515           << size << " cf. " << this->numPixels << "). Cannot save.\n";
516    duchampError("Cube::saveRecon",errmsg.str());
517  }
518  else {
519    if(this->numPixels>0){
520      if(this->reconAllocated) delete [] this->recon;
521      this->numPixels = size;
522      this->recon = new float[size];
523      this->reconAllocated = true;
524      for(int i=0;i<size;i++) this->recon[i] = input[i];
525      this->reconExists = true;
526    }
527  }
528}
529//--------------------------------------------------------------------
530
531void Cube::getRecon(float *output){
532  // Need check for change in number of pixels!
533  for(int i=0;i<this->numPixels;i++){
534    if(this->reconExists) output[i] = this->recon[i];
535    else output[i] = 0.;
536  }
537}
538//--------------------------------------------------------------------
539
540void Cube::removeMW()
541{
542  if(this->par.getFlagMW()){
543    for(int pix=0;pix<this->axisDim[0]*this->axisDim[1];pix++){
544      for(int z=0;z<this->axisDim[2];z++){
545        int pos = z*this->axisDim[0]*this->axisDim[1] + pix;
546        if(!this->isBlank(pos) && this->par.isInMW(z)) this->array[pos]=0.;
547      }
548    }
549  }
550}
551//--------------------------------------------------------------------
552
553void Cube::calcObjectWCSparams()
554{
555  /**
556   * Cube::calcObjectWCSparams()
557   *  A function that calculates the WCS parameters for each object in the
558   *  cube's list of detections.
559   *  Each object gets an ID number set (just the order in the list), and if
560   *   the WCS is good, the WCS paramters are calculated.
561   */
562 
563  for(int i=0; i<this->objectList.size();i++){
564    this->objectList[i].setID(i+1);
565    this->objectList[i].calcWCSparams(this->head);
566    this->objectList[i].setPeakSNR( (this->objectList[i].getPeakFlux() - this->Stats.getMiddle()) / this->Stats.getSpread() );
567  } 
568
569 
570}
571//--------------------------------------------------------------------
572
573void Cube::sortDetections()
574{
575  /**
576   * Cube::sortDetections()
577   *  A front end to the sort-by functions.
578   *  If there is a good WCS, the detection list is sorted by velocity.
579   *  Otherwise, it is sorted by increasing z-pixel value.
580   *  The ID numbers are then re-calculated.
581   */
582 
583  if(this->head.isWCS()) SortByVel(this->objectList);
584  else SortByZ(this->objectList);
585  for(int i=0; i<this->objectList.size();i++) this->objectList[i].setID(i+1);
586
587}
588//--------------------------------------------------------------------
589
590void Cube::updateDetectMap()
591{
592  /**
593   * Cube::updateDetectMap()
594   *  A function that, for each detected object in the cube's list, increments
595   *   the cube's detection map by the required amount at each pixel.
596   */
597
598  for(int obj=0;obj<this->objectList.size();obj++){
599    for(int pix=0;pix<this->objectList[obj].getSize();pix++) {
600      int spatialPos = this->objectList[obj].getX(pix)+
601        this->objectList[obj].getY(pix)*this->axisDim[0];
602      this->detectMap[spatialPos]++;
603    }
604  }
605}
606//--------------------------------------------------------------------
607
608void Cube::updateDetectMap(Detection obj)
609{
610  /**
611   * Cube::updateDetectMap(Detection)
612   *  A function that, for the given object, increments the cube's
613   *  detection map by the required amount at each pixel.
614   */
615  for(int pix=0;pix<obj.getSize();pix++) {
616    int spatialPos = obj.getX(pix)+obj.getY(pix)*this->axisDim[0];
617    this->detectMap[spatialPos]++;
618  }
619}
620//--------------------------------------------------------------------
621
622void Cube::setCubeStatsOld()
623{
624  /**
625   *  Cube::setCubeStatsOld()
626   *   Calculates the full statistics for the cube:
627   *     mean, rms, median, madfm
628   *   Only do this if the threshold has not been defined (ie. is still 0.,
629   *    its default).
630   *   Also work out the threshold and store it in the par set.
631   *   
632   *   For stats calculations, ignore BLANKs and MW channels.
633   */
634
635  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
636    // if the user has defined a threshold, set this in the StatsContainer
637    this->Stats.setThreshold( this->par.getThreshold() );
638  }
639  else{
640    // only work out the mean etc if we need to.
641    // the only reason we don't is if the user has specified a threshold.
642   
643    std::cout << "Calculating the cube statistics... " << std::flush;
644   
645    // get number of good pixels;
646    int goodSize = 0;
647    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
648      for(int z=0;z<this->axisDim[2];z++){
649        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
650        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
651      }
652    }
653
654    float *tempArray = new float[goodSize];
655
656    goodSize=0;
657    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
658      for(int z=0;z<this->axisDim[2];z++){
659        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
660        if(!this->isBlank(vox) && !this->par.isInMW(z))
661          tempArray[goodSize++] = this->array[vox];
662      }
663    }
664    if(!this->reconExists){
665      // if there's no recon array, calculate everything from orig array
666      this->Stats.calculate(tempArray,goodSize);
667    }
668    else{
669      // just get mean & median from orig array, and rms & madfm from recon
670      StatsContainer<float> origStats,reconStats;
671      origStats.calculate(tempArray,goodSize);
672      goodSize=0;
673      for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
674        for(int z=0;z<this->axisDim[2];z++){
675          int vox = z * this->axisDim[0] * this->axisDim[1] + p;
676          if(!this->isBlank(vox) && !this->par.isInMW(z))
677            tempArray[goodSize++] = this->array[vox] - this->recon[vox];
678        }
679      }
680      reconStats.calculate(tempArray,goodSize);
681
682      // Get the "middle" estimators from the original array.
683      // Get the "spread" estimators from the residual (orig-recon) array
684      this->Stats.setMean(origStats.getMean());
685      this->Stats.setMedian(origStats.getMedian());
686      this->Stats.setStddev(reconStats.getStddev());
687      this->Stats.setMadfm(reconStats.getMadfm());
688    }
689
690    delete [] tempArray;
691
692    this->Stats.setUseFDR( this->par.getFlagFDR() );
693    // If the FDR method has been requested
694    if(this->par.getFlagFDR())  this->setupFDR();
695    else{
696      // otherwise, calculate one based on the requested SNR cut level, and
697      //   then set the threshold parameter in the Par set.
698      this->Stats.setThresholdSNR( this->par.getCut() );
699      this->par.setThreshold( this->Stats.getThreshold() );
700    }
701   
702   
703  }
704  std::cout << "Using ";
705  if(this->par.getFlagFDR()) std::cout << "effective ";
706  std::cout << "flux threshold of: ";
707  float thresh = this->Stats.getThreshold();
708  if(this->par.getFlagNegative()) thresh *= -1.;
709  std::cout << thresh << std::endl;
710
711}
712//--------------------------------------------------------------------
713
714void Cube::setCubeStats()
715{
716  /**
717   *  Cube::setCubeStats()
718   *   Calculates the full statistics for the cube:
719   *     mean, rms, median, madfm
720   *   Only do this if the threshold has not been defined (ie. is still 0.,
721   *    its default).
722   *   Also work out the threshold and store it in the par set.
723   *   
724   *   Different from setCubeStatsOld as it doesn't use the getStats functions
725   *    but has own versions of them hardcoded to ignore BLANKs and
726   *    MW channels.
727   */
728
729  if(!this->par.getFlagFDR() && this->par.getFlagUserThreshold() ){
730    // if the user has defined a threshold, set this in the StatsContainer
731    this->Stats.setThreshold( this->par.getThreshold() );
732  }
733  else{
734    // only work out the mean etc if we need to.
735    // the only reason we don't is if the user has specified a threshold.
736   
737    std::cout << "Calculating the cube statistics... " << std::flush;
738   
739    // get number of good pixels;
740    int goodSize = 0;
741    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
742      for(int z=0;z<this->axisDim[2];z++){
743        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
744        if(!this->isBlank(vox) && !this->par.isInMW(z)) goodSize++;
745      }
746    }
747
748    float *tempArray = new float[goodSize];
749
750    goodSize=0;
751    for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
752      for(int z=0;z<this->axisDim[2];z++){
753        int vox = z * this->axisDim[0] * this->axisDim[1] + p;
754        if(!this->isBlank(vox) && !this->par.isInMW(z))
755          tempArray[goodSize++] = this->array[vox];
756      }
757    }
758    float mean,median,stddev,madfm;
759    mean = tempArray[0];
760    for(int i=1;i<goodSize;i++) mean += tempArray[i];
761    mean /= float(goodSize);
762    this->Stats.setMean(mean);
763    sort(tempArray,0,goodSize);
764    if((goodSize%2)==0)
765      median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
766    else median = tempArray[goodSize/2];
767    this->Stats.setMedian(median);
768   
769    if(!this->reconExists){
770      // if there's no recon array, calculate everything from orig array
771      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
772      for(int i=1;i<goodSize;i++)
773        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
774      stddev = sqrt(stddev/float(goodSize-1));
775      this->Stats.setStddev(stddev);
776
777      for(int i=0;i<goodSize;i++) tempArray[i] = absval(tempArray[i]-median);
778      sort(tempArray,0,goodSize);
779      if((goodSize%2)==0)
780        madfm = (tempArray[goodSize/2-1]+tempArray[goodSize/2])/2;
781      else madfm = tempArray[goodSize/2];
782      this->Stats.setMadfm(madfm);
783    }
784    else{
785      // just get mean & median from orig array, and rms & madfm from residual
786      // recompute array values to be residuals & then find stddev & madfm
787      goodSize = 0;
788      for(int p=0;p<this->axisDim[0]*this->axisDim[1];p++){
789        for(int z=0;z<this->axisDim[2];z++){
790          int vox = z * this->axisDim[0] * this->axisDim[1] + p;
791          if(!this->isBlank(vox) && !this->par.isInMW(z))
792            tempArray[goodSize++] = this->array[vox] - this->recon[vox];
793        }
794      }
795      mean = tempArray[0];
796      for(int i=1;i<goodSize;i++) mean += tempArray[i];
797      mean /= float(goodSize);
798      stddev = (tempArray[0]-mean) * (tempArray[0]-mean);
799      for(int i=1;i<goodSize;i++)
800        stddev += (tempArray[i]-mean)*(tempArray[i]-mean);
801      stddev = sqrt(stddev/float(goodSize-1));
802      this->Stats.setStddev(stddev);
803
804      sort(tempArray,0,goodSize);
805      if((goodSize%2)==0)
806        median = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
807      else median = tempArray[goodSize/2];
808      for(int i=0;i<goodSize;i++) tempArray[i] = absval(tempArray[i]-median);
809      sort(tempArray,0,goodSize);
810      if((goodSize%2)==0)
811        madfm = (tempArray[goodSize/2-1] + tempArray[goodSize/2])/2;
812      else madfm = tempArray[goodSize/2];
813      this->Stats.setMadfm(madfm);
814    }
815
816    delete [] tempArray;
817
818    this->Stats.setUseFDR( this->par.getFlagFDR() );
819    // If the FDR method has been requested
820    if(this->par.getFlagFDR())  this->setupFDR();
821    else{
822      // otherwise, calculate one based on the requested SNR cut level, and
823      //   then set the threshold parameter in the Par set.
824      this->Stats.setThresholdSNR( this->par.getCut() );
825      this->par.setThreshold( this->Stats.getThreshold() );
826    }
827   
828   
829  }
830  std::cout << "Using ";
831  if(this->par.getFlagFDR()) std::cout << "effective ";
832  std::cout << "flux threshold of: ";
833  float thresh = this->Stats.getThreshold();
834  if(this->par.getFlagNegative()) thresh *= -1.;
835  std::cout << thresh << std::endl;
836
837}
838//--------------------------------------------------------------------
839
840int Cube::setupFDR()
841{
842  /**
843   *  Cube::setupFDR()
844   *   Determines the critical Prob value for the False Discovery Rate
845   *    detection routine. All pixels with Prob less than this value will
846   *    be considered detections.
847   *   The Prob here is the probability, assuming a Normal distribution, of
848   *    obtaining a value as high or higher than the pixel value (ie. only the
849   *    positive tail of the PDF)
850   */
851
852  // first calculate p-value for each pixel -- assume Gaussian for now.
853
854  float *orderedP = new float[this->numPixels];
855  int count = 0;
856  float zStat;
857  for(int pix=0; pix<this->numPixels; pix++){
858
859    if( !(this->par.isBlank(this->array[pix])) ){
860      // only look at non-blank pixels
861      zStat = (this->array[pix] - this->Stats.getMiddle()) /
862        this->Stats.getSpread();
863     
864      orderedP[count++] = 0.5 * erfc(zStat/M_SQRT2);
865      // Need the factor of 0.5 here, as we are only considering the positive
866      //  tail of the distribution. Don't care about negative detections.
867    }
868  }
869
870  // now order them
871  sort(orderedP,0,count);
872 
873  // now find the maximum P value.
874  int max = 0;
875  float cN = 0.;
876  int psfCtr;
877  int numVox = int(this->par.getBeamSize()) * 2;
878  // why beamSize*2? we are doing this in 3D, so spectrally assume just the
879  //  neighbouring channels are correlated, but spatially all those within
880  //  the beam, so total number of voxels is 2*beamSize
881  for(psfCtr=1;psfCtr<=numVox;(psfCtr)++)
882    cN += 1./float(psfCtr);
883
884  for(int loopCtr=0;loopCtr<count;loopCtr++) {
885    if( orderedP[loopCtr] <
886        (double(loopCtr+1)*this->par.getAlpha()/(cN * double(count))) ) {
887      max = loopCtr;
888    }
889  }
890
891  this->Stats.setPThreshold( orderedP[max] );
892
893  delete [] orderedP;
894
895  // Find real value of the P threshold by finding the inverse of the
896  //  error function -- root finding with brute force technique
897  //  (relatively slow, but we only do it once).
898  zStat = 0;
899  float deltaZ = 0.1;
900  float tolerance = 1.e-6;
901  float zeroP = 0.5 * erfc(zStat/M_SQRT2) - this->Stats.getPThreshold();
902  do{
903    zStat+=deltaZ;
904    if((zeroP * (erfc(zStat/M_SQRT2)-this->Stats.getPThreshold()))<0.){
905      zStat-=deltaZ;
906      deltaZ/=2.;
907    }
908  }while(deltaZ>tolerance);
909  this->Stats.setThreshold( zStat*this->Stats.getSpread() +
910                            this->Stats.getMiddle() );
911
912}
913//--------------------------------------------------------------------
914
915float Cube::enclosedFlux(Detection obj)
916{
917  /**
918   *  float Cube::enclosedFlux(Detection obj)
919   *   A function to calculate the flux enclosed by the range
920   *    of pixels detected in the object obj (not necessarily all
921   *    pixels will have been detected).
922   */
923  obj.calcParams();
924  int xsize = obj.getXmax()-obj.getXmin()+1;
925  int ysize = obj.getYmax()-obj.getYmin()+1;
926  int zsize = obj.getZmax()-obj.getZmin()+1;
927  vector <float> fluxArray(xsize*ysize*zsize,0.);
928  for(int x=0;x<xsize;x++){
929    for(int y=0;y<ysize;y++){
930      for(int z=0;z<zsize;z++){
931        fluxArray[x+y*xsize+z*ysize*xsize] =
932          this->getPixValue(x+obj.getXmin(),
933                            y+obj.getYmin(),
934                            z+obj.getZmin());
935        if(this->par.getFlagNegative())
936          fluxArray[x+y*xsize+z*ysize*xsize] *= -1.;
937      }
938    }
939  }
940  float sum = 0.;
941  for(int i=0;i<fluxArray.size();i++)
942    if(!this->par.isBlank(fluxArray[i])) sum+=fluxArray[i];
943  return sum;
944}
945//--------------------------------------------------------------------
946
947void Cube::setupColumns()
948{
949  /**
950   *  Cube::setupColumns()
951   *   A front-end to the two setup routines in columns.cc.
952   */
953  this->calcObjectWCSparams(); 
954  // need this as the colSet functions use vel, RA, Dec, etc...
955 
956  this->fullCols.clear();
957  this->fullCols = getFullColSet(this->objectList, this->head);
958
959  this->logCols.clear();
960  this->logCols = getLogColSet(this->objectList, this->head);
961
962  int vel,fpeak,fint,pos,xyz,temp,snr;
963  vel = fullCols[VEL].getPrecision();
964  fpeak = fullCols[FPEAK].getPrecision();
965  snr = fullCols[SNRPEAK].getPrecision();
966  xyz = fullCols[X].getPrecision();
967  if(temp=fullCols[Y].getPrecision() > xyz) xyz = temp;
968  if(temp=fullCols[Z].getPrecision() > xyz) xyz = temp;
969  if(this->head.isWCS()) fint = fullCols[FINT].getPrecision();
970  else fint = fullCols[FTOT].getPrecision();
971  pos = fullCols[WRA].getPrecision();
972  if(temp=fullCols[WDEC].getPrecision() > pos) pos = temp;
973 
974  for(int obj=0;obj<this->objectList.size();obj++){
975    this->objectList[obj].setVelPrec(vel);
976    this->objectList[obj].setFpeakPrec(fpeak);
977    this->objectList[obj].setXYZPrec(xyz);
978    this->objectList[obj].setPosPrec(pos);
979    this->objectList[obj].setFintPrec(fint);
980    this->objectList[obj].setSNRPrec(snr);
981  }
982
983}
984//--------------------------------------------------------------------
985
986bool Cube::objAtSpatialEdge(Detection obj)
987{
988  /**
989   *  bool Cube::objAtSpatialEdge()
990   *   A function to test whether the object obj
991   *    lies at the edge of the cube's spatial field --
992   *    either at the boundary, or next to BLANKs
993   */
994
995  bool atEdge = false;
996
997  int pix = 0;
998  while(!atEdge && pix<obj.getSize()){
999    // loop over each pixel in the object, until we find an edge pixel.
1000    Voxel vox = obj.getPixel(pix);
1001    for(int dx=-1;dx<=1;dx+=2){
1002      if(((vox.getX()+dx)<0) || ((vox.getX()+dx)>=this->axisDim[0]))
1003        atEdge = true;
1004      else if(this->isBlank(vox.getX()+dx,vox.getY(),vox.getZ()))
1005        atEdge = true;
1006    }
1007    for(int dy=-1;dy<=1;dy+=2){
1008      if(((vox.getY()+dy)<0) || ((vox.getY()+dy)>=this->axisDim[1]))
1009        atEdge = true;
1010      else if(this->isBlank(vox.getX(),vox.getY()+dy,vox.getZ()))
1011        atEdge = true;
1012    }
1013    pix++;
1014  }
1015
1016  return atEdge;
1017}
1018//--------------------------------------------------------------------
1019
1020bool Cube::objAtSpectralEdge(Detection obj)
1021{
1022  /**
1023   *  bool Cube::objAtSpectralEdge()
1024   *   A function to test whether the object obj
1025   *    lies at the edge of the cube's spectral extent --
1026   *    either at the boundary, or next to BLANKs
1027   */
1028
1029  bool atEdge = false;
1030
1031  int pix = 0;
1032  while(!atEdge && pix<obj.getSize()){
1033    // loop over each pixel in the object, until we find an edge pixel.
1034    Voxel vox = obj.getPixel(pix);
1035    for(int dz=-1;dz<=1;dz+=2){
1036      if(((vox.getZ()+dz)<0) || ((vox.getZ()+dz)>=this->axisDim[2]))
1037        atEdge = true;
1038      else if(this->isBlank(vox.getX(),vox.getY(),vox.getZ()+dz))
1039        atEdge = true;
1040    }
1041    pix++;
1042  }
1043
1044  return atEdge;
1045}
1046//--------------------------------------------------------------------
1047
1048void Cube::setObjectFlags()
1049{
1050  /**
1051   *  void Cube::setObjectFlags()
1052   *   A function to set any warning flags for all the detected objects
1053   *    associated with the cube.
1054   *   Flags to be looked for:
1055   *       * Negative enclosed flux (N)
1056   *       * Object at edge of field (E)
1057   */
1058
1059  for(int i=0;i<this->objectList.size();i++){
1060
1061    if( this->enclosedFlux(this->objectList[i]) < 0. ) 
1062      this->objectList[i].addToFlagText("N");
1063
1064    if( this->objAtSpatialEdge(this->objectList[i]) )
1065      this->objectList[i].addToFlagText("E");
1066
1067    if( this->objAtSpectralEdge(this->objectList[i]) )
1068      this->objectList[i].addToFlagText("S");
1069
1070  }
1071
1072}
1073//--------------------------------------------------------------------
1074
1075void Cube::plotBlankEdges()
1076{
1077  if(this->par.drawBlankEdge()){
1078    int colour;
1079    cpgqci(&colour);
1080    cpgsci(MAGENTA);
1081    drawBlankEdges(this->array,this->axisDim[0],this->axisDim[1],this->par);
1082    cpgsci(colour);
1083  }
1084}
Note: See TracBrowser for help on using the repository browser.