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

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

Some major changes to the 2D smoothing. Fixing the normalisation of the kernel, so that the fluxes do the right thing in the smoothed array (ie. don't go up). Also moving to size_t types for a lot of the indexing variables. Finally, improving the memory management of the arrays in the function that calls the smoother.

File size: 8.7 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 = this->kernPA * M_PI/180.;
139
140  float normalisation = 2. * M_PI * sqrt(sigmaX2*sigmaY2);
141  float kernSum = 0.;
142  for(size_t i=0;i<this->kernWidth;i++){
143    for(size_t j=0;j<this->kernWidth;j++){
144      float xpt = int(i-kernelHW)*sin(posang) - int(j-kernelHW)*cos(posang);
145
146      float ypt = int(i-kernelHW)*cos(posang) + int(j-kernelHW)*sin(posang);
147      float rsq = (xpt*xpt/sigmaX2) + (ypt*ypt/sigmaY2);
148      this->kernel[i*this->kernWidth+j] = exp( -0.5 * rsq)/normalisation;
149      kernSum += this->kernel[i*this->kernWidth + j];
150      this->stddevScale +=
151        kernel[i*this->kernWidth+j]*kernel[i*this->kernWidth+j];
152    }
153  }
154 
155//  std::cerr << "Normalisation = " << normalisation << ", kernSum = " << kernSum << ", kernel size = " << kernWidth << ", kernelHW = " << kernelHW <<", majorSigma = " << majorSigma << ", sigmaX2="<< sigmaX2 <<", sigmaY2="<<sigmaY2<<"\n";
156
157  for(size_t i=0;i<this->kernWidth*this->kernWidth; i++) this->kernel[i] /= kernSum;
158  this->stddevScale = sqrt(this->stddevScale);
159}
160
161template <class Type>
162Type *GaussSmooth2D<Type>::smooth(Type *input, size_t xdim, size_t ydim, EDGES edgeTreatment)
163{
164  /// @details
165  /// Smooth a given two-dimensional array, of dimensions xdim
166  /// \f$\times\f$ ydim, with an elliptical gaussian. Simply runs as a
167  /// front end to GaussSmooth2D::smooth(float *, int, int, bool *) by
168  /// defining a mask that allows all pixels in the input array.
169  ///
170  ///  \param input The 2D array to be smoothed.
171  ///  \param xdim  The size of the x-dimension of the array.
172  ///  \param ydim  The size of the y-dimension of the array.
173  ///  \return The smoothed array.
174
175  Type *smoothed;
176  bool *mask = new bool[xdim*ydim];
177  for(size_t i=0;i<xdim*ydim;i++) mask[i]=true;
178  smoothed = this->smooth(input,xdim,ydim,mask,edgeTreatment);
179  delete [] mask;
180  return smoothed;
181}
182
183template <class Type>
184Type *GaussSmooth2D<Type>::smooth(Type *input, size_t xdim, size_t ydim, bool *mask, EDGES edgeTreatment)
185{
186  /// @details
187  ///  Smooth a given two-dimensional array, of dimensions xdim
188  ///  \f$\times\f$ ydim, with an elliptical gaussian, where the
189  ///  boolean array mask defines which values of the array are valid.
190  ///
191  ///  This function convolves the input array with the kernel that
192  ///  needs to have been defined. If it has not, the input array is
193  ///  returned unchanged.
194  ///
195  ///  The mask should be the same size as the input array, and have
196  ///  values of true for entries that are considered valid, and false
197  ///  for entries that are not. For instance, arrays from FITS files
198  ///  should have the mask entries corresponding to BLANK pixels set
199  ///  to false.
200  ///
201  ///  \param input The 2D array to be smoothed.
202  ///  \param xdim  The size of the x-dimension of the array.
203  ///  \param ydim  The size of the y-dimension of the array.
204  ///  \param mask The array showing which pixels in the input array
205  ///              are valid.
206  ///  \return The smoothed array.
207
208  if(!this->allocated) return input;
209  else{
210
211    Type *output = new Type[xdim*ydim];
212
213    size_t pos,comp,xcomp,ycomp,fpos;
214    int ct;
215    float fsum,kernsum=0;
216    unsigned int kernelHW = this->kernWidth/2;
217
218    for(size_t i=0;i<this->kernWidth;i++)
219      for(size_t j=0;j<this->kernWidth;j++)
220        kernsum += this->kernel[i*this->kernWidth+j];
221
222    for(size_t ypos = 0; ypos<ydim; ypos++){
223      for(size_t xpos = 0; xpos<xdim; xpos++){
224        pos = ypos*xdim + xpos;
225     
226        if(!mask[pos]) output[pos] = input[pos];
227        else if(edgeTreatment==TRUNCATE &&
228                ((xpos<=kernelHW)||((xdim-xpos)<=kernelHW)||(ypos<=kernelHW)||((ydim-ypos)<kernelHW))){
229          output[pos] = this->blankVal;
230        }
231        else{
232       
233          ct=0;
234          fsum=0.;
235          output[pos] = 0.;
236       
237          for(int yoff = -int(kernelHW); yoff<=int(kernelHW); yoff++){
238            ycomp = ypos + yoff;
239            if((ycomp>=0)&&(ycomp<ydim)){
240
241                for(int xoff = -int(kernelHW); xoff<=int(kernelHW); xoff++){
242                xcomp = xpos + xoff;         
243                if((xcomp>=0)&&(xcomp<xdim)){
244
245                  fpos = (xoff+kernelHW) + (yoff+kernelHW)*this->kernWidth;
246                  comp = ycomp*xdim + xcomp;
247                  if(mask[comp]){
248                    ct++;
249                    fsum += this->kernel[fpos];
250                    output[pos] += input[comp]*this->kernel[fpos];
251                  }
252
253                }
254              } // xoff loop
255
256            }
257          }// yoff loop
258          //      if(ct>0 && scaleByCoverage) output[pos] /= fsum;
259          if(ct>0 && edgeTreatment==SCALEBYCOVERAGE) output[pos] *= kernsum/fsum;
260 
261        } // else{
262
263      } //xpos loop
264    }   //ypos loop
265   
266    return output;
267  }
268
269}
Note: See TracBrowser for help on using the repository browser.