androidmatlabbitmaprenderscript

Using Renderscript convolution on a specific location of bitmap


In MATLAB there is an option for doing 2D convolutions, that you can use it so you can have each area where convolution happened. for example if you have done a 3x3 convolution you can say that save the result of each 3x3 window and use it. the command is like this:

X = conv2(A,B,'same');

the 'same' keyword is going to return the central part of the convolution of the same size as A.

I want to know is there anyway were I can do something like this with renderscript in Android? I will put a picture from MATLAB documentation so you can get a better understanding of what I mean. again the picture is from MATLAB documentation which is free.

The same keyword for a 2D convolution of matlab


Solution

  • You can use the Script.LaunchOptions class.

    Using this class you can set limits of execution of kernels.

    Example: you want to run a kernel on a "rectangle" of your data (so, in 2 dimensions), starting at x-index (0-based, inclusive) 3 and ending at the x-index (exclusive) 8 and, on the y side the limits are 11 and 22:

    Script.LaunchOptions launchOptions;
    launchOptions = new Script.LaunchOptions();
    launchOptions.setX(3, 8);
    launchOptions.setY(11, 22);
    
    // Kernel run
    myScript.forEach_myKernelName(inAlloc, outAlloc, launchOptions);
    

    Example: you want to apply a kernel over an image, with a 3-pixel-wide border (example directly taken from the FASTExample sample project):

    Script.LaunchOptions fastLaunchOptions;
    fastLaunchOptions = new Script.LaunchOptions();
    fastLaunchOptions.setX(3, inputImageSize.width - 3);
    fastLaunchOptions.setY(3, inputImageSize.height - 3);
    
    // ...
    
    scriptCFast.forEach_fastOptimized(
          grayAllocation, fastKpAllocation, fastLaunchOptions);
    

    Example: you want to apply a ScriptIntrinsicConvolve3x3 kernel over a restricted range:

    // Define the convolution
    ScriptIntrinsicConvolve3x3 convolve3x3 =
        ScriptIntrinsicConvolve3x3.create(mRS, Element.RGBA_8888(mRS));
    
    // Some coefficients
    float[] coefficients = {
            0.7f, 0, 0.5f,
            0, 1.0f, 0,
            0.5f, 0, 1.0f
    };
    convolve3x3.setCoefficients(coefficients);
    
    // Execute the allocation with limits
    Script.LaunchOptions launchOptions;
    launchOptions = new Script.LaunchOptions();
    launchOptions.setX(3, 8);
    launchOptions.setY(11, 22);
    
    convolve3x3.setInput(inputAllocation);
    convolve3x3.forEach(convolvedAllocation, launchOptions);
    

    Note: this process just executes a kernel over a certain range, but it is NOT going to create a new, smaller allocation. So, after having executed a kernel over certain limits, you should copy the result of it using a copy kernel, like the following one:

    // Store the input allocation
    rs_allocation inputAllocation;
    
    // Offset indices, which define the start point for 
    // the copy in the input allocation.
    int inputOffsetX;
    int inputOffsetY;
    
    uchar4 __attribute__((kernel)) copyAllocation(int x, int y) {
    
        return rsGetElementAt_uchar4(
             inputAllocation, x + inputOffsetX, y + inputOffsetY);
    
    }
    

    Invoked with:

    scriptC_main.set_inputAllocation(convolvedAllocation);
    scriptC_main.set_inputOffsetX(offsetX);
    scriptC_main.set_inputOffsetY(offsetY);
    scriptC_main.forEach_copyAllocation(outputAllocation);
    

    Edit: I specifically created an example for this case, where you can see the following process:

    Reference: RenderScript: parallel computing on Android, the easy way