source: tags/release-1.3.1/src/Detection/detection.cc @ 1391

Last change on this file since 1391 was 1183, checked in by MatthewWhiting, 11 years ago

Fixing a spelling mistake!

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