Search code examples
androidarrayscroprenderscript

Crop image data using Renderscript Launch Options


I have an byte[] array of a greyvalue image dataIn with dimension width * height. Now, I want to crop the image by applying a translation (dx, dy) and cut-off the out-of-border regions, so that the dataOut has dimension (width-abs(dx))*(height-abs(dy)).

In RenderScript I would use a 2-d uchar-Allocation both for the input and the output. In order to efficiently apply the crop operation, it was thinking of using the LaunchOptions with (for example) setX(dx,width) and setY(0, height-dy) and apply a trivial kernel that just takes values from a subset of the original dimensions.

However, when using the Launch Options, the out-Allocation still has the original size width*height, i.e. the cropped parts will just be shown as zeros - but, I actually want them to be removed, i.e. out-Allocation be of reduced dimension.

Question: is there a solution in RS in order to perform this cropping job more elegantly? Thanks for your feedback.

UPDATE: I think, I found the solution. It is by defining the out-Allocation as a script global at the reduced dimensions from the outset, passing dx and dy as well as globals and then apply rsSetElementAt_uchar to set the values of the out-Allocation. Will give an udpate later.


Solution

  • So, here is my quick rs-crop tool, taking 5ms for cropping a 500k pixel image. It uses LaunchOptions and reduced dimensions for the cropped output. Should you need to crop a Bitmap, just use element type U8_4 and allocation uchar_4 instead of U8 and uchar, respectively.

    The crop.rs file:

    #pragma version(1)
    #pragma rs java_package_name(com.xxx.yyy)
    #pragma rs_fp_relaxed
    
    int32_t width;
    int32_t height;
    
    rs_allocation croppedImg;
    uint xStart, yStart;
    
    void __attribute__((kernel)) doCrop(uchar in,uint32_t x, uint32_t y) {
        rsSetElementAt_uchar(croppedImg,in, x-xStart, y-yStart);
    }
    

    The Java part:

    // data1 is the byte[] array with (grayvalue) data of size 
    // width*height you want to crop.
    
    // define crop shift (dx, dy) here
    int dx=0;       // (-width < dx < width);
    int dy=250;     // (- height < dy < height);
    
    int xStart=0, xEnd=0;
    int yStart=0, yEnd=0;
    
    // x direction
    if (dx<0) {
        xStart= Math.abs(dx);
        xEnd=width;
    }
    else {
        xStart = 0;
        xEnd = width - Math.abs(dx);
    }
    
    // same for y direction
    if (dy<0) {
        yStart= Math.abs(dy);
        yEnd=height;
    }
    else {
        yStart = 0;
        yEnd = height - Math.abs(dy);
    }
    
    // initiate rs and crop script
    RenderScript rs = RenderScript.create(this);
    ScriptC_crop mcropScr=new ScriptC_crop (rs);
    
    // define allocations. Note the reduced size of cropAlloc 
    Type.Builder typeUCHAR = new Type.Builder(rs, Element.U8(rs));  
    typeUCHAR.setX(width).setY(height);
    
    inAlloc = Allocation.createTyped(rs, typeUCHAR.create());
    inAlloc.copyFrom(data1);
    
    Type.Builder TypeUCHARCropped = new Type.Builder(rs, Element.U8(rs));
    TypeUCHARCropped.setX(xEnd-xStart).setY(yEnd-yStart);
    
    Allocation cropAlloc = Allocation.createTyped(rs,  TypeUCHARCropped.create());
    mcropScr.set_croppedImg(cropAlloc);
    mcropScr.set_xStart(xStart);
    mcropScr.set_yStart(yStart);
    
    Script.LaunchOptions lo = new Script.LaunchOptions();
    lo.setX(xStart, xEnd);
    lo.setY(yStart, yEnd);
    
    mcropScr.forEach_doCrop(inAlloc, lo);
    
    
    byte[] data1_cropped =new byte[(xEnd-xStart)*(yEnd-yStart)];
    cropAlloc.copyTo(data1_cropped);