Search code examples
renderscriptimagefiltersobel

RenderScript Sobel lmplementation, different in- and output types


I want to implement a Sobel filter in RenderScript with uchar4 as Input allocation and float[] as Output allocation. I am not quite sure whether it is possible to use different types for Input and Output allocations in a RenderScript. I want to develop the solution myself, but would be grateful to get some advice on the best Renderscript structure to takle that Problem. Somewhere I read, that it is possible to use

float attribute((kernel)) root(uchar4 *v_in, uint32_t x, uint32_t y) { }

Would you recommend such Approach or can this be done without using actually a kernel, i.e. just a function? Thanks in advance.

My rs code for the Sobel (X direction) now looks as follows:

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

rs_allocation gIn;
int32_t width;
int32_t height;

float __attribute__((kernel)) sobelX(uchar4 *v_in, uint32_t x, uint32_t y)    {
float out=0;

 if (x>0 && y>0 && x<(width-1) && y<(height-1){
uchar4 c11=rsGetElementAt_uchar4(gIn, x-1, y-1);
uchar4 c21=rsGetElementAt_uchar4(gIn, x, y-1);
uchar4 c31=rsGetElementAt_uchar4(gIn, x+1, y-1);
uchar4 c13=rsGetElementAt_uchar4(gIn, x-1, y+1);
uchar4 c23=rsGetElementAt_uchar4(gIn, x, y+1);
uchar4 c33=rsGetElementAt_uchar4(gIn, x+1, y+1);

float4 f11=convert_float4(c11);
float4 f21=convert_float4(c21);
float4 f31=convert_float4(c31);
float4 f13=convert_float4(c13);
float4 f23=convert_float4(c23);
float4 f33=convert_float4(c33);

out= f11.r-f13.r + 2*(f21.r-f23.r) + f31.r-f33.r;
}

return out;
}

What I am struggling is passing the Parameters from Java side:

        float[][] gx = new float[width][height];
        ScriptC_sobel script;
        script=new ScriptC_sobel(rs);

        script.set_width(width) ;
        script.set_height(height) ;
        script.set_gIn(bmpGray);

        Allocation inAllocation = Allocation.createFromBitmap(rs, bmpGray, Allocation.MipmapControl.MIPMAP_NONE,
                Allocation.USAGE_SCRIPT);
        Allocation outAllocation = Allocation.createTyped(rs, float,2) ;

        script.forEach_sobelX(inAllocation, outAllocation);
        outAllocation.copyTo(gx) ;

I understand that, in order to use rsGetElementAt function (to access neighboring data within the kernel) I need to set the input allocation as a script global as well (rs_allocation gIn in rs code). However, I'm not sure how to handle this "double allocation" from the Java side. Also the outAllocation Statement in the Java code is probably not correct. Specifiyally I am not sure, whether the Kernel will returned this as float[] or as float[][].


Solution

  • It is possible to use different types for input and output. In your case, I would actually suggest:

    float __attribute__((kernel)) sobel(unchar4 *v_in, uint32_t x, uint32_t y) {}
    

    You certainly want to use a kernel, so that the performance can benefit from execution by multiple threads.

    Also, have a look at this example of doing 3x3 convolution in RS.

    UPDATE: generally, the best in/out parameters to use depend on the type of output you want this filter to generate - is it just the magnitude? Then uint output will most likely suffice.

    UPDATE2: If you are going to use a variable to pass input allocation, then you don't need it in the kernel parameters, i.e.:

    float __attribute__((kernel)) sobelX(uint32_t x, uint32_t y)
    

    The rest of the script looks ok (sans missing parenthesis in the conditional). As for the Java part, below I am pasting a demonstration of how you should prepare the output allocation and start the script. The kernel will then be invoked for every cell (i.e. every float) in the output allocation.

                float[] gx = new float[width * height];
                Type.Builder TypeIn = new Type.Builder(mRS, Element.F32(mRS));
                TypeIn.setX(width).setY(height);
    
                Allocation outAllocation = Allocation.createTyped(mRS, TypeIn.create());
    
                mScript.forEach_sobelX(outAllocation);