Search code examples
androidallocationrenderscript

RenderScript one input- and two output-Allocations


I managed to write a Kernel that transforms an input-Bitmap to a float[] of Sobel gradients (two separate Kernels for SobelX and SobelY). I did this by assigning the input-Bitmap as a global variable and then passing the Kernel based on the Output allocation and referencing the neighbors of the Input-Bitmap via rsGetElementAt. Since I actually want to calculate the Magnitude (hypot(Sx,Sy) AND the Direction (atan2(Sy,Sx)) it would be nice to do the whole thing in one Kernel-pass. If I only had to calculate the Magnitude Array, this could be done with the same structure (1 intput Bitmap, 1 Output float[]). Now I wonder, whether it is possible to just add an additional Allocation for the Direction Output (also a float[]). I tried this with the rs-function rsSetElementAt() as follows:

 #pragma version(1)
 #pragma rs java_package_name(com.example.xxx)
 #pragma rs_fp_relaxed

 rs_allocation gIn, direction;
 int32_t width;
 int32_t height;


 // Sobel, Magnitude und Direction
 float __attribute__((kernel)) sobel_XY(uint32_t x, uint32_t y) {
 float outX=0, outY=0;

if (x>0 && y>0 && x<(width-1) && y<(height-1)){

    uchar4 c11=rsGetElementAt_uchar4(gIn, x-1, y-1); uchar4 c12=rsGetElementAt_uchar4(gIn, x-1, y);uchar4 c13=rsGetElementAt_uchar4(gIn, x-1, y+1);
    uchar4 c21=rsGetElementAt_uchar4(gIn, x, y-1);uchar4 c23=rsGetElementAt_uchar4(gIn, x, y+1);
    uchar4 c31=rsGetElementAt_uchar4(gIn, x+1, y-1);uchar4 c32=rsGetElementAt_uchar4(gIn, x+1, y);uchar4 c33=rsGetElementAt_uchar4(gIn, x+1, y+1);

    float4 f11=rsUnpackColor8888(c11);float4 f12=rsUnpackColor8888(c12);float4 f13=rsUnpackColor8888(c13);
    float4 f21=rsUnpackColor8888(c21); float4 f23=rsUnpackColor8888(c23);
    float4 f31=rsUnpackColor8888(c31);float4 f32=rsUnpackColor8888(c32);float4 f33=rsUnpackColor8888(c33);

    outX= f11.r-f31.r + 2*(f12.r-f32.r) + f13.r-f33.r;
    outY= f11.r-f13.r + 2*(f21.r-f23.r) + f31.r-f33.r;

    float d = atan2(outY, outX);
    rsSetElementAt_float(direction, d, x, y);
    return hypot(outX, outY);

    }
}

And the corresponding Java code:

 ScriptC_sobel script;
 script=new ScriptC_sobel(rs);
 script.set_gIn(Allocation.createFromBitmap(rs, bmpGray));

 Type.Builder TypeOut = new Type.Builder(rs, Element.F32(rs));
 TypeOut.setX(width).setY(height);
 Allocation outAllocation = Allocation.createTyped(rs, TypeOut.create());

 // the following 3 lines shall reflect the allocation to the Direction output
 Type.Builder TypeDir = new Type.Builder(rs, Element.F32(rs));
 TypeDir.setX(width).setY(height);
 Allocation dirAllocation = Allocation.createTyped(rs, TypeDir.create());

 script.forEach_sobel_XY(outAllocation);
 outAllocation.copyTo(gm) ;
 dirAllocation.copyTo(gd);

Unfortunately this does not work. I am not sure, whether the problem is with the structural logic of the rs-kernel or is it because I cannot use a second Type.Builder assignment within the Java code (because the kernel is already tied to the Magnitude Output-allocation). Any help is highly appreciated. PS: I see that there is no link between the second Type.Builder assignment and the "direction" allocaton in rs - but how can this be achieved?


Solution

  • The outAllocation is passed as a parameter to the kernel. But the existence and location of dirAllocation also has to be communicated to the Renderscript side. Do this just before starting the script:

    script.set_direction(dirAllocation);
    

    Also, read about memory allocation in Renderscript.