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

Last change on this file since 1144 was 1133, checked in by MatthewWhiting, 11 years ago

Ticket #132 - New code to fit the ellipse to the moment-0 map thresholded at half its peak - this then provides FWHM esimates for major/minor axes. Also have adaptive units for these axes, scaling to get the numbers not too small and adjusting the units accordingly. 2D images also have the shape calculated too now.

File size: 14.3 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    std::vector<Scan>::iterator scn=this->scanlist.begin();
319    for(;scn!=this->scanlist.end();scn++){
320      int y = scn->itsY;
321      float yoff=scn->itsY - y0;
322      for(int x=scn->itsX;x<=scn->getXmax();x++){
323        float xoff=x-x0;
324        float flux = weightByFlux ? array[(x-xzero)+xdim*(y-yzero)] : 1;
325        sumxx += (xoff*xoff*flux);
326        sumyy += (yoff*yoff*flux);
327        sumxy += (xoff*yoff*flux);
328        sumflux += flux;
329      }
330    }
331
332    sumxx /= sumflux;
333    sumxy /= sumflux;
334    sumyy /= sumflux;
335
336    this->posAngle = 0.5 * atan2( 2*sumxy , (sumxx-sumyy) ) + M_PI/2.;
337    this->posAngle = fmod(this->posAngle, 2.*M_PI);
338    if(this->posAngle > M_PI) this->posAngle -= 2.*M_PI;
339    result = ( (sumxx + sumyy + sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy)) > 0 );
340    result = result && ( (sumxx + sumyy - sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy)) > 0 );
341    if(result){
342      this->majorAxis = sqrt( 2. * (sumxx + sumyy + sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy)) );
343      this->minorAxis = sqrt( 2. * (sumxx + sumyy - sqrt((sumxx-sumyy)*(sumxx-sumyy) + 4.*sumxy*sumxy)) );
344      this->minorAxis = std::max(this->minorAxis,0.1);
345    }
346    else this->majorAxis = this->minorAxis = 0.;
347    return result;
348  }
349
350
351  //------------------------------------------------------
352
353  double Object2D::getPositionAngle()
354  {
355
356    int sumxx=0;
357    int sumyy=0;
358    int sumxy=0;
359    std::vector<Scan>::iterator scn=this->scanlist.begin();
360    for(;scn!=this->scanlist.end();scn++){
361      sumyy += (scn->itsY*scn->itsY)*scn->itsXLen;
362      for(int x=scn->itsX;x<=scn->getXmax();x++){
363        sumxx += x*x;
364        sumxy += x*scn->itsY;
365      }
366    }
367
368    // Calculate net moments
369    double mxx = sumxx - this->xSum*this->xSum / double(this->numPix);
370    double myy = sumyy - this->ySum*this->ySum / double(this->numPix);
371    double mxy = sumxy - this->xSum*this->ySum / double(this->numPix);
372
373    if(mxy==0){
374      if(mxx>myy) return M_PI/2.;
375      else return 0.;
376    }
377
378    // Angle of the minimum moment
379    double tantheta = (mxx - myy + sqrt( (mxx-myy)*(mxx-myy) + 4.*mxy*mxy))/(2.*mxy);
380
381    return atan(tantheta);
382  }
383
384  //------------------------------------------------------
385
386  std::pair<double,double> Object2D::getPrincipleAxes()
387  {
388
389    double theta = this->getPositionAngle();
390    double x0 = this->getXaverage();
391    double y0 = this->getYaverage();
392    std::vector<double> majorAxes, minorAxes;
393    std::vector<Scan>::iterator scn=this->scanlist.begin();
394    for(;scn!=this->scanlist.end();scn++){
395      for(int x=scn->itsX;x<=scn->getXmax();x++){
396        majorAxes.push_back((x-x0+0.5)*cos(theta) + (scn->itsY-y0+0.5)*sin(theta));
397        minorAxes.push_back((x-x0+0.5)*sin(theta) + (scn->itsY-y0+0.5)*cos(theta));
398      }
399    }
400
401    std::sort(majorAxes.begin(),majorAxes.end());
402    std::sort(minorAxes.begin(),minorAxes.end());
403    int size = majorAxes.size();
404    std::pair<double,double> axes;
405    axes.first = fabs(majorAxes[0]-majorAxes[size-1]);
406    axes.second = fabs(minorAxes[0]-minorAxes[size-1]);
407    if(axes.first<0.5) axes.first=0.5;
408    if(axes.second<0.5) axes.second=0.5;
409    return axes;
410
411  }
412
413
414  //------------------------------------------------------
415
416
417  bool Object2D::canMerge(Object2D &other, float threshS, bool flagAdj)
418  {
419    long gap;
420    if(flagAdj) gap = 1;
421    else gap = long( ceil(threshS) );
422    bool near = this->isNear(other,gap);
423    if(near) return this->isClose(other,threshS,flagAdj);
424    else return near;
425  }
426
427  //------------------------------------------------------
428
429  bool Object2D::isNear(Object2D &other, long gap)
430  {
431    bool areNear;
432    // Test X ranges
433    if((this->xmin-gap)<other.xmin) areNear=((this->xmax+gap)>=other.xmin);
434    else areNear=(other.xmax>=(this->xmin-gap));
435    // Test Y ranges
436    if(areNear){
437      if((this->ymin-gap)<other.ymin) areNear=areNear&&((this->ymax+gap)>=other.ymin);
438      else areNear=areNear&&(other.ymax>=(this->ymin-gap));
439    }
440    return areNear;
441  }
442
443  //------------------------------------------------------
444
445  bool Object2D::isClose(Object2D &other, float threshS, bool flagAdj)
446  {
447
448    long gap = long(ceil(threshS));
449    if(flagAdj) gap=1;
450
451    bool close = false;
452
453    long ycommonMin=std::max(this->ymin-gap,other.ymin)-gap;
454    long ycommonMax=std::min(this->ymax+gap,other.ymax)+gap;
455
456    std::vector<Scan>::iterator iter1,iter2;
457    for(iter1=this->scanlist.begin();!close && iter1!=this->scanlist.end();iter1++){
458      if(iter1->itsY >= ycommonMin && iter1->itsY<=ycommonMax){
459        for(iter2=other.scanlist.begin();!close && iter2!=other.scanlist.end();iter2++){
460          if(iter2->itsY >= ycommonMin && iter2->itsY<=ycommonMax){
461             
462            if(abs(iter1->itsY-iter2->itsY)<=gap){
463              if(flagAdj) {
464                if((iter1->itsX-gap)>iter2->itsX) close=((iter2->itsX+iter2->itsXLen-1)>=(iter1->itsX-gap));
465                else close = ( (iter1->itsX+iter1->itsXLen+gap-1)>=iter2->itsX);
466              }
467              else close = (minSep(*iter1,*iter2) <= threshS);
468            }
469
470          }
471        }
472      }
473    }
474           
475    return close;
476   
477  }
478
479
480
481}
Note: See TracBrowser for help on using the repository browser.