source: trunk/src/Utils/GaussSmooth2D.tcc

Last change on this file was 1393, checked in by MatthewWhiting, 10 years ago

A large changeset that moves all use of bool arrays (ie. bool *) to std::vector<bool>. This will be a bit safer, plus potentially cheaper in memory usage. Interfaces to all stats functions with masks have changed accordingly, as well as the Gaussian smoothing functions and some spectral utility functions.

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