Свертка изображения по Гауссу в области Фурье: работает, но не должна

Проблема в том, что я не могу полностью понять принципы свертки в частотной области.

У меня есть image of size 256x256, который я хочу свернуть с 3x3 gaussian matrix. Его коэффициенты (1/16, 1/8, 1/4):

PlainImage<float> FourierRunner::getGaussMask(int sz)
{
    PlainImage<float> G(3,3);
    *G.at(0, 0) = 1.0/16; *G.at(0, 1) = 1.0/8; *G.at(0, 2) = 1.0/16;
    *G.at(1, 0) = 1.0/8; *G.at(1, 1) = 1.0/4; *G.at(1, 2) = 1.0/8;
    *G.at(2, 0) = 1.0/16; *G.at(2, 1) = 1.0/8; *G.at(2, 2) = 1.0/16;
    return G;
}


Чтобы получить БПФ как изображения, так и ядра фильтра, я обнуляю их. sz_common означает увеличенный размер. Изображение и ядро ​​перемещаются в центр h и g ComplexImage соответственно, поэтому они заполняются нулями справа, слева, снизу и сверху.


Я читал, что размер должен быть sz_common >= sz+gsz-1 из-за свойство круговой свертки: фильтр может изменять нежелательные значения изображения на границах.
Но это не работает: адекватные результаты будут только тогда, когда sz_common = sz, когда sz_common = sz+gsz-1 или sz_common = 2*sz, после IFFT я получаю свернутое изображение в 2-3 раза меньше!
Почему?
Также меня смущает, что значения матрицы фильтров должны быть умножены на 256, как и значения пикселей: другие вопросы по SO содержат код Matlab без такой нормализации. Как и в предыдущем случае, без такого умножения работает плохо: получается черное изображение. Почему?
// fft_in сдвинутое изображение Фурье с центром в [sz/2;sz/2]

void FourierRunner::convolveImage(ComplexImage& fft_in)
{
    int sz = 256; // equal to fft_in.width()

    // Get original complex image (backward fft_in)
    ComplexImage original_complex = fft_in;
    fft2d_backward(fft_in, original_complex);

    int gsz = 3;
    PlainImage<float> filter = getGaussMask(gsz);
    ComplexImage filter_complex = ComplexImage::fromFloat(filter);

    int sz_common = pow2ceil(sz); // should be sz+gsz-1 ???

    ComplexImage h = ComplexImage::zeros(sz_common,sz_common);
    ComplexImage g = ComplexImage::zeros(sz_common,sz_common);

    copyImageToCenter(h, original_complex);
    copyImageToCenter(g, filter_complex);

    LOOP_2D(sz_common, sz_common) g.setPoint(x, y, g.at(x, y)*256);

    fft2d_forward(g, g);
    fft2d_forward(h, h);
    fft2d_fft_shift(g);

    // CONVOLVE
    LOOP_2D(sz_common,sz_common) h.setPoint(x, y, h.at(x, y)*g.at(x, y));

    copyImageToCenter(fft_in, h);
    fft2d_backward(fft_in, fft_in);
    fft2d_fft_shift(fft_in);

    // TEST DIFFERENCE BTW DOMAINS
    PlainImage<float> frequency_res(sz,sz);
    writeComplexToPlainImage(fft_in, frequency_res);

    fft2d_forward(fft_in, fft_in);
}

Я попытался обнулить изображение справа и снизу, так что меньшее изображение копируется в начало большего, но это тоже не работает.
Я написал свертку в пространственной области, чтобы сравнить результаты, результаты размытия по частоте < em> почти то же, что и в пространственной области (средняя ошибка между пикселями составляет 5), только когда sz_common = sz.


Итак, не могли бы вы объяснить явления заполнения нулями и нормализации для этого случая? Заранее спасибо.


person Olha    schedule 20.09.2015    source источник


Ответы (1)


Свертка в пространственной области эквивалентна умножению в области Фурье.

Это правда для непрерывных функций, которые определены повсюду.
Однако на практике у нас есть дискретные сигналы и ядра свертки.
Которые требуют более бережного обращения.

Если у вас есть изображение размером M x N и ядро ​​размером MM x NN, если вы примените к ним DFT (FFT - эффективный способ вычисления DFT), вы получите функции размера M x N и MM x NN соответственно.
Более того, приведенная выше теорема об эквивалентности умножения требует умножения одинаковых частот друг на друга.

Поскольку практически ядро ​​намного меньше изображения, обычно оно заполняется нулями до размера изображения.
Теперь, применяя ДПФ, вы получите матрицы того же размера M x N и сможете умножьте их.
Тем не менее, это будет эквивалент круговой свертки между изображением и ядром.

Чтобы применить линейную свертку, вы должны сделать их обоих размером (M + MM - 1) x (N + NN - 1).
Обычно это делается путем применения граничного условия «Репликация» к изображению и нулевой панели Ядро.

Наслаждаться...

P.S. Не могли бы вы поддержать новое предложение сообщества для SE по адресу - http://area51.stackexchange.com/proposals/86832/ .
Нам нужно больше людей, чтобы следить за ними, отвечать на вопросы с менее чем 10 голосами «за» и задавать больше вопросов.

Спасибо.

person Royi    schedule 23.09.2015