source: trunk/src/Detection/detection.cc @ 447

Last change on this file since 447 was 429, checked in by MatthewWhiting, 16 years ago

Updated verification results to account for new beam sizes etc.

File size: 19.5 KB
Line 
1// -----------------------------------------------------------------------
2// detection.cc : Member functions for the Detection class.
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 <iostream>
29#include <iomanip>
30#include <vector>
31#include <string>
32#include <wcslib/wcs.h>
33#include <math.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38#include <duchamp/PixelMap/Voxel.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Detection/detection.hh>
41
42using std::setw;
43using std::setprecision;
44using std::endl;
45using std::vector;
46
47using namespace PixelInfo;
48
49namespace duchamp
50{
51
52
53  Detection::Detection()
54  {
55    this->flagWCS=false;
56    this->negSource = false;
57    this->flagText="";
58    this->totalFlux = peakFlux = 0.;
59    this->centreType="centroid";
60  }
61
62  Detection::Detection(const Detection& d)
63  {
64    operator=(d);
65  }
66
67  Detection& Detection::operator= (const Detection& d)
68  {
69    if(this == &d) return *this;
70    this->pixelArray   = d.pixelArray;
71    this->xSubOffset   = d.xSubOffset;
72    this->ySubOffset   = d.ySubOffset;
73    this->zSubOffset   = d.zSubOffset;
74    this->totalFlux    = d.totalFlux;
75    this->intFlux            = d.intFlux;
76    this->peakFlux     = d.peakFlux;
77    this->xpeak        = d.xpeak;
78    this->ypeak        = d.ypeak;
79    this->zpeak        = d.zpeak;
80    this->peakSNR      = d.peakSNR;
81    this->xCentroid    = d.xCentroid;
82    this->yCentroid    = d.yCentroid;
83    this->zCentroid    = d.zCentroid;
84    this->centreType   = d.centreType;
85    this->negSource    = d.negSource;
86    this->flagText     = d.flagText;
87    this->id           = d.id;
88    this->name         = d.name;
89    this->flagWCS      = d.flagWCS;
90    this->specOK       = d.specOK;
91    this->raS          = d.raS;
92    this->decS         = d.decS;
93    this->ra           = d.ra;
94    this->dec        = d.dec;
95    this->raWidth            = d.raWidth;
96    this->decWidth     = d.decWidth;
97    this->specUnits    = d.specUnits;
98    this->fluxUnits    = d.fluxUnits;
99    this->intFluxUnits = d.intFluxUnits;
100    this->lngtype            = d.lngtype;
101    this->lattype            = d.lattype;
102    this->vel          = d.vel;
103    this->velWidth     = d.velWidth;
104    this->velMin       = d.velMin;
105    this->velMax       = d.velMax;
106    this->posPrec      = d.posPrec;
107    this->xyzPrec      = d.xyzPrec;
108    this->fintPrec     = d.fintPrec;
109    this->fpeakPrec    = d.fpeakPrec;
110    this->velPrec            = d.velPrec;
111    this->snrPrec      = d.snrPrec;
112    return *this;
113  }
114
115  //--------------------------------------------------------------------
116
117  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
118  {
119    /**
120     *  A test to see whether there is a 1-1 correspondence between
121     *  the given list of Voxels and the voxel positions contained in
122     *  this Detection's pixel list. No testing of the fluxes of the
123     *  Voxels is done.
124     *
125     * \param voxelList The std::vector list of Voxels to be tested.
126     */
127
128    bool listsMatch = true;
129    // compare sizes
130    listsMatch = listsMatch && (voxelList.size() == this->getSize());
131    if(!listsMatch) return listsMatch;
132
133    // make sure all voxels are in Detection
134    for(int i=0;i<voxelList.size();i++)
135      listsMatch = listsMatch && this->pixelArray.isInObject(voxelList[i]);
136    // make sure all Detection pixels are in voxel list
137    int v1=0, mysize=this->getSize();
138    while(listsMatch && v1<mysize){
139      bool inList = false;
140      int v2=0;
141      Voxel test = this->getPixel(v1);
142      while(!inList && v2<voxelList.size()){
143        inList = inList || test.match(voxelList[v2]);
144        v2++;
145      }
146      listsMatch = listsMatch && inList;
147      v1++;
148    }
149
150    return listsMatch;
151
152  }
153
154  //--------------------------------------------------------------------
155
156  void Detection::calcFluxes(std::vector<Voxel> voxelList)
157  {
158    /**
159     *  A function that calculates total & peak fluxes (and the location
160     *  of the peak flux) for a Detection.
161     *
162     *  \param fluxArray The array of flux values to calculate the
163     *  flux parameters from.
164     *  \param dim The dimensions of the flux array.
165     */
166
167    this->totalFlux = this->peakFlux = 0;
168    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
169
170    // first check that the voxel list and the Detection's pixel list
171    // have a 1-1 correspondence
172
173    if(!this->voxelListsMatch(voxelList)){
174      duchampError("Detection::calcFluxes","Voxel list provided does not match");
175      return;
176    }
177
178    for(int i=0;i<voxelList.size();i++) {
179      long x = voxelList[i].getX();
180      long y = voxelList[i].getY();
181      long z = voxelList[i].getZ();
182      float f = voxelList[i].getF();
183      this->totalFlux += f;
184      this->xCentroid += x*f;
185      this->yCentroid += y*f;
186      this->zCentroid += z*f;
187      if( (i==0) ||  //first time round
188          (this->negSource&&(f<this->peakFlux)) ||
189          (!this->negSource&&(f>this->peakFlux))   )
190        {
191          this->peakFlux = f;
192          this->xpeak =    x;
193          this->ypeak =    y;
194          this->zpeak =    z;
195        }
196    }
197
198    this->xCentroid /= this->totalFlux;
199    this->yCentroid /= this->totalFlux;
200    this->zCentroid /= this->totalFlux;
201  }
202  //--------------------------------------------------------------------
203
204  void Detection::calcFluxes(float *fluxArray, long *dim)
205  {
206    /**
207     *  A function that calculates total & peak fluxes (and the location
208     *  of the peak flux) for a Detection.
209     *
210     *  \param fluxArray The array of flux values to calculate the
211     *  flux parameters from.
212     *  \param dim The dimensions of the flux array.
213     */
214
215    this->totalFlux = this->peakFlux = 0;
216    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
217    long y,z,count=0;
218
219    for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
220      ChanMap *tempmap = new ChanMap;
221      *tempmap = this->pixelArray.getChanMap(m);
222      z = tempmap->getZ();
223      for(int s=0; s<tempmap->getNumScan(); s++){
224        Scan *tempscan = new Scan;
225        *tempscan = tempmap->getScan(s);
226        y = tempscan->getY();
227        for(long x=tempscan->getX(); x<=tempscan->getXmax(); x++){
228
229          float f = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
230          this->totalFlux += f;
231          this->xCentroid += x*f;
232          this->yCentroid += y*f;
233          this->zCentroid += z*f;
234          if( (count==0) ||  //first time round
235              (this->negSource&&(f<this->peakFlux)) ||
236              (!this->negSource&&(f>this->peakFlux))   )
237            {
238              this->peakFlux = f;
239              this->xpeak =    x;
240              this->ypeak =    y;
241              this->zpeak =    z;
242            }
243          count++;
244        }
245        delete tempscan;
246      }
247      delete tempmap;
248    }
249
250    this->xCentroid /= this->totalFlux;
251    this->yCentroid /= this->totalFlux;
252    this->zCentroid /= this->totalFlux;
253  }
254  //--------------------------------------------------------------------
255
256  //  void Detection::calcWCSparams(float *fluxArray, long *dim, FitsHeader &head)
257  void Detection::calcWCSparams(FitsHeader &head)
258  {
259    /**
260     *  Use the input wcs to calculate the position and velocity
261     *    information for the Detection.
262     *  Quantities calculated:
263     *  <ul><li> RA: ra [deg], ra (string), ra width.
264     *      <li> Dec: dec [deg], dec (string), dec width.
265     *      <li> Vel: vel [km/s], min & max vel, vel width.
266     *      <li> coord type for all three axes, nuRest,
267     *      <li> name (IAU-style, in equatorial or Galactic)
268     *  </ul>
269     *
270     *  Note that the regular parameters are NOT recalculated!
271     *
272     *  \param head FitsHeader object that contains the WCS information.
273     */
274
275    if(head.isWCS()){
276
277      double *pixcrd = new double[15];
278      double *world  = new double[15];
279      /*
280        define a five-point array in 3D:
281        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
282        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
283        and convert to world coordinates.   
284      */
285      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
286      pixcrd[9]  = this->getXmin()-0.5;
287      pixcrd[12] = this->getXmax()+0.5;
288      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
289      pixcrd[10] = this->getYmin()-0.5;
290      pixcrd[13] = this->getYmax()+0.5;
291      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
292      pixcrd[5] = this->getZmin();
293      pixcrd[8] = this->getZmax();
294      int flag = head.pixToWCS(pixcrd, world, 5);
295      delete [] pixcrd;
296      if(flag!=0) duchampError("calcWCSparams",
297                               "Error in calculating the WCS for this object.\n");
298      else{
299
300        // world now has the WCS coords for the five points
301        //    -- use this to work out WCS params
302 
303        this->specOK = head.canUseThirdAxis();
304        this->lngtype = head.WCS().lngtyp;
305        this->lattype = head.WCS().lattyp;
306        this->specUnits = head.getSpectralUnits();
307        this->fluxUnits = head.getFluxUnits();
308        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
309        this->intFluxUnits = head.getIntFluxUnits();
310        this->ra   = world[0];
311        this->dec  = world[1];
312        this->raS  = decToDMS(this->ra, this->lngtype);
313        this->decS = decToDMS(this->dec,this->lattype);
314        this->raWidth = angularSeparation(world[9],world[1],
315                                          world[12],world[1]) * 60.;
316        this->decWidth  = angularSeparation(world[0],world[10],
317                                            world[0],world[13]) * 60.;
318        this->name = head.getIAUName(this->ra, this->dec);
319        this->vel    = head.specToVel(world[2]);
320        this->velMin = head.specToVel(world[5]);
321        this->velMax = head.specToVel(world[8]);
322        this->velWidth = fabs(this->velMax - this->velMin);
323
324        //      this->calcIntegFlux(fluxArray,dim,head);
325   
326        this->flagWCS = true;
327      }
328      delete [] world;
329
330    }
331  }
332  //--------------------------------------------------------------------
333
334  void Detection::calcIntegFlux(std::vector<Voxel> voxelList, FitsHeader &head)
335  {
336    /**
337     *  Uses the input WCS to calculate the velocity-integrated flux,
338     *   putting velocity in units of km/s.
339     *  The fluxes used are taken from the Voxels, rather than an
340     *   array of flux values.
341     *  Integrates over full spatial and velocity range as given
342     *   by the extrema calculated by calcWCSparams.
343     *
344     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
345     *  corrected by the beam size (in pixels). This is done by
346     *  multiplying the integrated flux by the number of spatial pixels,
347     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
348     *  pix/beam --> Jy)
349     *
350     *  \param voxelList The list of Voxels with flux information
351     *  \param head FitsHeader object that contains the WCS information.
352     */
353
354    if(!voxelListsMatch(voxelList)){
355      duchampError("Detection::calcIntegFlux","Voxel list provided does not match");
356      return;
357    }
358
359    if(head.getNumAxes() > 2) {
360
361      // include one pixel either side in each direction
362      long xsize = (this->getXmax()-this->getXmin()+3);
363      long ysize = (this->getYmax()-this->getYmin()+3);
364      long zsize = (this->getZmax()-this->getZmin()+3);
365      vector <bool> isObj(xsize*ysize*zsize,false);
366      double *localFlux = new double[xsize*ysize*zsize];
367      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
368
369      for(int i=0;i<voxelList.size();i++){
370        long x = voxelList[i].getX();
371        long y = voxelList[i].getY();
372        long z = voxelList[i].getZ();
373        long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
374          + (z-this->getZmin()+1)*xsize*ysize;
375        localFlux[pos] = voxelList[i].getF();
376        isObj[pos] = true;
377      }
378 
379      // work out the WCS coords for each pixel
380      double *world  = new double[xsize*ysize*zsize];
381      double xpt,ypt,zpt;
382      for(int i=0;i<xsize*ysize*zsize;i++){
383        xpt = double( this->getXmin() -1 + i%xsize );
384        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
385        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
386        world[i] = head.pixToVel(xpt,ypt,zpt);
387      }
388
389      double integrated = 0.;
390      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
391        for(int z=0; z<zsize; z++){
392          int pos =  z*xsize*ysize + pix;
393          if(isObj[pos]){ // if it's an object pixel...
394            double deltaVel;
395            if(z==0)
396              deltaVel = (world[pos+xsize*ysize] - world[pos]);
397            else if(z==(zsize-1))
398              deltaVel = (world[pos] - world[pos-xsize*ysize]);
399            else
400              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
401            integrated += localFlux[pos] * fabs(deltaVel);
402          }
403        }
404      }
405      this->intFlux = integrated;
406
407      delete [] world;
408      delete [] localFlux;
409
410    }
411    else // in this case there is just a 2D image.
412      this->intFlux = this->totalFlux;
413
414    if(head.isWCS()){
415      // correct for the beam size if the flux units string ends in "/beam"
416      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
417    }
418
419  }
420  //--------------------------------------------------------------------
421
422  void Detection::calcIntegFlux(float *fluxArray, long *dim, FitsHeader &head)
423  {
424    /**
425     *  Uses the input WCS to calculate the velocity-integrated flux,
426     *   putting velocity in units of km/s.
427     *  Integrates over full spatial and velocity range as given
428     *   by the extrema calculated by calcWCSparams.
429     *
430     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
431     *  corrected by the beam size (in pixels). This is done by
432     *  multiplying the integrated flux by the number of spatial pixels,
433     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
434     *  pix/beam --> Jy)
435     *
436     *  \param fluxArray The array of flux values.
437     *  \param dim The dimensions of the flux array.
438     *  \param head FitsHeader object that contains the WCS information.
439     */
440
441    if(head.getNumAxes() > 2) {
442
443      // include one pixel either side in each direction
444      long xsize = (this->getXmax()-this->getXmin()+3);
445      long ysize = (this->getYmax()-this->getYmin()+3);
446      long zsize = (this->getZmax()-this->getZmin()+3);
447      vector <bool> isObj(xsize*ysize*zsize,false);
448      double *localFlux = new double[xsize*ysize*zsize];
449      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
450      // work out which pixels are object pixels
451      for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
452        ChanMap tempmap = this->pixelArray.getChanMap(m);
453        long z = this->pixelArray.getChanMap(m).getZ();
454        for(int s=0; s<this->pixelArray.getChanMap(m).getNumScan(); s++){
455          long y = this->pixelArray.getChanMap(m).getScan(s).getY();
456          for(long x=this->pixelArray.getChanMap(m).getScan(s).getX();
457              x<=this->pixelArray.getChanMap(m).getScan(s).getXmax();
458              x++){
459            long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
460              + (z-this->getZmin()+1)*xsize*ysize;
461            localFlux[pos] = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
462            isObj[pos] = true;
463          }
464        }
465      }
466 
467      // work out the WCS coords for each pixel
468      double *world  = new double[xsize*ysize*zsize];
469      double xpt,ypt,zpt;
470      for(int i=0;i<xsize*ysize*zsize;i++){
471        xpt = double( this->getXmin() -1 + i%xsize );
472        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
473        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
474        world[i] = head.pixToVel(xpt,ypt,zpt);
475      }
476
477      double integrated = 0.;
478      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
479        for(int z=0; z<zsize; z++){
480          int pos =  z*xsize*ysize + pix;
481          if(isObj[pos]){ // if it's an object pixel...
482            double deltaVel;
483            if(z==0)
484              deltaVel = (world[pos+xsize*ysize] - world[pos]);
485            else if(z==(zsize-1))
486              deltaVel = (world[pos] - world[pos-xsize*ysize]);
487            else
488              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
489            integrated += localFlux[pos] * fabs(deltaVel);
490          }
491        }
492      }
493      this->intFlux = integrated;
494
495      delete [] world;
496      delete [] localFlux;
497
498    }
499    else // in this case there is just a 2D image.
500      this->intFlux = this->totalFlux;
501
502    if(head.isWCS()){
503      // correct for the beam size if the flux units string ends in "/beam"
504      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
505    }
506
507  }
508  //--------------------------------------------------------------------
509
510  Detection operator+ (Detection lhs, Detection rhs)
511  {
512    /**
513     *  Combines two objects by adding all the pixels using the Object3D
514     *  operator.
515     *
516     *  The pixel parameters are recalculated in the process (equivalent
517     *  to calling pixels().calcParams()), but WCS parameters are not.
518     */
519    Detection output = lhs;
520    output.pixelArray = lhs.pixelArray + rhs.pixelArray;
521    return output;
522  }
523  //--------------------------------------------------------------------
524
525  void Detection::setOffsets(Param &par)
526  {
527    /**
528     * This function stores the values of the offsets for each cube axis.
529     * The offsets are the starting values of the cube axes that may differ from
530     *  the default value of 0 (for instance, if a subsection is being used).
531     * The values will be used when the detection is outputted.
532     */
533    this->xSubOffset = par.getXOffset();
534    this->ySubOffset = par.getYOffset();
535    this->zSubOffset = par.getZOffset();
536  }
537  //--------------------------------------------------------------------
538
539  bool Detection::hasEnoughChannels(int minNumber)
540  {
541    /**
542     * A function to determine if the Detection has enough
543     * contiguous channels to meet the minimum requirement
544     * given as the argument.
545     * \param minNumber How many channels is the minimum acceptable number?
546     * \return True if there is at least one occurence of minNumber consecutive
547     * channels present to return true. False otherwise.
548     */
549
550    // Preferred method -- need a set of minNumber consecutive channels present.
551    this->pixelArray.order();
552    int numChannels = 0;
553    bool result = false;
554    int size = this->pixelArray.getNumChanMap();
555    if(size>0) numChannels++;
556    if( numChannels >= minNumber) result = true;
557    for(int i=1;(i<size && !result);i++) {
558      if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) == 1)
559        numChannels++;
560      else if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) >= 2)
561        numChannels = 1;
562
563      if( numChannels >= minNumber) result = true;
564    }
565    return result;
566 
567  }
568  //--------------------------------------------------------------------
569
570  std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
571  {
572    /**
573     *  A convenient way of printing the coordinate values for each
574     *  pixel in the Detection. 
575     *
576     *  NOTE THAT THERE IS CURRENTLY NO FLUX INFORMATION BEING PRINTED!
577     *
578     *  Use as front end to the Object3D::operator<< function.
579     */ 
580
581    theStream << obj.pixelArray << "---\n";
582    return theStream;
583  }
584  //--------------------------------------------------------------------
585
586}
Note: See TracBrowser for help on using the repository browser.