source: trunk/src/PixelMap/Object2D.cc @ 1339

Last change on this file since 1339 was 1212, checked in by MatthewWhiting, 11 years ago

Simplifying the principle axes calculations, and how they are called from findShape.

File size: 14.2 KB
Line 
1// -----------------------------------------------------------------------
2// Object2D.cc: Member functions for the Object2D 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 <duchamp/PixelMap/Object2D.hh>
29#include <duchamp/PixelMap/Scan.hh>
30#include <duchamp/PixelMap/Voxel.hh>
31#include <iostream>
32#include <vector>
33#include <list>
34#include <algorithm>
35#include <math.h>
36
37namespace PixelInfo
38{
39
40  Object2D::Object2D()
41  {
42    this->numPix = 0;
43  }
44
45  Object2D::Object2D(const Object2D& o)
46  {
47    operator=(o);
48  } 
49
50  Object2D& Object2D::operator= (const Object2D& o)
51  {
52    if(this == &o) return *this;
53    this->scanlist  = o.scanlist;
54    this->numPix    = o.numPix;
55    this->xSum      = o.xSum;
56    this->ySum      = o.ySum;
57    this->xmin      = o.xmin;
58    this->ymin      = o.ymin;
59    this->xmax      = o.xmax;
60    this->ymax      = o.ymax;
61    this->majorAxis = o.majorAxis;
62    this->minorAxis = o.minorAxis;
63    this->posAngle  = o.posAngle;
64    return *this;
65  } 
66  //------------------------------------------------------
67
68  Object2D operator+ (Object2D lhs, Object2D rhs)
69  {
70    Object2D output = lhs;
71    for(std::vector<Scan>::iterator s=rhs.scanlist.begin();s!=rhs.scanlist.end();s++)
72      output.addScan(*s);
73    return output;
74  }
75
76  //------------------------------------------------------
77  void Object2D::addPixel(long x, long y)
78  {
79    ///  This function has three parts to it:
80    ///  <ul><li> First, it searches through the existing scans to see if
81    ///            a) there is a scan of the same y-value present, and
82    ///            b) whether the (x,y) pixel lies in or next to an existing
83    ///            Scan. If so, it is added and the Scan is grown if need be.
84    ///            If this isn't the case, a new Scan of length 1 is added to
85    ///            the list.
86    ///      <li> If the Scan list was altered, all are checked to see whether
87    ///            there is now a case of Scans touching. If so, they are
88    ///            combined and added to the end of the list.
89    ///      <li> If the pixel was added, the parameters are updated and the
90    ///           pixel counter incremented.
91    ///  </ul>
92
93
94    bool flagDone=false,flagChanged=false, isNew=false;
95
96    for (std::vector<Scan>::iterator scan=this->scanlist.begin(); !flagDone && scan!=this->scanlist.end(); scan++){
97      if(y == scan->itsY){ // if the y value is already in the list
98        if(scan->isInScan(x,y)) flagDone = true; // pixel already here.
99        else{ // it's a new pixel!
100          if((x==(scan->itsX-1)) || (x == (scan->itsX+scan->itsXLen)) ){
101            // if it is adjacent to the existing range, add to that range
102            if(x==(scan->itsX-1)) scan->growLeft();
103            else                  scan->growRight();
104            flagDone = true;
105            flagChanged = true;
106            isNew = true;
107          }
108        }     
109      }
110    }
111
112    if(!flagDone){
113      // we got to the end of the scanlist, so there is no pre-existing scan with this y value
114      // add a new scan consisting of just this pixel
115      Scan newOne(y,x,1);
116      this->scanlist.push_back(newOne);
117      isNew = true;
118    }
119    else if(flagChanged){
120      // this is true only if one of the pre-existing scans has changed
121      //
122      // need to clean up, to see if there is a case of two scans when
123      // there should be one. Only need to look at scans with matching
124      // y-value
125      //
126      // because there has been only one pixel added, only 2 AT MOST
127      // scans will need to be combined, so once we meet a pair that
128      // overlap, we can finish.
129
130      bool combined = false;
131      std::vector<Scan>::iterator scan1=this->scanlist.begin(),scan2;
132      for (; !combined && scan1!=this->scanlist.end(); scan1++){
133        if(scan1->itsY==y){
134          scan2=scan1;
135          scan2++;
136          for(; !combined && scan2!=this->scanlist.end(); scan2++){
137            if(scan2->itsY==y){
138              combined = scan1->addScan(*scan2);
139              if(combined){
140                this->scanlist.erase(scan2);
141              }
142            }   
143          }
144        }
145      }
146
147    }
148
149    if(isNew){ 
150      // update the centres, mins, maxs and increment the pixel counter
151      if(this->numPix==0){
152        this->xSum = this->xmin = this->xmax = x;
153        this->ySum = this->ymin = this->ymax = y;
154      }
155      else{
156        this->xSum += x;
157        this->ySum += y;
158        if(x<this->xmin) this->xmin = x;
159        if(x>this->xmax) this->xmax = x;
160        if(y<this->ymin) this->ymin = y;
161        if(y>this->ymax) this->ymax = y;
162      }
163      this->numPix++;
164    }
165
166  }
167  //------------------------------------------------------
168
169  void Object2D::addScan(Scan &scan)
170  {
171    long y=scan.getY();
172    for(long x=scan.getX();x<=scan.getXmax();x++) this->addPixel(x,y);
173
174  }
175  //------------------------------------------------------
176
177  bool Object2D::isInObject(long x, long y)
178  {
179
180    std::vector<Scan>::iterator scn;
181    bool returnval=false;
182    for(scn=this->scanlist.begin();scn!=this->scanlist.end()&&!returnval;scn++){
183
184      returnval = ((y == scn->itsY) && (x>= scn->itsX) && (x<=scn->getXmax()));
185
186    }
187       
188    return returnval;
189
190  }
191  //------------------------------------------------------
192
193  std::ostream& operator<< ( std::ostream& theStream, Object2D& obj)
194  {
195    if(obj.scanlist.size()>1) obj.order();
196    for(std::vector<Scan>::iterator s=obj.scanlist.begin();s!=obj.scanlist.end();s++)
197      theStream << *s << "\n";
198    theStream<<"---\n";
199    return theStream;
200
201  }
202  //------------------------------------------------------
203
204  void Object2D::calcParams()
205  {
206    this->xSum = 0;
207    this->ySum = 0;
208    std::vector<Scan>::iterator s;
209    for(s=this->scanlist.begin();s!=this->scanlist.end();s++){
210
211      if(s==this->scanlist.begin()){
212        this->ymin = this->ymax = s->itsY;
213        this->xmin = s->itsX;
214        this->xmax = s->getXmax();
215      }
216      else{
217        if(this->ymin>s->itsY)    this->ymin = s->itsY;
218        if(this->xmin>s->itsX)    this->xmin = s->itsX;
219        if(this->ymax<s->itsY)    this->ymax = s->itsY;
220        if(this->xmax<s->getXmax()) this->xmax = s->getXmax();
221      }
222
223      this->ySum += s->itsY*s->getXlen();
224      for(int x=s->itsX;x<=s->getXmax();x++)
225        this->xSum += x;
226
227    }
228
229  }
230  //------------------------------------------------------
231
232  void Object2D::cleanup()
233  {
234    std::vector<Scan>::iterator s1=this->scanlist.begin(),s2;
235    for (; s1!=this->scanlist.end(); s1++){
236      s2=s1; s2++;
237      for (; s2!=this->scanlist.end(); s2++){
238
239        if(overlap(*s1,*s2)){
240          s1->addScan(*s2);
241          this->scanlist.erase(s2);
242        }
243      }
244    }
245
246  }
247  //------------------------------------------------------
248
249  long Object2D::getNumDistinctY()
250  {
251    std::vector<long> ylist;
252    if(this->scanlist.size()==0) return 0;
253    if(this->scanlist.size()==1) return 1;
254    std::vector<Scan>::iterator scn;
255    for(scn=this->scanlist.begin();scn!=this->scanlist.end();scn++){
256      bool inList = false;
257      unsigned int j=0;
258      long y = scn->itsY;
259      while(!inList && j<ylist.size()){
260        if(y==ylist[j]) inList=true;
261        j++;
262      };
263      if(!inList) ylist.push_back(y);
264    }
265    return ylist.size();
266  }
267  //------------------------------------------------------
268
269  long Object2D::getNumDistinctX()
270  {
271    std::vector<long> xlist;
272    if(this->scanlist.size()==0) return 0;
273    if(this->scanlist.size()==1) return 1;
274    std::vector<Scan>::iterator scn;
275    for(scn=this->scanlist.begin();scn!=this->scanlist.end();scn++){
276      for(int x=scn->itsX;x<scn->getXmax();x++){
277        bool inList = false;
278        unsigned int j=0;
279        while(!inList && j<xlist.size()){
280          if(x==xlist[j]) inList=true;
281          j++;
282        };
283        if(!inList) xlist.push_back(x);
284      }
285    }
286    return xlist.size();
287  }
288  //------------------------------------------------------
289
290  bool Object2D::scanOverlaps(Scan &scan)
291  {
292    bool returnval = false;
293    for(std::vector<Scan>::iterator s=this->scanlist.begin();!returnval&&s!=this->scanlist.end();s++){
294      returnval = returnval || s->overlaps(scan);
295    }
296    return returnval;
297  }
298
299  //------------------------------------------------------
300
301  void Object2D::addOffsets(long xoff, long yoff)
302  {
303    std::vector<Scan>::iterator scn;
304    for(scn=this->scanlist.begin();scn!=this->scanlist.end();scn++)
305      scn->addOffsets(xoff,yoff);
306    this->xSum += xoff*numPix;
307    this->xmin += xoff; xmax += xoff;
308    this->ySum += yoff*numPix;
309    this->ymin += yoff; ymax += yoff;
310  }
311
312  //------------------------------------------------------
313
314  bool Object2D::findEllipse(bool weightByFlux, float *array, size_t xdim, size_t ydim, int xzero, int yzero, float x0, float y0)
315  {
316    bool result=true;
317    double sumxx=0.,sumyy=0.,sumxy=0.,sumflux=0.;
318    int ct=0;
319    std::vector<Scan>::iterator scn=this->scanlist.begin();
320    for(;scn!=this->scanlist.end();scn++){
321      int y = scn->itsY;
322      float yoff=scn->itsY - y0;
323      for(int x=scn->itsX;x<=scn->getXmax();x++){
324          ct++;
325        float xoff=x-x0;
326        float flux = weightByFlux ? array[(x-xzero)+xdim*(y-yzero)] : 1;
327        sumxx += (xoff*xoff*flux);
328        sumyy += (yoff*yoff*flux);
329        sumxy += (xoff*yoff*flux);
330        sumflux += flux;
331      }
332    }
333   
334    if(ct>0){
335        sumxx /= sumflux;
336        sumxy /= sumflux;
337        sumyy /= sumflux;
338    }
339
340    this->posAngle = 0.5 * atan2( 2*sumxy , (sumxx-sumyy) ) + M_PI/2.;
341    this->posAngle = fmod(this->posAngle, 2.*M_PI);
342    if(this->posAngle > M_PI) this->posAngle -= 2.*M_PI;
343    double plusroot = sumxx + sumyy + sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy);
344    result = ( plusroot > 0 );
345    double minusroot = sumxx + sumyy - sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy);
346    result = result && ( minusroot > 0 );
347    if(result){
348      this->majorAxis = sqrt( 2. * plusroot );
349      this->minorAxis = sqrt( 2. * minusroot );
350      this->minorAxis = std::max(this->minorAxis,0.1);
351    }
352    else this->majorAxis = this->minorAxis = 0.;
353    return result;
354  }
355
356
357  //------------------------------------------------------
358
359  double Object2D::getPositionAngle()
360  {
361
362    int sumxx=0;
363    int sumyy=0;
364    int sumxy=0;
365    std::vector<Scan>::iterator scn=this->scanlist.begin();
366    for(;scn!=this->scanlist.end();scn++){
367      sumyy += (scn->itsY*scn->itsY)*scn->itsXLen;
368      for(int x=scn->itsX;x<=scn->getXmax();x++){
369        sumxx += x*x;
370        sumxy += x*scn->itsY;
371      }
372    }
373
374    // Calculate net moments
375    double mxx = sumxx - this->xSum*this->xSum / double(this->numPix);
376    double myy = sumyy - this->ySum*this->ySum / double(this->numPix);
377    double mxy = sumxy - this->xSum*this->ySum / double(this->numPix);
378
379    if(mxy==0){
380      if(mxx>myy) return M_PI/2.;
381      else return 0.;
382    }
383
384    // Angle of the minimum moment
385    double tantheta = (mxx - myy + sqrt( (mxx-myy)*(mxx-myy) + 4.*mxy*mxy))/(2.*mxy);
386
387    return atan(tantheta);
388  }
389
390  //------------------------------------------------------
391
392  std::pair<double,double> Object2D::getPrincipalAxes()
393  {
394
395    double theta = this->getPositionAngle();
396    double x0 = this->getXaverage();
397    double y0 = this->getYaverage();
398    std::vector<Scan>::iterator scn=this->scanlist.begin();
399    double maj,min,majMax=0.,majMin=0.,minMax=0.,minMin=0.;
400    for(;scn!=this->scanlist.end();scn++){
401      for(int x=scn->itsX;x<=scn->getXmax();x++){
402        maj = (x-x0+0.5)*cos(theta) + (scn->itsY-y0+0.5)*sin(theta);
403        min = (x-x0+0.5)*sin(theta) + (scn->itsY-y0+0.5)*cos(theta);
404        majMax=std::max(maj,majMax);
405        majMin=std::min(maj,majMin);
406        minMax=std::max(min,minMax);
407        minMin=std::min(min,minMin);
408      }
409    }
410
411    std::pair<double,double> axes;
412    axes.first=std::max(majMax-majMin,1.);
413    axes.second=std::max(minMax-minMin,1.);
414    return axes;
415
416  }
417
418
419  //------------------------------------------------------
420
421
422  bool Object2D::canMerge(Object2D &other, float threshS, bool flagAdj)
423  {
424    long gap;
425    if(flagAdj) gap = 1;
426    else gap = long( ceil(threshS) );
427    bool near = this->isNear(other,gap);
428    if(near) return this->isClose(other,threshS,flagAdj);
429    else return near;
430  }
431
432  //------------------------------------------------------
433
434  bool Object2D::isNear(Object2D &other, long gap)
435  {
436    bool areNear;
437    // Test X ranges
438    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
439    else areNear=(other.xmax>=(this->xmin-gap));
440    // Test Y ranges
441    if(areNear){
442      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
443      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
444    }
445    return areNear;
446  }
447
448  //------------------------------------------------------
449
450  bool Object2D::isClose(Object2D &other, float threshS, bool flagAdj)
451  {
452
453    long gap = long(ceil(threshS));
454    if(flagAdj) gap=1;
455
456    bool close = false;
457
458    long ycommonMin=std::max(this->ymin-gap,other.ymin)-gap;
459    long ycommonMax=std::min(this->ymax+gap,other.ymax)+gap;
460
461    std::vector<Scan>::iterator iter1,iter2;
462    for(iter1=this->scanlist.begin();!close && iter1!=this->scanlist.end();iter1++){
463      if(iter1->itsY >= ycommonMin && iter1->itsY<=ycommonMax){
464        for(iter2=other.scanlist.begin();!close && iter2!=other.scanlist.end();iter2++){
465          if(iter2->itsY >= ycommonMin && iter2->itsY<=ycommonMax){
466             
467            if(abs(iter1->itsY-iter2->itsY)<=gap){
468              if(flagAdj) {
469                if((iter1->itsX-gap)>iter2->itsX) close=((iter2->itsX+iter2->itsXLen-1)>=(iter1->itsX-gap));
470                else close = ( (iter1->itsX+iter1->itsXLen+gap-1)>=iter2->itsX);
471              }
472              else close = (minSep(*iter1,*iter2) <= threshS);
473            }
474
475          }
476        }
477      }
478    }
479           
480    return close;
481   
482  }
483
484
485
486}
Note: See TracBrowser for help on using the repository browser.