1. Tổng quan
CameraX là 1 thư viện hỗ trợ của Jetpack, nếu bạn chưa biết đến Jetpack thì có thể hình dung đây là một tập hợp các thư viện, công cụ và các hướng dẫn giúp developer phát triển ứng dụng một cách dễ dàng với việc hạn chế code nhàm chán, đơn giản những tác vụ phức tạp. Bạn có thể tham khảo thêm Jetpack tại đây
CameraX sẽ giúp bạn xây dựng ứng dụng camera một cách dễ dàng với việc cung cấp các API phù hợp với các thiết bị Android 5.0 (API 21). So với Camera2, việc sử dụng CameraX giúp bạn không phải bận tâm về vấn đề tương thích với các thiết bị.
Ưu điểm khi sử dụng CameraX:
- Dễ sử dụng
CameraX giới thiệu 1 số use case cho phép bạn tập trung vào các công việc cần thực hiện:
- Preview: hiển thị 1 image preview
- Image analysis: cho phép truy cập bộ đệm CPU cho việc analysis để sử dụng các thuật toán machine learning như MLKit
- Image capture: chụp và lưu ảnh
- Tính nhất quán giữa các thiết bị
Việc quản lý camera trong ứng dụng không hề dễ dàng ví dụ như: aspect ratio, orientation, rotation, preview size, high-resolution image size. Nhưng với CameraX thì việc đó sẽ đơn giản hơn nhiều,
- Trải nghiệm mới
CameraX có thêm add-on là Extensions cho phép bạn truy cập các tính năng được cung cấp cho camera. Một số tính năng mà extension này cung cấp là: Portrait, HDR, Night, Beauty.
2. Camerax Architecture
Chúng ta cùng nhau tìm hiểu ví dụ sử dụng CameraX:
1 2 3 4 5 6 7 8 9 | val preview <span class="token operator">=</span> Preview<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> val viewFinder<span class="token operator">:</span> PreviewView <span class="token operator">=</span> <span class="token function">findViewById</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>id<span class="token punctuation">.</span>previewView<span class="token punctuation">)</span> <span class="token comment">// PreviewView has a built-in surface provider and is the recommended provider</span> preview<span class="token punctuation">.</span><span class="token function">setSurfaceProvider</span><span class="token punctuation">(</span>viewFinder<span class="token punctuation">.</span>previewSurfaceProvider<span class="token punctuation">)</span> <span class="token comment">// The use case is bound to an Android Lifecycle with the following code</span> cameraProvider<span class="token punctuation">.</span><span class="token function">bindToLifecycle</span><span class="token punctuation">(</span>lifecycleOwner<span class="token punctuation">,</span> cameraSelector<span class="token punctuation">,</span> preview<span class="token punctuation">)</span> |
CameraX được liên kết với vòng đời ứng dụng sử dụng method bindToLifecycle(). CameraX lắng nghe vòng đời để quyết định khi nào mở camera, khi nào chụp ảnh, khi nào tắt camera. Và hãy sử dụng CameraX.unbindAll() khi bạn muốn hủy liên kết với vòng đời.
- Config
CameraX tự động cung cấp tính năng chỉ định của thiết bị mà ứng dụng sẽ được chạy, tức là CameraX sẽ tự động xác định độ phân giải tốt nhất để sử dụng nếu bạn không cung cấp độ phân giải chỉ định hoặc nếu độ phân giải bạn cài đặt không được hỗ trợ trên thiết bị.
Mục đích của CameraX là khởi tạo thành công phiên làm việc với camera, điều đó có nghĩa CameraX sẽ thỏa hiệp độ phân giải và tỷ lệ ảnh dựa trên sự cho phép của thiết bị.
1 2 3 4 5 6 7 | val imageCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .setTargetAspectRatio(screenAspectRatio) .setTargetRotation(rotation) .setTargetResolution(resolution) .build() |
3. Preview
Để thêm preview vào ứng dụng, sử dụng PreviewView, View này có thể crop, scale, rotate
- Config CameraXConfig.Provider
Implement CameraXConfig.Provider trong class Application
1 2 3 4 5 6 7 8 9 | <span class="token keyword">import</span> androidx<span class="token punctuation">.</span>camera<span class="token punctuation">.</span>camera2<span class="token punctuation">.</span>Camera2Config <span class="token keyword">import</span> androidx<span class="token punctuation">.</span>camera<span class="token punctuation">.</span>core<span class="token punctuation">.</span>CameraXConfig <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyCameraXApplication</span> <span class="token operator">:</span> <span class="token function">Application</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> CameraXConfig<span class="token punctuation">.</span>Provider <span class="token punctuation">{</span> override fun <span class="token function">getCameraXConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> CameraXConfig <span class="token punctuation">{</span> <span class="token keyword">return</span> Camera2Config<span class="token punctuation">.</span><span class="token function">defaultConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Thêm PreviewView vào layout
1 2 3 4 5 6 | <span class="token operator"><</span>FrameLayout android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/container"</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>camera<span class="token punctuation">.</span>view<span class="token punctuation">.</span>PreviewView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/preview_view"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>FrameLayout<span class="token operator">></span> |
- Request CameraProvider
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">import</span> androidx<span class="token punctuation">.</span>camera<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ProcessCameraProvider <span class="token keyword">import</span> com<span class="token punctuation">.</span>google<span class="token punctuation">.</span>common<span class="token punctuation">.</span>util<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>ListenableFuture <span class="token keyword">class</span> <span class="token class-name">MainActivity</span> <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> lateinit var cameraProviderFuture <span class="token operator">:</span> ListenableFuture<span class="token generics function"><span class="token punctuation"><</span>ProcessCameraProvider<span class="token punctuation">></span></span> override fun <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> cameraProviderFuture <span class="token operator">=</span> ProcessCameraProvider<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Check CameraProvider
1 2 3 4 5 | cameraProviderFuture<span class="token punctuation">.</span><span class="token function">addListener</span><span class="token punctuation">(</span>Runnable <span class="token punctuation">{</span> val cameraProvider <span class="token operator">=</span> cameraProviderFuture<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token function">bindPreview</span><span class="token punctuation">(</span>cameraProvider<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> ContextCompat<span class="token punctuation">.</span><span class="token function">getMainExecutor</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span> |
- Chọn camera và liên kết với lifecycle
Khi đã tạo và check CameraProvider thành công, thực hiện các bước dưới đây:
Tạo Preview và connect với PreviewView
Chỉ định tùy chọn camera LensFacing
Liên kết với lifecycle
1 2 3 4 5 6 7 8 9 10 11 12 13 | fun <span class="token function">bindPreview</span><span class="token punctuation">(</span>cameraProvider <span class="token operator">:</span> ProcessCameraProvider<span class="token punctuation">)</span> <span class="token punctuation">{</span> var preview <span class="token operator">:</span> Preview <span class="token operator">=</span> Preview<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> previewView<span class="token punctuation">.</span><span class="token function">setSurfaceProvider</span><span class="token punctuation">(</span>previewView<span class="token punctuation">.</span>previewSurfaceProvider<span class="token punctuation">)</span> var cameraSelector <span class="token operator">:</span> CameraSelector <span class="token operator">=</span> CameraSelector<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">requireLensFacing</span><span class="token punctuation">(</span>CameraSelector<span class="token punctuation">.</span>LENS_FACING_BACK<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> cameraProvider<span class="token punctuation">.</span><span class="token function">bindToLifecycle</span><span class="token punctuation">(</span><span class="token keyword">this</span> as LifecycleOwner<span class="token punctuation">,</span> cameraSelector<span class="token punctuation">,</span> preview<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
4. Analyze Image
Image analysis cung cấp bộ đệm CPU để thực thi các công việc phân tích ảnh sử dụng computer vision, machine learning.
1 2 3 4 5 6 7 8 9 10 11 12 | val imageAnalysis <span class="token operator">=</span> ImageAnalysis<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setTargetResolution</span><span class="token punctuation">(</span><span class="token function">Size</span><span class="token punctuation">(</span><span class="token number">1280</span><span class="token punctuation">,</span> <span class="token number">720</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">setBackpressureStrategy</span><span class="token punctuation">(</span>ImageAnalysis<span class="token punctuation">.</span>STRATEGY_KEEP_ONLY_LATEST<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> imageAnalysis<span class="token punctuation">.</span><span class="token function">setAnalyzer</span><span class="token punctuation">(</span>executor<span class="token punctuation">,</span> ImageAnalysis<span class="token punctuation">.</span>Analyzer <span class="token punctuation">{</span> image <span class="token operator">-</span><span class="token operator">></span> val rotationDegrees <span class="token operator">=</span> image<span class="token punctuation">.</span>imageInfo<span class="token punctuation">.</span>rotationDegrees <span class="token comment">// insert your code here.</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> cameraProvider<span class="token punctuation">.</span><span class="token function">bindToLifecycle</span><span class="token punctuation">(</span><span class="token keyword">this</span> as LifecycleOwner<span class="token punctuation">,</span> cameraSelector<span class="token punctuation">,</span> imageAnalysis<span class="token punctuation">,</span> preview<span class="token punctuation">)</span> |
Image analysis có thể làm việc trong 2 chế độ: blocking và non-blocking.
Chế độ blocking được enable sử dụng setBackpressureStrategy() với giá trị STRATEGY_BLOCK_PRODUCER. Trong chế độ này, bộ thực thi sẽ nhận các frame từ camera theo thứ tự tuần tự.
Chế độ non-blocking sử dụng setBackpressureStrategy() với giá trị STRATEGY_KEEP_ONLY_LATEST, chế độ này sẽ chỉ nhận frame cuối cùng từ camera
Sau khi config xong các chế độ, bạn sử dụng method setAnalyzer() để sử dụng các cách phân tích ảnh và đừng quên bind vào Lifecycle.
5. Image Capture
Sau khi config xong camera ở các bước trên, giờ đây bạn có thể chụp lại bức ảnh và lưu lại
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fun <span class="token function">onClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> val outputFileOptions <span class="token operator">=</span> ImageCapture<span class="token punctuation">.</span>OutputFileOptions<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token function">File</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span><span class="token punctuation">)</span> imageCapture<span class="token punctuation">.</span><span class="token function">takePicture</span><span class="token punctuation">(</span>outputFileOptions<span class="token punctuation">,</span> object <span class="token operator">:</span> ImageCapture<span class="token punctuation">.</span>OnImageSavedListener <span class="token punctuation">{</span> override fun <span class="token function">onError</span><span class="token punctuation">(</span>error<span class="token operator">:</span> ImageCapture<span class="token punctuation">.</span>ImageCaptureError<span class="token punctuation">,</span> message<span class="token operator">:</span> String<span class="token punctuation">,</span> exc<span class="token operator">:</span> Throwable<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// insert your code here.</span> <span class="token punctuation">}</span> override fun <span class="token function">onImageSaved</span><span class="token punctuation">(</span>outputFileResults<span class="token operator">:</span> ImageCapture<span class="token punctuation">.</span>OutputFileResults<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// insert your code here.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Bài viết đến đây cũng đủ dài, xin hẹn bạn đọc bài viết tới mình sẽ viết thêm về các extension bá đạo và vô cùng ảo diệu của CameraX này có thể kể đến như: HDR, Bokeh,…
Tham khảo
https://developer.android.com/training/camerax
https://codelabs.developers.google.com/codelabs/camerax-getting-started/#0