21 Convolve::Convolve() : m_mode(FFTW_UNDEFINED)
24 const size_t FFTW_FACTORS[] = {13, 11, 7, 5, 3, 2};
25 m_implemented_factors.assign(FFTW_FACTORS,
26 FFTW_FACTORS +
sizeof(FFTW_FACTORS) /
sizeof(FFTW_FACTORS[0]));
29 Convolve::Workspace::Workspace()
30 : h_src(0), w_src(0), h_kernel(0), w_kernel(0), w_fftw(0), h_fftw(0), in_src(0), out_src(0),
31 in_kernel(0), out_kernel(0), dst_fft(0), h_dst(0), w_dst(0),
33 h_offset(0), w_offset(0), p_forw_src(nullptr), p_forw_kernel(nullptr), p_back(nullptr)
37 Convolve::Workspace::~Workspace()
42 void Convolve::Workspace::clear()
53 fftw_free((fftw_complex*)out_src);
61 fftw_free((fftw_complex*)out_kernel);
74 if (p_forw_src !=
nullptr)
75 fftw_destroy_plan(p_forw_src);
76 if (p_forw_kernel !=
nullptr)
77 fftw_destroy_plan(p_forw_kernel);
78 if (p_back !=
nullptr)
79 fftw_destroy_plan(p_back);
90 if (m_mode == FFTW_UNDEFINED)
93 int h_src = (int)source.size();
94 int w_src = (int)(source.size() ? source[0].size() : 0);
95 int h_kernel = (int)kernel.size();
96 int w_kernel = (kernel.size() ? (int)kernel[0].size() : 0);
99 init(h_src, w_src, h_kernel, w_kernel);
101 fftw_circular_convolution(source, kernel);
105 result.resize(ws.h_dst);
106 for (
int i = 0; i < ws.h_dst; i++) {
107 result[i].resize(ws.w_dst, 0);
108 for (
int j = 0; j < ws.w_dst; j++) {
110 if (m_mode == FFTW_CIRCULAR_SAME_SHIFTED) {
111 result[i][j] = ws.dst_fft[((i + int(ws.h_kernel / 2.0)) % ws.h_fftw) * ws.w_fftw
112 + (j + int(ws.w_kernel / 2.0)) % ws.w_fftw];
114 result[i][j] = ws.dst_fft[(i + ws.h_offset) * ws.w_fftw + j + ws.w_offset];
127 source2d.push_back(source);
128 kernel2d.push_back(kernel);
132 if (result2d.size() != 1)
134 result = result2d[0];
142 if (!h_src || !w_src || !h_kernel || !w_kernel) {
143 std::ostringstream os;
144 os <<
"Convolve::init() -> Panic! Wrong dimensions " << h_src <<
" " << w_src <<
" "
145 << h_kernel <<
" " << w_kernel << std::endl;
152 ws.h_kernel = h_kernel;
153 ws.w_kernel = w_kernel;
155 case FFTW_LINEAR_FULL:
157 ws.h_fftw = find_closest_factor(h_src + h_kernel - 1);
158 ws.w_fftw = find_closest_factor(w_src + w_kernel - 1);
159 ws.h_dst = h_src + h_kernel - 1;
160 ws.w_dst = w_src + w_kernel - 1;
165 case FFTW_LINEAR_SAME_UNPADDED:
167 ws.h_fftw = h_src + int(h_kernel / 2.0);
168 ws.w_fftw = w_src + int(w_kernel / 2.0);
173 ws.h_offset = int(ws.h_kernel / 2.0);
174 ws.w_offset = int(ws.w_kernel / 2.0);
176 case FFTW_LINEAR_SAME:
178 ws.h_fftw = find_closest_factor(h_src +
int(h_kernel / 2.0));
179 ws.w_fftw = find_closest_factor(w_src +
int(w_kernel / 2.0));
184 ws.h_offset = int(ws.h_kernel / 2.0);
185 ws.w_offset = int(ws.w_kernel / 2.0);
187 case FFTW_LINEAR_VALID:
189 if (ws.h_kernel > ws.h_src || ws.w_kernel > ws.w_src) {
194 std::cout <<
"The 'valid' convolution results in an empty matrix" << std::endl;
195 throw std::runtime_error(
"The 'valid' convolution results in an empty matrix");
197 ws.h_fftw = find_closest_factor(h_src);
198 ws.w_fftw = find_closest_factor(w_src);
199 ws.h_dst = h_src - h_kernel + 1;
200 ws.w_dst = w_src - w_kernel + 1;
203 ws.h_offset = ws.h_kernel - 1;
204 ws.w_offset = ws.w_kernel - 1;
206 case FFTW_CIRCULAR_SAME:
207 case FFTW_CIRCULAR_SAME_SHIFTED:
222 <<
"Unrecognized convolution mode, possible modes are "
223 <<
"FFTW_LINEAR_FULL, FFTW_LINEAR_SAME, FFTW_LINEAR_SAME_UNPADDED, FFTW_LINEAR_VALID, "
224 <<
"FFTW_CIRCULAR_SAME, FFTW_CIRCULAR_SHIFTED " << std::endl;
228 ws.in_src =
new double[ws.h_fftw * ws.w_fftw];
229 ws.out_src = (
double*)fftw_malloc(
sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw / 2 + 1));
230 ws.in_kernel =
new double[ws.h_fftw * ws.w_fftw];
231 ws.out_kernel = (
double*)fftw_malloc(
sizeof(fftw_complex) * ws.h_fftw * (ws.w_fftw / 2 + 1));
233 ws.dst_fft =
new double[ws.h_fftw * ws.w_fftw];
237 ws.p_forw_src = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_src, (fftw_complex*)ws.out_src,
239 if (ws.p_forw_src ==
nullptr)
241 "Convolve::init() -> Error! Can't initialise p_forw_src plan.");
243 ws.p_forw_kernel = fftw_plan_dft_r2c_2d(ws.h_fftw, ws.w_fftw, ws.in_kernel,
244 (fftw_complex*)ws.out_kernel, FFTW_ESTIMATE);
245 if (ws.p_forw_kernel ==
nullptr)
247 "Convolve::init() -> Error! Can't initialise p_forw_kernel plan.");
250 ws.p_back = fftw_plan_dft_c2r_2d(ws.h_fftw, ws.w_fftw, (fftw_complex*)ws.out_kernel, ws.dst_fft,
252 if (ws.p_back ==
nullptr)
254 "Convolve::init() -> Error! Can't initialise p_back plan.");
261 void Convolve::fftw_circular_convolution(
const double2d_t& src,
const double2d_t& kernel)
263 if (ws.h_fftw <= 0 || ws.w_fftw <= 0)
265 "Convolve::fftw_convolve() -> Panic! Initialisation is missed.");
267 double *ptr, *ptr_end, *ptr2;
270 for (ptr = ws.in_src, ptr_end = ws.in_src + ws.h_fftw * ws.w_fftw; ptr != ptr_end; ++ptr)
272 for (ptr = ws.in_kernel, ptr_end = ws.in_kernel + ws.h_fftw * ws.w_fftw; ptr != ptr_end; ++ptr)
277 for (
int i = 0; i < ws.h_src; ++i)
278 for (
int j = 0; j < ws.w_src; ++j, ++ptr)
279 ws.in_src[(i % ws.h_fftw) * ws.w_fftw + (j % ws.w_fftw)] += src[i][j];
282 for (
int i = 0; i < ws.h_kernel; ++i)
283 for (
int j = 0; j < ws.w_kernel; ++j, ++ptr)
284 ws.in_kernel[(i % ws.h_fftw) * ws.w_fftw + (j % ws.w_fftw)] += kernel[i][j];
287 fftw_execute(ws.p_forw_src);
288 fftw_execute(ws.p_forw_kernel);
292 double re_s, im_s, re_k, im_k;
293 for (ptr = ws.out_src, ptr2 = ws.out_kernel,
294 ptr_end = ws.out_src + 2 * ws.h_fftw * (ws.w_fftw / 2 + 1);
295 ptr != ptr_end; ++ptr, ++ptr2) {
300 *(ptr2 - 1) = re_s * re_k - im_s * im_k;
301 *ptr2 = re_s * im_k + im_s * re_k;
306 fftw_execute(ws.p_back);
308 for (ptr = ws.dst_fft, ptr_end = ws.dst_fft + ws.w_fftw * ws.h_fftw; ptr != ptr_end; ++ptr)
309 *ptr /= double(ws.h_fftw * ws.w_fftw);
317 int Convolve::find_closest_factor(
int n)
323 while (!is_optimal(j))
332 bool Convolve::is_optimal(
int n)
337 for (
size_t i = 0; i < m_implemented_factors.size(); i++) {
338 while ((ntest % m_implemented_factors[i]) == 0) {
339 ntest = ntest / m_implemented_factors[i];
Defines class MathFunctions::Convolve.
Defines many exception classes in namespace Exceptionss.
std::vector< double1d_t > double2d_t
definition of 2d vector of double
std::vector< double > double1d_t
definition of 1d vector of double
void init(int h_src, int w_src, int h_kernel, int w_kernel)
prepare arrays for 2D convolution of given vectors
void setMode(EConvolutionMode mode)
Sets convolution mode.
void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
convolution in 1D