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

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