I tried using the CameraX library with Android Java Fragment

I tried CameraX introduced at Google I / O 2019 on Fragment in Java, so I will briefly introduce it.

Preparation

It's easy to proceed while watching the official Tutorial, so I will proceed while watching this.

Creating a project and adding dependencies are just as per the tutorial, so I will omit it in this article.

Added view for preview

Add TextureView to layout.xml to be displayed in Fragment.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF">

    <TextureView
        android:id="@+id/view_finder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <RelativeLayout
        android:id="@+id/camera_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#456700"
        android:padding="15dp">

        <ImageButton
            android:id="@+id/camera_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/common_backbtn"
            android:layout_alignParentLeft="true"/>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/camera_bottom_control"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#456700"
        android:padding="20dp">

        <Button
            android:id="@+id/capture_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="To shoot"
            />
        
    </RelativeLayout>

</RelativeLayout>

It looks like this ↓ スクリーンショット 2019-12-12 15.20.00.png

TextureView is placed on the entire surface and screen elements are superimposed on it.

Launch camera

All you have to do is set the preview and bind it to the fragment life cycle.

It's easy because you just bind the PreviewConfig created by CameraX.bindToLifecycle.

public void startCamera() {
	PreviewConfig.Builder builder = new PreviewConfig.Builder();

	builder.setTargetResolution(new Size(App.width, App.height));
	PreviewConfig previewConfig = builder.build();

	preview = new Preview(previewConfig);
	preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
		@Override
		public void onUpdated(@NonNull Preview.PreviewOutput output) {
			ViewGroup viewGroup = (ViewGroup) viewFinder.getParent();
			viewGroup.removeView(viewFinder);
			viewGroup.addView(viewFinder, 0);

			viewFinder.setSurfaceTexture(output.getSurfaceTexture());
			updateTransform();
		}
	});
	CameraX.bindToLifecycle(this, preview);
}
    
public void updateTransform() {
	Matrix matrix = new Matrix();

	float centerX = viewFinder.getWidth() / 2f;
	float centerY = viewFinder.getHeight() / 2f;

	float rotationDegrees;
	switch (viewFinder.getDisplay().getRotation()) {
		case Surface.ROTATION_0:
			rotationDegrees = 0f;
			break;
		case Surface.ROTATION_90:
			rotationDegrees = 90f;
			break;
		case Surface.ROTATION_180:
			rotationDegrees = 180f;
			break;
		case Surface.ROTATION_270:
			rotationDegrees = 270f;
			break;
		default:
			return;
	}

	matrix.postRotate(-rotationDegrees, centerX, centerY);
	viewFinder.setTransform(matrix);
}

Basically, just write this, and if you call startCamera after getting the permission of the camera, the preview will be displayed.

Camera capture

You can't use it for anything just by previewing the camera, so let's capture the image at that moment when capture_button is pressed.

Add the following code before binding the startCamera method above.

ImageCaptureConfig.Builder imageBuilder = new ImageCaptureConfig.Builder();
imageBuilder.setTargetResolution(new Size(App.width, App.height));
imageBuilder.setCaptureMode(ImageCapture.CaptureMode.MAX_QUALITY);
imageBuilder.setFlashMode(FlashMode.ON);
ImageCaptureConfig imageCaptureConfig = imageBuilder.build();
imageCapture = new ImageCapture(imageCaptureConfig);

I am setting up image capture. Here, Flash is turned on to maximize the quality. After creating the ImageCaptureConfig, let's also add the ImageCaptureConfig to the bind method.

CameraX.bindToLifecycle(this, preview, imageCapture);

Now you are ready to go.

You can capture by calling imageCapture.takePicture (), so let's set an event for the button.

btnCamera.setOnClickListener((View v) -> {

	imageCapture.takePicture(CameraFragment.this, new ImageCapture.OnImageCapturedListener() {
			@Override
			public void onCaptureSuccess(ImageProxy imageProxy, int rotationDegrees) {

			}

			@Override
			public void onError(
				@NonNull ImageCapture.ImageCaptureError imageCaptureError, @NonNull String message,
								@Nullable Throwable cause) {

			}
		});
	}
});
	

Since Executor is required for the argument of takePicture, here we will implement Executor in Fragment so that the processing after taking a picture can be executed by UiThread.


public class CameraFragment implements Executor {

    @Override
    public void execute(Runnable command) {
		if(getActivity() != null){
			getActivity().runOnUiThread(command);
		}
	}
}

When you press the shooting button and the shooting is successful, onCaptureSuccess (ImageProxy imageProxy, int rotationDegrees) is called and the image can be acquired from imageProxy!

Impressions

It's easy ...

What was it that was written on Camera2?

It's a difficult word to implement the camera, so I've been waiting for this.

Addictive point

Well, I have described it very easily, but I will introduce it because there were some addictive points.

① Shutter on does not sound!

There is no shutter sound. Yes.

I was looking for something that could be set in the capture settings, but it's not found anywhere ...

If you want to make it sound, try to force it.

MediaActionSound sound = new MediaActionSound();
sound.play(MediaActionSound.SHUTTER_CLICK);

② ImageProxy How do you convert to Bitmap?

Yes, this is it.

public Bitmap imageProxyToBitmap(ImageProxy image, int rotationDegrees) {
	ImageProxy.PlaneProxy planeProxy = image.getPlanes()[0];
	ByteBuffer buffer = planeProxy.getBuffer();
	byte[] bytes = new byte[buffer.remaining()];
	buffer.get(bytes);

	Matrix mat = new Matrix();
	mat.postRotate(rotationDegrees);

	if (rotationDegrees != 0) {
		return rotate(BitmapFactory.decodeByteArray(bytes, 0, bytes.length), rotationDegrees);
	} else {
		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
	}
}

public Bitmap rotate(Bitmap in, int angle) {
		Matrix mat = new Matrix();
		mat.postRotate(angle);
		return Bitmap.createBitmap(in, 0, 0, in.getWidth(), in.getHeight(), mat, true);
}

③ How to arrange TextureView

Q. Why is TextureView all over the layout file? Shouldn't it fit in the preview area?

A. I dare to do it all over.

If the preview size and the size aspect ratio of the Texture View do not match, the preview will be displayed distorted, or if you create a screen with a preview area that is smaller than the screen size, you shot that the Texture View is made to fit the preview area. The preview will be very difficult to see because things will be displayed in a reduced size in the TextureView.

If you put TextureView on the entire surface, all the above problems will be solved, and you can shoot with the same feeling as the camera on the entire surface that you usually use, so you can shoot without any discomfort.

If you want an image of only the area visible to the user, you can cut it out after shooting.

At the end

Please note that CameraX is still an alpha version, so the interface may change in the release version.

Also, although not introduced here, it seems that there is an API that makes it easy to use device-specific vendor effects (out of focus, HDR, night view), so I would like to try that as well in the future.

Recommended Posts

I tried using the CameraX library with Android Java Fragment
I tried using OpenCV with Java + Tomcat
I tried using Java REPL
I tried using the GitHub repository as a library server
I tried to interact with Java
I tried UDP communication with Java
I tried the Java framework "Quarkus"
I made a lock pattern using the volume key with the Android app. Fragment edition
I tried using JWT in Java
[Android] I tried using Coordinator Layout.
[Android] [Library] I tried using an animation library called "Before After animation".
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
I tried to make an Android application with MVC now (Java)
I tried using Elasticsearch API in Java
I tried using Realm with Swift UI
I tried using Scalar DL with Docker
I tried the new era in Java
I tried using OnlineConverter with SpringBoot + JODConverter
I tried the AutoValue library in Intellij
Try using the Wii remote with Java
I tried using Docker for the first time
I tried to make Basic authentication with Java
[Android] I quit SQLite and tried using Realm
I made blackjack with Ruby (I tried using minitest)
[API] I tried using the zip code search API
I tried using the profiler of IntelliJ IDEA
I tried to break a block with java (1)
[Java] I tried to connect using a connection pool with Servlet (tomcat) & MySQL & Java
I tried using Gson
I tried using TestNG
I tried using Galasa
I tried using a database connection in Android development
I tried using the Server Push function of Servlet 4.0
I tried to implement TCP / IP + BIO with JAVA
[Java 11] I tried to execute Java without compiling with javac
Try implementing the Eratosthenes sieve using the Java standard library
I tried to operate SQS using AWS Java SDK
[Rails] I tried playing with the comment send button
I tried using the Migration Toolkit for Application Binaries
I tried using Log4j2 on a Java EE server
I tried OCR processing a PDF file with Java
I tried to implement Stalin sort with Java Collector
Try global hooking in Java using the JNativeHook library
I tried to investigate the mechanism of Emscripten by using it with the Sudoku solver
I tried to create a java8 development environment with Chocolatey
I tried to modernize a Java EE application with OpenShift.
I tried DI with Ruby
[Rails] I tried using the button_to method for the first time
I tried to increase the processing speed with spiritual engineering
[JDBC] I tried to access the SQLite3 database from Java.
I tried to summarize the basics of kotlin and java
I tried Drools (Java, InputStream)
[Java] Try editing the elements of the Json string using the library
I tried the Docker tutorial!
I tried using Apache Wicket
I tried the VueJS tutorial!
Using Mapper with Java (Spring)
I tried to build the environment little by little using docker
I tried the FizzBuzz problem
I tried UPSERT with PostgreSQL.