安卓实时傅里叶变换 - Renderscript

5
我正在尝试对传入的预览相机帧应用二维傅里叶变换。 以下是我的renderScript代码,它在每个onSurfaceTextureUpdated上执行:
#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秒后就会冻结。
我的代码有什么问题吗?它太难处理了吗?我所问的是否可能(我在三星Galaxy S4 Mini上运行)?我只想在相机帧上应用实时简单DFT。
1个回答

3

很难说为什么您的图像没有显示更新,没有看到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已经给出了output Allocation中的特定位置,因此你需要对所有输入点与该特定输出点相关的总和进行求和。
使用预计算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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接