source: trunk/src/Utils/GaussSmooth2D.tcc @ 913

Last change on this file since 913 was 913, checked in by MatthewWhiting, 12 years ago

A large swathe of changes aimed at improving warning/error/exception handling. Now make use of macros and streams. Also, there is now a distinction between DUCHAMPERROR and DUCHAMPTHROW.

File size: 8.0 KB
RevLine 
[301]1// -----------------------------------------------------------------------
[684]2// GaussSmooth2D.tcc: Member functions for the GaussSmooth2D class.
[301]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// -----------------------------------------------------------------------
[275]28#include <iostream>
[683]29#include <sstream>
30#include <duchamp/duchamp.hh>
[636]31#include <duchamp/config.h>
[275]32#include <math.h>
[635]33#ifdef HAVE_VALUES_H
34#include <values.h>
35#endif
[636]36#ifdef MAXFLOAT
37#define MAXVAL MAXFLOAT
38#else
39#define MAXVAL 1.e38F
40#endif
[638]41#include <duchamp/Utils/GaussSmooth2D.hh>
[275]42
[913]43using namespace duchamp;
44
[516]45template <class Type>
[682]46void GaussSmooth2D<Type>::defaults()
47{
48  this->allocated=false;
49  this->blankVal = Type(-99);
50  this->kernWidth = -1;
51}
52
53template <class Type>
[638]54GaussSmooth2D<Type>::GaussSmooth2D()
[275]55{
[682]56  this->defaults();
[419]57}
58
[516]59template <class Type>
[638]60GaussSmooth2D<Type>::~GaussSmooth2D()
[275]61{
[682]62  if(this->allocated) delete [] kernel;
[419]63}
[275]64
[516]65template <class Type>
[638]66GaussSmooth2D<Type>::GaussSmooth2D(const GaussSmooth2D& g)
[365]67{
68  operator=(g);
69}
70
[516]71template <class Type>
[638]72GaussSmooth2D<Type>& GaussSmooth2D<Type>::operator=(const GaussSmooth2D& g)
[365]73{
74  if(this==&g) return *this;
75  this->kernMaj   = g.kernMaj;
76  this->kernMin   = g.kernMin;
77  this->kernPA    = g.kernPA;
78  this->kernWidth = g.kernWidth;
79  this->stddevScale = g.stddevScale;
[682]80  this->blankVal    = g.blankVal;
[526]81  if(this->allocated) delete [] this->kernel;
[365]82  this->allocated = g.allocated;
83  if(this->allocated){
[516]84    this->kernel = new Type[this->kernWidth*this->kernWidth];
[365]85    for(int i=0;i<this->kernWidth*this->kernWidth;i++)
86      this->kernel[i] = g.kernel[i];
87  }
88  return *this;
89}
90
[516]91template <class Type>
[638]92GaussSmooth2D<Type>::GaussSmooth2D(float maj, float min, float pa)
[275]93{
[682]94  this->defaults();
[275]95  this->define(maj, min, pa);
[419]96}
[275]97
[516]98template <class Type>
[638]99GaussSmooth2D<Type>::GaussSmooth2D(float maj)
[275]100{
[682]101  this->defaults();
[275]102  this->define(maj, maj, 0);
[419]103}
[275]104
[516]105template <class Type>
[638]106void GaussSmooth2D<Type>::define(float maj, float min, float pa)
[275]107{
108
109  this->kernMaj = maj;
110  this->kernMin = min;
111  this->kernPA  = pa;
112
113  // The parameters kernMaj & kernMin are the FWHM in the major and
114  // minor axis directions. We correct these to the sigma_x and
115  // sigma_y parameters for the 2D gaussian by halving and dividing by
116  // sqrt(2 ln(2)). Actually work with sigma_x^2 to make things
117  // easier.
118  float sigmaX2 = (this->kernMaj*this->kernMaj/4.) / (2.*M_LN2);
119  float sigmaY2 = (this->kernMin*this->kernMin/4.) / (2.*M_LN2);
120
[618]121  // First determine the size of the kernel.  Calculate the size based
122  // on the number of pixels needed to make the exponential drop to
123  // less than the minimum floating-point value. Use the major axis to
124  // get the largest square that includes the ellipse.
125  float majorSigma = this->kernMaj / (4.*M_LN2);
[636]126  int kernelHW = int(ceil(majorSigma * sqrt(-2.*log(1. / MAXVAL))));
[275]127
[682]128  if(this->kernWidth < 0) this->kernWidth = 2*kernelHW + 1;
129  else if(this->kernWidth < 2*kernelHW + 1){
[913]130    DUCHAMPWARN("GaussSmooth2D::define","You have provided a kernel smaller than optimal (" << this->kernWidth << " cf. " << 2*kernelHW + 1 <<")");
[682]131  }
132
[275]133  if(this->allocated) delete [] this->kernel;
[516]134  this->kernel = new Type[this->kernWidth*this->kernWidth];
[275]135  this->allocated = true;
136  this->stddevScale=0.;
137  float posang = this->kernPA * M_PI/180.;
138
139
140  for(int i=0;i<this->kernWidth;i++){
141    for(int j=0;j<this->kernWidth;j++){
142      float xpt = (i-kernelHW)*sin(posang) - (j-kernelHW)*cos(posang);
143
144      float ypt = (i-kernelHW)*cos(posang) + (j-kernelHW)*sin(posang);
145      float rsq = (xpt*xpt/sigmaX2) + (ypt*ypt/sigmaY2);
146      kernel[i*this->kernWidth+j] = exp( -0.5 * rsq);
147      this->stddevScale +=
148        kernel[i*this->kernWidth+j]*kernel[i*this->kernWidth+j];
149    }
150  }
151  this->stddevScale = sqrt(this->stddevScale);
152}
153
[516]154template <class Type>
[682]155Type *GaussSmooth2D<Type>::smooth(Type *input, int xdim, int ydim, EDGES edgeTreatment)
[275]156{
[528]157  /// @details
158  /// Smooth a given two-dimensional array, of dimensions xdim
159  /// \f$\times\f$ ydim, with an elliptical gaussian. Simply runs as a
[638]160  /// front end to GaussSmooth2D::smooth(float *, int, int, bool *) by
[528]161  /// defining a mask that allows all pixels in the input array.
162  ///
163  ///  \param input The 2D array to be smoothed.
164  ///  \param xdim  The size of the x-dimension of the array.
165  ///  \param ydim  The size of the y-dimension of the array.
166  ///  \return The smoothed array.
167
[516]168  Type *smoothed;
[275]169  bool *mask = new bool[xdim*ydim];
170  for(int i=0;i<xdim*ydim;i++) mask[i]=true;
[682]171  smoothed = this->smooth(input,xdim,ydim,mask,edgeTreatment);
[275]172  delete [] mask;
173  return smoothed;
174}
175
[516]176template <class Type>
[682]177Type *GaussSmooth2D<Type>::smooth(Type *input, int xdim, int ydim, bool *mask, EDGES edgeTreatment)
[275]178{
[528]179  /// @details
180  ///  Smooth a given two-dimensional array, of dimensions xdim
181  ///  \f$\times\f$ ydim, with an elliptical gaussian, where the
182  ///  boolean array mask defines which values of the array are valid.
183  ///
184  ///  This function convolves the input array with the kernel that
185  ///  needs to have been defined. If it has not, the input array is
186  ///  returned unchanged.
187  ///
188  ///  The mask should be the same size as the input array, and have
189  ///  values of true for entries that are considered valid, and false
190  ///  for entries that are not. For instance, arrays from FITS files
191  ///  should have the mask entries corresponding to BLANK pixels set
192  ///  to false.
193  ///
194  ///  \param input The 2D array to be smoothed.
195  ///  \param xdim  The size of the x-dimension of the array.
196  ///  \param ydim  The size of the y-dimension of the array.
197  ///  \param mask The array showing which pixels in the input array
198  ///              are valid.
199  ///  \return The smoothed array.
[275]200
201  if(!this->allocated) return input;
202  else{
203
[516]204    Type *output = new Type[xdim*ydim];
[275]205
206    int pos,comp,xcomp,ycomp,fpos,ct;
[516]207    float fsum,kernsum=0;
[275]208    int kernelHW = this->kernWidth/2;
209
[516]210    for(int i=0;i<this->kernWidth;i++)
211      for(int j=0;j<this->kernWidth;j++)
212        kernsum += this->kernel[i*this->kernWidth+j];
213
[275]214    for(int ypos = 0; ypos<ydim; ypos++){
215      for(int xpos = 0; xpos<xdim; xpos++){
216        pos = ypos*xdim + xpos;
217     
218        if(!mask[pos]) output[pos] = input[pos];
[682]219        else if(edgeTreatment==TRUNCATE &&
220                ((xpos<=kernelHW)||((xdim-xpos)<=kernelHW)||(ypos<=kernelHW)||((ydim-ypos)<kernelHW))){
221          output[pos] = this->blankVal;
222        }
[275]223        else{
224       
225          ct=0;
226          fsum=0.;
227          output[pos] = 0.;
228       
229          for(int yoff = -kernelHW; yoff<=kernelHW; yoff++){
230            ycomp = ypos + yoff;
231            if((ycomp>=0)&&(ycomp<ydim)){
232
233              for(int xoff = -kernelHW; xoff<=kernelHW; xoff++){
234                xcomp = xpos + xoff;         
235                if((xcomp>=0)&&(xcomp<xdim)){
236
237                  fpos = (xoff+kernelHW) + (yoff+kernelHW)*this->kernWidth;
238                  comp = ycomp*xdim + xcomp;
239                  if(mask[comp]){
240                    ct++;
241                    fsum += this->kernel[fpos];
242                    output[pos] += input[comp]*this->kernel[fpos];
243                  }
244
245                }
246              } // xoff loop
247
248            }
249          }// yoff loop
[682]250          //      if(ct>0 && scaleByCoverage) output[pos] /= fsum;
251          if(ct>0 && edgeTreatment==SCALEBYCOVERAGE) output[pos] *= kernsum/fsum;
[516]252 
[275]253        } // else{
254
255      } //xpos loop
256    }   //ypos loop
257   
258    return output;
259  }
260
261}
Note: See TracBrowser for help on using the repository browser.