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

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

Ticket #132 - allowing fitting of an ellipse to the moment-0 map of a detection. Also allowing it to be written to annotation files and to the moment-0 cutout in the spectral output.

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