Вопрос или проблема
Я хотел бы применить функцию convolve2d к моей матрице boost::numeric::ublas::matrix с небольшим 3×3 ядром, используя метод fft -> умножение -> ifft. Какой лучший подход на C++/Boost?
Матрица плотная, каждое значение уникально.
Ответ или решение
Для выполнения двумерной свертки с использованием метода быстрого преобразования Фурье (FFT) в C++ с использованием библиотеки Boost uBLAS, вам необходимо выполнить следующие шаги:
-
Подготовка среды: Убедитесь, что у вас установлены библиотеки Boost и FFTW (или аналогичная библиотека для FFT). Вам потребуется интегрировать их в ваш проект.
-
Создание функции свертки: Вы можете создать функцию, которая будет выполнять свертку 2D с использованием FFT.
Вот примерный код, демонстрирующий, как это можно сделать:
#include <iostream>
#include <boost/numeric/ublas/matrix.hpp>
#include <fftw3.h>
namespace ublas = boost::numeric::ublas;
ublas::matrix<float> convolve2d(const ublas::matrix<float> &input, const ublas::matrix<float> &kernel) {
int input_rows = input.size1();
int input_cols = input.size2();
int kernel_rows = kernel.size1();
int kernel_cols = kernel.size2();
// Вычисляем размеры выходной матрицы
int output_rows = input_rows + kernel_rows - 1;
int output_cols = input_cols + kernel_cols - 1;
// Создаем обратные входные данные в виде увеличенной матрицы
ublas::matrix<float> padded_input(output_rows, output_cols);
for (int i = 0; i < input_rows; ++i) {
for (int j = 0; j < input_cols; ++j) {
padded_input(i, j) = input(i, j);
}
}
// Создаем буферы для FFT
int fft_size = output_rows * output_cols;
fftwf_complex *input_fft = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size);
fftwf_complex *kernel_fft = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size);
fftwf_complex *output_fft = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fft_size);
// Преобразуем ядро в одноразмерный формат и заполняем буфер
for (int i = 0; i < kernel_rows; ++i) {
for (int j = 0; j < kernel_cols; ++j) {
kernel_fft[i * output_cols + j][0] = kernel(i, j); // действительная часть
kernel_fft[i * output_cols + j][1] = 0; // мнимая часть
}
}
// Применяем FFT к входной матрице
fftwf_plan plan_forward_input = fftwf_plan_dft_2d(input_rows, input_cols, (fftwf_complex*)padded_input.data().begin(), input_fft, FFTW_ESTIMATE);
fftwf_execute(plan_forward_input);
fftwf_destroy_plan(plan_forward_input);
// Применяем FFT к ядру
fftwf_plan plan_forward_kernel = fftwf_plan_dft_2d(kernel_rows, kernel_cols, kernel_fft, kernel_fft, FFTW_ESTIMATE);
fftwf_execute(plan_forward_kernel);
fftwf_destroy_plan(plan_forward_kernel);
// Перемножаем в частотной области
for (int i = 0; i < fft_size; ++i) {
output_fft[i][0] = input_fft[i][0] * kernel_fft[i][0] - input_fft[i][1] * kernel_fft[i][1];
output_fft[i][1] = input_fft[i][0] * kernel_fft[i][1] + input_fft[i][1] * kernel_fft[i][0];
}
// Обратное FFT
fftwf_plan plan_backward = fftwf_plan_dft_2d(output_rows, output_cols, output_fft, input_fft, FFTW_ESTIMATE);
fftwf_execute(plan_backward);
fftwf_destroy_plan(plan_backward);
// Заполняем выходную матрицу из обратно преобразованных данных
ublas::matrix<float> output(output_rows, output_cols);
for (int i = 0; i < output_rows; ++i) {
for (int j = 0; j < output_cols; ++j) {
output(i, j) = input_fft[i * output_cols + j][0] / fft_size; // Нормируем
}
}
// Освобождаем память
fftwf_free(input_fft);
fftwf_free(kernel_fft);
fftwf_free(output_fft);
return output;
}
int main() {
// Пример использования
ublas::matrix<float> input(4, 4);
ublas::matrix<float> kernel(3, 3);
// Заполнение входной матрицы
input(0, 0) = 1; input(0, 1) = 2; input(0, 2) = 3; input(0, 3) = 4;
input(1, 0) = 5; input(1, 1) = 6; input(1, 2) = 7; input(1, 3) = 8;
input(2, 0) = 9; input(2, 1) = 10; input(2, 2) = 11; input(2, 3) = 12;
input(3, 0) = 13; input(3, 1) = 14; input(3, 2) = 15; input(3, 3) = 16;
// Заполнение ядра
kernel(0, 0) = 1; kernel(0, 1) = 0; kernel(0, 2) = -1;
kernel(1, 0) = 1; kernel(1, 1) = 0; kernel(1, 2) = -1;
kernel(2, 0) = 1; kernel(2, 1) = 0; kernel(2, 2) = -1;
ublas::matrix<float> result = convolve2d(input, kernel);
// Печать результата
for (size_t i = 0; i < result.size1(); ++i) {
for (size_t j = 0; j < result.size2(); ++j) {
std::cout << result(i, j) << " ";
}
std::cout << std::endl;
}
return 0;
}
Объяснение кода:
-
Инициализация и подготовка: Создаем новые матрицы для входных данных и ядер, а также буферы для FFT.
-
FFT: Используем библиотеку FFTW для выполнения FFT на входной матрице и ядре.
-
Перемножение: Перемножаем преобразованные данные в частотной области.
-
Обратное FFT: Применяем обратное FFT, чтобы получить выходную матрицу.
-
Нормализация: Нормализуем результирующие значения, деля на размер FFT.
-
Освобождение памяти: Освобождаем выделенные ресурсы.
Заключение
Этот код обеспечивает конволюцию 2D с использованием метода FFT. Убедитесь, что вы правильно настроили проект с необходимыми библиотеками, а также протестируйте код на ваших данных.