source: branches/OptimisedGrowerTesting/src/Detection/detection.cc @ 1441

Last change on this file since 1441 was 1302, checked in by MatthewWhiting, 11 years ago

Had to move the new drawBorders function out of detection.cc, so that it could be built without pgplot.

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 = !this->negSource;
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            (this->negSource&&(f<this->peakFlux)) ||
321            (!this->negSource&&(f>this->peakFlux))   )
322          {
323            this->peakFlux = f;
324            this->xpeak =    x;
325            this->ypeak =    y;
326            this->zpeak =    z;
327          }
328      }
329    }
330
331    this->xCentroid /= this->totalFlux;
332    this->yCentroid /= this->totalFlux;
333    this->zCentroid /= this->totalFlux;
334  }
335  //--------------------------------------------------------------------
336
337  void Detection::calcFluxes(std::map<Voxel,float> &voxelMap)
338  {
339    ///  @details
340    ///  A function that calculates total & peak fluxes (and the location
341    ///  of the peak flux) for a Detection.
342    ///
343    ///  \param fluxArray The array of flux values to calculate the
344    ///  flux parameters from.
345    ///  \param dim The dimensions of the flux array.
346   
347    //    this->haveParams = true;
348
349    this->totalFlux = this->peakFlux = 0;
350    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
351
352    std::vector<Voxel> voxelList = this->getPixelSet();
353    std::vector<Voxel>::iterator vox;
354    for(vox=voxelList.begin();vox<voxelList.end();vox++){
355      if(voxelMap.find(*vox) == voxelMap.end()){
356        DUCHAMPERROR("Detection::calcFluxes","Voxel list provided does not match");
357        return;
358      }
359      else {
360        long x = vox->getX();
361        long y = vox->getY();
362        long z = vox->getZ();
363        float f = voxelMap[*vox];
364        this->totalFlux += f;
365        this->xCentroid += x*f;
366        this->yCentroid += y*f;
367        this->zCentroid += z*f;
368        if( (vox==voxelList.begin()) ||  //first time round
369            (this->negSource&&(f<this->peakFlux)) ||
370            (!this->negSource&&(f>this->peakFlux))   )
371          {
372            this->peakFlux = f;
373            this->xpeak =    x;
374            this->ypeak =    y;
375            this->zpeak =    z;
376          }
377      }
378    }
379
380    this->xCentroid /= this->totalFlux;
381    this->yCentroid /= this->totalFlux;
382    this->zCentroid /= this->totalFlux;
383  }
384  //--------------------------------------------------------------------
385
386  void Detection::calcFluxes(float *fluxArray, size_t *dim)
387  {
388    ///  @details
389    ///  A function that calculates total & peak fluxes (and the location
390    ///  of the peak flux) for a Detection.
391    ///
392    ///  \param fluxArray The array of flux values to calculate the
393    ///  flux parameters from.
394    ///  \param dim The dimensions of the flux array.
395
396    //    this->haveParams = true;
397
398    this->totalFlux = this->peakFlux = 0;
399    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
400
401    std::vector<Voxel> voxList = this->getPixelSet();
402    std::vector<Voxel>::iterator vox=voxList.begin();
403    for(;vox<voxList.end();vox++){
404
405      long x=vox->getX();
406      long y=vox->getY();
407      long z=vox->getZ();
408      size_t ind = vox->arrayIndex(dim);
409      float f = fluxArray[ind];
410      this->totalFlux += f;
411      this->xCentroid += x*f;
412      this->yCentroid += y*f;
413      this->zCentroid += z*f;
414      if( (vox==voxList.begin()) ||
415          (this->negSource&&(f<this->peakFlux)) ||
416          (!this->negSource&&(f>this->peakFlux))   )
417        {
418          this->peakFlux = f;
419          this->xpeak = x;
420          this->ypeak = y;
421          this->zpeak = z;
422        }
423 
424    }
425
426    this->xCentroid /= this->totalFlux;
427    this->yCentroid /= this->totalFlux;
428    this->zCentroid /= this->totalFlux;
429  }
430  //--------------------------------------------------------------------
431
432  void Detection::calcWCSparams(FitsHeader &head)
433  {
434    ///  @details
435    ///  Use the input wcs to calculate the position and velocity
436    ///    information for the Detection.
437    ///  Quantities calculated:
438    ///  <ul><li> RA: ra [deg], ra (string), ra width.
439    ///      <li> Dec: dec [deg], dec (string), dec width.
440    ///      <li> Vel: vel [km/s], min & max vel, vel width.
441    ///      <li> coord type for all three axes, nuRest,
442    ///      <li> name (IAU-style, in equatorial or Galactic)
443    ///  </ul>
444    ///
445    ///  Note that the regular parameters are NOT recalculated!
446    ///
447    ///  \param head FitsHeader object that contains the WCS information.
448
449    if(head.isWCS()){
450
451      double *pixcrd = new double[15];
452      double *world  = new double[15];
453      /*
454        define a five-point array in 3D:
455        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
456        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
457        and convert to world coordinates.   
458      */
459      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
460      pixcrd[9]  = this->getXmin()-0.5;
461      pixcrd[12] = this->getXmax()+0.5;
462      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
463      pixcrd[10] = this->getYmin()-0.5;
464      pixcrd[13] = this->getYmax()+0.5;
465      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
466      pixcrd[5] = this->getZmin();
467      pixcrd[8] = this->getZmax();
468      int flag = head.pixToWCS(pixcrd, world, 5);
469      delete [] pixcrd;
470      if(flag!=0) {
471        DUCHAMPERROR("calcWCSparams", "Error in calculating the WCS for this object.");
472      }
473      else{
474       
475        // world now has the WCS coords for the five points
476        //    -- use this to work out WCS params
477 
478        this->haveParams = true;
479
480        this->specOK = head.canUseThirdAxis();
481        this->lngtype = head.lngtype();
482        this->lattype = head.lattype();
483        this->specUnits = head.getSpectralUnits();
484        this->specType  = head.getSpectralType();
485        this->fluxUnits = head.getFluxUnits();
486        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
487        this->intFluxUnits = head.getIntFluxUnits();
488        this->ra   = world[0];
489        this->dec  = world[1];
490        int precision = -int(log10(fabs(head.WCS().cdelt[head.WCS().lng]*3600./10.)));
491        this->raS  = decToDMS(this->ra, this->lngtype,precision);
492        this->decS = decToDMS(this->dec,this->lattype,precision);
493        this->raWidth = angularSeparation(world[9],world[1],
494                                          world[12],world[1]) * 60.;
495        this->decWidth  = angularSeparation(world[0],world[10],
496                                            world[0],world[13]) * 60.;
497
498        this->name = head.getIAUName(this->ra, this->dec);
499        this->vel    = head.specToVel(world[2]);
500        this->velMin = head.specToVel(world[5]);
501        this->velMax = head.specToVel(world[8]);
502        this->velWidth = fabs(this->velMax - this->velMin);
503
504        this->flagWCS = true;
505      }
506      delete [] world;
507
508    }
509    else {
510      double x=this->getXcentre(),y=this->getYcentre(),z=this->getZmin();
511      this->velMin = head.pixToVel(x,y,z);
512      z=this->getZmax();
513      this->velMax = head.pixToVel(x,y,z);
514      this->velWidth = fabs(this->velMax - this->velMin);
515    }
516
517     
518  }
519  //--------------------------------------------------------------------
520
521  void Detection::calcIntegFlux(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
522  {
523    ///  @details
524    ///  Uses the input WCS to calculate the velocity-integrated flux,
525    ///   putting velocity in units of km/s.
526    ///  The fluxes used are taken from the Voxels, rather than an
527    ///   array of flux values.
528    ///  Integrates over full spatial and velocity range as given
529    ///   by the extrema calculated by calcWCSparams.
530    ///
531    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
532    ///  corrected by the beam size (in pixels). This is done by
533    ///  multiplying the integrated flux by the number of spatial pixels,
534    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
535    ///  pix/beam --> Jy)
536    ///
537    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
538    ///  \param voxelList The list of Voxels with flux information
539    ///  \param head FitsHeader object that contains the WCS information.
540
541    if(!this->voxelListCovered(voxelList)){
542      DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
543      return;
544    }
545
546    if(!head.is2D()){
547
548      this->haveParams = true;
549
550      const int border = 1;  // include one pixel either side in each direction
551      size_t xsize = size_t(this->xmax-this->xmin+2*border+1);
552      size_t ysize = size_t(this->ymax-this->ymin+2*border+1);
553      size_t zsize = size_t(this->zmax-this->zmin+2*border+1);
554      size_t xzero = size_t(std::max(0L,this->xmin-border));
555      size_t yzero = size_t(std::max(0L,this->ymin-border));
556      size_t zzero = size_t(std::max(0L,this->zmin-border));
557      size_t spatsize=xsize*ysize;
558      size_t size = xsize*ysize*zsize;
559      std::vector <bool> isObj(size,false);
560      double *localFlux = new double[size];
561      for(size_t i=0;i<size;i++) localFlux[i]=0.;
562
563      std::vector<Voxel>::iterator vox;
564      for(vox=voxelList.begin();vox<voxelList.end();vox++){
565        if(this->isInObject(*vox)){
566          size_t pos=(vox->getX()-xzero) + (vox->getY()-yzero)*xsize + (vox->getZ()-zzero)*spatsize;
567          localFlux[pos] = vox->getF();
568          isObj[pos] = true;
569        }
570      }
571 
572      // work out the WCS coords for each pixel
573      double *world  = new double[size];
574      double xpt,ypt,zpt;
575      for(size_t i=0;i<size;i++){
576        xpt = double( this->getXmin() - border + i%xsize );
577        ypt = double( this->getYmin() - border + (i/xsize)%ysize );
578        zpt = double( this->getZmin() - border + i/(xsize*ysize) );
579        world[i] = head.pixToVel(xpt,ypt,zpt);
580      }
581
582      double integrated = 0.;
583      for(size_t pix=0; pix<spatsize; pix++){ // loop over each spatial pixel.
584        for(size_t z=0; z<zsize; z++){
585          size_t pos =  z*xsize*ysize + pix;
586          if(isObj[pos]){ // if it's an object pixel...
587            double deltaVel;
588            if(z==0)
589              deltaVel = (world[pos+xsize*ysize] - world[pos]);
590            else if(z==(zsize-1))
591              deltaVel = (world[pos] - world[pos-xsize*ysize]);
592            else
593              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
594            integrated += localFlux[pos] * fabs(deltaVel);
595          }
596        }
597      }
598      this->intFlux = integrated;
599
600      delete [] world;
601      delete [] localFlux;
602
603      calcVelWidths(zdim,voxelList,head);
604
605    }
606    else // in this case there is just a 2D image.
607      this->intFlux = this->totalFlux;
608
609    if(head.isWCS()){
610      // correct for the beam size if the flux units string ends in "/beam"
611      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
612    }
613
614  }
615  //--------------------------------------------------------------------
616
617  void Detection::calcIntegFlux(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
618  {
619    ///  @details
620    ///  Uses the input WCS to calculate the velocity-integrated flux,
621    ///   putting velocity in units of km/s.
622    ///  The fluxes used are taken from the Voxels, rather than an
623    ///   array of flux values.
624    ///  Integrates over full spatial and velocity range as given
625    ///   by the extrema calculated by calcWCSparams.
626    ///
627    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
628    ///  corrected by the beam size (in pixels). This is done by
629    ///  multiplying the integrated flux by the number of spatial pixels,
630    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
631    ///  pix/beam --> Jy)
632    ///
633    ///  \param zdim The size of the spectral axis (needed to find the velocity widths)
634    ///  \param voxelList The list of Voxels with flux information
635    ///  \param head FitsHeader object that contains the WCS information.
636
637    if(!head.is2D()){
638
639      this->haveParams = true;
640
641      this->intFlux = 0.;
642      std::vector<Voxel> voxelList = this->getPixelSet();
643      std::vector<Voxel>::iterator vox;
644      for(vox=voxelList.begin();vox<voxelList.end();vox++){
645          if(voxelMap.find(*vox) == voxelMap.end()){
646              DUCHAMPERROR("Detection::calcIntegFlux","Voxel list provided does not match");
647              return;
648          }     
649          else {
650              this->intFlux += voxelMap[*vox];
651          }
652      }
653      this->intFlux *= fabs(head.WCS().cdelt[head.WCS().spec]);
654
655      calcVelWidths(zdim,voxelMap,head);
656
657    }
658    else // in this case there is just a 2D image.
659      this->intFlux = this->totalFlux;
660
661    if(head.isWCS()){
662      // correct for the beam size if the flux units string ends in "/beam"
663      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
664    }
665
666  }
667  //--------------------------------------------------------------------
668
669  void Detection::calcIntegFlux(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
670  {
671    ///  @details
672    ///  Uses the input WCS to calculate the velocity-integrated flux,
673    ///   putting velocity in units of km/s.
674    ///  Integrates over full spatial and velocity range as given
675    ///   by the extrema calculated by calcWCSparams.
676    ///
677    ///  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
678    ///  corrected by the beam size (in pixels). This is done by
679    ///  multiplying the integrated flux by the number of spatial pixels,
680    ///  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
681    ///  pix/beam --> Jy)
682    ///
683    ///  \param fluxArray The array of flux values.
684    ///  \param dim The dimensions of the flux array.
685    ///  \param head FitsHeader object that contains the WCS information.
686
687    this->haveParams = true;
688
689    const int border=1; // include one pixel either side in each direction
690    size_t xsize = std::min(size_t(this->xmax-this->xmin+2*border+1),dim[0]);
691    size_t ysize = std::min(size_t(this->ymax-this->ymin+2*border+1),dim[1]);
692    size_t zsize = std::min(size_t(this->zmax-this->zmin+2*border+1),dim[2]);
693    size_t xzero = size_t(std::max(0L,this->xmin-border));
694    size_t yzero = size_t(std::max(0L,this->ymin-border));
695    size_t zzero = size_t(std::max(0L,this->zmin-border));
696    size_t spatsize = xsize*ysize;
697    size_t size = xsize*ysize*zsize;
698    std::vector <bool> isObj(size,false);
699    double *localFlux = new double[size];
700    for(size_t i=0;i<size;i++) localFlux[i]=0.;
701    float *momMap = new float[spatsize];
702    for(size_t i=0;i<spatsize;i++) momMap[i]=0.;
703    // work out which pixels are object pixels
704    std::vector<Voxel> voxlist = this->getPixelSet();
705    for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
706      size_t spatpos=(v->getX()-xzero) + (v->getY()-yzero)*xsize;
707      size_t pos= spatpos + (v->getZ()-zzero)*spatsize;
708      localFlux[pos] = fluxArray[v->arrayIndex(dim)];
709      momMap[spatpos] += fluxArray[v->arrayIndex(dim)]*head.WCS().cdelt[head.WCS().spec];
710      isObj[pos] = true;
711    }
712 
713     if(!head.is2D()){
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     }
754      else // in this case there is just a 2D image.
755      this->intFlux = this->totalFlux;
756   
757     delete [] localFlux;
758     delete [] momMap;
759   
760
761    if(head.isWCS()){
762      // correct for the beam size if the flux units string ends in "/beam" and we have beam info
763      if(head.needBeamSize()) this->intFlux  /= head.beam().area();
764    }
765
766  }
767  //--------------------------------------------------------------------
768
769  void Detection::findShape(const float *fluxArray, const size_t *dim, FitsHeader &head)
770  {
771      const long border=1; // include one pixel either side in each direction
772      size_t x1 = size_t(std::max(long(0),this->xmin-border));
773      size_t y1 = size_t(std::max(long(0),this->ymin-border));
774      size_t x2 = size_t(std::min(long(dim[0])-1,this->xmax+border));
775      size_t y2 = size_t(std::min(long(dim[1])-1,this->ymax+border));
776      size_t xsize = x2-x1+1;
777      size_t ysize = y2-y1+1;
778      size_t spatsize = xsize*ysize;
779
780      float *momentMap = new float[spatsize];
781      for(size_t i=0;i<spatsize;i++) momentMap[i]=0.;
782      // work out which pixels are object pixels
783      std::vector<Voxel> voxlist = this->getPixelSet();
784      float delta = (head.isWCS() && head.getWCS()->spec>=0) ? fabs(head.WCS().cdelt[head.WCS().spec]) : 1.;
785      float sign = this->negSource ? -1. : 1.;
786      for(std::vector<Voxel>::iterator v=voxlist.begin();v<voxlist.end();v++){
787          size_t spatpos=(v->getX()-x1) + (v->getY()-y1)*xsize;
788          if(spatpos<spatsize)
789              momentMap[spatpos] += fluxArray[v->arrayIndex(dim)] * delta * sign;
790          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);
791      }
792
793      size_t smldim[2]; smldim[0]=xsize; smldim[1]=ysize;
794      Image *smlIm = new Image(smldim);
795      smlIm->saveArray(momentMap,spatsize);
796      smlIm->setMinSize(1);
797      float max = *std::max_element(momentMap,momentMap+spatsize);
798      smlIm->stats().setThreshold(max/2.);
799      std::vector<Object2D> objlist=smlIm->findSources2D();
800
801      Object2D combined;
802      for(size_t i=0;i<objlist.size();i++) combined = combined + objlist[i];
803      std::string extraFlag="";
804      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
805      if(!ellipseGood) {
806          ellipseGood = combined.findEllipse(false, momentMap, xsize, ysize, 0,0, this->getXcentre()-x1, this->getYcentre()-y1); // if that fails, remove the flux weighting
807          extraFlag="W";
808      }
809      float scale=head.getShapeScale();
810      if(ellipseGood){
811          // multiply axes by 2 to go from semi-major to FWHM...
812          this->majorAxis = combined.major() * head.getAvPixScale() * 2. * scale;
813          this->minorAxis = combined.minor() * head.getAvPixScale() * 2. * scale;
814          this->posang =    combined.posAng() * 180. / M_PI;
815          // std::cerr << "*** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
816      }
817      else {
818          extraFlag="w";
819          std::pair<double,double> axes = combined.getPrincipalAxes();
820          this->majorAxis = std::max(axes.first,axes.second) * head.getAvPixScale() * scale;
821          this->minorAxis = std::min(axes.first,axes.second) * head.getAvPixScale() * scale;
822          this->posang = combined.getPositionAngle() * 180. / M_PI;
823          // std::cerr << "** " << combined.getSize()<< " " << majorAxis<<" " << minorAxis << " " << posang<< " " << scale <<"\n";
824     }
825      this->flagText += extraFlag;
826     
827
828      delete smlIm;
829      delete [] momentMap;
830  }
831
832  //--------------------------------------------------------------------
833
834  void Detection::calcVelWidths(size_t zdim, std::vector<Voxel> voxelList, FitsHeader &head)
835  {
836    ///  @details
837    /// Calculates the widths of the detection at 20% and 50% of the
838    /// peak integrated flux. The procedure is as follows: first
839    /// generate an integrated flux spectrum (using all given voxels
840    /// that lie in the object's spatial map); find the peak; starting
841    /// at the spectral edges of the detection, move in or out until
842    /// you reach the 20% or 50% peak flux level. Linear interpolation
843    /// between points is done.
844    ///
845    ///  \param zdim The size of the spectral axis in the cube
846    ///  \param voxelList The list of Voxels with flux information
847    ///  \param head FitsHeader object that contains the WCS information.
848
849    float *intSpec = new float[zdim];
850    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
851       
852    Object2D spatMap = this->getSpatialMap();
853    for(int s=0;s<spatMap.getNumScan();s++){
854      std::vector<Voxel>::iterator vox;
855      for(vox=voxelList.begin();vox<voxelList.end();vox++){
856        if(spatMap.isInObject(*vox)){
857          intSpec[vox->getZ()] += vox->getF();
858        }
859      }
860    }
861   
862    calcVelWidths(zdim, intSpec, head);
863
864    delete [] intSpec;
865
866  }
867
868  //--------------------------------------------------------------------
869
870  void Detection::calcVelWidths(size_t zdim, std::map<Voxel,float> voxelMap, FitsHeader &head)
871  {
872    ///  @details
873    /// Calculates the widths of the detection at 20% and 50% of the
874    /// peak integrated flux. The procedure is as follows: first
875    /// generate an integrated flux spectrum (using all given voxels
876    /// that lie in the object's spatial map); find the peak; starting
877    /// at the spectral edges of the detection, move in or out until
878    /// you reach the 20% or 50% peak flux level. Linear interpolation
879    /// between points is done.
880    ///
881    ///  \param zdim The size of the spectral axis in the cube
882    ///  \param voxelList The list of Voxels with flux information
883    ///  \param head FitsHeader object that contains the WCS information.
884
885    float *intSpec = new float[zdim];
886    for(size_t i=0;i<zdim;i++) intSpec[i]=0;
887       
888    std::vector<Voxel> voxelList = this->getPixelSet();
889    std::vector<Voxel>::iterator vox;
890    for(vox=voxelList.begin();vox<voxelList.end();vox++){
891      if(voxelMap.find(*vox) == voxelMap.end()){
892        DUCHAMPERROR("Detection::calcVelWidths","Voxel list provided does not match");
893        return;
894      }
895      else {
896        intSpec[vox->getZ()] += voxelMap[*vox];
897      }
898    }
899
900    calcVelWidths(zdim, intSpec, head);
901
902    delete [] intSpec;
903
904  }
905
906  //--------------------------------------------------------------------
907
908  void Detection::calcVelWidths(size_t zdim, float *intSpec, FitsHeader &head)
909  {
910
911    // finding the 20% & 50% points.  Start at the velmin & velmax
912    //  points. Then, if the int flux there is above the 20%/50%
913    //  limit, go out, otherwise go in. This is to deal with the
914    //  problems from double- (or multi-) peaked sources.
915
916    this->haveParams = true;
917
918    double zpt,xpt=double(this->getXcentre()),ypt=double(this->getYcentre());
919    bool goLeft;
920   
921    if(this->negSource){
922      // if we've inverted the source, need to make the feature
923      // positive for the interpolation/extrapolation to work
924      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
925    }
926
927    float peak=0.;
928    size_t peakLoc=0;
929    for(size_t z=this->getZmin();z<=size_t(this->getZmax());z++) {
930      if(z==0 || peak<intSpec[z]){
931        peak = intSpec[z];
932        peakLoc = z;
933      }
934    }
935   
936    size_t z=this->getZmin();
937    goLeft = intSpec[z]>peak*0.5;
938    if(goLeft) while(z>0 && intSpec[z]>peak*0.5) z--;
939    else       while(z<peakLoc && intSpec[z]<peak*0.5) z++;
940    if(z==0) this->v50min = this->velMin;
941    else{
942      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
943      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
944      this->v50min = head.pixToVel(xpt,ypt,zpt);
945    }
946    z=this->getZmax();
947    goLeft = intSpec[z]<peak*0.5;
948    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.5) z--;
949    else       while(z<zdim    && intSpec[z]>peak*0.5) z++;
950    if(z==zdim) this->v50max = this->velMax;
951    else{
952      if(goLeft) zpt = z + (peak*0.5-intSpec[z])/(intSpec[z+1]-intSpec[z]);
953      else       zpt = z - (peak*0.5-intSpec[z])/(intSpec[z-1]-intSpec[z]);
954      this->v50max = head.pixToVel(xpt,ypt,zpt);
955    }
956    z=this->getZmin();
957    goLeft = intSpec[z]>peak*0.2;
958    if(goLeft) while(z>0 && intSpec[z]>peak*0.2) z--;
959    else       while(z<peakLoc && intSpec[z]<peak*0.2) z++;
960    if(z==0) this->v20min = this->velMin;
961    else{
962      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
963      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
964      this->v20min = head.pixToVel(xpt,ypt,zpt);
965    }
966    z=this->getZmax();
967    goLeft = intSpec[z]<peak*0.2;
968    if(goLeft) while(z>peakLoc && intSpec[z]<peak*0.2) z--;
969    else       while(z<zdim    && intSpec[z]>peak*0.2) z++;
970    if(z==zdim) this->v20max = this->velMax;
971    else{
972      if(goLeft) zpt = z + (peak*0.2-intSpec[z])/(intSpec[z+1]-intSpec[z]);
973      else       zpt = z - (peak*0.2-intSpec[z])/(intSpec[z-1]-intSpec[z]);
974      this->v20max = head.pixToVel(xpt,ypt,zpt);
975    }
976
977    this->w20 = fabs(this->v20min - this->v20max);
978    this->w50 = fabs(this->v50min - this->v50max);
979   
980    if(this->negSource){
981      // un-do the inversion, in case intSpec is needed elsewhere
982      for(size_t i=0;i<zdim;i++) intSpec[i] *= -1.;
983    }
984
985
986  }
987  //--------------------------------------------------------------------
988
989  void Detection::calcVelWidths(float *fluxArray, size_t *dim, FitsHeader &head, Param &par)
990  {
991    ///  @details
992    /// Calculates the widths of the detection at 20% and 50% of the
993    /// peak integrated flux. The procedure is as follows: first
994    /// generate an integrated flux spectrum (summing each spatial
995    /// pixel's spectrum); find the peak; starting at the spectral
996    /// edges of the detection, move in or out until you reach the 20%
997    /// or 50% peak flux level. Linear interpolation between points is
998    /// done.
999    ///
1000    ///  \param fluxArray The array of flux values.
1001    ///  \param dim The dimensions of the flux array.
1002    ///  \param head FitsHeader object that contains the WCS information.
1003
1004    if(dim[2] > 2){
1005
1006      float *intSpec = new float[dim[2]];
1007      size_t size=dim[0]*dim[1]*dim[2];
1008      bool *mask = par.makeBlankMask(fluxArray,size);
1009      getIntSpec(*this,fluxArray,dim,mask,1.,intSpec);
1010
1011      this->calcVelWidths(dim[2],intSpec,head);
1012
1013      delete [] intSpec;
1014
1015    }
1016    else{
1017      this->v50min = this->v20min = this->velMin;
1018      this->v50max = this->v20max = this->velMax;
1019      this->w20 = fabs(this->v20min - this->v20max);
1020      this->w50 = fabs(this->v50min - this->v50max);
1021    }
1022
1023  }
1024  //--------------------------------------------------------------------
1025
1026  void Detection::setOffsets(Param &par)
1027  {
1028    ///  @details
1029    /// This function stores the values of the offsets for each cube axis.
1030    /// The offsets are the starting values of the cube axes that may differ from
1031    ///  the default value of 0 (for instance, if a subsection is being used).
1032    /// The values will be used when the detection is outputted.
1033
1034    this->xSubOffset = par.getXOffset();
1035    this->ySubOffset = par.getYOffset();
1036    this->zSubOffset = par.getZOffset();
1037  }
1038  //--------------------------------------------------------------------
1039
1040  bool Detection::hasEnoughChannels(int minNumber)
1041  {
1042    ///  @details
1043    /// A function to determine if the Detection has enough
1044    /// contiguous channels to meet the minimum requirement
1045    /// given as the argument.
1046    /// \param minNumber How many channels is the minimum acceptable number?
1047    /// \return True if there is at least one occurence of minNumber consecutive
1048    /// channels present to return true. False otherwise.
1049
1050    // Preferred method -- need a set of minNumber consecutive channels present.
1051
1052    int numChan = this->getMaxAdjacentChannels();
1053    bool result = (numChan >= minNumber);
1054
1055    return result;
1056 
1057  }
1058  //--------------------------------------------------------------------
1059 
1060  void Detection::addDetection(Detection &other)
1061  {
1062    for(std::map<long, Object2D>::iterator it = other.chanlist.begin(); it!=other.chanlist.end();it++)
1063      //      this->addChannel(*it);
1064      this->addChannel(it->first, it->second);
1065    this->haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them 
1066  }
1067
1068  Detection operator+ (Detection &lhs, Detection &rhs)
1069  {
1070    Detection output = lhs;
1071    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
1072      output.addChannel(it->first, it->second);
1073    output.haveParams = false; // make it appear as if the parameters haven't been calculated, so that we can re-calculate them
1074    return output;
1075  }
1076   
1077
1078  bool Detection::canMerge(Detection &other, Param &par)
1079  {
1080    bool near = this->isNear(other,par);
1081    if(near) return this->isClose(other,par);
1082    else return near;
1083  }
1084
1085  bool Detection::isNear(Detection &other, Param &par)
1086  {
1087
1088    bool flagAdj = par.getFlagAdjacent();
1089    float threshS = par.getThreshS();
1090    float threshV = par.getThreshV();
1091
1092    long gap;
1093    if(flagAdj) gap = 1;
1094    else gap = long( ceil(threshS) );
1095
1096    bool areNear;
1097    // Test X ranges
1098    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
1099    else areNear=(other.xmax>=(this->xmin-gap));
1100    // Test Y ranges
1101    if(areNear){
1102      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
1103      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
1104    }
1105    // Test Z ranges
1106    if(areNear){
1107      gap = long(ceil(threshV));
1108      if((this->zmin-gap)<other.zmin) areNear=areNear&&((this->zmax+gap)>=other.zmin);
1109      else areNear=areNear&&(other.zmax>=(this->zmin-gap));
1110    }
1111   
1112    return areNear;
1113
1114  }
1115
1116  bool Detection::isClose(Detection &other, Param &par)
1117  {
1118    bool close = false;   // this will be the value returned
1119   
1120    bool flagAdj = par.getFlagAdjacent();
1121    float threshS = par.getThreshS();
1122    float threshV = par.getThreshV();
1123
1124    //
1125    // If we get to here, the pixel ranges overlap -- so we do a
1126    // pixel-by-pixel comparison to make sure they are actually
1127    // "close" according to the thresholds.  Otherwise, close=false,
1128    // and so don't need to do anything else before returning.
1129    //
1130
1131    std::vector<long> zlist1 = this->getChannelList();
1132    std::vector<long> zlist2 = other.getChannelList();
1133    Scan test1,test2;
1134
1135    for(size_t ct1=0; (!close && (ct1<zlist1.size())); ct1++){
1136      for(size_t ct2=0; (!close && (ct2<zlist2.size())); ct2++){
1137       
1138        if(abs(zlist1[ct1]-zlist2[ct2])<=threshV){
1139             
1140          Object2D temp1 = this->getChanMap(zlist1[ct1]);
1141          Object2D temp2 = other.getChanMap(zlist2[ct2]);
1142
1143          close = temp1.canMerge(temp2,threshS,flagAdj);
1144
1145        }
1146
1147      }
1148    }
1149       
1150    return close;
1151   
1152  }
1153
1154
1155
1156}
Note: See TracBrowser for help on using the repository browser.