BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
Convolve Class Reference

Convolution of two real vectors (in 1D or 2D) using Fast Fourier Transform. More...

Collaboration diagram for Convolve:
[legend]

Classes

class  Workspace
 Workspace for Fourier convolution. More...
 

Public Types

typedef std::vector< double > double1d_t
 definition of 1d vector of double More...
 
typedef std::vector< double1d_tdouble2d_t
 definition of 2d vector of double More...
 
enum  EConvolutionMode {
  FFTW_LINEAR_FULL , FFTW_LINEAR_SAME_UNPADDED , FFTW_LINEAR_SAME , FFTW_LINEAR_VALID ,
  FFTW_CIRCULAR_SAME , FFTW_CIRCULAR_SAME_SHIFTED , FFTW_UNDEFINED
}
 convolution modes use LINEAR_SAME or CIRCULAR_SAME_SHIFTED for maximum performance More...
 

Public Member Functions

 Convolve ()
 
void fftconvolve (const double1d_t &source, const double1d_t &kernel, double1d_t &result)
 convolution in 1D More...
 
void fftconvolve (const double2d_t &source, const double2d_t &kernel, double2d_t &result)
 convolution in 2D More...
 
void init (int h_src, int w_src, int h_kernel, int w_kernel)
 prepare arrays for 2D convolution of given vectors More...
 
void setMode (EConvolutionMode mode)
 Sets convolution mode. More...
 

Private Member Functions

void fftw_circular_convolution (const double2d_t &source, const double2d_t &kernel)
 compute circual convolution of source and kernel using fast Fourier transformation More...
 
int find_closest_factor (int n)
 find closest number X>n that can be factorised according to fftw3 favorite factorisation More...
 
bool is_optimal (int n)
 if a number can be factorised using only favorite fftw3 factors More...
 

Private Attributes

std::vector< size_t > m_implemented_factors
 
EConvolutionMode m_mode
 convolution mode More...
 
Workspace ws
 input and output data for fftw3 More...
 

Detailed Description

Convolution of two real vectors (in 1D or 2D) using Fast Fourier Transform.

Usage: std::vector<double> signal, kernel, result; Convolve cv; cv.fftconvolve(signal, kernel, result)

Given code rely on code from Jeremy Fix page, http://jeremy.fix.free.fr/spip.php?article15, see also "Efficient convolution using the Fast Fourier Transform, Application in C++" by Jeremy Fix, May 30, 2011

Definition at line 38 of file Convolve.h.

Member Typedef Documentation

◆ double1d_t

typedef std::vector<double> Convolve::double1d_t

definition of 1d vector of double

Definition at line 41 of file Convolve.h.

◆ double2d_t

typedef std::vector<double1d_t> Convolve::double2d_t

definition of 2d vector of double

Definition at line 44 of file Convolve.h.

Member Enumeration Documentation

◆ EConvolutionMode

convolution modes use LINEAR_SAME or CIRCULAR_SAME_SHIFTED for maximum performance

Enumerator
FFTW_LINEAR_FULL 
FFTW_LINEAR_SAME_UNPADDED 
FFTW_LINEAR_SAME 
FFTW_LINEAR_VALID 
FFTW_CIRCULAR_SAME 
FFTW_CIRCULAR_SAME_SHIFTED 
FFTW_UNDEFINED 

Definition at line 50 of file Convolve.h.

50  {
58  };
@ FFTW_LINEAR_FULL
Definition: Convolve.h:51
@ FFTW_CIRCULAR_SAME_SHIFTED
Definition: Convolve.h:56
@ FFTW_LINEAR_VALID
Definition: Convolve.h:54
@ FFTW_UNDEFINED
Definition: Convolve.h:57
@ FFTW_LINEAR_SAME_UNPADDED
Definition: Convolve.h:52
@ FFTW_CIRCULAR_SAME
Definition: Convolve.h:55
@ FFTW_LINEAR_SAME
Definition: Convolve.h:53

Constructor & Destructor Documentation

◆ Convolve()

Convolve::Convolve ( )

Definition at line 20 of file Convolve.cpp.

21 {
22  // storing favorite fftw3 prime factors
23  const size_t FFTW_FACTORS[] = {13, 11, 7, 5, 3, 2};
24  m_implemented_factors.assign(FFTW_FACTORS,
25  FFTW_FACTORS + sizeof(FFTW_FACTORS) / sizeof(FFTW_FACTORS[0]));
26 }
EConvolutionMode m_mode
convolution mode
Definition: Convolve.h:121
std::vector< size_t > m_implemented_factors
Definition: Convolve.h:122

References m_implemented_factors.

Member Function Documentation

◆ fftconvolve() [1/2]

void Convolve::fftconvolve ( const double1d_t source,
const double1d_t kernel,
double1d_t result 
)

convolution in 1D

Definition at line 138 of file Convolve.cpp.

139 {
140  // we simply create 2d arrays with length of first dimension equal to 1, and call 2d convolution
141  double2d_t source2d, kernel2d;
142  source2d.push_back(source);
143  kernel2d.push_back(kernel);
144 
145  double2d_t result2d;
146  fftconvolve(source2d, kernel2d, result2d);
147  if (result2d.size() != 1)
148  throw std::runtime_error("Convolve::fftconvolve -> Panic in 1d");
149  result = result2d[0];
150 }
std::vector< double1d_t > double2d_t
definition of 2d vector of double
Definition: Convolve.h:44
void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
convolution in 1D
Definition: Convolve.cpp:138

Referenced by ConvolutionDetectorResolution::apply1dConvolution(), and ConvolutionDetectorResolution::apply2dConvolution().

◆ fftconvolve() [2/2]

void Convolve::fftconvolve ( const double2d_t source,
const double2d_t kernel,
double2d_t result 
)

convolution in 2D

Definition at line 102 of file Convolve.cpp.

103 {
104  // set default convolution mode, if not defined
105  if (m_mode == FFTW_UNDEFINED)
107 
108  int h_src = (int)source.size();
109  int w_src = (int)(source.size() ? source[0].size() : 0);
110  int h_kernel = (int)kernel.size();
111  int w_kernel = (kernel.size() ? (int)kernel[0].size() : 0);
112 
113  // initialisation
114  init(h_src, w_src, h_kernel, w_kernel);
115  // Compute the circular convolution
116  fftw_circular_convolution(source, kernel);
117 
118  // results
119  result.clear();
120  result.resize(ws.h_dst);
121  for (int i = 0; i < ws.h_dst; i++) {
122  result[i].resize(ws.w_dst, 0);
123  for (int j = 0; j < ws.w_dst; j++) {
124  // result[i][j]=ws.dst[i*ws.w_dst+j];
126  result[i][j] = ws.dst_fft[((i + int(ws.h_kernel / 2.0)) % ws.h_fftw) * ws.w_fftw
127  + (j + int(ws.w_kernel / 2.0)) % ws.w_fftw];
128  } else {
129  result[i][j] = ws.dst_fft[(i + ws.h_offset) * ws.w_fftw + j + ws.w_offset];
130  }
131  }
132  }
133 }
double * dst_fft
result of product of FFT(source) and FFT(kernel)
Definition: Convolve.h:110
Workspace ws
input and output data for fftw3
Definition: Convolve.h:119
void init(int h_src, int w_src, int h_kernel, int w_kernel)
prepare arrays for 2D convolution of given vectors
Definition: Convolve.cpp:155
void setMode(EConvolutionMode mode)
Sets convolution mode.
Definition: Convolve.h:70
void fftw_circular_convolution(const double2d_t &source, const double2d_t &kernel)
compute circual convolution of source and kernel using fast Fourier transformation
Definition: Convolve.cpp:273

References Convolve::Workspace::dst_fft, fftw_circular_convolution(), FFTW_CIRCULAR_SAME_SHIFTED, FFTW_LINEAR_SAME, FFTW_UNDEFINED, Convolve::Workspace::h_dst, Convolve::Workspace::h_fftw, Convolve::Workspace::h_kernel, Convolve::Workspace::h_offset, init(), m_mode, setMode(), Convolve::Workspace::w_dst, Convolve::Workspace::w_fftw, Convolve::Workspace::w_kernel, Convolve::Workspace::w_offset, and ws.

Here is the call graph for this function:

◆ fftw_circular_convolution()

void Convolve::fftw_circular_convolution ( const double2d_t source,
const double2d_t kernel 
)
private

compute circual convolution of source and kernel using fast Fourier transformation

Definition at line 273 of file Convolve.cpp.

274 {
275  if (ws.h_fftw <= 0 || ws.w_fftw <= 0)
276  throw std::runtime_error("Convolve::fftw_convolve() -> Panic! Initialisation is missed.");
277 
278  double *ptr, *ptr_end, *ptr2;
279 
280  // Reset the content of ws.in_src
281  for (ptr = ws.in_src, ptr_end = ws.in_src + ws.h_fftw * ws.w_fftw; ptr != ptr_end; ++ptr)
282  *ptr = 0.0;
283  for (ptr = ws.in_kernel, ptr_end = ws.in_kernel + ws.h_fftw * ws.w_fftw; ptr != ptr_end; ++ptr)
284  *ptr = 0.0;
285 
286  // Then we build our periodic signals
287  // ptr = src;
288  for (int i = 0; i < ws.h_src; ++i)
289  for (int j = 0; j < ws.w_src; ++j, ++ptr)
290  ws.in_src[(i % ws.h_fftw) * ws.w_fftw + (j % ws.w_fftw)] += src[i][j];
291 
292  // ptr = kernel;
293  for (int i = 0; i < ws.h_kernel; ++i)
294  for (int j = 0; j < ws.w_kernel; ++j, ++ptr)
295  ws.in_kernel[(i % ws.h_fftw) * ws.w_fftw + (j % ws.w_fftw)] += kernel[i][j];
296 
297  // And we compute their packed FFT
298  fftw_execute(ws.p_forw_src);
299  fftw_execute(ws.p_forw_kernel);
300 
301  // Compute the element-wise product on the packed terms
302  // Let's put the element wise products in ws.in_kernel
303  double re_s, im_s, re_k, im_k;
304  for (ptr = ws.out_src, ptr2 = ws.out_kernel,
305  ptr_end = ws.out_src + 2 * ws.h_fftw * (ws.w_fftw / 2 + 1);
306  ptr != ptr_end; ++ptr, ++ptr2) {
307  re_s = *ptr;
308  im_s = *(++ptr);
309  re_k = *ptr2;
310  im_k = *(++ptr2);
311  *(ptr2 - 1) = re_s * re_k - im_s * im_k;
312  *ptr2 = re_s * im_k + im_s * re_k;
313  }
314 
315  // Compute the backward FFT
316  // Carefull, The backward FFT does not preserve the output
317  fftw_execute(ws.p_back);
318  // Scale the transform
319  for (ptr = ws.dst_fft, ptr_end = ws.dst_fft + ws.w_fftw * ws.h_fftw; ptr != ptr_end; ++ptr)
320  *ptr /= double(ws.h_fftw * ws.w_fftw);
321 }
double * out_src
result of Fourier transformation of source
Definition: Convolve.h:104
fftw_plan p_back
Definition: Convolve.h:115
double * in_src
adjusted input 'source' array
Definition: Convolve.h:102
double * in_kernel
adjusted input 'kernel' array
Definition: Convolve.h:106
fftw_plan p_forw_src
Definition: Convolve.h:113
fftw_plan p_forw_kernel
Definition: Convolve.h:114
double * out_kernel
result of Fourier transformation of kernel
Definition: Convolve.h:108

References Convolve::Workspace::dst_fft, Convolve::Workspace::h_fftw, Convolve::Workspace::h_kernel, Convolve::Workspace::h_src, Convolve::Workspace::in_kernel, Convolve::Workspace::in_src, Convolve::Workspace::out_kernel, Convolve::Workspace::out_src, Convolve::Workspace::p_back, Convolve::Workspace::p_forw_kernel, Convolve::Workspace::p_forw_src, Convolve::Workspace::w_fftw, Convolve::Workspace::w_kernel, Convolve::Workspace::w_src, and ws.

Referenced by fftconvolve().

◆ find_closest_factor()

int Convolve::find_closest_factor ( int  n)
private

find closest number X>n that can be factorised according to fftw3 favorite factorisation

Definition at line 328 of file Convolve.cpp.

329 {
330  if (is_optimal(n)) {
331  return n;
332  } else {
333  int j = n + 1;
334  while (!is_optimal(j))
335  ++j;
336  return j;
337  }
338 }
bool is_optimal(int n)
if a number can be factorised using only favorite fftw3 factors
Definition: Convolve.cpp:343

References is_optimal().

Referenced by init().

Here is the call graph for this function:

◆ init()

void Convolve::init ( int  h_src,
int  w_src,
int  h_kernel,
int  w_kernel 
)

prepare arrays for 2D convolution of given vectors

Definition at line 155 of file Convolve.cpp.

156 {
157  if (!h_src || !w_src || !h_kernel || !w_kernel) {
158  std::ostringstream os;
159  os << "Convolve::init() -> Panic! Wrong dimensions " << h_src << " " << w_src << " "
160  << h_kernel << " " << w_kernel << std::endl;
161  throw std::runtime_error(os.str());
162  }
163 
164  ws.clear();
165  ws.h_src = h_src;
166  ws.w_src = w_src;
167  ws.h_kernel = h_kernel;
168  ws.w_kernel = w_kernel;
169  switch (m_mode) {
170  case FFTW_LINEAR_FULL:
171  // Full Linear convolution
172  ws.h_fftw = find_closest_factor(h_src + h_kernel - 1);
173  ws.w_fftw = find_closest_factor(w_src + w_kernel - 1);
174  ws.h_dst = h_src + h_kernel - 1;
175  ws.w_dst = w_src + w_kernel - 1;
176  // Here we just keep the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
177  ws.h_offset = 0;
178  ws.w_offset = 0;
179  break;
181  // Same Linear convolution
182  ws.h_fftw = h_src + int(h_kernel / 2.0);
183  ws.w_fftw = w_src + int(w_kernel / 2.0);
184  ws.h_dst = h_src;
185  ws.w_dst = w_src;
186  // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1]
187  // real part elements of out_src
188  ws.h_offset = int(ws.h_kernel / 2.0);
189  ws.w_offset = int(ws.w_kernel / 2.0);
190  break;
191  case FFTW_LINEAR_SAME:
192  // Same Linear convolution
193  ws.h_fftw = find_closest_factor(h_src + int(h_kernel / 2.0));
194  ws.w_fftw = find_closest_factor(w_src + int(w_kernel / 2.0));
195  ws.h_dst = h_src;
196  ws.w_dst = w_src;
197  // Here we just keep the first [h_filt/2:h_filt/2+h_dst-1 ; w_filt/2:w_filt/2+w_dst-1]
198  // real part elements of out_src
199  ws.h_offset = int(ws.h_kernel / 2.0);
200  ws.w_offset = int(ws.w_kernel / 2.0);
201  break;
202  case FFTW_LINEAR_VALID:
203  // Valid Linear convolution
204  if (ws.h_kernel > ws.h_src || ws.w_kernel > ws.w_src) {
205  ws.h_fftw = 0;
206  ws.w_fftw = 0;
207  ws.h_dst = 0;
208  ws.w_dst = 0;
209  std::cout << "The 'valid' convolution results in an empty matrix" << std::endl;
210  throw std::runtime_error("The 'valid' convolution results in an empty matrix");
211  } else {
212  ws.h_fftw = find_closest_factor(h_src);
213  ws.w_fftw = find_closest_factor(w_src);
214  ws.h_dst = h_src - h_kernel + 1;
215  ws.w_dst = w_src - w_kernel + 1;
216  }
217  // Here we just take [h_dst x w_dst] elements starting at [h_kernel-1;w_kernel-1]
218  ws.h_offset = ws.h_kernel - 1;
219  ws.w_offset = ws.w_kernel - 1;
220  break;
221  case FFTW_CIRCULAR_SAME:
223  // Circular convolution, modulo N
224  // We don't padd with zeros because if we do, we need to padd with at least h_kernel/2;
225  // w_kernel/2 elements plus the wrapp around, which in facts leads to too much
226  // computations compared to the gain obtained with the optimal size
227  ws.h_fftw = h_src;
228  ws.w_fftw = w_src;
229  ws.h_dst = h_src;
230  ws.w_dst = w_src;
231  // We copy the first [0:h_dst-1 ; 0:w_dst-1] real part elements of out_src
232  ws.h_offset = 0;
233  ws.w_offset = 0;
234  break;
235  default:
236  std::cout
237  << "Unrecognized convolution mode, possible modes are "
238  << "FFTW_LINEAR_FULL, FFTW_LINEAR_SAME, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_VALID, "
239  << "FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SHIFTED " << std::endl;
240  break;
241  }
242 
243  ws.in_src = new double[ws.h_fftw * ws.w_fftw];
244  ws.out_src = (double*)fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw / 2 + 1));
245  ws.in_kernel = new double[ws.h_fftw * ws.w_fftw];
246  ws.out_kernel = (double*)fftw_malloc(sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw / 2 + 1));
247 
248  ws.dst_fft = new double[ws.h_fftw * ws.w_fftw];
249  // ws.dst = new double[ws.h_dst * ws.w_dst];
250 
251  // Initialization of the plans
252  ws.p_forw_src = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_src, (fftw_complex*)ws.out_src,
253  FFTW_ESTIMATE);
254  if (ws.p_forw_src == nullptr)
255  throw std::runtime_error("Convolve::init() -> Error! Can't initialise p_forw_src plan.");
256 
257  ws.p_forw_kernel = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_kernel,
258  (fftw_complex*)ws.out_kernel, FFTW_ESTIMATE);
259  if (ws.p_forw_kernel == nullptr)
260  throw std::runtime_error("Convolve::init() -> Error! Can't initialise p_forw_kernel plan.");
261 
262  // The backward FFT takes ws.out_kernel as input
263  ws.p_back = fftw_plan_dft_c2r_2d(ws.h_fftw, ws.w_fftw, (fftw_complex*)ws.out_kernel, ws.dst_fft,
264  FFTW_ESTIMATE);
265  if (ws.p_back == nullptr)
266  throw std::runtime_error("Convolve::init() -> Error! Can't initialise p_back plan.");
267 }
int find_closest_factor(int n)
find closest number X>n that can be factorised according to fftw3 favorite factorisation
Definition: Convolve.cpp:328

References Convolve::Workspace::clear(), Convolve::Workspace::dst_fft, FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SAME_SHIFTED, FFTW_LINEAR_FULL, FFTW_LINEAR_SAME, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_VALID, find_closest_factor(), Convolve::Workspace::h_dst, Convolve::Workspace::h_fftw, Convolve::Workspace::h_kernel, Convolve::Workspace::h_offset, Convolve::Workspace::h_src, Convolve::Workspace::in_kernel, Convolve::Workspace::in_src, m_mode, Convolve::Workspace::out_kernel, Convolve::Workspace::out_src, Convolve::Workspace::p_back, Convolve::Workspace::p_forw_kernel, Convolve::Workspace::p_forw_src, Convolve::Workspace::w_dst, Convolve::Workspace::w_fftw, Convolve::Workspace::w_kernel, Convolve::Workspace::w_offset, Convolve::Workspace::w_src, and ws.

Referenced by fftconvolve().

Here is the call graph for this function:

◆ is_optimal()

bool Convolve::is_optimal ( int  n)
private

if a number can be factorised using only favorite fftw3 factors

Definition at line 343 of file Convolve.cpp.

344 {
345  if (n == 1)
346  return false;
347  size_t ntest = n;
348  for (size_t i = 0; i < m_implemented_factors.size(); i++) {
349  while ((ntest % m_implemented_factors[i]) == 0) {
350  ntest = ntest / m_implemented_factors[i];
351  }
352  }
353  return ntest == 1;
354 }

References m_implemented_factors.

Referenced by find_closest_factor().

◆ setMode()

void Convolve::setMode ( EConvolutionMode  mode)
inline

Sets convolution mode.

Definition at line 70 of file Convolve.h.

70 { m_mode = mode; }

References m_mode.

Referenced by fftconvolve().

Member Data Documentation

◆ m_implemented_factors

std::vector<size_t> Convolve::m_implemented_factors
private

Definition at line 122 of file Convolve.h.

Referenced by Convolve(), and is_optimal().

◆ m_mode

EConvolutionMode Convolve::m_mode
private

convolution mode

Definition at line 121 of file Convolve.h.

Referenced by fftconvolve(), init(), and setMode().

◆ ws

Workspace Convolve::ws
private

input and output data for fftw3

Definition at line 119 of file Convolve.h.

Referenced by fftconvolve(), fftw_circular_convolution(), and init().


The documentation for this class was generated from the following files: