【问题标题】:Renderscript ScriptIntrinsicYuvToRGB and Bitmap allocationRenderscript ScriptIntrinsicYuvToRGB 和 Bitmap 分配
【发布时间】:2015-12-24 12:57:26
【问题描述】:

我想制作自己的 SurfaceView 并向那里发送我从 onPreviewFrame(byte[] data, Camera camera) 方法获得的帧。 为此,我需要将帧从 Yuv 转换为 ARGB,并将它们绘制到 Canvas 到我的 SurfaceView。

这是我的 onPreviewFrame:

        public void onPreviewFrame(byte[] data, Camera camera) {

        if (camera != null) {
            camera.addCallbackBuffer(data);
        }

        // using RenderScript
        Bitmap bitmap = RenderScriptFilter.convertYuvToRgbIntrinsic(rs, data, PREVIEW_WIDTH, PREVIEW_HEIGHT);

        if (mFilterSurfaceView != null) {
            SurfaceHolder holder = mFilterSurfaceView.getHolder();
            Canvas canvas = holder.lockCanvas();

            // draw bitmap to the canvas
            canvas.drawBitmap(bitmap, 0, 0, mPaint);

            holder.unlockCanvasAndPost(canvas);
            mFrames++;
            System.gc();
        }
    }
};

这里是 convertYuvToRgbIntrinsic 方法:

    public static Bitmap convertYuvToRgbIntrinsic(RenderScript rs, byte[] data, int w, int h) {

    int imageWidth = w ;
    int imageHeight = h ;

    ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(rs));

    // Create the input allocation  memory for Renderscript to work with
    Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs))
            .setX(imageWidth)
            .setY(imageHeight)
            .setYuvFormat(android.graphics.ImageFormat.NV21);

    Allocation aIn = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
    // Set the YUV frame data into the input allocation
    aIn.copyFrom(data);


    // Create the output allocation
    Type.Builder rgbType = new Type.Builder(rs, Element.RGBA_8888(rs))
            .setX(imageWidth)
            .setY(imageHeight);

    Allocation aOut = Allocation.createTyped(rs, rgbType.create(), Allocation.USAGE_SCRIPT);



    yuvToRgbIntrinsic.setInput(aIn);
    // Run the script for every pixel on the input allocation and put the result in aOut
    yuvToRgbIntrinsic.forEach(aOut);

    // Create an output bitmap and copy the result into that bitmap
    Bitmap outBitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888);
    aOut.copyTo(outBitmap);

    return outBitmap ;

}

我认为问题出现是因为 System.gc() 方法。 因为当我尝试使用 640x480 时一切正常,但使用 1280x720 时会出现一些错误:

12-30 13:06:59.063 18034-18178/youten.redo.y2ndkyuv420sp E/RenderScript:rsAssert 失败:cmd->cmdID

我知道 System.gc() 是一种不好的做法,但是在这里如何分配内存呢? bitmap.recycle();位图=空;没有解决问题。

【问题讨论】:

    标签: bitmap garbage-collection android-camera android-renderscript


    【解决方案1】:

    我不知道是什么触发了您的错误,以及这与垃圾收集器有何关系。无论如何,1280x720 位图会很快耗尽您的内存。

    为每一帧重新创建所有渲染脚本分配会适得其反。重点是一次做好所有准备,然后重复使用。

    附上找到我使用的实用功能:

    public Bitmap convertYuvImageToBitmap(Context context, YuvImage yuvImage) {
    
        int w = yuvImage.getWidth();
        int h = yuvImage.getHeight();
    
        if (rs == null) {
            // once
            rs = RenderScript.create(context);
            yuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
        }
    
        if (yuvAllocation == null || yuvAllocation.getBytesSize() < yuvImage.getYuvData().length) {
            yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuvImage.getYuvData().length);
            yuvAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
            Log.w(TAG, "allocate in " + yuvAllocation.getBytesSize() + " " + w + "x" + h);
        }
    
        if (rgbaAllocation == null || 
                rgbaAllocation.getBytesSize() < rgbaAllocation.getElement().getBytesSize()*w*h) {
            rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(w).setY(h);
            rgbaAllocation = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
            Log.w(TAG, "allocate out " + rgbaAllocation.getBytesSize() + " " + w + "x" + h);
        }
    
        yuvAllocation.copyFrom(yuvImage.getYuvData());
    
        yuvToRgb.setInput(yuvAllocation);
        yuvToRgb.forEach(rgbaAllocation);
    
        if (bmpout == null) {
            bmpout = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        }
        rgbaAllocation.copyTo(bmpout);
        return bmpout;
    }
    

    【讨论】:

    • 谢谢!您的代码运行良好。我已经使用 Nexus 7 进行了测试,可以在 1920x1080 中保留大约 20 帧的内存,并且效果很好。早些时候,我在 Lenovo Yoga Tab 2 上测试了我的旧代码,但它抛出了错误。因此,似乎此延迟功能并不适合所有设备。无论如何,使用 Renderscript 从 yuv 到 rgb 的转换比使用 jni 快得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多