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

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