source: trunk/src/Utils/GaussSmooth.cc @ 618

Last change on this file since 618 was 618, checked in by MatthewWhiting, 15 years ago

Changing the size of the kernel for the gaussian smoothing -- it is now sufficiently big to encompass all pixels down to the minimum representable floating-point value.

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