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

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

Ticket #77 - Implementing the two new parameters, one to select the edge method, and the other to set the cutoff for determining the kernel size.

File size: 9.0 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, float cutoff)
93{
94  this->defaults();
95  this->define(maj, min, pa, cutoff);
96}
97
98template <class Type>
99GaussSmooth2D<Type>::GaussSmooth2D(float maj, float min, float pa)
100{
101  this->defaults();
102  this->define(maj, min, pa, 1./MAXVAL);
103}
104
105template <class Type>
106GaussSmooth2D<Type>::GaussSmooth2D(float maj)
107{
108  this->defaults();
109  this->define(maj, maj, 0, 1./MAXVAL);
110}
111
112template <class Type>
113void GaussSmooth2D<Type>::define(float maj, float min, float pa, float cutoff)
114{
115
116  this->kernMaj = maj;
117  this->kernMin = min;
118  this->kernPA  = pa;
119
120  // The parameters kernMaj & kernMin are the FWHM in the major and
121  // minor axis directions. We correct these to the sigma_x and
122  // sigma_y parameters for the 2D gaussian by halving and dividing by
123  // sqrt(2 ln(2)). Actually work with sigma_x^2 to make things
124  // easier.
125  float sigmaX2 = (this->kernMaj*this->kernMaj/4.) / (2.*M_LN2);
126  float sigmaY2 = (this->kernMin*this->kernMin/4.) / (2.*M_LN2);
127
128  // First determine the size of the kernel.  Calculate the size based
129  // on the number of pixels needed to make the exponential drop to
130  // less than the minimum floating-point value. Use the major axis to
131  // get the largest square that includes the ellipse.
132  // float majorSigma = this->kernMaj / (4.*M_LN2);
133  float majorSigma = this->kernMaj / (2*sqrt(2.*M_LN2));
134//  unsigned int kernelHW = (unsigned int)(ceil(majorSigma * sqrt(-2.*log(1. / MAXVAL))));
135  unsigned int kernelHW = (unsigned int)(ceil(majorSigma * sqrt(-2.*log(cutoff))));
136
137  if(this->kernWidth == 0) this->kernWidth = size_t(2*kernelHW + 1);
138  else if(this->kernWidth < 2*kernelHW + 1){
139    DUCHAMPWARN("GaussSmooth2D::define","You have provided a kernel smaller than optimal (" << this->kernWidth << " cf. " << 2*kernelHW + 1 <<")");
140  }
141  std::cout << "GaussSmooth2D - defined a kernel of full width " << this->kernWidth << " using cutoff="<<cutoff<< " and majorSigma="<<majorSigma<<"\n";
142
143  if(this->allocated) delete [] this->kernel;
144  this->kernel = new Type[this->kernWidth*this->kernWidth];
145  this->allocated = true;
146  this->stddevScale=0.;
147  float posang = -1.*(this->kernPA+90.) * M_PI/180.;
148
149  float normalisation = 2. * M_PI * sqrt(sigmaX2*sigmaY2);
150  for(size_t i=0;i<this->kernWidth;i++){
151    for(size_t j=0;j<this->kernWidth;j++){
152      float xpt = int(i-kernelHW)*sin(posang) - int(j-kernelHW)*cos(posang);
153
154      float ypt = int(i-kernelHW)*cos(posang) + int(j-kernelHW)*sin(posang);
155      float rsq = (xpt*xpt/sigmaX2) + (ypt*ypt/sigmaY2);
156      this->kernel[i*this->kernWidth+j] = exp( -0.5 * rsq)/normalisation;
157      this->stddevScale += kernel[i*this->kernWidth+j]*kernel[i*this->kernWidth+j];
158    }
159  }
160 
161//  std::cerr << "Normalisation = " << normalisation << ", kernSum = " << kernSum << ", kernel size = " << kernWidth << ", kernelHW = " << kernelHW <<", majorSigma = " << majorSigma << ", sigmaX2="<< sigmaX2 <<", sigmaY2="<<sigmaY2<<"\n";
162
163//  for(size_t i=0;i<this->kernWidth*this->kernWidth; i++) this->kernel[i] /= kernSum;
164  this->stddevScale = sqrt(this->stddevScale);
165}
166
167template <class Type>
168Type *GaussSmooth2D<Type>::smooth(Type *input, size_t xdim, size_t ydim, EDGES edgeTreatment)
169{
170  /// @details
171  /// Smooth a given two-dimensional array, of dimensions xdim
172  /// \f$\times\f$ ydim, with an elliptical gaussian. Simply runs as a
173  /// front end to GaussSmooth2D::smooth(float *, int, int, bool *) by
174  /// defining a mask that allows all pixels in the input array.
175  ///
176  ///  \param input The 2D array to be smoothed.
177  ///  \param xdim  The size of the x-dimension of the array.
178  ///  \param ydim  The size of the y-dimension of the array.
179  ///  \return The smoothed array.
180
181  Type *smoothed;
182  bool *mask = new bool[xdim*ydim];
183  for(size_t i=0;i<xdim*ydim;i++) mask[i]=true;
184  smoothed = this->smooth(input,xdim,ydim,mask,edgeTreatment);
185  delete [] mask;
186  return smoothed;
187}
188
189template <class Type>
190Type *GaussSmooth2D<Type>::smooth(Type *input, size_t xdim, size_t ydim, bool *mask, EDGES edgeTreatment)
191{
192  /// @details
193  ///  Smooth a given two-dimensional array, of dimensions xdim
194  ///  \f$\times\f$ ydim, with an elliptical gaussian, where the
195  ///  boolean array mask defines which values of the array are valid.
196  ///
197  ///  This function convolves the input array with the kernel that
198  ///  needs to have been defined. If it has not, the input array is
199  ///  returned unchanged.
200  ///
201  ///  The mask should be the same size as the input array, and have
202  ///  values of true for entries that are considered valid, and false
203  ///  for entries that are not. For instance, arrays from FITS files
204  ///  should have the mask entries corresponding to BLANK pixels set
205  ///  to false.
206  ///
207  ///  \param input The 2D array to be smoothed.
208  ///  \param xdim  The size of the x-dimension of the array.
209  ///  \param ydim  The size of the y-dimension of the array.
210  ///  \param mask The array showing which pixels in the input array
211  ///              are valid.
212  ///  \return The smoothed array.
213
214  if(!this->allocated) return input;
215  else{
216
217    Type *output = new Type[xdim*ydim];
218
219    size_t pos,comp,xcomp,ycomp,fpos;
220    int ct;
221    float fsum,kernsum=0;
222    unsigned int kernelHW = this->kernWidth/2;
223
224    for(size_t i=0;i<this->kernWidth;i++)
225      for(size_t j=0;j<this->kernWidth;j++)
226        kernsum += this->kernel[i*this->kernWidth+j];
227
228    for(size_t ypos = 0; ypos<ydim; ypos++){
229      for(size_t xpos = 0; xpos<xdim; xpos++){
230        pos = ypos*xdim + xpos;
231     
232        if(!mask[pos]) output[pos] = input[pos];
233        else if(edgeTreatment==TRUNCATE &&
234                ((xpos<=kernelHW)||((xdim-xpos)<=kernelHW)||(ypos<=kernelHW)||((ydim-ypos)<kernelHW))){
235          output[pos] = this->blankVal;
236        }
237        else{
238       
239          ct=0;
240          fsum=0.;
241          output[pos] = 0.;
242       
243          for(int yoff = -int(kernelHW); yoff<=int(kernelHW); yoff++){
244            ycomp = ypos + yoff;
245            if((ycomp>=0)&&(ycomp<ydim)){
246
247                for(int xoff = -int(kernelHW); xoff<=int(kernelHW); xoff++){
248                xcomp = xpos + xoff;         
249                if((xcomp>=0)&&(xcomp<xdim)){
250
251                  fpos = (xoff+kernelHW) + (yoff+kernelHW)*this->kernWidth;
252                  comp = ycomp*xdim + xcomp;
253                  if(mask[comp]){
254                    ct++;
255                    fsum += this->kernel[fpos];
256                    output[pos] += input[comp]*this->kernel[fpos];
257                  }
258
259                }
260              } // xoff loop
261
262            }
263          }// yoff loop
264          //      if(ct>0 && scaleByCoverage) output[pos] /= fsum;
265          if(ct>0 && edgeTreatment==SCALEBYCOVERAGE) output[pos] *= kernsum/fsum;
266 
267        } // else{
268
269      } //xpos loop
270    }   //ypos loop
271   
272    return output;
273  }
274
275}
Note: See TracBrowser for help on using the repository browser.