source: trunk/src/Detection/detection.cc @ 458

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

Move getVertexSet out of drawMomentCutout.cc, otherwise it isn't visible when pgplot is not being used.

File size: 21.2 KB
Line 
1// -----------------------------------------------------------------------
2// detection.cc : Member functions for the Detection 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 <iomanip>
30#include <vector>
31#include <string>
32#include <wcslib/wcs.h>
33#include <math.h>
34#include <duchamp/duchamp.hh>
35#include <duchamp/param.hh>
36#include <duchamp/fitsHeader.hh>
37#include <duchamp/Utils/utils.hh>
38#include <duchamp/PixelMap/Voxel.hh>
39#include <duchamp/PixelMap/Object3D.hh>
40#include <duchamp/Detection/detection.hh>
41
42using std::setw;
43using std::setprecision;
44using std::endl;
45using std::vector;
46
47using namespace PixelInfo;
48
49namespace duchamp
50{
51
52
53  Detection::Detection()
54  {
55    this->flagWCS=false;
56    this->negSource = false;
57    this->flagText="";
58    this->totalFlux = peakFlux = 0.;
59    this->centreType="centroid";
60  }
61
62  Detection::Detection(const Detection& d)
63  {
64    operator=(d);
65  }
66
67  Detection& Detection::operator= (const Detection& d)
68  {
69    if(this == &d) return *this;
70    this->pixelArray   = d.pixelArray;
71    this->xSubOffset   = d.xSubOffset;
72    this->ySubOffset   = d.ySubOffset;
73    this->zSubOffset   = d.zSubOffset;
74    this->totalFlux    = d.totalFlux;
75    this->intFlux            = d.intFlux;
76    this->peakFlux     = d.peakFlux;
77    this->xpeak        = d.xpeak;
78    this->ypeak        = d.ypeak;
79    this->zpeak        = d.zpeak;
80    this->peakSNR      = d.peakSNR;
81    this->xCentroid    = d.xCentroid;
82    this->yCentroid    = d.yCentroid;
83    this->zCentroid    = d.zCentroid;
84    this->centreType   = d.centreType;
85    this->negSource    = d.negSource;
86    this->flagText     = d.flagText;
87    this->id           = d.id;
88    this->name         = d.name;
89    this->flagWCS      = d.flagWCS;
90    this->specOK       = d.specOK;
91    this->raS          = d.raS;
92    this->decS         = d.decS;
93    this->ra           = d.ra;
94    this->dec        = d.dec;
95    this->raWidth            = d.raWidth;
96    this->decWidth     = d.decWidth;
97    this->specUnits    = d.specUnits;
98    this->fluxUnits    = d.fluxUnits;
99    this->intFluxUnits = d.intFluxUnits;
100    this->lngtype            = d.lngtype;
101    this->lattype            = d.lattype;
102    this->vel          = d.vel;
103    this->velWidth     = d.velWidth;
104    this->velMin       = d.velMin;
105    this->velMax       = d.velMax;
106    this->posPrec      = d.posPrec;
107    this->xyzPrec      = d.xyzPrec;
108    this->fintPrec     = d.fintPrec;
109    this->fpeakPrec    = d.fpeakPrec;
110    this->velPrec            = d.velPrec;
111    this->snrPrec      = d.snrPrec;
112    return *this;
113  }
114
115  //--------------------------------------------------------------------
116
117  bool Detection::voxelListsMatch(std::vector<Voxel> voxelList)
118  {
119    /**
120     *  A test to see whether there is a 1-1 correspondence between
121     *  the given list of Voxels and the voxel positions contained in
122     *  this Detection's pixel list. No testing of the fluxes of the
123     *  Voxels is done.
124     *
125     * \param voxelList The std::vector list of Voxels to be tested.
126     */
127
128    bool listsMatch = true;
129    // compare sizes
130    listsMatch = listsMatch && (voxelList.size() == this->getSize());
131    if(!listsMatch) return listsMatch;
132
133    // make sure all voxels are in Detection
134    for(int i=0;i<voxelList.size();i++)
135      listsMatch = listsMatch && this->pixelArray.isInObject(voxelList[i]);
136    // make sure all Detection pixels are in voxel list
137    int v1=0, mysize=this->getSize();
138    while(listsMatch && v1<mysize){
139      bool inList = false;
140      int v2=0;
141      Voxel test = this->getPixel(v1);
142      while(!inList && v2<voxelList.size()){
143        inList = inList || test.match(voxelList[v2]);
144        v2++;
145      }
146      listsMatch = listsMatch && inList;
147      v1++;
148    }
149
150    return listsMatch;
151
152  }
153
154  //--------------------------------------------------------------------
155
156  void Detection::calcFluxes(std::vector<Voxel> voxelList)
157  {
158    /**
159     *  A function that calculates total & peak fluxes (and the location
160     *  of the peak flux) for a Detection.
161     *
162     *  \param fluxArray The array of flux values to calculate the
163     *  flux parameters from.
164     *  \param dim The dimensions of the flux array.
165     */
166
167    this->totalFlux = this->peakFlux = 0;
168    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
169
170    // first check that the voxel list and the Detection's pixel list
171    // have a 1-1 correspondence
172
173    if(!this->voxelListsMatch(voxelList)){
174      duchampError("Detection::calcFluxes","Voxel list provided does not match");
175      return;
176    }
177
178    for(int i=0;i<voxelList.size();i++) {
179      long x = voxelList[i].getX();
180      long y = voxelList[i].getY();
181      long z = voxelList[i].getZ();
182      float f = voxelList[i].getF();
183      this->totalFlux += f;
184      this->xCentroid += x*f;
185      this->yCentroid += y*f;
186      this->zCentroid += z*f;
187      if( (i==0) ||  //first time round
188          (this->negSource&&(f<this->peakFlux)) ||
189          (!this->negSource&&(f>this->peakFlux))   )
190        {
191          this->peakFlux = f;
192          this->xpeak =    x;
193          this->ypeak =    y;
194          this->zpeak =    z;
195        }
196    }
197
198    this->xCentroid /= this->totalFlux;
199    this->yCentroid /= this->totalFlux;
200    this->zCentroid /= this->totalFlux;
201  }
202  //--------------------------------------------------------------------
203
204  void Detection::calcFluxes(float *fluxArray, long *dim)
205  {
206    /**
207     *  A function that calculates total & peak fluxes (and the location
208     *  of the peak flux) for a Detection.
209     *
210     *  \param fluxArray The array of flux values to calculate the
211     *  flux parameters from.
212     *  \param dim The dimensions of the flux array.
213     */
214
215    this->totalFlux = this->peakFlux = 0;
216    this->xCentroid = this->yCentroid = this->zCentroid = 0.;
217    long y,z,count=0;
218
219    for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
220      ChanMap *tempmap = new ChanMap;
221      *tempmap = this->pixelArray.getChanMap(m);
222      z = tempmap->getZ();
223      for(int s=0; s<tempmap->getNumScan(); s++){
224        Scan *tempscan = new Scan;
225        *tempscan = tempmap->getScan(s);
226        y = tempscan->getY();
227        for(long x=tempscan->getX(); x<=tempscan->getXmax(); x++){
228
229          float f = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
230          this->totalFlux += f;
231          this->xCentroid += x*f;
232          this->yCentroid += y*f;
233          this->zCentroid += z*f;
234          if( (count==0) ||  //first time round
235              (this->negSource&&(f<this->peakFlux)) ||
236              (!this->negSource&&(f>this->peakFlux))   )
237            {
238              this->peakFlux = f;
239              this->xpeak =    x;
240              this->ypeak =    y;
241              this->zpeak =    z;
242            }
243          count++;
244        }
245        delete tempscan;
246      }
247      delete tempmap;
248    }
249
250    this->xCentroid /= this->totalFlux;
251    this->yCentroid /= this->totalFlux;
252    this->zCentroid /= this->totalFlux;
253  }
254  //--------------------------------------------------------------------
255
256  //  void Detection::calcWCSparams(float *fluxArray, long *dim, FitsHeader &head)
257  void Detection::calcWCSparams(FitsHeader &head)
258  {
259    /**
260     *  Use the input wcs to calculate the position and velocity
261     *    information for the Detection.
262     *  Quantities calculated:
263     *  <ul><li> RA: ra [deg], ra (string), ra width.
264     *      <li> Dec: dec [deg], dec (string), dec width.
265     *      <li> Vel: vel [km/s], min & max vel, vel width.
266     *      <li> coord type for all three axes, nuRest,
267     *      <li> name (IAU-style, in equatorial or Galactic)
268     *  </ul>
269     *
270     *  Note that the regular parameters are NOT recalculated!
271     *
272     *  \param head FitsHeader object that contains the WCS information.
273     */
274
275    if(head.isWCS()){
276
277      double *pixcrd = new double[15];
278      double *world  = new double[15];
279      /*
280        define a five-point array in 3D:
281        (x,y,z), (x,y,z1), (x,y,z2), (x1,y1,z), (x2,y2,z)
282        [note: x = central point, x1 = minimum x, x2 = maximum x etc.]
283        and convert to world coordinates.   
284      */
285      pixcrd[0]  = pixcrd[3] = pixcrd[6] = this->getXcentre();
286      pixcrd[9]  = this->getXmin()-0.5;
287      pixcrd[12] = this->getXmax()+0.5;
288      pixcrd[1]  = pixcrd[4] = pixcrd[7] = this->getYcentre();
289      pixcrd[10] = this->getYmin()-0.5;
290      pixcrd[13] = this->getYmax()+0.5;
291      pixcrd[2] = pixcrd[11] = pixcrd[14] = this->getZcentre();
292      pixcrd[5] = this->getZmin();
293      pixcrd[8] = this->getZmax();
294      int flag = head.pixToWCS(pixcrd, world, 5);
295      delete [] pixcrd;
296      if(flag!=0) duchampError("calcWCSparams",
297                               "Error in calculating the WCS for this object.\n");
298      else{
299
300        // world now has the WCS coords for the five points
301        //    -- use this to work out WCS params
302 
303        this->specOK = head.canUseThirdAxis();
304        this->lngtype = head.WCS().lngtyp;
305        this->lattype = head.WCS().lattyp;
306        this->specUnits = head.getSpectralUnits();
307        this->fluxUnits = head.getFluxUnits();
308        // if fluxUnits are eg. Jy/beam, make intFluxUnits = Jy km/s
309        this->intFluxUnits = head.getIntFluxUnits();
310        this->ra   = world[0];
311        this->dec  = world[1];
312        this->raS  = decToDMS(this->ra, this->lngtype);
313        this->decS = decToDMS(this->dec,this->lattype);
314        this->raWidth = angularSeparation(world[9],world[1],
315                                          world[12],world[1]) * 60.;
316        this->decWidth  = angularSeparation(world[0],world[10],
317                                            world[0],world[13]) * 60.;
318        this->name = head.getIAUName(this->ra, this->dec);
319        this->vel    = head.specToVel(world[2]);
320        this->velMin = head.specToVel(world[5]);
321        this->velMax = head.specToVel(world[8]);
322        this->velWidth = fabs(this->velMax - this->velMin);
323
324        //      this->calcIntegFlux(fluxArray,dim,head);
325   
326        this->flagWCS = true;
327      }
328      delete [] world;
329
330    }
331  }
332  //--------------------------------------------------------------------
333
334  void Detection::calcIntegFlux(std::vector<Voxel> voxelList, FitsHeader &head)
335  {
336    /**
337     *  Uses the input WCS to calculate the velocity-integrated flux,
338     *   putting velocity in units of km/s.
339     *  The fluxes used are taken from the Voxels, rather than an
340     *   array of flux values.
341     *  Integrates over full spatial and velocity range as given
342     *   by the extrema calculated by calcWCSparams.
343     *
344     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
345     *  corrected by the beam size (in pixels). This is done by
346     *  multiplying the integrated flux by the number of spatial pixels,
347     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
348     *  pix/beam --> Jy)
349     *
350     *  \param voxelList The list of Voxels with flux information
351     *  \param head FitsHeader object that contains the WCS information.
352     */
353
354    if(!voxelListsMatch(voxelList)){
355      duchampError("Detection::calcIntegFlux","Voxel list provided does not match");
356      return;
357    }
358
359    if(head.getNumAxes() > 2) {
360
361      // include one pixel either side in each direction
362      long xsize = (this->getXmax()-this->getXmin()+3);
363      long ysize = (this->getYmax()-this->getYmin()+3);
364      long zsize = (this->getZmax()-this->getZmin()+3);
365      vector <bool> isObj(xsize*ysize*zsize,false);
366      double *localFlux = new double[xsize*ysize*zsize];
367      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
368
369      for(int i=0;i<voxelList.size();i++){
370        long x = voxelList[i].getX();
371        long y = voxelList[i].getY();
372        long z = voxelList[i].getZ();
373        long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
374          + (z-this->getZmin()+1)*xsize*ysize;
375        localFlux[pos] = voxelList[i].getF();
376        isObj[pos] = true;
377      }
378 
379      // work out the WCS coords for each pixel
380      double *world  = new double[xsize*ysize*zsize];
381      double xpt,ypt,zpt;
382      for(int i=0;i<xsize*ysize*zsize;i++){
383        xpt = double( this->getXmin() -1 + i%xsize );
384        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
385        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
386        world[i] = head.pixToVel(xpt,ypt,zpt);
387      }
388
389      double integrated = 0.;
390      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
391        for(int z=0; z<zsize; z++){
392          int pos =  z*xsize*ysize + pix;
393          if(isObj[pos]){ // if it's an object pixel...
394            double deltaVel;
395            if(z==0)
396              deltaVel = (world[pos+xsize*ysize] - world[pos]);
397            else if(z==(zsize-1))
398              deltaVel = (world[pos] - world[pos-xsize*ysize]);
399            else
400              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
401            integrated += localFlux[pos] * fabs(deltaVel);
402          }
403        }
404      }
405      this->intFlux = integrated;
406
407      delete [] world;
408      delete [] localFlux;
409
410    }
411    else // in this case there is just a 2D image.
412      this->intFlux = this->totalFlux;
413
414    if(head.isWCS()){
415      // correct for the beam size if the flux units string ends in "/beam"
416      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
417    }
418
419  }
420  //--------------------------------------------------------------------
421
422  void Detection::calcIntegFlux(float *fluxArray, long *dim, FitsHeader &head)
423  {
424    /**
425     *  Uses the input WCS to calculate the velocity-integrated flux,
426     *   putting velocity in units of km/s.
427     *  Integrates over full spatial and velocity range as given
428     *   by the extrema calculated by calcWCSparams.
429     *
430     *  If the flux units end in "/beam" (eg. Jy/beam), then the flux is
431     *  corrected by the beam size (in pixels). This is done by
432     *  multiplying the integrated flux by the number of spatial pixels,
433     *  and dividing by the beam size in pixels (e.g. Jy/beam * pix /
434     *  pix/beam --> Jy)
435     *
436     *  \param fluxArray The array of flux values.
437     *  \param dim The dimensions of the flux array.
438     *  \param head FitsHeader object that contains the WCS information.
439     */
440
441    if(head.getNumAxes() > 2) {
442
443      // include one pixel either side in each direction
444      long xsize = (this->getXmax()-this->getXmin()+3);
445      long ysize = (this->getYmax()-this->getYmin()+3);
446      long zsize = (this->getZmax()-this->getZmin()+3);
447      vector <bool> isObj(xsize*ysize*zsize,false);
448      double *localFlux = new double[xsize*ysize*zsize];
449      for(int i=0;i<xsize*ysize*zsize;i++) localFlux[i]=0.;
450      // work out which pixels are object pixels
451      for(int m=0; m<this->pixelArray.getNumChanMap(); m++){
452        ChanMap tempmap = this->pixelArray.getChanMap(m);
453        long z = this->pixelArray.getChanMap(m).getZ();
454        for(int s=0; s<this->pixelArray.getChanMap(m).getNumScan(); s++){
455          long y = this->pixelArray.getChanMap(m).getScan(s).getY();
456          for(long x=this->pixelArray.getChanMap(m).getScan(s).getX();
457              x<=this->pixelArray.getChanMap(m).getScan(s).getXmax();
458              x++){
459            long pos = (x-this->getXmin()+1) + (y-this->getYmin()+1)*xsize
460              + (z-this->getZmin()+1)*xsize*ysize;
461            localFlux[pos] = fluxArray[x + y*dim[0] + z*dim[0]*dim[1]];
462            isObj[pos] = true;
463          }
464        }
465      }
466 
467      // work out the WCS coords for each pixel
468      double *world  = new double[xsize*ysize*zsize];
469      double xpt,ypt,zpt;
470      for(int i=0;i<xsize*ysize*zsize;i++){
471        xpt = double( this->getXmin() -1 + i%xsize );
472        ypt = double( this->getYmin() -1 + (i/xsize)%ysize );
473        zpt = double( this->getZmin() -1 + i/(xsize*ysize) );
474        world[i] = head.pixToVel(xpt,ypt,zpt);
475      }
476
477      double integrated = 0.;
478      for(int pix=0; pix<xsize*ysize; pix++){ // loop over each spatial pixel.
479        for(int z=0; z<zsize; z++){
480          int pos =  z*xsize*ysize + pix;
481          if(isObj[pos]){ // if it's an object pixel...
482            double deltaVel;
483            if(z==0)
484              deltaVel = (world[pos+xsize*ysize] - world[pos]);
485            else if(z==(zsize-1))
486              deltaVel = (world[pos] - world[pos-xsize*ysize]);
487            else
488              deltaVel = (world[pos+xsize*ysize] - world[pos-xsize*ysize]) / 2.;
489            integrated += localFlux[pos] * fabs(deltaVel);
490          }
491        }
492      }
493      this->intFlux = integrated;
494
495      delete [] world;
496      delete [] localFlux;
497
498    }
499    else // in this case there is just a 2D image.
500      this->intFlux = this->totalFlux;
501
502    if(head.isWCS()){
503      // correct for the beam size if the flux units string ends in "/beam"
504      if(head.needBeamSize()) this->intFlux  /= head.getBeamSize();
505    }
506
507  }
508  //--------------------------------------------------------------------
509
510  Detection operator+ (Detection lhs, Detection rhs)
511  {
512    /**
513     *  Combines two objects by adding all the pixels using the Object3D
514     *  operator.
515     *
516     *  The pixel parameters are recalculated in the process (equivalent
517     *  to calling pixels().calcParams()), but WCS parameters are not.
518     */
519    Detection output = lhs;
520    output.pixelArray = lhs.pixelArray + rhs.pixelArray;
521    return output;
522  }
523  //--------------------------------------------------------------------
524
525  void Detection::setOffsets(Param &par)
526  {
527    /**
528     * This function stores the values of the offsets for each cube axis.
529     * The offsets are the starting values of the cube axes that may differ from
530     *  the default value of 0 (for instance, if a subsection is being used).
531     * The values will be used when the detection is outputted.
532     */
533    this->xSubOffset = par.getXOffset();
534    this->ySubOffset = par.getYOffset();
535    this->zSubOffset = par.getZOffset();
536  }
537  //--------------------------------------------------------------------
538
539  bool Detection::hasEnoughChannels(int minNumber)
540  {
541    /**
542     * A function to determine if the Detection has enough
543     * contiguous channels to meet the minimum requirement
544     * given as the argument.
545     * \param minNumber How many channels is the minimum acceptable number?
546     * \return True if there is at least one occurence of minNumber consecutive
547     * channels present to return true. False otherwise.
548     */
549
550    // Preferred method -- need a set of minNumber consecutive channels present.
551    this->pixelArray.order();
552    int numChannels = 0;
553    bool result = false;
554    int size = this->pixelArray.getNumChanMap();
555    if(size>0) numChannels++;
556    if( numChannels >= minNumber) result = true;
557    for(int i=1;(i<size && !result);i++) {
558      if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) == 1)
559        numChannels++;
560      else if( (this->pixelArray.getZ(i) - this->pixelArray.getZ(i-1)) >= 2)
561        numChannels = 1;
562
563      if( numChannels >= minNumber) result = true;
564    }
565    return result;
566 
567  }
568  //--------------------------------------------------------------------
569
570  std::vector<int> Detection::getVertexSet()
571  {
572    /**
573     * Gets a list of points being the end-points of 1-pixel long
574     * segments drawing a border around the spatial extend of a
575     * detection. The vector is a series of 4 integers, being: x_0,
576     * y_0, x_1, y_1.
577     * \return The vector of vertex positions.
578     */
579    std::vector<int> vertexSet;
580
581    int xmin = this->getXmin() - 1;
582    int xmax = this->getXmax() + 1;
583    int ymin = this->getYmin() - 1;
584    int ymax = this->getYmax() + 1;
585    int xsize = xmax - xmin + 1;
586    int ysize = ymax - ymin + 1;
587
588    std::vector<Voxel> voxlist = this->pixelArray.getPixelSet();
589    std::vector<bool> isObj(xsize*ysize,false);
590    for(int i=0;i<voxlist.size();i++){
591      int pos = (voxlist[i].getX()-xmin) +
592        (voxlist[i].getY()-ymin)*xsize;
593      isObj[pos] = true;
594    }
595    voxlist.clear();
596   
597    for(int x=xmin; x<=xmax; x++){
598      // for each column...
599      for(int y=ymin+1;y<=ymax;y++){
600        int current  = (y-ymin)*xsize + x-xmin;
601        int previous = (y-ymin-1)*xsize + x-xmin;
602        if((isObj[current]&&!isObj[previous])   ||
603           (!isObj[current]&&isObj[previous])){
604          vertexSet.push_back(x);
605          vertexSet.push_back(y);
606          vertexSet.push_back(x+1);
607          vertexSet.push_back(y);
608        }
609      }
610    }
611    for(int y=ymin; y<=ymax; y++){
612      // now for each row...
613      for(int x=xmin+1;x<=xmax;x++){
614        int current  = (y-ymin)*xsize + x-xmin;
615        int previous = (y-ymin)*xsize + x-xmin - 1;
616        if((isObj[current]&&!isObj[previous])   ||
617           (!isObj[current]&&isObj[previous])){
618          vertexSet.push_back(x);
619          vertexSet.push_back(y);
620          vertexSet.push_back(x);
621          vertexSet.push_back(y+1);
622        }
623      }
624    }
625
626    return vertexSet;
627 
628  }
629  //--------------------------------------------------------------------
630
631  std::ostream& operator<< ( std::ostream& theStream, Detection& obj)
632  {
633    /**
634     *  A convenient way of printing the coordinate values for each
635     *  pixel in the Detection. 
636     *
637     *  NOTE THAT THERE IS CURRENTLY NO FLUX INFORMATION BEING PRINTED!
638     *
639     *  Use as front end to the Object3D::operator<< function.
640     */ 
641
642    theStream << obj.pixelArray << "---\n";
643    return theStream;
644  }
645  //--------------------------------------------------------------------
646
647}
Note: See TracBrowser for help on using the repository browser.