Как сделать прямое с последующим обратным БПФ в Halide

В настоящее время я пытаюсь сделать вперед, а затем обратное FFT, однако, похоже, это не работает. БПФ, который я использую, находится в файле fft.cpp (Halide/apps/fft). Моя цель в настоящее время просто пытается сохранить плитку изображения размером 16x16. Эта плитка 16x16 должна быть направлена ​​вперед, а затем в обратную сторону от плитки 16x16. Моя проблема в том, что мой выходной буфер по какой-то причине имеет значения 9000. Вот мой код:

//A program to make an fft of an image both ways (r2c, c2r)
//Plan of action:
//1.)Load in image into buffer using load_image (uint8)
//2.)Then cast it to a float
//3.)Then convert float buffer to a function
//4.)Then set fft2d settings
//5.)Then call real to complex
//6.)Then call complex to real
//7.)Then realize it to an output buffer
//8.)Then save the image

#include <stdio.h>
#include "Halide.h"
#include "fft.h"
#include "halide_image_io.h"

using namespace Halide;
using namespace Halide::Tools;
using namespace std;


template <typename Type1, typename Type2>
void compare(Halide::Buffer<Type1> org, Halide::Buffer<Type2> other);

Var x{"x"}, y{"y"}, c{"c"};
Func real_result;
ComplexFunc complex_result("Complex_Result");
int colour_channel_to_fft = 1; //or 1 , or 2
int tileSize = 16;

int main(){
    Halide::Buffer<uint8_t> unsignedIntTempBuffer = load_image("rgb.png");

    //2.) Then cast it to a float
    Func uint8_tToFloat;
    uint8_tToFloat(x,y,c) = Halide::cast<float>(unsignedIntTempBuffer(x,y,c));

    Halide::Buffer<float> input;
    input = uint8_tToFloat.realize(unsignedIntTempBuffer.width(),unsignedIntTempBuffer.height(),unsignedIntTempBuffer.channels()); //Input becomes a float buffer

    //3.)Then convert float buffer to a greysacle function
    Func in;
    in(x,y) = input(x,y,colour_channel_to_fft); //Third parameter states which RGB grey scale to use

    Halide::Buffer<float> temp;
    temp = in.realize(input.width(), input.height());

    //4.)Then set fft2d settings - the current setting are defaulted
    Fft2dDesc desc;
    desc.gain = 1.0f;
    desc.vector_width = 0;
    desc.parallel = false;

    //5.)Then call real to complex
    complex_result = fft2d_r2c(in, tileSize, tileSize,get_jit_target_from_environment(), desc);    //Max dimension size of 767

    //Load the complex result into the complexBuffer
    Halide::Buffer<float> complexBuffer;
    complexBuffer = complex_result.realize();

    ComplexFunc cmplxIn;
    cmplxIn(x, y) = ComplexExpr(re(complexBuffer(x, y)), im(complexBuffer(x, y))); //IN GENERATOR THEY USE CHANNEL 1 & 0? Not possible due to us only using one channel for real input
    //6.)Then call complex to real
    real_result = fft2d_c2r(cmplxIn,tileSize,tileSize,get_jit_target_from_environment(),desc);
    Halide::Buffer<float>output;
    output = real_result.realize(); // as output(x,y,c) = re(complex_result(x,y)); doesn't work (seg fault)

    Func floatToUInt8;
    floatToUInt8(x,y,c) = Halide::cast<uint8_t>(output(x,y));

    Halide::Buffer<uint8_t> finalOutput = floatToUInt8.realize(tileSize, tileSize, input.channels());//, input.channels());
    save_image(finalOutput, "forwardThenReverseFFT.png");
    cout << "Success" << endl;
    //Func -> Buffer must use a realize
}

template <typename Type1, typename Type2>
void compare(Halide::Buffer<Type1> org, Halide::Buffer<Type2> other){
    string channel = "";
    if (colour_channel_to_fft == 0) channel = "Red";
    else if (colour_channel_to_fft == 1) channel = "Green";
    else if (colour_channel_to_fft == 2) channel = "Blue";
    else cout<< "You have chosen an incorrect channel";
    std::cout << "Original: " << std::endl << channel << " channel value at (0,0) = " << org(3,3) << std::endl;
    std::cout << "FFTd: " << std::endl << channel << " channel value at (0,0) = " << other(0,0) << std::endl << std::endl;
}

Сохраненное изображение: https://i.stack.imgur.com/9Rqtm.png Похоже, что это не имеет отношения к исходному изображению ни на одном из каналов.

Любые идеи относительно того, что я делаю неправильно?


person Roof_Jumper    schedule 17.12.2019    source источник
comment
Я не использовал Halide, но подозреваю, что проблема может быть в cmplxIn. Может быть, попробовать что-нибудь вместе cmplxIn(x,y)=ComplexExpr(real(complexBuffer(x,y)), imag(complexBuffer(x,y)));   -  person SleuthEye    schedule 17.12.2019
comment
О, да, хорошее определение, я должен был подумать об этом! Теперь я попробовал произвольный доступ, чтобы увидеть, что имеет комплексный буфер, однако, похоже, он хранит значения только в реальной части, так что, возможно, мой комплексный буфер как число с плавающей запятой не поддерживает сложные типы когда ему назначено complex_result.realize()   -  person Roof_Jumper    schedule 18.12.2019
comment
Да и это тоже. Вероятно, вам следует использовать что-то вроде Halide::Buffer<std::complex<float> > complexBuffer;   -  person SleuthEye    schedule 18.12.2019


Ответы (1)


SleuthEye на правильном пути. Я думаю, что часть проблемы заключается в том, что результатом fft2d_r2c является функция с кортежным значением (см. https://halide-lang.org/tutorials/tutorial_lesson_13_tuples.html). ComplexExpr/ComplexFunc являются обертками вокруг Tuple и Func со значением кортежа. Немного удивительно, что он вообще компилируется/запускается, чтобы присвоить реализацию этого Halide::Buffer.

Другая проблема заключается в том, что для реализации нужен размер. (Возможно, вы сможете обойтись без этого, потому что БПФ предъявляют требования к границам вывода.) Для БПФ это немного сложно, потому что комплексная область определена лишь частично. Для БПФ 16x16 комплексная область равна 16x9, остальную часть области можно вычислить, используя свойство сопряженной симметрии ДПФ. (https://github.com/halide/Halide/blob/b465318a583ff58114ac63ecd8125ca7e648ae35/apps/fft/fft.h#L55-L56).

Я подозреваю, что вам нужно что-то вроде этого:

//Load the complex result into the complexBuffer
Halide::Realization complexBuffer = complex_result.realize(16, 9);
Halide::Buffer<float> realPart = complexBuffer[0];
Halide::Buffer<float> imagPart = complexBuffer[1];

// Now construct the complex part for `fft2d_c2r` from these two buffers.

Я думаю, что если вы попытаетесь реализовать (перейти к/от Halide в сложном домене), все будет немного сложнее, чем необходимо. Обычно, если вы используете fft2d_r2c, выполняете некоторую работу, а затем используете fft2d_c2r, все в одном конвейере, вывод границ Halide будет означать, что вам не нужно слишком беспокоиться о нечетных границах области DFT.

person dsharlet    schedule 18.12.2019