如何在 Halide 中进行正向 FFT 和反向 FFT
How to do a forward followed by inverse FFT in Halide
我目前正在尝试先进行正向 fft,然后进行反向 fft,但它似乎不起作用。
我使用的 FFT 是 fft.cpp (Halide/apps/fft) 中的那个。
我目前的目标只是尝试保存一张 16x16 的图像图块。
这个 16x16 块应该向前,然后是 16x16 块的反向 fft。
我的问题是出于某种原因,我的输出缓冲区的值为 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
这似乎与任何通道上的原始图像都没有关联。
关于我做错了什么有什么想法吗?
SleuthEye 走在正确的轨道上。我认为部分问题在于 fft2d_r2c
的结果是一个元组值函数(参见 https://halide-lang.org/tutorials/tutorial_lesson_13_tuples.html)。 ComplexExpr
/ComplexFunc
是 Tuple
和元组值 Func
的包装器。有点令人惊讶的是,它甚至 compiles/runs 将此实现分配给 Halide::Buffer
.
另一个问题是实现需要一个大小。 (你可能没有这个就可以逃脱,因为 FFT 对输出的边界有要求。)对于 FFT,它有点棘手,因为复数域只是部分定义的。对于 16x16 FFT,复数域为 16x9,域的其余部分可以通过利用 DFT 的共轭对称性 属性 来计算。 (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.
我认为如果你尝试去实现(去to/from Halide in the complex domain),事情会比必要的更困难一些。通常,如果您使用 fft2d_r2c
,做一些工作,然后使用 fft2d_c2r
,所有这些都在一个管道中,Halide 的边界推断意味着您不必太担心DFT域。
我目前正在尝试先进行正向 fft,然后进行反向 fft,但它似乎不起作用。 我使用的 FFT 是 fft.cpp (Halide/apps/fft) 中的那个。 我目前的目标只是尝试保存一张 16x16 的图像图块。 这个 16x16 块应该向前,然后是 16x16 块的反向 fft。 我的问题是出于某种原因,我的输出缓冲区的值为 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 这似乎与任何通道上的原始图像都没有关联。
关于我做错了什么有什么想法吗?
SleuthEye 走在正确的轨道上。我认为部分问题在于 fft2d_r2c
的结果是一个元组值函数(参见 https://halide-lang.org/tutorials/tutorial_lesson_13_tuples.html)。 ComplexExpr
/ComplexFunc
是 Tuple
和元组值 Func
的包装器。有点令人惊讶的是,它甚至 compiles/runs 将此实现分配给 Halide::Buffer
.
另一个问题是实现需要一个大小。 (你可能没有这个就可以逃脱,因为 FFT 对输出的边界有要求。)对于 FFT,它有点棘手,因为复数域只是部分定义的。对于 16x16 FFT,复数域为 16x9,域的其余部分可以通过利用 DFT 的共轭对称性 属性 来计算。 (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.
我认为如果你尝试去实现(去to/from Halide in the complex domain),事情会比必要的更困难一些。通常,如果您使用 fft2d_r2c
,做一些工作,然后使用 fft2d_c2r
,所有这些都在一个管道中,Halide 的边界推断意味着您不必太担心DFT域。