source: tags/release-1.6.1/src/Detection/detection.cc @ 1441

Last change on this file since 1441 was 1393, checked in by MatthewWhiting, 10 years ago

A large changeset that moves all use of bool arrays (ie. bool *) to std::vector<bool>. This will be a bit safer, plus potentially cheaper in memory usage. Interfaces to all stats functions with masks have changed accordingly, as well as the Gaussian smoothing functions and some spectral utility functions.

File size: 39.0 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 <map>
32#include <string>
33#include <wcslib/wcs.h>
34#include <math.h>
35#include <duchamp/duchamp.hh>
36#include <duchamp/param.hh>
37#include <duchamp/fitsHeader.hh>
38#include <duchamp/Utils/utils.hh>
39#include <duchamp/PixelMap/Voxel.hh>
40#include <duchamp/PixelMap/Object3D.hh>
41#include <duchamp/Detection/detection.hh>
42#include <duchamp/Cubes/cubeUtils.hh>
43#include <duchamp/Outputs/columns.hh>
44
45using namespace PixelInfo;
46
47namespace duchamp
48{
49
50  void Detection::defaultDetection()
51  {
52    this->xSubOffset = 0;
53    this->ySubOffset = 0;
54    this->zSubOffset = 0;
55    this->haveParams = false;
56    this->totalFlux = 0.;
57    this->eTotalFlux = 0.;
58    this->peakFlux = 0.;
59    this->intFlux = 0.;
60    this->eIntFlux = 0.;
61    this->xpeak = 0;
62    this->ypeak = 0;
63    this->zpeak = 0;
64    this->peakSNR = 0.;
65    this->xCentroid = 0.;
66    this->yCentroid = 0.;
67    this->zCentroid = 0.;
68    this->centreType="centroid";
69    this->negSource = false;
70    this->flagText="";
71    this->id = -1;
72    this->name = "";
73    this->flagWCS=false;
74    this->specOK = true;
75    this->raS = "";
76    this->decS = "";
77    this->ra = 0.;
78    this->dec = 0.;
79    this->raWidth = 0.;
80    this->decWidth = 0.;
81    this->majorAxis = 0.;
82    this->minorAxis = 0.;
83    this->posang = 0.;
84    this->specUnits = "";
85    this->specType = "";
86    this->fluxUnits = "";
87    this->intFluxUnits = "";
88    this->lngtype = "RA";
89    this->lattype = "DEC";
90    this->vel = 0.;
91    this->velWidth = 0.;
92    this->velMin = 0.;
93    this->velMax = 0.;
94    this->w20 = 0.;
95    this->v20min = 0.;
96    this->v20max = 0.;
97    this->w50 = 0.;
98    this->v50min = 0.;
99    this->v50max = 0.;
100    this->posPrec = Catalogues::prPOS;
101    this->xyzPrec = Catalogues::prXYZ;
102    this->fintPrec = Catalogues::prFLUX;
103    this->fpeakPrec = Catalogues::prFLUX;
104    this->velPrec = Catalogues::prVEL;
105    this->snrPrec = Catalogues::prSNR;
106  }
107
108  Detection::Detection():
109    Object3D()
110  {
111    this->defaultDetection();
112  }
113
114  Detection::Detection(const Object3D& o):
115    Object3D(o)
116  {
117    this->defaultDetection();
118  }
119
120  Detection::Detection(const Detection& d):
121    Object3D(d)
122  {
123    operator=(d);
124  }
125
126  Detection& Detection::operator= (const Detection& d)
127  {
128    ((Object3D &) *this) = d;
129    this->xSubOffset   = d.xSubOffset;
130    this->ySubOffset   = d.ySubOffset;
131    this->zSubOffset   = d.zSubOffset;
132    this->haveParams   = d.haveParams;
133    this->totalFlux    = d.totalFlux;
134    this->eTotalFlux   = d.eTotalFlux;
135    this->intFlux      = d.intFlux;
136    this->eIntFlux     = d.eIntFlux;
137    this->peakFlux     = d.peakFlux;
138    this->xpeak        = d.xpeak;
139    this->ypeak        = d.ypeak;
140    this->zpeak        = d.zpeak;
141    this->peakSNR      = d.peakSNR;
142    this->xCentroid    = d.xCentroid;
143    this->yCentroid    = d.yCentroid;
144    this->zCentroid    = d.zCentroid;
145    this->centreType   = d.centreType;
146    this->negSource    = d.negSource;
147    this->flagText     = d.flagText;
148    this->id           = d.id;
149    this->name         = d.name;
150    this->flagWCS      = d.flagWCS;
151    this->specOK       = d.specOK;
152    this->raS          = d.raS;
153    this->decS         = d.decS;
154    this->ra           = d.ra;
155    this->dec          = d.dec;
156    this->raWidth      = d.raWidth;
157    this->decWidth     = d.decWidth;
158    this->majorAxis    = d.majorAxis;
159    this->minorAxis    = d.minorAxis;
160    this->posang       = d.posang;
161    this->specUnits    = d.specUnits;
162    this->specType     = d.specType;
163    this->fluxUnits    = d.fluxUnits;
164    this->intFluxUnits = d.intFluxUnits;
165    this->lngtype      = d.lngtype;
166    this->lattype      = d.lattype;
167    this->vel          = d.vel;
168    this->velWidth     = d.velWidth;
169    this->velMin       = d.velMin;
170    this->velMax       = d.velMax;
171    this->w20          = d.w20;
172    this->v20min       = d.v20min;
173    this->v20max       = d.v20max;
174    this->w50          = d.w50;
175    this->v50min       = d.v50min;
176    this->v50max       = d.v50max;
177    this->posPrec      = d.posPrec;
178    this->xyzPrec      = d.xyzPrec;
179    this->fintPrec     = d.fintPrec;
180    this->fpeakPrec    = d.fpeakPrec;
181    this->velPrec      = d.velPrec;
182    this->snrPrec      = d.snrPrec;
183    return *this;
184  }
185
186  //--------------------------------------------------------------------
187  float Detection::getXcentre()
188  {
189    if(this->centreType=="peak") return this->xpeak;
190    else if(this->centreType=="average") return this->getXaverage();
191    else return this->xCentroid;
192  }
193
194  float Detection::getYcentre()
195  {
196    if(this->centreType=="peak") return this->ypeak;
197    else if(this->centreType=="average") return this->getYaverage();
198    else return this->yCentroid;
199  }
200
201  float Detection::getZcentre()
202  {
203    if(this->centreType=="peak") return this->zpeak;
204    else if(this->centreType=="average") return this->getZaverage();
205    else return this->zCentroid;
206  }
207
208  //--------------------------------------------------------------------
209
210  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
211  {
212    /// @details
213    ///  A test to see whether there is a 1-1 correspondence between
214    ///  the given list of Voxels and the voxel positions contained in
215    ///  this Detection's pixel list. No testing of the fluxes of the
216    ///  Voxels is done.
217    ///
218    /// \param voxelList The std::vector list of Voxels to be tested.
219
220    bool listsMatch = true;
221    // compare sizes
222    listsMatch = listsMatch && (voxelList.size() == this->getSize());
223    if(!listsMatch) return listsMatch;
224
225    // make sure all Detection pixels are in voxel list
226    listsMatch = listsMatch && this->voxelListCovered(voxelList);
227
228    // make sure all voxels are in Detection
229    std::vector<Voxel>::iterator vox;
230    for(vox=voxelList.begin();vox<voxelList.end();vox++)
231      listsMatch = listsMatch && this->isInObject(*vox);
232
233    return listsMatch;
234
235  }
236  //--------------------------------------------------------------------
237
238  bool Detection::voxelListCovered(std::vector<Voxel> voxelList)
239  {
240    ///  @details
241    ///  A test to see whether the given list of Voxels contains each
242    ///  position in this Detection's pixel list. It does not look for
243    ///  a 1-1 correspondence: the given list can be a super-set of the
244    ///  Detection. No testing of the fluxes of the Voxels is done.
245    ///
246    /// \param voxelList The std::vector list of Voxels to be tested.
247
248    bool listsMatch = true;
249
250    // make sure all Detection pixels are in voxel list
251    size_t v1=0;
252    std::vector<Voxel> detpixlist = this->getPixelSet();
253    while(listsMatch && v1<detpixlist.size()){
254      bool inList = false;
255      size_t v2=0;
256      while(!inList && v2<voxelList.size()){
257        inList = inList || detpixlist[v1].match(voxelList[v2]);
258        v2++;
259      }
260      listsMatch = listsMatch && inList;
261      v1++;
262    }
263
264    return listsMatch;
265
266  }
267  //--------------------------------------------------------------------
268
269    void Detection::invert()
270    {
271        /// @details
272        /// A front-end to simplify the inversion that is done at the Cube level.
273        /// The fluxes are made negative, and the negSource flag is flipped.
274        /// This can be used at any time - it doesn't matter which way the inversion is happening.
275
276       this->negSource = true;
277        this->totalFlux = -1. * this->totalFlux;
278        this->intFlux = -1. * this->intFlux;
279        this->peakFlux = -1. * this->peakFlux;
280
281    }
282
283  //--------------------------------------------------------------------
284
285  void Detection::calcFluxes(std::vector<Voxel> voxelList)
286  {
287    ///  @details
288    ///  A function that calculates total & peak fluxes (and the location
289    ///  of the peak flux) for a Detection.
290    ///
291    ///  \param fluxArray The array of flux values to calculate the
292    ///  flux parameters from.
293    ///  \param dim The dimensions of the flux array.
294   
295    //    this->haveParams = true;
296
297    this->totalFlux = this->peakFlux = 0;
298    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
299
300    // first check that the voxel list and the Detection's pixel list
301    // have a 1-1 correspondence
302
303    if(!this->voxelListCovered(voxelList)){
304      DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
305      return;
306    }
307
308    std::vector<Voxel>::iterator vox;
309    for(vox=voxelList.begin();vox<voxelList.end();vox++){
310      if(this->isInObject(*vox)){
311        long x = vox->getX();
312        long y = vox->getY();
313        long z = vox->getZ();
314        float f = vox->getF();
315        this->totalFlux += f;
316        this->xCentroid += x*f;
317        this->yCentroid += y*f;
318        this->zCentroid += z*f;
319        if( (vox==voxelList.begin()) ||  //first time round
320            (f>this->peakFlux) )   
321          {
322            this->peakFlux = f;
323            this->xpeak =    x;
324            this->ypeak =    y;
325            this->zpeak =    z;
326          }
327      }
328    }
329
330    this->xCentroid /= this->totalFlux;
331    this->yCentroid /= this->totalFlux;
332    this->zCentroid /= this->totalFlux;
333  }
334  //--------------------------------------------------------------------
335
336  void Detection::calcFluxes(std::map<Voxel,float> &voxelMap)
337  {
338    ///  @details
339    ///  A function that calculates total & peak fluxes (and the location
340    ///  of the peak flux) for a Detection.
341    ///
342    ///  \param fluxArray The array of flux values to calculate the
343    ///  flux parameters from.
344    ///  \param dim The dimensions of the flux array.
345   
346    //    this->haveParams = true;
347
348    this->totalFlux = this->peakFlux = 0;
349    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
350
351    std::vector<Voxel> voxelList = this->getPixelSet();
352    std::vector<Voxel>::iterator vox;
353    for(vox=voxelList.begin();vox<voxelList.end();vox++){
354      if(voxelMap.find(*vox) == voxelMap.end()){
355        DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
356        return;
357      }
358      else {
359        long x = vox->getX();
360        long y = vox->getY();
361        long z = vox->getZ();
362        float f = voxelMap[*vox];
363        this->totalFlux += f;
364        this->xCentroid += x*f;
365        this->yCentroid += y*f;
366        this->zCentroid += z*f;
367        if( (vox==voxelList.begin()) ||  //first time round
368            (f>this->peakFlux) )
369          {
370            this->peakFlux = f;
371            this->xpeak =    x;
372            this->ypeak =    y;
373            this->zpeak =    z;
374          }
375      }
376    }
377
378    this->xCentroid /= this->totalFlux;
379    this->yCentroid /= this->totalFlux;
380    this->zCentroid /= this->totalFlux;
381  }
382  //--------------------------------------------------------------------
383
384  void Detection::calcFluxes(float *fluxArray, size_t *dim)
385  {
386    ///  @details
387    ///  A function that calculates total & peak fluxes (and the location
388    ///  of the peak flux) for a Detection.
389    ///
390    ///  \param fluxArray The array of flux values to calculate the
391    ///  flux parameters from.
392    ///  \param dim The dimensions of the flux array.
393
394    //    this->haveParams = true;
395
396    this->totalFlux = this->peakFlux = 0;
397    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
398
399    std::vector<Voxel> voxList = this->getPixelSet();
400    std::vector<Voxel>::iterator vox=voxList.begin();
401    for(;vox<voxList.end();vox++){
402
403      long x=vox->getX();
404      long y=vox->getY();
405      long z=vox->getZ();
406      size_t ind = vox->arrayIndex(dim);
407      float f = fluxArray[ind];
408      this->totalFlux += f;
409      this->xCentroid += x*f;
410      this->yCentroid += y*f;
411      this->zCentroid += z*f;
412      if( (vox==voxList.begin()) || (f > this->peakFlux) )
413        {
414          this->peakFlux = f;
415          this->xpeak = x;
416          this->ypeak = y;
417          this->zpeak = z;
418        }
419 
420    }
421
422    this->xCentroid /= this->totalFlux;
423    this->yCentroid /= this->totalFlux;
424    this->zCentroid /= this->totalFlux;
425  }
426  //--------------------------------------------------------------------
427
428  void Detection::calcWCSparams(FitsHeader &head)
429  {
430    ///  @details
431    ///  Use the input wcs to calculate the position and velocity
432    ///    information for the Detection.
433    ///  Quantities calculated:
434    ///  <ul><li> RA: ra [deg], ra (string), ra width.
435    ///      <li> Dec: dec [deg], dec (string), dec width.
436    ///      <li> Vel: vel [km/s], min & max vel, vel width.
437    ///      <li> coord type for all three axes, nuRest,
438    ///      <li> name (IAU-style, in equatorial or Galactic)
439    ///  </ul>
440    ///
441    ///  Note that the regular parameters are NOT recalculated!
442    ///
443    ///  \param head FitsHeader object that contains the WCS information.
444
445    if(head.isWCS()){
446
447      double *pixcrd = new double[15];
448      double *world  = new double[15];
449      /*
450        define a five-point array in 3D:
451        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
452        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
453        and convert to world coordinates.   
454      */
455      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
456      pixcrd[9]  = this->getXmin()-0.5;
457      pixcrd[12] = this->getXmax()+0.5;
458      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
459      pixcrd[10] = this->getYmin()-0.5;
460      pixcrd[13] = this->getYmax()+0.5;
461      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
462      pixcrd[5] = this->getZmin();
463      pixcrd[8] = this->getZmax();
464      int flag = head.pixToWCS(pixcrd, world, 5);
465      delete [] pixcrd;
466      if(flag!=0) {
467        DUCHAMPERROR("calcWCSparams", "Error in calculating the WCS for this object.");
468      }
469      else{
470       
471        // world now has the WCS coords for the five points
472        //    -- use this to work out WCS params
473 
474        this->haveParams = true;
475
476        this->specOK = head.canUseThirdAxis();
477        this->lngtype = head.lngtype();
478        this->lattype = head.lattype();
479        this->specUnits = head.getSpectralUnits();
480        this->specType  = head.getSpectralType();
481        this->fluxUnits = head.getFluxUnits();
482        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
483        this->intFluxUnits = head.getIntFluxUnits();
484        this->ra   = world[0];
485        this->dec  = world[1];
486        int precision = -int(log10(fabs(head.WCS().cdelt[head.WCS().lng]*3600./10.)));
487        this->raS  = decToDMS(this->ra, this->lngtype,precision);
488        this->decS = decToDMS(this->dec,this->lattype,precision);
489        this->raWidth = angularSeparation(world[9],world[1],
490                                          world[12],world[1]) * 60.;
491        this->decWidth  = angularSeparation(world[0],world[10],
492                                            world[0],world[13]) * 60.;
493
494        this->name = head.getIAUName(this->ra, this->dec);
495        this->vel    = head.specToVel(world[2]);
496        this->velMin = head.specToVel(world[5]);
497        this->velMax = head.specToVel(world[8]);
498        this->velWidth = fabs(this->velMax - this->velMin);
499
500        this->flagWCS = true;
501      }
502      delete [] world;
503
504    }
505    else {
506      double x=this->getXcentre(),y=this->getYcentre(),z=this->getZmin();
507      this->velMin = head.pixToVel(x,y,z);
508      z=this->getZmax();
509      this->velMax = head.pixToVel(x,y,z);
510      this->velWidth = fabs(this->velMax - this->velMin);
511    }
512
513     
514  }
515  //--------------------------------------------------------------------
516
517  void Detection::calcIntegFlux(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
518  {
519    ///  @details
520    ///  Uses the input WCS to calculate the velocity-integrated flux,
521    ///   putting velocity in units of km/s.
522    ///  The fluxes used are taken from the Voxels, rather than an
523    ///   array of flux values.
524    ///  Integrates over full spatial and velocity range as given
525    ///   by the extrema calculated by calcWCSparams.
526    ///
527    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
528    ///  corrected by the beam size (in pixels). This is done by
529    ///  multiplying the integrated flux by the number of spatial pixels,
530    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
531    ///  pix/beam --> Jy)
532    ///
533    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
534    ///  \param voxelList The list of Voxels with flux information
535    ///  \param head FitsHeader object that contains the WCS information.
536
537    if(!this->voxelListCovered(voxelList)){
538      DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
539      return;
540    }
541
542    if(!head.is2D()){
543
544      this->haveParams = true;
545
546      const int border = 1;  // include one pixel either side in each direction
547      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
548      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
549      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
550      size_t xzero = size_t(std::max(0L,this->xmin-border));
551      size_t yzero = size_t(std::max(0L,this->ymin-border));
552      size_t zzero = size_t(std::max(0L,this->zmin-border));
553      size_t spatsize=xsize*ysize;
554      size_t size = xsize*ysize*zsize;
555      std::vector <bool> isObj(size,false);
556      double *localFlux = new double[size];
557      for(size_t i=0;i<size;i++) localFlux[i]=0.;
558
559      std::vector<Voxel>::iterator vox;
560      for(vox=voxelList.begin();vox<voxelList.end();vox++){
561        if(this->isInObject(*vox)){
562          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
563          localFlux[pos] = vox->getF();
564          isObj[pos] = true;
565        }
566      }
567 
568      // work out the WCS coords for each pixel
569      double *world  = new double[size];
570      double xpt,ypt,zpt;
571      for(size_t i=0;i<size;i++){
572        xpt = double( this->getXmin() - border + i%xsize );
573        ypt = double( this->getYmin() - border + (i/xsize)%ysize );
574        zpt = double( this->getZmin() - border + i/(xsize*ysize) );
575        world[i] = head.pixToVel(xpt,ypt,zpt);
576      }
577
578      double integrated = 0.;
579      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
580        for(size_t z=0; z<zsize; z++){
581          size_t pos =  z*xsize*ysize + pix;
582          if(isObj[pos]){ // if it's an object pixel...
583            double deltaVel;
584            if(z==0)
585              deltaVel = (world[pos+xsize*ysize] - world[pos]);
586            else if(z==(zsize-1))
587              deltaVel = (world[pos] - world[pos-xsize*ysize]);
588            else
589              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
590            integrated += localFlux[pos] * fabs(deltaVel);
591          }
592        }
593      }
594      this->intFlux = integrated;
595
596      delete [] world;
597      delete [] localFlux;
598
599      calcVelWidths(zdim,voxelList,head);
600
601    }
602    else // in this case there is just a 2D image.
603      this->intFlux = this->totalFlux;
604
605    if(head.isWCS()){
606      // correct for the beam size if the flux units string ends in "/beam"
607      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
608    }
609
610  }
611  //--------------------------------------------------------------------
612
613  void Detection::calcIntegFlux(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
614  {
615    ///  @details
616    ///  Uses the input WCS to calculate the velocity-integrated flux,
617    ///   putting velocity in units of km/s.
618    ///  The fluxes used are taken from the Voxels, rather than an
619    ///   array of flux values.
620    ///  Integrates over full spatial and velocity range as given
621    ///   by the extrema calculated by calcWCSparams.
622    ///
623    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
624    ///  corrected by the beam size (in pixels). This is done by
625    ///  multiplying the integrated flux by the number of spatial pixels,
626    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
627    ///  pix/beam --> Jy)
628    ///
629    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
630    ///  \param voxelList The list of Voxels with flux information
631    ///  \param head FitsHeader object that contains the WCS information.
632
633    if(!head.is2D()){
634
635      this->haveParams = true;
636
637      this->intFlux = 0.;
638      std::vector<Voxel> voxelList = this->getPixelSet();
639      std::vector<Voxel>::iterator vox;
640      for(vox=voxelList.begin();vox<voxelList.end();vox++){
641          if(voxelMap.find(*vox) == voxelMap.end()){
642              DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
643              return;
644          }     
645          else {
646              this->intFlux += voxelMap[*vox];
647          }
648      }
649      this->intFlux *= fabs(head.WCS().cdelt[head.WCS().spec]);
650
651      calcVelWidths(zdim,voxelMap,head);
652
653    }
654    else // in this case there is just a 2D image.
655      this->intFlux = this->totalFlux;
656
657    if(head.isWCS()){
658      // correct for the beam size if the flux units string ends in "/beam"
659      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
660    }
661
662  }
663  //--------------------------------------------------------------------
664
665  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
666  {
667    ///  @details
668    ///  Uses the input WCS to calculate the velocity-integrated flux,
669    ///   putting velocity in units of km/s.
670    ///  Integrates over full spatial and velocity range as given
671    ///   by the extrema calculated by calcWCSparams.
672    ///
673    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
674    ///  corrected by the beam size (in pixels). This is done by
675    ///  multiplying the integrated flux by the number of spatial pixels,
676    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
677    ///  pix/beam --> Jy)
678    ///
679    ///  \param fluxArray The array of flux values.
680    ///  \param dim The dimensions of the flux array.
681    ///  \param head FitsHeader object that contains the WCS information.
682
683    this->haveParams = true;
684
685    if (head.is2D() ){
686      // in this case there is just a 2D image.
687      this->intFlux = this->totalFlux;
688    }
689    else {
690
691      const int border=1; // include one pixel either side in each direction
692      size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
693      size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
694      size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
695      size_t xzero = size_t(std::max(0L,this->xmin-border));
696      size_t yzero = size_t(std::max(0L,this->ymin-border));
697      size_t zzero = size_t(std::max(0L,this->zmin-border));
698      size_t spatsize = xsize*ysize;
699      size_t size = xsize*ysize*zsize;
700      std::vector <bool> isObj(size,false);
701      double *localFlux = new double[size];
702      for(size_t i=0;i<size;i++) localFlux[i]=0.;
703      float *momMap = new float[spatsize];
704      for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
705      // work out which pixels are object pixels
706      std::vector<Voxel> voxlist = this->getPixelSet();
707      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
708        size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
709        size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
710        localFlux[pos] = fluxArray[v->arrayIndex(dim)];
711        momMap[spatpos] += fluxArray[v->arrayIndex(dim)]*head.WCS().cdelt[head.WCS().spec];
712        isObj[pos] = true;
713      }
714 
715      // work out the WCS coords for each pixel
716      double *world  = new double[size];
717      double xpt,ypt,zpt;
718      size_t i=0;
719      for(size_t z=zzero;z<zzero+zsize;z++){
720        for(size_t y=yzero;y<yzero+ysize;y++){
721          for(size_t x=xzero;x<xzero+xsize;x++){
722            xpt=double(x);
723            ypt=double(y);
724            zpt=double(z);
725            world[i++] = head.pixToVel(xpt,ypt,zpt);
726          }
727        }
728      }
729
730      double integrated = 0.;
731      for(size_t pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
732        for(size_t z=0; z<zsize; z++){
733          size_t pos =  z*xsize*ysize + pix;
734          if(isObj[pos]){ // if it's an object pixel...
735            double deltaVel;
736            if(z==0)
737              deltaVel = (world[pos+xsize*ysize] - world[pos]);
738            else if(z==(zsize-1))
739              deltaVel = (world[pos] - world[pos-xsize*ysize]);
740            else
741              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
742            integrated += localFlux[pos] * fabs(deltaVel);
743          }
744        }
745      }
746
747      delete [] world;
748     
749      this->intFlux = integrated;
750     
751      calcVelWidths(fluxArray, dim, head, par);
752         
753      delete [] localFlux;
754      delete [] momMap;
755
756    }   
757
758    if(head.isWCS()){
759      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
760      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
761    }
762
763  }
764  //--------------------------------------------------------------------
765
766  void Detection::findShape(const float *fluxArray, const size_t *dim, FitsHeader &head)
767  {
768      const long border=1; // include one pixel either side in each direction
769      size_t x1 = size_t(std::max(long(0),this->xmin-border));
770      size_t y1 = size_t(std::max(long(0),this->ymin-border));
771      size_t x2 = size_t(std::min(long(dim[0])-1,this->xmax+border));
772      size_t y2 = size_t(std::min(long(dim[1])-1,this->ymax+border));
773      size_t xsize = x2-x1+1;
774      size_t ysize = y2-y1+1;
775      size_t spatsize = xsize*ysize;
776
777      float *momentMap = new float[spatsize];
778      for(size_t i=0;i<spatsize;i++) momentMap[i]=0.;
779      // work out which pixels are object pixels
780      std::vector<Voxel> voxlist = this->getPixelSet();
781      float delta = (head.isWCS() && head.getWCS()->spec>=0) ? fabs(head.WCS().cdelt[head.WCS().spec]) : 1.;
782      float sign = this->negSource ? -1. : 1.;
783      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
784          size_t spatpos=(v->getX()-x1) + (v->getY()-y1)*xsize;
785          if(spatpos<spatsize)
786              momentMap[spatpos] += fluxArray[v->arrayIndex(dim)] * delta * sign;
787          else DUCHAMPTHROW("findShape","Memory overflow - accessing spatpos="<<spatpos<<" when spatsize="<<spatsize <<". Pixel is (x,y)=("<<v->getX() <<","<<v->getY()<<") and (x1,y1)="<<x1<<","<<y1<<"), (x2,y2)="<<x2<<","<<y2<<"), xsize="<<xsize);
788      }
789      if(this->negSource)
790        for(size_t i=0;i<spatsize;i++) momentMap[i]*=-1.;
791
792      size_t smldim[2]; smldim[0]=xsize; smldim[1]=ysize;
793      Image *smlIm = new Image(smldim);
794      smlIm->saveArray(momentMap,spatsize);
795      smlIm->setMinSize(1);
796      float max = *std::max_element(momentMap,momentMap+spatsize);
797      smlIm->stats().setThreshold(max/2.);
798      std::vector<Object2D> objlist=smlIm->findSources2D();
799
800      Object2D combined;
801      for(size_t i=0;i<objlist.size();i++) combined = combined + objlist[i];
802      std::string extraFlag="";
803      bool ellipseGood = combined.findEllipse(true, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // try first by weighting the pixels by their flux
804      if(!ellipseGood) {
805          ellipseGood = combined.findEllipse(false, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // if that fails, remove the flux weighting
806          extraFlag="W";
807      }
808      float scale=head.getShapeScale();
809      if(ellipseGood){
810          // multiply axes by 2 to go from semi-major to FWHM...
811          this->majorAxis = combined.major() * head.getAvPixScale() * 2. * scale;
812          this->minorAxis = combined.minor() * head.getAvPixScale() * 2. * scale;
813          this->posang =    combined.posAng() * 180. / M_PI;
814          // std::cerr << "*** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
815      }
816      else {
817          extraFlag="w";
818          std::pair<double,double> axes = combined.getPrincipalAxes();
819          this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale() * scale;
820          this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale() * scale;
821          this->posang = combined.getPositionAngle() * 180. / M_PI;
822          // std::cerr << "** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
823     }
824      this->flagText += extraFlag;
825     
826
827      delete smlIm;
828      delete [] momentMap;
829  }
830
831  //--------------------------------------------------------------------
832
833  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
834  {
835    ///  @details
836    /// Calculates the widths of the detection at 20% and 50% of the
837    /// peak integrated flux. The procedure is as follows: first
838    /// generate an integrated flux spectrum (using all given voxels
839    /// that lie in the object's spatial map); find the peak; starting
840    /// at the spectral edges of the detection, move in or out until
841    /// you reach the 20% or 50% peak flux level. Linear interpolation
842    /// between points is done.
843    ///
844    ///  \param zdim The size of the spectral axis in the cube
845    ///  \param voxelList The list of Voxels with flux information
846    ///  \param head FitsHeader object that contains the WCS information.
847
848    float *intSpec = new float[zdim];
849    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
850       
851    Object2D spatMap = this->getSpatialMap();
852    for(int s=0;s<spatMap.getNumScan();s++){
853      std::vector<Voxel>::iterator vox;
854      for(vox=voxelList.begin();vox<voxelList.end();vox++){
855        if(spatMap.isInObject(*vox)){
856          intSpec[vox->getZ()] += vox->getF();
857        }
858      }
859    }
860   
861    calcVelWidths(zdim, intSpec, head);
862
863    delete [] intSpec;
864
865  }
866
867  //--------------------------------------------------------------------
868
869  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
870  {
871    ///  @details
872    /// Calculates the widths of the detection at 20% and 50% of the
873    /// peak integrated flux. The procedure is as follows: first
874    /// generate an integrated flux spectrum (using all given voxels
875    /// that lie in the object's spatial map); find the peak; starting
876    /// at the spectral edges of the detection, move in or out until
877    /// you reach the 20% or 50% peak flux level. Linear interpolation
878    /// between points is done.
879    ///
880    ///  \param zdim The size of the spectral axis in the cube
881    ///  \param voxelList The list of Voxels with flux information
882    ///  \param head FitsHeader object that contains the WCS information.
883
884    float *intSpec = new float[zdim];
885    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
886       
887    std::vector<Voxel> voxelList = this->getPixelSet();
888    std::vector<Voxel>::iterator vox;
889    for(vox=voxelList.begin();vox<voxelList.end();vox++){
890      if(voxelMap.find(*vox) == voxelMap.end()){
891        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
892        return;
893      }
894      else {
895        intSpec[vox->getZ()] += voxelMap[*vox];
896      }
897    }
898
899    calcVelWidths(zdim, intSpec, head);
900
901    delete [] intSpec;
902
903  }
904
905  //--------------------------------------------------------------------
906
907  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
908  {
909
910    // finding the 20% & 50% points.  Start at the velmin & velmax
911    //  points. Then, if the int flux there is above the 20%/50%
912    //  limit, go out, otherwise go in. This is to deal with the
913    //  problems from double- (or multi-) peaked sources.
914
915    this->haveParams = true;
916
917    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
918    bool goLeft;
919   
920    if(this->negSource){
921      // if we've inverted the source, need to make the feature
922      // positive for the interpolation/extrapolation to work
923      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
924    }
925
926    float peak=0.;
927    size_t peakLoc=0;
928    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
929      if(z==0 || peak<intSpec[z]){
930        peak = intSpec[z];
931        peakLoc = z;
932      }
933    }
934   
935    size_t z=this->getZmin();
936    goLeft = intSpec[z]>peak*0.5;
937    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
938    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
939    if(z==0) this->v50min = this->velMin;
940    else{
941      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
942      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
943      this->v50min = head.pixToVel(xpt,ypt,zpt);
944    }
945    z=this->getZmax();
946    goLeft = intSpec[z]<peak*0.5;
947    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
948    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
949    if(z==zdim) this->v50max = this->velMax;
950    else{
951      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
952      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
953      this->v50max = head.pixToVel(xpt,ypt,zpt);
954    }
955    z=this->getZmin();
956    goLeft = intSpec[z]>peak*0.2;
957    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
958    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
959    if(z==0) this->v20min = this->velMin;
960    else{
961      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
962      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
963      this->v20min = head.pixToVel(xpt,ypt,zpt);
964    }
965    z=this->getZmax();
966    goLeft = intSpec[z]<peak*0.2;
967    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
968    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
969    if(z==zdim) this->v20max = this->velMax;
970    else{
971      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
972      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
973      this->v20max = head.pixToVel(xpt,ypt,zpt);
974    }
975
976    this->w20 = fabs(this->v20min - this->v20max);
977    this->w50 = fabs(this->v50min - this->v50max);
978   
979    if(this->negSource){
980      // un-do the inversion, in case intSpec is needed elsewhere
981      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
982    }
983
984
985  }
986  //--------------------------------------------------------------------
987
988  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
989  {
990    ///  @details
991    /// Calculates the widths of the detection at 20% and 50% of the
992    /// peak integrated flux. The procedure is as follows: first
993    /// generate an integrated flux spectrum (summing each spatial
994    /// pixel's spectrum); find the peak; starting at the spectral
995    /// edges of the detection, move in or out until you reach the 20%
996    /// or 50% peak flux level. Linear interpolation between points is
997    /// done.
998    ///
999    ///  \param fluxArray The array of flux values.
1000    ///  \param dim The dimensions of the flux array.
1001    ///  \param head FitsHeader object that contains the WCS information.
1002
1003    if(dim[2] > 2){
1004
1005      float *intSpec = new float[dim[2]];
1006      size_t size=dim[0]*dim[1]*dim[2];
1007      std::vector<bool> mask = par.makeBlankMask(fluxArray,size);
1008      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
1009
1010      this->calcVelWidths(dim[2],intSpec,head);
1011
1012      delete [] intSpec;
1013
1014    }
1015    else{
1016      this->v50min = this->v20min = this->velMin;
1017      this->v50max = this->v20max = this->velMax;
1018      this->w20 = fabs(this->v20min - this->v20max);
1019      this->w50 = fabs(this->v50min - this->v50max);
1020    }
1021
1022  }
1023  //--------------------------------------------------------------------
1024
1025  void Detection::setOffsets(Param &par)
1026  {
1027    ///  @details
1028    /// This function stores the values of the offsets for each cube axis.
1029    /// The offsets are the starting values of the cube axes that may differ from
1030    ///  the default value of 0 (for instance, if a subsection is being used).
1031    /// The values will be used when the detection is outputted.
1032
1033    this->xSubOffset = par.getXOffset();
1034    this->ySubOffset = par.getYOffset();
1035    this->zSubOffset = par.getZOffset();
1036  }
1037  //--------------------------------------------------------------------
1038
1039  bool Detection::hasEnoughChannels(int minNumber)
1040  {
1041    ///  @details
1042    /// A function to determine if the Detection has enough
1043    /// contiguous channels to meet the minimum requirement
1044    /// given as the argument.
1045    /// \param minNumber How many channels is the minimum acceptable number?
1046    /// \return True if there is at least one occurence of minNumber consecutive
1047    /// channels present to return true. False otherwise.
1048
1049    // Preferred method -- need a set of minNumber consecutive channels present.
1050
1051    int numChan = this->getMaxAdjacentChannels();
1052    bool result = (numChan >= minNumber);
1053
1054    return result;
1055 
1056  }
1057  //--------------------------------------------------------------------
1058 
1059  void Detection::addDetection(Detection &other)
1060  {
1061    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
1062      //      this->addChannel(*it);
1063      this->addChannel(it->first, it->second);
1064    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1065  }
1066
1067  Detection operator+ (Detection &lhs, Detection &rhs)
1068  {
1069    Detection output = lhs;
1070    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1071      output.addChannel(it->first, it->second);
1072    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
1073    return output;
1074  }
1075   
1076
1077  bool Detection::canMerge(Detection &other, Param &par)
1078  {
1079    bool near = this->isNear(other,par);
1080    if(near) return this->isClose(other,par);
1081    else return near;
1082  }
1083
1084  bool Detection::isNear(Detection &other, Param &par)
1085  {
1086
1087    bool flagAdj = par.getFlagAdjacent();
1088    float threshS = par.getThreshS();
1089    float threshV = par.getThreshV();
1090
1091    long gap;
1092    if(flagAdj) gap = 1;
1093    else gap = long( ceil(threshS) );
1094
1095    bool areNear;
1096    // Test X ranges
1097    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1098    else areNear=(other.xmax>=(this->xmin-gap));
1099    // Test Y ranges
1100    if(areNear){
1101      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1102      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1103    }
1104    // Test Z ranges
1105    if(areNear){
1106      gap = long(ceil(threshV));
1107      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1108      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1109    }
1110   
1111    return areNear;
1112
1113  }
1114
1115  bool Detection::isClose(Detection &other, Param &par)
1116  {
1117    bool close = false;   // this will be the value returned
1118   
1119    bool flagAdj = par.getFlagAdjacent();
1120    float threshS = par.getThreshS();
1121    float threshV = par.getThreshV();
1122
1123    //
1124    // If we get to here, the pixel ranges overlap -- so we do a
1125    // pixel-by-pixel comparison to make sure they are actually
1126    // "close" according to the thresholds.  Otherwise, close=false,
1127    // and so don't need to do anything else before returning.
1128    //
1129
1130    std::vector<long> zlist1 = this->getChannelList();
1131    std::vector<long> zlist2 = other.getChannelList();
1132    Scan test1,test2;
1133
1134    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1135      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1136       
1137        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1138             
1139          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1140          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1141
1142          close = temp1.canMerge(temp2,threshS,flagAdj);
1143
1144        }
1145
1146      }
1147    }
1148       
1149    return close;
1150   
1151  }
1152
1153
1154
1155}
Note: See TracBrowser for help on using the repository browser.