简体   繁体   English

Android傅里叶变换实时 - Renderscript

[英]Android Fourier Transform Realtime - Renderscript

I am trying to apply a 2D Fourier Transform on incoming preview camera frames. 我正在尝试对传入的预览相机帧应用2D傅里叶变换。 So here is my renderScript code that executes on each onSurfaceTextureUpdated : 所以这是我在每个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);
}

The inPixels is set by this method, inPixels由此方法设置,

public void setInAllocation(Bitmap bmp) {
    inAllocation = Allocation.createFromBitmap(rs, bmp);
    fourierScript.set_inPixels(inAllocation);
};

Now the maths behind my code? 现在我的代码背后的数学? Basically apply Euler's formula, ignore the phase term as I can't do much with imaginary numbers, and draw the magnitude only, that is the real (cosine) part. 基本上应用欧拉公式,忽略相位项,因为我不能用虚数做多少,只绘制幅度,即真实(余弦)部分。 I of course grayscale the image as you can see. 我当然可以看到灰度图像。

Here are my resources: 这是我的资源:

1) http://homepages.inf.ed.ac.uk/rbf/HIPR2/fourier.htm "... In image processing, often only the magnitude of the Fourier Transform is displayed, as it contains most of the information of the geometric structure of the spatial domain image .." 1) http://homepages.inf.ed.ac.uk/rbf/HIPR2/fourier.htm “...... 在图像处理中,通常只显示傅立叶变换的幅度,因为它包含了大部分信息。空间域图像的几何结构 。“

2) http://www.nayuki.io/page/how-to-implement-the-discrete-fourier-transform Where I got the Euler formula, and how I applied it. 2) http://www.nayuki.io/page/how-to-implement-the-discrete-fourier-transform 我得到了欧拉公式,以及我如何应用它。

My problem, is that when I start my app, it gives me the original image, whatever the camera sees, and nothing more. 我的问题是,当我启动我的应用程序时,它会给我原始图像,无论相机看到什么,仅此而已。 It also freezes after 2 to 3 seconds. 它也会在2到3秒后冻结。

What is wrong with my code? 我的代码出了什么问题? Is it too much to handle? 处理太多了吗? Is what I am asking possible (I am running this on a Samsung Galaxy S4 Mini)? 我问的是可能的(我在三星Galaxy S4 Mini上运行)? I just want to apply realtime simple DFT on a camera frame. 我只是想在相机框架上应用实时简单的DFT。

It's tough to say why your image would not be showing updates without seeing the Java code. 很难说为什么你的图像不会在没有看到Java代码的情况下显示更新。 However, here are a few things you might try to help. 但是,您可以尝试一些帮助。

  • If you can handle lower precision, use float instead of double as this will improve performance 如果您可以处理较低的精度,请使用float而不是double因为这样可以提高性能

  • If you can handle lower precision, use #pragma rs_fp_relaxed which will help performance 如果您可以处理较低的精度,请使用#pragma rs_fp_relaxed来帮助提高性能

  • You can re-structure your RS to have a setup function which should be called before it is run for the first time. 您可以重新构建RS以具有设置功能,该功能应在首次运行之前调用。 Use this to setup the width/height and pre-calculate the fixed parts of the FFT equation 使用它来设置宽度/高度并预先计算FFT方程的固定部分

It will look something like this: 它看起来像这样:

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);
        }
    }
}
  • Re-structure your kernel to get the output Allocation element and the x and y coordinates being operated on: 重构内核以获取输出Allocation元素以及操作的xy坐标:

void __attribute__((kernel))doFft(uchar4 out, uint32_t x, uint32_t y)

  • Before each frame, set the input allocation similar to what you have done then re-structure your loop to use the pre-calculated parts of the angle. 在每个帧之前,设置类似于您所做的输入分配,然后重新构造循环以使用预先计算的角度部分。

  • Previously, the kernel was looping over all coordinates in the input, calculating a greyscale pixel value, running it through something similar to the equation you found, then setting it to be a new pixel value and when done saving that value from the final iteration of the loop to be the output value. 以前,内核循环遍历输入中的所有坐标,计算灰度像素值,通过类似于您找到的等式运行它,然后将其设置为新的像素值,并在完成时从最后一次迭代中保存该值。循环作为输出值。 This isn't exactly what you want. 这不完全是你想要的。 RS is already giving you a specific location in the output Allocation , so you need to do the summation of all input points in relation to that specific output point. RS已经在输出 Allocation为您提供了特定位置,因此您需要对与该特定输出点相关的所有输入点进行求和。

Using the pre-calc Allocation and the new form of the kernel, it could look like this: 使用预计算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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM