- vừa được xem lúc

Cơ bản về CameraX

0 0 31

Người đăng: Xuan Nghia

Theo Viblo Asia

Hôm nay mình sẽ viết một bài về CameraX của Android Jetpack. Về cơ bản thì CameraX là một thư viện của bộ Jetpack nó giúp việc xây dựng một app về camera trở nên dễ dàng hơn, nó rút gọn code hơn so với thư viện trước đó và còn có thêm các extension cho phép thêm các hiệu ứng trên các dòng thiết bị có hỗ trợ. Ví dụ như: HDR, Beauty, chụp đêm...

CameraX structure

Các dev sử dụng CameraX để giao tiếp với máy ảnh của thiết bị thông qua một phần trừu tượng gọi là use case và một vài use case hiện có sẵn:

Preview: cho phép trên app hiển thị những gì camera đang chụp.
Image analysis: cung cấp bộ đệm có thể truy cập CPU để phân tích ảnh, dùng nhiều trong Machine Learning.
Image capture: Chụp và lưu ảnh.

Các use case này có thể được dùng song song với nhau.

Tạo một app CameraX đơn giản

Configure

Minimum API là 21 và dùng Android Stuido 3.6 trở lên.
Mở build.gradle(Module: app) thêm các dependencies bên dưới vào .

 def camerax_version = "1.0.0-beta12" // CameraX core library using camera2 implementation implementation "androidx.camera:camera-camera2:$camerax_version" implementation "androidx.camera:camera-lifecycle:$camerax_version" implementation "androidx.camera:camera-view:1.0.0-alpha19"

CameraX cần một số phương thức của Java 8, vì vậy chúng ta cần đặt các tùy chọn biên dịch của mình cho phù hợp.

android { compileSdkVersion 30 buildToolsVersion "30.0.2" .... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } ...
}

Trong file AndroidManifest.xml

<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />

Sau đó sync lại gradle.

Code UI đơn giản

Trong file activity_main.xml thêm đoạn code như sau:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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.jp.gyao.camerax.MainActivity"> <androidx.appcompat.widget.AppCompatButton android:id="@+id/btCamera" android:layout_width="100dp" android:layout_height="100dp" android:text="Take a Photo" android:elevation="10dp" android:includeFontPadding="false" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> //Thẻ preview cho phép hiển thị lên màn hình điện thoại những gì camera thấy được. <androidx.camera.view.PreviewView android:id="@+id/viewFinder" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>

Code các chức năng chính

Trong MainActivity.ktcủa mình thì trong onCreate () sẽ để hàm xin quyền Camera. Mặc dù ở onCreate () đã xin quyền hoàn tất nhưng máy ảnh sẽ chưa hoạt động cho đến khi ta triển khai các phương thức trong tệp.

typealias LumaListener = (luma: Double) -> Unit class MainActivity : AppCompatActivity() { private var imageCapture: ImageCapture? = null private lateinit var outputDirectory: File private lateinit var cameraExecutor: ExecutorService override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Xin quyền máy ảnh if (allPermissionsGranted()) { startCamera() } else { ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS) } // Lắng nghe sự kiện chụp ảnh. camera_capture_button.setOnClickListener { takePhoto() } //Một biến để lưu trữ đường dẫn ảnh. outputDirectory = getOutputDirectory() //Tạo một luồng mới chạy độc lập. cameraExecutor = Executors.newSingleThreadExecutor() } private fun takePhoto() {} private fun startCamera() {} private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission( baseContext, it) == PackageManager.PERMISSION_GRANTED } private fun getOutputDirectory(): File { val mediaDir = externalMediaDirs.firstOrNull()?.let { File(it, resources.getString(R.string.app_name)).apply { mkdirs() } } return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir } override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } companion object { private const val TAG = "CameraXCoBan" private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private const val REQUEST_CODE_PERMISSIONS = 10 private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) }
}

Implement Preview use case

Trong một ứng dụng máy ảnh, viewfinder được sử dụng để cho phép người dùng xem trước ảnh họ sẽ chụp. Ta có thể triển khai viewfinder bằng cách sử dụng lớp CameraX Preview.

private fun startCamera() { // Sử dụng để ràng buộc vòng đời của máy ảnh với vòng đời của View. Điều này giúp loại bỏ nhiệm vụ mở và đóng máy ảnh vì CameraX nhận biết được vòng đời. val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener(Runnable { val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() // Preview val preview = Preview.Builder() .build() .also { it.setSurfaceProvider(viewFinder.surfaceProvider) } // Lựa chọn mặc định dùng camera sau. val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA // Bên trong khối try, hãy đảm bảo không có gì liên kết với cameraProvider, sau đó liên kết cameraSelector và đối tượng Preview với cameraProvider. try { // Huỷ liên kết với vòng đời của View trước khi liên kết trở lại cameraProvider.unbindAll() // Liên kết Preview use case đến Camera. cameraProvider.bindToLifecycle( this, cameraSelector, preview) } catch(exc: Exception) { Log.e(TAG, "Liên kết thất bại", exc) } }, ContextCompat.getMainExecutor(this)) //Chạy trên luồng chính.
}

Implement ImageCapture use case

 private fun takePhoto() { // Đầu tiên, hãy kiểm tra ImageCapture. Nếu trường hợp sử dụng là null, thì ta thoát ra khỏi hàm. val imageCapture = imageCapture ?: return // Tiếp theo, tạo một tệp để lưu giữ hình ảnh. Thêm dấu thời gian để tên tệp sẽ là duy nhất. val photoFile = File(outputDirectory, SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".png") // Tạo một đối tượng OutputFileOptions. Đối tượng này là nơi ta có thể chỉ định mọi thứ về đầu ra của mình. Ở đây mình sẽ lưu vào trong tệp vừa tạo ở trên. val outputOption = ImageCapture.OutputFileOptions.Builder(photoFile).build() // Khi gọi hàm takePicture() trên đối tượng imageCapture. Truyền vào outputOptions, trình thực thi và một lệnh gọi lại khi ảnh được lưu. imageCapture.takePicture( outputOption, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { // Nếu việc chụp không bị lỗi, ảnh đã được chụp thành công! Lưu ảnh vào tệp bạn đã tạo trước đó. override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { val savedUri = Uri.fromFile(photoFile) val msg = "Đường dẫn ảnh: $savedUri" Toast.makeText(_@.com, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } // Nếu thất bại thì in ra log. override fun onError(exception: ImageCaptureException) { Log.e(TAG, "Không lấy được ảnh: ${exception.message}", exception) } }) }

Tiếp đó vào trong hàmstartCamera() thêm vào đoạn code sau:\

imageCapture = ImageCapture.Builder().build()
... // Thêm biến imageCapture vào trong cameraProvider
cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture)

Implement ImageAnalys use case

Để làm cho ứng dụng máy ảnh thú vị hơn thì mình sử dụng tính năng ImageAnalys. Nó cho phép xác định một lớp tùy chỉnh triển khai giao diện ImageAnalysis.Analyzer và sẽ được gọi với các khung mà máy ảnh hướng đến.

Thêm bộ phân tích này vào dưới dạng một lớp bên trong trong MainActivity.kt. Máy phân tích ghi lại độ sáng trung bình của hình ảnh. Để tạo một trình phân tích, ta ghi đè chức năng phân tích trong một lớp triển khai giao diện ImageAnalysis.Analyzer.

 private class LuminosityAnaLyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer { private fun ByteBuffer.toByteArray(): ByteArray { rewind() // Đưa bộ đệm về 0 val data = ByteArray(remaining()) //copy bộ đệm vào mảng byte get(data) return data } override fun analyze(image: ImageProxy) { val buffer = image.planes[0].buffer val data = buffer.toByteArray() val pixels = data.map { it.toInt() and 0xFF } val luma = pixels.average() listener(luma) image.close() } }

Với lớp triển khai giao diện ImageAnalysis.Analyzer, tất cả những gì chúng ta cần làm là khởi tạo một phiên bản của LuminosityAnalyzer trong ImageAnalysis, tương tự như các trường hợp sử dụng khác và cập nhật lại hàmstartCamera()trước khi gọi đến CameraX.bindToLifecycle():

private fun startCamera(){
...
val imageAnalyzer = ImageAnalysis.Builder() .build() .also { it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma -> Log.d(TAG, "Average luminosity: $luma") }) } ... cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture, imageAnalyzer) }

Vậy là mình đã hướng dẫn các bạn làm một App CameraX đơn giản rồi, cảm ơn các bạn đã xem qua ???????

Link source code

https://github.com/nghiaptx-2124/CameraX.git

Tài liệu tham khảo

https://codelabs.developers.google.com/codelabs/camerax-getting-started

https://developer.android.com/training/camerax/architecture

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 281

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 218

- vừa được xem lúc

[Android] Hiển thị Activity trên màn hình khóa - Show Activity over lock screen

Xin chào các bạn, Hôm nay là 30 tết rồi, ngồi ngắm trời chờ đón giao thừa, trong lúc rảnh rỗi mình quyết định ngồi viết bài sau 1 thời gian vắng bóng. .

0 0 107

- vừa được xem lúc

Tìm hiểu Proguard trong Android

1. Proguard là gì . Cụ thể nó giúp ứng dụng của chúng ta:. .

0 0 101

- vừa được xem lúc

Làm ứng dụng học toán đơn giản với React Native - Phần 6

Chào các bạn một năm mới an khang thịnh vượng, dồi dào sức khỏe. Lại là mình đây Đây là link app mà các bạn đang theo dõi :3 https://play.google.com/store/apps/details?id=com.

0 0 68

- vừa được xem lúc

20 Plugin hữu ích cho Android Studio

1. CodeGlance. Plugin này sẽ nhúng một minimap vào editor cùng với thanh cuộn cũng khá là lớn. Nó sẽ giúp chúng ta xem trước bộ khung của code và cho phép điều hướng đến đoạn code mà ta mong muốn một cách nhanh chóng.

0 0 315