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

Last change on this file since 472 was 472, checked in by MatthewWhiting, 16 years ago

A number of updates:

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