What I've tried so far:
Convert every frame into bitmap, blur it with library and put it into ImageView
which is in front of camera preview. Obviously was too slow - something like 1 fps.
Then I started to use RenderScript which blurs every frame and result of processing should be placed in TextureView
which is cover camera preview.
Essential peaces of code of that approach:
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>
And unfortunately it is still to slow - 3-4 FPS. Also blurring overlay is rotated, but it's another problem.
I've created test project on Github where you can quickly reproduce problem and check how it is possible to optimise. Looking forward for your ideas.
UPD I was able to improve performance with scaling down input date before blurring. I pushed those changes to test repo. Now I have really good (15-20 FPS) performance even on low end devices, but with low res (HD for instance), and not good enough on FHD and UHD ((8-12 FPS).
Wide Aperture Lens The aperture of the lens is one setting that helps create that background blur. But different lenses have different aperture settings available. Ideally, for a blurred background, you should use a lens that has at least an f/2.8 aperture available. Lower f-numbers will offer even more blur.
Window blurs, or cross-window blurs, are used to blur the screen behind the given window. There are two types of window blurs, which can be used to achieve different visual effects: Background blur allows you to create windows with blurred backgrounds, creating a frosted glass effect.
I have achieved nice live camera blur effect with RenderScript and previewing the output result to the ImageView.
First I created a BlurBuilder class which uses renderscript
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;
}}
I used TextureView to preview the camera preview
Add a ImageView over the TextureView
Add a setSurfaceTextureListener and I added a little tweak
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Bitmap bm = BlurBuilder.blur(getApplicationContext(),textureView.getBitmap());
imageView.setImageBitmap(bm);
}
PS: I used Opengles in TextureView.
My Result: https://vimeo.com/517761912 Device Name: Samsung j7 prime OS Version: Android 8.1 Camera: 13mp 30fps (Device capacity)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With