有效模糊相机预览

时间:2017-10-20 14:48:10

标签: android camera blur renderscript

到目前为止我已尝试过:

将每个帧转换为位图,使用test project on Github将其模糊,并将其放入相机预览前面的ImageView。显然太慢了 - 比如 1 fps

然后我开始使用 RenderScript 模糊每一帧,处理结果应该放在TextureView中,这是封面相机预览。

该方法的基本代码和平:

使用BlurFilter

ScriptIntrinsicBlur.create(rs, Element.RGBA_8888(rs)).apply {
    setRadius(BLUR_RADIUS)
}
private val yuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.RGBA_8888(rs))
private var surface: SurfaceTexture? = null

private fun setupSurface() {
    if (surface != null) {
        aBlurOut?.surface = Surface(surface)
    }
}

fun reset(width: Int, height: Int) {
    aBlurOut?.destroy()

    this.width = width
    this.height = height

    val tbConvIn = Type.Builder(rs, Element.U8(rs))
            .setX(width)
            .setY(height)
            .setYuvFormat(android.graphics.ImageFormat.NV21)
    aConvIn = Allocation.createTyped(rs, tbConvIn.create(), Allocation.USAGE_SCRIPT)

    val tbConvOut = Type.Builder(rs, Element.RGBA_8888(rs))
            .setX(width)
            .setY(height)
    aConvOut = Allocation.createTyped(rs, tbConvOut.create(), Allocation.USAGE_SCRIPT)

    val tbBlurOut = Type.Builder(rs, Element.RGBA_8888(rs))
            .setX(width)
            .setY(height)
    aBlurOut = Allocation.createTyped(rs, tbBlurOut.create(),
            Allocation.USAGE_SCRIPT or Allocation.USAGE_IO_OUTPUT)

    setupSurface()
}

fun execute(yuv: ByteArray) {
    if (surface != null) {
        //YUV -> RGB
        aConvIn!!.copyFrom(yuv)
        yuvToRgb.setInput(aConvIn)
        yuvToRgb.forEach(aConvOut)
        //RGB -> BLURED RGB
        blurRc.setInput(aConvOut)
        blurRc.forEach(aBlurOut)
        aBlurOut!!.ioSend()
    }
}

MainActivity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    initQrScanner()
}

override fun onStart() {
    super.onStart()
    fotoapparat.start()
}

override fun onStop() {
    fotoapparat.stop()
    super.onStop()
}

private fun initQrScanner() {
    val filter = BlurFilter(RenderScript.create(this))
    tvWholeOverlay.surfaceTextureListener = filter

    fotoapparat = Fotoapparat
            .with(this)
            .into(cvQrScanner)
            .frameProcessor({
                if (it.size.width != filter.width || it.size.height != filter.height) {
                    filter.reset(it.size.width, it.size.height)
                }
                filter.execute(it.image)
            })
            .build()
}

activity_main.xml中

<android.support.constraint.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blur.andrey.blurtest.MainActivity">

    <io.fotoapparat.view.CameraView
        android:id="@+id/cvQrScanner"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <TextureView
        android:id="@+id/tvWholeOverlay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

不幸的是它仍然会变慢 - 3-4 FPS 。旋转模糊叠加也是一种旋转,但这是另一个问题。

我已经创建了 {{3}} ,您可以快速重现问题并检查如何进行优化。期待您的想法。

UPD 我可以通过在模糊之前缩小输入日期来提高性能。我将这些更改推送到测试回购。现在即使在低端设备上我也有非常好的(15-20 FPS)性能,但是具有低分辨率(例如HD),并且在FHD和UHD上不够好((8- 12 FPS)

3 个答案:

答案 0 :(得分:8)

我认为您可以采取的最佳方法是将CameraView替换为TextureView并将其用作相机预览。您可以在此处https://developer.android.com/reference/android/view/TextureView.html轻松找到如何执行此操作的示例 https://github.com/dalinaum/TextureViewDemo/blob/master/src/kr/gdg/android/textureview/CameraActivity.java

下一步是在TextureView上使用模糊着色器。你可以在这里找到一个例子: https://github.com/nekocode/blur-using-textureview/blob/master/app/src/main/java/cn/nekocode/blurringview/sample/BlurFilter.java

您可以使用此着色器或找到更漂亮的着色器。

如果您想要一个随时可用的解决方案,您可以查看此库: https://github.com/krazykira/VidEffects

它允许您将着色器应用于视频视图,因此您可以再次使用模糊着色器来获得所需的效果。

此库的示例:https://stackoverflow.com/a/38125734/3286059

修改

所以我从Nekocode分叉了一个很棒的库,并添加了一个工作模糊过滤器。 请看我最近的提交。 https://github.com/Dimezis/CameraFilter

正如我所说,TextureView + GL着色器。要启用模糊着色器,请单击右上角的菜单,然后选择相应的选项。

如果您需要更快/更好/更简单的模糊着色器,可以在https://www.shadertoy.com

上搜索一个

在某些设备上,您可能会翻转相机预览,但这是另一个需要解决的问题。

答案 1 :(得分:1)

我使用 RenderScript 实现了不错的实时相机模糊效果,并将输出结果预览到 ImageView。

  1. 首先我创建了一个使用渲染脚本的 BlurBuilder 类

     public class BlurBuilder {
     private static final float BITMAP_SCALE = 4f;
     private static final float BLUR_RADIUS = 25f; // 0 - 25
    
     public static Bitmap blur(Context context, Bitmap image) {
         int width = Math.round(image.getWidth() * BITMAP_SCALE);
         int height = Math.round(image.getHeight() * BITMAP_SCALE);
         Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
         Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
         RenderScript rs = RenderScript.create(context);
         ScriptIntrinsicBlur intrinsicBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
         Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
         Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
         intrinsicBlur.setRadius(BLUR_RADIUS);
         intrinsicBlur.setInput(tmpIn);
         intrinsicBlur.forEach(tmpOut);
         tmpOut.copyTo(outputBitmap);
         return outputBitmap;
     }}
    
  2. 我使用 TextureView 来预览相机预览

  3. 在 TextureView 上添加一个 ImageView

  4. 添加一个 setSurfaceTextureListener 并且我添加了一些调整

@Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        Bitmap bm = BlurBuilder.blur(getApplicationContext(),textureView.getBitmap());
        imageView.setImageBitmap(bm);
    }

PS:我在 TextureView 中使用了 Opengles。

我的结果:https://vimeo.com/517761912 设备名称:三星 j7 prime 操作系统版本:Android 8.1 相机:13mp 30fps(设备容量)

答案 2 :(得分:-4)

你需要什么样的模糊?在运行时更改视频非常复杂的任务,并且最有效的方式是使用简单的Mask。只有面具,将适合我们的任务。即使是为了保存视频,我们不仅应用转换,还使用我们的Mask录制视频帧。

在您的情况下,您可以使用Blue操作将CameraView包装到新的Mask中。因此,您将包含此处的布局。

< FrameLayout> 
    < /CameraView> 
    ......
< /FrameLayout>

然后将模糊蒙版应用于FrameLayout视图。对于前者设置背景,就像它显示In This Example

一样