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

Last change on this file since 1213 was 1194, checked in by MatthewWhiting, 11 years ago

Fixing the kernel scaling in the Gaussian smoother, and some tidying up of the Cube smoothing functions.

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