source: trunk/src/PixelMap/Object3D.cc @ 1302

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

Ticket #200 - Largely resolved. The getVertexSet function now resides in Object3D, and returns a set of Voxel vectors, that describe the bounding vertices of the object (in 2D projection). Changes also made to the functions that call this, and to the verification outputs that are affected (notably the .ann and .reg files).

File size: 17.8 KB
Line 
1// -----------------------------------------------------------------------
2// Object3D.cc: Member functions for Object3D 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 <iostream>
29#include <fstream>
30#include <sstream>
31#include <duchamp/PixelMap/Voxel.hh>
32#include <duchamp/PixelMap/Line.hh>
33#include <duchamp/PixelMap/Scan.hh>
34#include <duchamp/PixelMap/Object2D.hh>
35#include <duchamp/PixelMap/Object3D.hh>
36#include <duchamp/Utils/Section.hh>
37#include <vector>
38#include <map>
39
40namespace PixelInfo
41{
42  Object3D::Object3D()
43  {
44    this->numVox=0;
45    this->xSum = 0;
46    this->ySum = 0;
47    this->zSum = 0;
48    this->xmin = this->xmax = this->ymin = this->ymax = this->zmin = this->zmax = -1;
49  }
50  //--------------------------------------------
51
52  Object3D::Object3D(const Object3D& o)
53  {
54    operator=(o);
55  }
56  //--------------------------------------------
57
58  Object3D& Object3D::operator= (const Object3D& o)
59  {
60    if(this == &o) return *this;
61    this->chanlist = o.chanlist;
62    this->numVox  = o.numVox;
63    this->spatialMap = o.spatialMap;
64    this->xSum    = o.xSum;
65    this->ySum    = o.ySum;
66    this->zSum    = o.zSum;
67    this->xmin    = o.xmin;
68    this->ymin    = o.ymin;
69    this->zmin    = o.zmin;
70    this->xmax    = o.xmax;
71    this->ymax    = o.ymax;
72    this->zmax    = o.zmax;
73    return *this;
74  }
75  //--------------------------------------------
76
77  Object3D operator+ (Object3D lhs, Object3D rhs)
78  {
79    Object3D output = lhs;
80    for(std::map<long, Object2D>::iterator it = rhs.chanlist.begin(); it!=rhs.chanlist.end();it++)
81      output.addChannel(it->first, it->second);
82      //      output.addChannel(*it);
83    return output;
84  }
85
86  //--------------------------------------------
87  float Object3D::getXaverage()
88  {
89    if(numVox>0) return xSum/float(numVox);
90    else return 0.;
91  }
92  //--------------------------------------------
93
94  float Object3D::getYaverage()
95  {
96    if(numVox>0) return ySum/float(numVox);
97    else return 0.;
98  }
99  //--------------------------------------------
100
101  float Object3D::getZaverage()
102  {
103    if(numVox>0) return zSum/float(numVox);
104    else return 0.;
105  }
106  //--------------------------------------------
107 
108  bool Object3D::isInObject(long x, long y, long z)
109  {
110    std::map<long,Object2D>::iterator it=this->chanlist.begin();
111    while(it!=this->chanlist.end() && it->first!=z) it++;
112
113    if(it==this->chanlist.end()) return false;
114    else                         return it->second.isInObject(x,y);
115  }
116  //--------------------------------------------
117
118  void Object3D::addPixel(long x, long y, long z)
119  {
120 
121    std::map<long,Object2D>::iterator it=this->chanlist.begin();
122    while(it!=this->chanlist.end() && it->first!=z) it++;
123
124    if(it==this->chanlist.end()){ //new channel
125      Object2D obj;
126      obj.addPixel(x,y);
127      chanlist.insert( std::pair<int,Object2D>(z,obj) );
128      // update the centres, min & max, as well as the number of voxels
129      if(this->numVox==0){
130        this->xSum = this->xmin = this->xmax = x;
131        this->ySum = this->ymin = this->ymax = y;
132        this->zSum = this->zmin = this->zmax = z;
133      }
134      else{
135        this->xSum += x;
136        this->ySum += y;
137        this->zSum += z;
138        if(x<this->xmin) this->xmin = x;
139        if(x>this->xmax) this->xmax = x;
140        if(y<this->ymin) this->ymin = y;
141        if(y>this->ymax) this->ymax = y;
142        // since we've ordered the maplist, the min & max z fall out naturally
143        this->zmin = this->chanlist.begin()->first;
144        this->zmax = this->chanlist.rbegin()->first;
145      }
146      this->numVox++;   
147    }
148    else{ // existing channel
149      // This method deals with the case of a new pixel being added AND
150      // with the new pixel already existing in the Object2D
151 
152      // Remove that channel's information from the Object's information
153      this->xSum -= it->second.xSum;
154      this->ySum -= it->second.ySum;
155      this->zSum -= z*it->second.numPix;
156      this->numVox -= it->second.numPix;
157
158      // Add the pixel
159      it->second.addPixel(x,y);
160   
161      this->numVox += it->second.numPix;
162      this->xSum += it->second.xSum;
163      this->ySum += it->second.ySum;
164      this->zSum += z*it->second.numPix;
165      if(x<this->xmin) this->xmin = x;
166      if(x>this->xmax) this->xmax = x;
167      if(y<this->ymin) this->ymin = y;
168      if(y>this->ymax) this->ymax = y;
169      // don't need to do anything to zmin/zmax -- the z-value is
170      // already in the list
171    }
172
173    this->spatialMap.addPixel(x,y);
174
175  }
176  //--------------------------------------------
177
178  void Object3D::addScan(Scan s, long z)
179  {
180    long y=s.getY();
181    for(int x=s.getX(); x<=s.getXmax(); x++)
182      this->addPixel(x,y,z);
183  }
184
185  //--------------------------------------------
186
187  // void Object3D::addChannel(const std::pair<long, Object2D> *chan)
188  // {
189  //   long z = chan->first;
190  //   Object2D
191  // }
192
193
194  void Object3D::addChannel(const long &z, Object2D &obj)
195  {
196
197    std::map<long,Object2D>::iterator it=this->chanlist.begin();
198    while(it!=this->chanlist.end() && it->first!=z) it++;
199
200    if(it == this->chanlist.end()){ // channel z is not already in object, so add it.
201      this->chanlist.insert(std::pair<long,Object2D>(z,obj));
202      if(this->numVox == 0){ // if there are no other pixels, so initialise mins,maxs,sums
203        this->xmin = obj.xmin;
204        this->xmax = obj.xmax;
205        this->ymin = obj.ymin;
206        this->ymax = obj.ymax;
207        this->zmin = this->zmax = z;
208        this->xSum = obj.xSum;
209        this->ySum = obj.ySum;
210        this->zSum = z * obj.getSize();
211      }
212      else{ // there are other pixels in other channels, so update mins, maxs, sums
213        if(obj.xmin<this->xmin) this->xmin = obj.xmin;
214        if(obj.xmax>this->xmax) this->xmax = obj.xmax;
215        if(obj.ymin<this->ymin) this->ymin = obj.ymin;
216        if(obj.ymax>this->ymax) this->ymax = obj.ymax;
217        if(z<this->zmin) this->zmin = z;
218        if(z>this->zmax) this->zmax = z;
219        this->xSum += obj.xSum;
220        this->ySum += obj.ySum;
221        this->zSum += z * obj.getSize();
222      }
223      this->numVox += obj.getSize();
224    }
225    else{ // channel is already present, so need to combine objects.
226      this->xSum -= it->second.xSum;
227      this->ySum -= it->second.ySum;
228      this->zSum -= z*it->second.getSize();
229      this->numVox -= it->second.getSize();
230      it->second = it->second + obj;
231      this->xSum += it->second.xSum;
232      this->ySum += it->second.ySum;
233      this->zSum += z*it->second.getSize();
234      this->numVox += it->second.getSize();
235      if(obj.xmin<this->xmin) this->xmin = obj.xmin;
236      if(obj.xmax>this->xmax) this->xmax = obj.xmax;
237      if(obj.ymin<this->ymin) this->ymin = obj.ymin;
238      if(obj.ymax>this->ymax) this->ymax = obj.ymax;
239    }
240
241    this->spatialMap = this->spatialMap + obj;
242
243  }
244
245  //--------------------------------------------
246 
247  void Object3D::calcParams()
248  {
249    this->xSum = 0;
250    this->ySum = 0;
251    this->zSum = 0;
252    this->numVox = 0;
253
254    this->zmin = this->chanlist.begin()->first;
255    this->zmax = this->chanlist.rbegin()->first;
256    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
257        it!=this->chanlist.end();it++){
258
259      it->second.calcParams();
260
261      if(it==this->chanlist.begin()){
262        this->xmin = it->second.getXmin();
263        this->xmax = it->second.getXmax();
264        this->ymin = it->second.getYmin();
265        this->ymax = it->second.getYmax();
266      }
267      else{
268        if(it->second.xmin<this->xmin) this->xmin = it->second.xmin;
269        if(it->second.xmax>this->xmax) this->xmax = it->second.xmax;
270        if(it->second.ymin<this->ymin) this->ymin = it->second.ymin;
271        if(it->second.ymax>this->ymax) this->ymax = it->second.ymax;
272      }
273
274      this->xSum += it->second.xSum;
275      this->ySum += it->second.ySum;
276      this->zSum += it->first * it->second.getSize();
277      this->numVox += it->second.getSize();
278
279    }
280
281  }
282  //------------------------------------------------------
283
284  void Object3D::write(std::string filename)
285  {
286    std::ofstream outfile(filename.c_str(), std::ios::out | std::ios::binary | std::ios::app);
287    size_t size=this->chanlist.size();
288    outfile.write(reinterpret_cast<const char*>(&size), sizeof size);
289    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
290        it!=this->chanlist.end();it++){
291     
292      outfile.write(reinterpret_cast<const char*>(&(it->first)), sizeof it->first);
293      size=it->second.scanlist.size();
294      outfile.write(reinterpret_cast<const char*>(&size), sizeof size);
295      for(std::vector<Scan>::iterator s=it->second.scanlist.begin();s!=it->second.scanlist.end();s++){
296        outfile.write(reinterpret_cast<const char*>(&(s->itsX)),sizeof s->itsX);
297        outfile.write(reinterpret_cast<const char*>(&(s->itsXLen)),sizeof s->itsXLen);
298        outfile.write(reinterpret_cast<const char*>(&(s->itsY)),sizeof s->itsY);
299      }
300
301    }
302    outfile.close();
303     
304  }
305
306  std::streampos Object3D::read(std::string filename, std::streampos loc)
307  {
308    std::ifstream infile(filename.c_str(), std::ios::in | std::ios::binary);
309    infile.seekg(loc);
310    size_t mapsize,scanlistsize;
311    long z,x,xl,y;
312    infile.read(reinterpret_cast<char*>(&mapsize), sizeof mapsize);
313    for(size_t i=0;i<mapsize;i++){
314      infile.read(reinterpret_cast<char*>(&z), sizeof z);
315      Object2D obj;
316      infile.read(reinterpret_cast<char*>(&scanlistsize), sizeof scanlistsize);
317      for(size_t j=0;j<scanlistsize;j++){
318        infile.read(reinterpret_cast<char*>(&x),sizeof x);
319        infile.read(reinterpret_cast<char*>(&xl),sizeof xl);
320        infile.read(reinterpret_cast<char*>(&y),sizeof y);
321        Scan scn(y,x,xl);
322        obj.addScan(scn);
323      }
324      this->addChannel(z,obj);
325    }
326    std::streampos newloc = infile.tellg();
327    infile.close();
328    return newloc;
329  }
330
331
332  void Object3D::print(std::ostream& theStream)
333  {
334    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
335        it!=this->chanlist.end();it++){
336      for(std::vector<Scan>::iterator s=it->second.scanlist.begin();s!=it->second.scanlist.end();s++){
337        theStream << *s << "," << it->first << "\n";
338      }
339    } 
340    theStream << "\n";
341  }
342
343
344  std::ostream& operator<< ( std::ostream& theStream, Object3D& obj)
345  {
346    obj.print(theStream);
347    return theStream;
348  }
349  //--------------------------------------------
350
351  std::vector<Voxel> Object3D::getPixelSet()
352  {
353    /// @details Returns a vector of the Voxels in the object. All
354    /// flux values are set to 0.
355
356    std::vector<Voxel> voxList(this->numVox);
357    long count = 0;
358    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
359        it!=this->chanlist.end();it++){
360      long z = it->first;
361      for(std::vector<Scan>::iterator s=it->second.scanlist.begin();s!=it->second.scanlist.end();s++){
362        long y = s->getY();
363        for(long x=s->getX(); x<=s->getXmax(); x++){
364          voxList[count].setXYZF(x,y,z,0);
365          count++;
366        }
367      }
368    }
369    return voxList;
370
371  }
372
373  //--------------------------------------------------------------------
374
375  std::vector<Voxel> Object3D::getPixelSet(float *array, size_t *dim)
376  {
377    /// @details Returns a vector of Voxels with the flux values for each voxel
378    /// taken from the array provided. No check is made as to whether
379    /// the pixels fall in the array boundaries
380    /// @param array Array of pixel values
381    /// @param dim Array of axis dimensions
382
383    std::vector<Voxel> voxList(this->numVox);
384    long count = 0;
385    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
386        it!=this->chanlist.end();it++){
387      long z = it->first;
388      for(std::vector<Scan>::iterator s=it->second.scanlist.begin();s!=it->second.scanlist.end();s++){
389        long y = s->getY();
390        for(long x=s->getX(); x<=s->getXmax(); x++){
391          voxList[count].setXYZF(x,y,z,array[x+dim[0]*y+dim[0]*dim[1]*z]);
392          count++;
393        }
394      }
395    }
396    return voxList;
397
398  }
399
400  //--------------------------------------------------------------------
401
402  std::vector<long> Object3D::getChannelList()
403  {
404
405    std::vector<long> chanlist;
406    for(std::map<long, Object2D>::iterator it = this->chanlist.begin();
407        it != this->chanlist.end(); it++){
408      chanlist.push_back(it->first);
409    }
410    return chanlist;
411  }
412
413  //--------------------------------------------------------------------
414
415  Object2D Object3D::getChanMap(long z)
416  {
417    Object2D obj;
418    std::map<long,Object2D>::iterator it=this->chanlist.begin();
419    while(it!=this->chanlist.end() && it->first!=z) it++;
420
421    if(it==this->chanlist.end()) obj = Object2D();
422    else obj = it->second;
423
424    return obj;
425  }
426
427  //--------------------------------------------------------------------
428
429  unsigned int Object3D::getMaxAdjacentChannels()
430  {
431    /// @details Find the maximum number of contiguous channels in the
432    /// object. Since there can be gaps in the channels included in an
433    /// object, we run through the list of channels and keep track of
434    /// sizes of contiguous segments. Then return the largest size.
435
436    unsigned int maxnumchan=0;
437    unsigned int zcurrent=0, zprevious,zcount=0;
438    std::map<long, Object2D>::iterator it;
439    for(it = this->chanlist.begin(); it!=this->chanlist.end();it++)
440      {
441        if(it==this->chanlist.begin()){
442          zcount++;
443          zcurrent = it->first;
444        }
445        else{
446          zprevious = zcurrent;
447          zcurrent = it->first;
448          if(zcurrent-zprevious>1){
449            maxnumchan = std::max(maxnumchan, zcount);
450            zcount=1;
451          }
452          else zcount++;
453        }
454      }
455    maxnumchan = std::max(maxnumchan,zcount);
456    return maxnumchan;
457  }
458
459  //--------------------------------------------------------------------
460
461  void Object3D::addOffsets(long xoff, long yoff, long zoff)
462  {
463    std::map<long,Object2D> newmap;
464    for(std::map<long, Object2D>::iterator it = this->chanlist.begin(); it!=this->chanlist.end();it++){
465      std::pair<long,Object2D> newOne(it->first+zoff, it->second);
466      newOne.second.addOffsets(xoff,yoff);
467      newmap.insert(newOne);
468    }
469    this->chanlist.clear();
470    this->chanlist = newmap;
471    if(this->numVox>0){
472      this->xSum += xoff*numVox;
473      this->xmin += xoff; this->xmax += xoff;
474      this->ySum += yoff*numVox;
475      this->ymin += yoff; this->ymax += yoff;
476      this->zSum += zoff*numVox;
477      this->zmin += zoff; this->zmax += zoff;
478    }
479  }
480
481
482  duchamp::Section Object3D::getBoundingSection(int boundary)
483  {
484    std::stringstream ss;
485    ss << "[" << this->xmin-boundary <<":"<<this->xmax+boundary<<","<< this->ymin-boundary <<":"<<this->ymax+boundary<<","<< this->zmin-boundary <<":"<<this->zmax+boundary<<"]";
486    std::string sec=ss.str();
487    duchamp::Section section(sec);
488    return section;
489  }
490
491  std::vector<std::vector<Voxel> > Object3D::getVertexSet()
492  {
493    ///  @details
494    /// Gets a list of points being the end-points of 1-pixel long
495    /// segments drawing a border around the spatial extend of a
496    /// detection. The vector is a series of 4 integers, being: x_0,
497    /// y_0, x_1, y_1.
498    /// \return The vector of vertex positions.
499
500    std::vector<int> vertexSet;
501
502    int xmin = this->getXmin() - 1;
503    int xmax = this->getXmax() + 1;
504    int ymin = this->getYmin() - 1;
505    int ymax = this->getYmax() + 1;
506    int xsize = xmax - xmin + 1;
507    int ysize = ymax - ymin + 1;
508
509    std::vector<Voxel> voxlist = this->getPixelSet();
510    std::vector<bool> isObj(xsize*ysize,false);
511    std::vector<Voxel>::iterator vox;
512    for(vox=voxlist.begin();vox<voxlist.end();vox++){
513      size_t pos = (vox->getX()-xmin) +
514        (vox->getY()-ymin)*xsize;
515      isObj[pos] = true;
516    }
517    voxlist.clear();
518
519    std::vector<Line> linelist;   
520    for(int x=xmin; x<=xmax; x++){
521      // for each column...
522      for(int y=ymin+1;y<=ymax;y++){
523        int current  = (y-ymin)*xsize + x-xmin;
524        int previous = (y-ymin-1)*xsize + x-xmin;
525        if((isObj[current]&&!isObj[previous])   ||
526           (!isObj[current]&&isObj[previous])){
527          vertexSet.push_back(x);
528          vertexSet.push_back(y);
529          vertexSet.push_back(x+1);
530          vertexSet.push_back(y);
531          linelist.push_back(Line(x,y,x+1,y));
532        }
533      }
534    }
535    for(int y=ymin; y<=ymax; y++){
536      // now for each row...
537      for(int x=xmin+1;x<=xmax;x++){
538        int current  = (y-ymin)*xsize + x-xmin;
539        int previous = (y-ymin)*xsize + x-xmin - 1;
540        if((isObj[current]&&!isObj[previous])   ||
541           (!isObj[current]&&isObj[previous])){
542          vertexSet.push_back(x);
543          vertexSet.push_back(y);
544          vertexSet.push_back(x);
545          vertexSet.push_back(y+1);
546          linelist.push_back(Line(x,y,x,y+1));
547        }
548      }
549    }
550
551    std::vector<std::vector<Voxel> > vertSet;
552    while(linelist.size()>0){
553        std::vector<Voxel> orderedVertices;
554        std::vector<Line>::iterator line=linelist.begin();
555        orderedVertices.push_back(line->start());
556        Voxel comp=line->end(),prev=line->start();
557        orderedVertices.push_back(comp);
558        linelist.erase(line);
559        bool atEnd=false;
560        do{
561            bool foundIt=false;
562            line=linelist.begin();
563            while(line!=linelist.end() && !foundIt){
564                foundIt = line->has(comp);
565                atEnd = line->has(orderedVertices[0]);
566                if(comp == line->start()){
567                    orderedVertices.push_back(line->end());
568                    comp=line->end();
569                }
570                else if (comp==line->end()){
571                    orderedVertices.push_back(line->start());
572                    comp=line->start();
573                }
574                if(foundIt) linelist.erase(line);
575                else line++;
576            }
577        }  while(linelist.size()>0 && !atEnd);
578        vertSet.push_back(orderedVertices);
579    }
580
581    return vertSet;
582 
583  }
584
585
586
587
588}
Note: See TracBrowser for help on using the repository browser.