Android preview YUV转换 RenderScript 优化

PS:2018/0807

  不可以使用生产者模式 去不断的textureView.getBitmap, 否则RenderThread线程会 非常奇怪的抛出异常(疑似当前Frame被释放,再get的native层空指针)。应该在TextureView.SurfaceTextureListener的onSurfaceTextureUpdated(SurfaceTexture surface) 回调内去textureView.getBitmap

PS:2018/05/07

   最近又发现TextureView的getBitmap方法 更快,比RenderScript还快。具体就是讲Camera 的Preview set到TextureView上,然后调用TextureView的getBitmap方法。推荐用生产者模式去getBitmap,我试验的是整幅图也就10+ms

 

Context

    YUV格式转换为Bitmap比较耗时,使用RenderScript从50ms降到3、4ms(和像素值、手机相关,但比例差不多)

    RenderScript耗时的地方是内存Allocation的分配,RenderScript的初始化,所以要设置为属性 缓存起来,不要重复创建。只要预览的宽高不变是不用变Allocation的大小的。

 

RenderScript

    简单讲就是 Android系统为 解决高计算作业问题 采用了基于 C语言风格的RenderScript。具体:系统利用GPU、CPU多核来处理并发调度,使程序员只专注于算法。系统自己提供了几个常用的RenderScript,高斯模糊、直方图、yuv格式转换。

    具体怎么看官网,不累述了,直接上代码

    参考博客:  Camera使用RenderScript   

 

代码

    private void initRenderScript() {
        renderScript = RenderScript.create(this);
        yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(renderScript, Element.U8_4(renderScript));
        script = new ScriptC_rotator(renderScript);
    }
    public Bitmap YUV_toRGB(byte[] yuvByteArray, int width, int height) {

        Allocation in = getYuvAllocationIn(yuvByteArray);
        Allocation out = getAllocationOut(width, height);

        in.copyFrom(yuvByteArray);

        yuvToRgbIntrinsic.setInput(in);
        yuvToRgbIntrinsic.forEach(out);
        Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        out.copyTo(bmp);
        bmp = rotate(bmp);
        return bmp;
    }
    private Allocation getYuvAllocationIn(byte[] yuvByteArray) {
        if (mInAllocation == null || yuvByteArray.length != preYuvInLength) {
            preYuvInLength = yuvByteArray.length;
            Type.Builder yuvType = new Type.Builder(renderScript, Element.U8(renderScript)).setX(yuvByteArray.length);
            mInAllocation =  Allocation.createTyped(renderScript, yuvType.create(), Allocation.USAGE_SCRIPT);
        }
        return mInAllocation;
    }
    private Allocation getAllocationOut(int W, int H) {
        if (preW != W || preH != H) {
            preW = W;
            preH = H;
            Type.Builder rgbaType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript)).setX(W).setY(H);
            outAllocation = Allocation.createTyped(renderScript, rgbaType.create(), Allocation.USAGE_SCRIPT);
        }
        return outAllocation;
    }

 

    public Bitmap rotate(Bitmap bitmap) {
        Bitmap.Config config = bitmap.getConfig();
        int targetHeight = bitmap.getWidth();
        int targetWidth = bitmap.getHeight();

        script.set_inWidth(bitmap.getWidth());
        script.set_inHeight(bitmap.getHeight());

        Allocation sourceAllocation = getFromRotateAllocation(bitmap);
        sourceAllocation.copyFrom(bitmap);
        script.set_inImage(sourceAllocation);
        bitmap.recycle();

        Bitmap target = Bitmap.createBitmap(targetWidth, targetHeight, config);
        final Allocation targetAllocation = getToRotateAllocation(target);
        script.forEach_rotate_270_clockwise(targetAllocation, targetAllocation);

        targetAllocation.copyTo(target);

        return target;
    }

 

    private Allocation getFromRotateAllocation(Bitmap bitmap) {
        int targetHeight = bitmap.getWidth();
        int targetWidth = bitmap.getHeight();
        if (targetHeight != preRotateHeight || targetWidth != preRotateWidth) {
            preRotateHeight = targetHeight;
            preRotateWidth = targetWidth;
            fromRotateAllocation = Allocation.createFromBitmap(renderScript, bitmap,
                    Allocation.MipmapControl.MIPMAP_NONE,
                    Allocation.USAGE_SCRIPT);
        }
        return fromRotateAllocation;
    }

    private Allocation getToRotateAllocation(Bitmap bitmap) {
        int targetHeight = bitmap.getWidth();
        int targetWidth = bitmap.getHeight();
        if (targetHeight != preRotateHeight || targetWidth != preRotateWidth) {
            toRotateAllocation =  Allocation.createFromBitmap(renderScript, bitmap,
                    Allocation.MipmapControl.MIPMAP_NONE,
                    Allocation.USAGE_SCRIPT);
        }
        return toRotateAllocation;
    }

Preview拿的是物理设备方向,所以用时需要旋转,旋转的RenderScript是自己写的。

main/rs/your.package.name(包名)/rotate.rs

#pragma version(1)
#pragma rs java_package_name(your.package.name)

rs_allocation inImage;
int inWidth;
int inHeight;

uchar4 __attribute__ ((kernel)) rotate_90_clockwise (uchar4 in, uint32_t x, uint32_t y) {
    uint32_t inX  = inWidth - 1 - y;
    uint32_t inY = x;
    const uchar4 *out = rsGetElementAt(inImage, inX, inY);
    return *out;
}

uchar4 __attribute__ ((kernel)) rotate_270_clockwise (uchar4 in, uint32_t x, uint32_t y) {
    uint32_t inX = y;
    uint32_t inY = inHeight - 1 - x;

    const uchar4 *out = rsGetElementAt(inImage, inX, inY);
    return *out;
}

#pragma rs java_package_name(your.package.name)  这一行里的your.package.name需要替换你放置rotate.rs的地方

build后系统自动生成ScriptC_rotator 类,上文的script就是ScriptC_rotator的对象

 

 

 

 

 

你可能感兴趣的:(Android)