我正在尝试在传入的预览相机帧上应用2D傅立叶变换。
所以这是我在每个onSurfaceTextureUpdated上执行的renderScript代码:

#pragma version(1)
#pragma rs java_package_name(foo.camerarealtimefilters)

rs_allocation inPixels;
int height;
int width;

void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {

    float3 fourierPixel;
    for(int k=0; k<=width; k++){
        for(int l=0; l<=height; l++){
            float3 pixel = convert_float4(rsGetElementAt_uchar4(inPixels, k, l)).rgb;
            float greyOrigPixel = (pixel.r + pixel.g + pixel.b)/3;
            float angle = 2 * M_PI * ( ((x * k) / width) + ((y * l) / height) );
            fourierPixel.rgb = greyOrigPixel*cos(angle);
        };
    };

    out->xyz = convert_uchar3(fourierPixel);
}

inPixels是通过这种方法设置的,
public void setInAllocation(Bitmap bmp) {
    inAllocation = Allocation.createFromBitmap(rs, bmp);
    fourierScript.set_inPixels(inAllocation);
};

现在我的代码背后的数学是什么?基本上应用欧拉公式,忽略相位项,因为我对虚数不能做太多,而仅画出幅度,即实数(余弦)部分。我当然可以对图像进行灰度处理,如您所见。

这是我的资源:

1)http://homepages.inf.ed.ac.uk/rbf/HIPR2/fourier.htm
“... 在图像处理中,通常仅显示傅立叶变换的幅度,因为它包含空间域图像的大部分几何结构信息。

2)http://www.nayuki.io/page/how-to-implement-the-discrete-fourier-transform
我在哪里获得欧拉公式,以及如何应用它。

我的问题是,当我启动应用程序时,它会给我原始图像,无论摄像机看到什么,仅此而已。 2到3秒后它也会卡住。

我的代码有什么问题?太多了吗?我要问的可能吗(我正在Samsung Galaxy S4 Mini上运行它)?我只想在摄像机框架上应用实时简单DFT。

最佳答案

很难说为什么您的图像不显示Java代码就不会显示更新。但是,您可以尝试以下一些方法。

  • 如果可以处理较低的精度,请使用float而不是double,因为这将提高
  • 的性能
  • 如果您可以处理较低的精度,请使用#pragma rs_fp_relaxed,这将有助于提高
  • 的性能。
  • 您可以重新构造RS以使其具有设置功能,该功能应在首次运行之前被调用。使用此设置宽度/高度并预先计算FFT方程
  • 的固定部分

    它看起来像这样:
    rs_allocation angles;
    uint32_t      width;
    uint32_t      height;
    uint32_t      total;
    
    void setupPreCalc(uint32_t w, uint32_t h) {
        uint32_t x;
        uint32_t y;
        float curAngle;
    
        width = w;
        height = h;
        total = w * h;
        for (x = 0; x < width; x++) {
            for (y = 0; y < height; y++) {
                curAngle = 2 * M_PI * (y * width + x);
                rsSetElementAt_float(angles, curAngle, x, y);
            }
        }
    }
    
  • 重新构建内核,以获取输出Allocation元素以及正在操作的xy坐标:
  • void __attribute__((kernel))doFft(uchar4 out, uint32_t x, uint32_t y)
  • 在每帧之前,将输入分配设置为与完成的操作类似,然后重新构建循环以使用预先计算的角度部分。
  • 以前,内核在输入中循环所有坐标,计算灰度像素值,通过类似于您找到的方程式运行它,然后将其设置为新的像素值,并在完成最后一次迭代时保存该值循环的值作为输出值。这不是您想要的。 RS已经在输出Allocation中为您提供了特定位置,因此您需要对与该特定输出点有关的所有输入点进行求和。

  • 使用pre-calc Allocation和新的内核形式,它看起来可能像这样:
    void __attribute__((kernel)) doFft(uchar4 out, uint32_t x, uint32_t y) {
        //  Loop over all input allocation points
        uint32_t inX;
        uint32_t inY;
        float    curAngle;
        float4   curPixel;
        float4   curSum = 0.0;
    
        for (inX = 0; inX < width; inX++) {
            for (inY = 0; inY < height; inY++) {
                curPixel = convert_float4(rsGetElementAt_uchar4(inPixels, x, y));
                curPixel.rgb = (curPixel.r + curPixel.g + curPixel.b) / 3;
    
                curAngle = rsGetElementAt_float(angles, inX, inY);
                curAngle = curAngle * ((x + (y * width)) / total);
    
                curSum += curPixel * cos(curAngle);
            }
        }
    
        out = convert_uchar4(curSum);
    }
    

    10-05 21:36