RenderScript应用(四)实现相机模糊切换效果

使用RenderScript实现相机模糊切换效果

实现原理

步骤:
1.得到相机每一帧数据的回调
2.将YUV格式转换为ARGB格式
3.缩小图片
4.模糊图片
5.显示到视图中。

具体实现代码

renderscript相关代码

YUV转换为ARGB代码如下:

// Needed directive for RS to work
#pragma version(1)

// The java_package_name directive needs to use your Activity's package path
#pragma rs java_package_name(cn.wps.moffice.main.scan.util.camera)

rs_allocation inputAllocation;

int wIn, hIn;
int numTotalPixels;

// Function to invoke before applying conversion
void setInputImageSize(int _w, int _h)
{
    wIn = _w;
    hIn = _h;
    numTotalPixels = wIn * hIn;
}

// Kernel that converts a YUV element to a RGBA one
uchar4 __attribute__((kernel)) convert(uint32_t x, uint32_t y)
{

    // YUV 4:2:0 planar image, with 8 bit Y samples, followed by
    // interleaved V/U plane with 8bit 2x2 subsampled chroma samples
    int baseIdx = x + y * wIn;
    int baseUYIndex = numTotalPixels + (y >> 1) * wIn + (x & 0xfffffe);

    uchar _y = rsGetElementAt_uchar(inputAllocation, baseIdx);
    uchar _v = rsGetElementAt_uchar(inputAllocation, baseUYIndex);
    uchar _u = rsGetElementAt_uchar(inputAllocation, baseUYIndex + 1);
    _y = _y < 16 ? 16 : _y;

    short Y = ((short)_y) - 16;
    short U = ((short)_u) - 128;
    short V = ((short)_v) - 128;

    uchar4 out;
    out.r = (uchar) clamp(((Y * 298 + V * 409 + 128) >> 8), 0, 255);
    out.g = (uchar) clamp(((Y * 298 - U * 100 - V * 208 + 128) >> 8), 0, 255);
    out.b = (uchar) clamp(((Y * 298 + U * 516 + 128) >> 8), 0, 255);
    out.a = 255;

    return out;
}

缩小图片代码如下:

// Needed directive for RS to work
#pragma version(1)
// Change java_package_name directive to match your Activity's package path
#pragma rs java_package_name(cn.wps.moffice.main.scan.util.camera)


rs_allocation inputAllocation;

// Sets image sizes and calculates the scale factor
static float scaleInv;
static int inputWidth, inputHeight, outputWidth, outputHeight;

void setInformation(int _inputWidth, int _inputHeight,
    int _outputWidth, int _outputHeight){

    inputWidth = _inputWidth;
    inputHeight = _inputHeight;
    outputWidth = _outputWidth;
    outputHeight = _outputHeight;

    // Calculates inverse scale factor, by which
    // to round coordinates.
    //
    // Ex:
    // Input size is 100
    // Output desired size is 25
    //
    // Scale factor is 25 / 100 = 0.25
    // Inverse scale factor is 1 / 0.25 = 4
    //
    // When iterating directly on the output
    // allocation, to get input element it is needed
    // to use the inverse scale factor.
    //
    // Current output element index is 20
    // Respective input element index is 20 * 4 = 80
    //
    scaleInv = (float)inputWidth/(float)outputWidth;
}

uchar4 __attribute__((kernel)) resizeNearest(uint32_t x, uint32_t y) {

    float fX = clamp(x*scaleInv, 0.0f, (float)inputWidth);
    float fY = clamp(y*scaleInv, 0.0f, (float)inputHeight);

    return rsGetElementAt_uchar4(inputAllocation, fX, fY);
}


Java层相关代码

获取相机每一帧的回调

    private void prepareCamera() {
        float perPixel = ImageFormat.getBitsPerPixel(mParameters.getPreviewFormat()) / 8f;
        int previewBufferSize = (int) (mParameters.getPreviewSize().width * mParameters.getPreviewSize().height * perPixel);
        mCameraDevice.addCallbackBuffer(new byte[previewBufferSize]);
        mCameraDevice.addCallbackBuffer(new byte[previewBufferSize]);
        mCameraDevice.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
            @Override
            public void onPreviewFrame(byte[] data, Camera camera) {
                if (isScreenShot) {
                    isScreenShot = false;
                    screenShot(data);
                }
                camera.addCallbackBuffer(data);
            }
        });
    }
    private void screenShot(final byte[] pixel) {
        if(!Utils.hasKitKat()) return;
        GlobalThreadPool.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                final Bitmap bitmap = BitmapTrans.rotateBitmap(imgProcess.processImg(pixel, mParameters.getPreviewSize().width, mParameters.getPreviewSize().height), 90);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        iv_capture_view.setImageBitmap(bitmap);
                        iv_capture_view.setVisibility(View.VISIBLE);
                        AlphaAnimation inAnimation = new AlphaAnimation(0f, 1.0f);
                        inAnimation.setDuration(250);
                        iv_capture_view.startAnimation(inAnimation);
                    }
                });
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        iv_capture_view.setVisibility(View.INVISIBLE);
                        AlphaAnimation outAnimation = new AlphaAnimation(1f, 0f);
                        outAnimation.setDuration(250);
                        iv_capture_view.startAnimation(outAnimation);

                        tv_camera_tip.setVisibility(View.VISIBLE);
                        AlphaAnimation inAnimation = new AlphaAnimation(0f, 1.0f);
                        inAnimation.setDuration(250);
                        tv_camera_tip.startAnimation(inAnimation);

                    }
                }, 600);
            }
        });

    }

执行Renderscript的Java工具类

package cn.wps.moffice.main.scan.util.img;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.renderscript.Type;

import cn.wps.moffice.main.scan.util.GlobalThreadPool;
import cn.wps.moffice.main.scan.util.camera.ScriptC_customYUVToRGBAConverter;
import cn.wps.moffice.main.scan.util.camera.ScriptC_resize_img;
import cn.wps.moffice.util.KSLog2;

/**
 * Created by CW80JD2 on 2017/8/10.
 */

public class RSImgProcess {
    private RenderScript mRS;
    final float per = 0.2f;
    private int outWidth, outHeight;
    private ScriptC_customYUVToRGBAConverter customYUVToRGBAScript;
    private ScriptC_resize_img resizeScript;
    private ScriptIntrinsicBlur blurScript;
    private Object lock = new Object();
    private boolean isInit = false;

    public RSImgProcess(final Context context) {
        if(!Utils.hasKitKat()) return;
        GlobalThreadPool.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    init(context);
                    isInit = true;
                    lock.notifyAll();
                }

            }
        });

    }


    private void init(Context context) {
        // Creates a RS context.
        mRS = RenderScript.create(context);
        customYUVToRGBAScript = new ScriptC_customYUVToRGBAConverter(mRS);
        // Instantiates the blur script
        blurScript = ScriptIntrinsicBlur.create(mRS, Element.RGBA_8888(mRS));
        resizeScript = new ScriptC_resize_img(mRS);
    }

    private Allocation blur(Allocation inputAllocation, int width, int height) {
        // Creates an output Allocation where to store the blur result
        Type.Builder tbOutput = new Type.Builder(mRS, Element.RGBA_8888(mRS));
        tbOutput.setX(width);
        tbOutput.setY(height);
        Allocation outputAllocation = Allocation.createTyped(mRS, tbOutput.create());

        // Sets the blur radius (pixels)
        blurScript.setRadius(25);
        // Compute the blurred allocation
        blurScript.setInput(inputAllocation);
        blurScript.forEach(outputAllocation);
        return outputAllocation;
    }

    private Allocation resize(Allocation inputAllocation, int width, int height) {
        outWidth = (int) (width * per + 0.5);
        outHeight =  (int) (height * per + 0.5);
        // Creates an output Allocation where to store the blur result
        Type.Builder tbOutput = new Type.Builder(mRS, Element.RGBA_8888(mRS));
        tbOutput.setX(outWidth);
        tbOutput.setY(outHeight);
        Allocation outputAllocation = Allocation.createTyped(mRS, tbOutput.create());

        resizeScript.set_inputAllocation(inputAllocation);
        resizeScript.invoke_setInformation(width, height, outWidth, outHeight);
        resizeScript.forEach_resizeNearest(outputAllocation);
        return outputAllocation;
    }


    private Allocation YUVToRGBA(byte[] dataIn, int width, int height) {
        // Calculates expected YUV bytes count as YUV is not a human friendly way of storing data:
        // https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
        int expectedBytes = (int) (width * height * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8f);

        Type.Builder yuvTypeBuilder = new Type.Builder(mRS, Element.U8(mRS)).setX(expectedBytes);
        Type yuvType = yuvTypeBuilder.create();
        Allocation inputAllocation = Allocation.createTyped(mRS, yuvType, Allocation.USAGE_SCRIPT);
        inputAllocation.copyFrom(dataIn);

        customYUVToRGBAScript.invoke_setInputImageSize(width, height);
        customYUVToRGBAScript.set_inputAllocation(inputAllocation);

        // Creates temporary allocation that will match camera preview size
        Type.Builder rgbaType = new Type.Builder(mRS, Element.RGBA_8888(mRS)).setX(width).setY(height);
        Allocation midAllocation = Allocation.createTyped(mRS, rgbaType.create(), Allocation.USAGE_SCRIPT);

        customYUVToRGBAScript.forEach_convert(midAllocation);
        return midAllocation;
    }


    public Bitmap processImg(byte[] dataIn, int width, int height) {
        if(!Utils.hasKitKat()) return null;
        synchronized (lock) {
            while (!isInit) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        long startTime = System.currentTimeMillis();
        Allocation allocation = YUVToRGBA(dataIn, width, height);
        Allocation resizeAllocation = resize(allocation, width, height);
        Allocation outputAllocation = blur(resizeAllocation, outWidth, outHeight);

        // Creates output Bitmap, matching input one size.
        Bitmap outBitmap = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        // Copy calculation result to the output Bitmap.
        outputAllocation.copyTo(outBitmap);

        KSLog2.KSLog().d("processImg = " + (System.currentTimeMillis() - startTime));
        return outBitmap;
    }

}

效果

这里写图片描述

总结

使用Renderscript有什么优点?
1.通过GPU来计算从而减少CPU的使用来达到更高的用户响应(应为使主线程使用CPU来刷新界面)
2.计算速度更加快(GPU由于具有很多处理单元,能够更好地做并行计算)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值