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

Last change on this file since 771 was 771, checked in by MatthewWhiting, 14 years ago

Fixing a problem with the new merging code, where the length of the scans was wrongly calculated.

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