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

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

Adding a bool parameter to the GaussSmooth? smooth function that allows the user to select whether or not to scale by the kernel coverage. It defaults to true and will be set that way by all Duchamp calls.

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.
112  // For the moment, just calculate the size based on the number of
113  // pixels needed to make the exponential drop to less than the
114  // stated precision. Use the major axis to get the largest square
115  // that includes the ellipse.
116  const float precision = 1.e-4;
117  int kernelHW = int(ceil(sqrt(-1.*log(precision)*sigmaX2)));
118  this->kernWidth = 2*kernelHW + 1;
119//   std::cerr << "Making a kernel of width " << this->kernWidth << "\n";
120
121  if(this->allocated) delete [] this->kernel;
122  this->kernel = new Type[this->kernWidth*this->kernWidth];
123  this->allocated = true;
124  this->stddevScale=0.;
125  float posang = this->kernPA * M_PI/180.;
126
127
128  for(int i=0;i<this->kernWidth;i++){
129    for(int j=0;j<this->kernWidth;j++){
130      float xpt = (i-kernelHW)*sin(posang) - (j-kernelHW)*cos(posang);
131
132      float ypt = (i-kernelHW)*cos(posang) + (j-kernelHW)*sin(posang);
133      float rsq = (xpt*xpt/sigmaX2) + (ypt*ypt/sigmaY2);
134      kernel[i*this->kernWidth+j] = exp( -0.5 * rsq);
135      this->stddevScale +=
136        kernel[i*this->kernWidth+j]*kernel[i*this->kernWidth+j];
137    }
138  }
139  this->stddevScale = sqrt(this->stddevScale);
140//   std::cerr << "Stddev scaling factor = " << this->stddevScale << "\n";
141}
142template void GaussSmooth<float>::define(float maj, float min, float pa);
143template void GaussSmooth<double>::define(float maj, float min, float pa);
144
145template <class Type>
146Type *GaussSmooth<Type>::smooth(Type *input, int xdim, int ydim, bool scaleByCoverage)
147{
148  /**
149   * Smooth a given two-dimensional array, of dimensions xdim
150   * \f$\times\f$ ydim, with an elliptical gaussian. Simply runs as a
151   * front end to GaussSmooth::smooth(float *, int, int, bool *) by
152   * defining a mask that allows all pixels in the input array.
153   *
154   *  \param input The 2D array to be smoothed.
155   *  \param xdim  The size of the x-dimension of the array.
156   *  \param ydim  The size of the y-dimension of the array.
157   *  \return The smoothed array.
158   */
159  Type *smoothed;
160  bool *mask = new bool[xdim*ydim];
161  for(int i=0;i<xdim*ydim;i++) mask[i]=true;
162  smoothed = this->smooth(input,xdim,ydim,mask,scaleByCoverage);
163  delete [] mask;
164  return smoothed;
165}
166template float *GaussSmooth<float>::smooth(float *input, int xdim, int ydim, bool scaleByCoverage);
167template double *GaussSmooth<double>::smooth(double *input, int xdim, int ydim, bool scaleByCoverage);
168
169template <class Type>
170Type *GaussSmooth<Type>::smooth(Type *input, int xdim, int ydim, bool *mask, bool scaleByCoverage)
171{
172  /**
173   *  Smooth a given two-dimensional array, of dimensions xdim
174   *  \f$\times\f$ ydim, with an elliptical gaussian, where the
175   *  boolean array mask defines which values of the array are valid.
176   *
177   *  This function convolves the input array with the kernel that
178   *  needs to have been defined. If it has not, the input array is
179   *  returned unchanged.
180   *
181   *  The mask should be the same size as the input array, and have
182   *  values of true for entries that are considered valid, and false
183   *  for entries that are not. For instance, arrays from FITS files
184   *  should have the mask entries corresponding to BLANK pixels set
185   *  to false.
186   *
187   *  \param input The 2D array to be smoothed.
188   *  \param xdim  The size of the x-dimension of the array.
189   *  \param ydim  The size of the y-dimension of the array.
190   *  \param mask The array showing which pixels in the input array
191   *              are valid.
192   *  \return The smoothed array.
193   */
194
195
196  if(!this->allocated) return input;
197  else{
198
199    Type *output = new Type[xdim*ydim];
200
201    int pos,comp,xcomp,ycomp,fpos,ct;
202    float fsum,kernsum=0;
203    int kernelHW = this->kernWidth/2;
204
205    for(int i=0;i<this->kernWidth;i++)
206      for(int j=0;j<this->kernWidth;j++)
207        kernsum += this->kernel[i*this->kernWidth+j];
208
209    for(int ypos = 0; ypos<ydim; ypos++){
210      for(int xpos = 0; xpos<xdim; xpos++){
211        pos = ypos*xdim + xpos;
212     
213        if(!mask[pos]) output[pos] = input[pos];
214        else{
215       
216          ct=0;
217          fsum=0.;
218          output[pos] = 0.;
219       
220          for(int yoff = -kernelHW; yoff<=kernelHW; yoff++){
221            ycomp = ypos + yoff;
222            if((ycomp>=0)&&(ycomp<ydim)){
223
224              for(int xoff = -kernelHW; xoff<=kernelHW; xoff++){
225                xcomp = xpos + xoff;         
226                if((xcomp>=0)&&(xcomp<xdim)){
227
228                  fpos = (xoff+kernelHW) + (yoff+kernelHW)*this->kernWidth;
229                  comp = ycomp*xdim + xcomp;
230                  if(mask[comp]){
231                    ct++;
232                    fsum += this->kernel[fpos];
233                    output[pos] += input[comp]*this->kernel[fpos];
234                  }
235
236                }
237              } // xoff loop
238
239            }
240          }// yoff loop
241//        if(ct>0 && scaleByCoverage) output[pos] /= fsum;
242          if(ct>0 && scaleByCoverage) output[pos] *= kernsum/fsum;
243 
244        } // else{
245
246      } //xpos loop
247    }   //ypos loop
248   
249    return output;
250  }
251
252}
253template float *GaussSmooth<float>::smooth(float *input, int xdim, int ydim, bool *mask, bool scaleByCoverage);
254template double *GaussSmooth<double>::smooth(double *input, int xdim, int ydim, bool *mask, bool scaleByCoverage);
Note: See TracBrowser for help on using the repository browser.