Bạn có thể sử dụng Bộ công cụ học máy để gắn nhãn cho các đối tượng được nhận dạng trong hình ảnh. Mô hình mặc định được cung cấp cùng với Bộ công cụ học máy hỗ trợ hơn 400 nhãn khác nhau.
Tính năng | Không đi kèm | Theo cụm |
---|---|---|
Triển khai | Mô hình được tải xuống động qua Dịch vụ Google Play. | Mô hình được liên kết tĩnh với bạn tại thời điểm tạo bản dựng. |
Kích thước ứng dụng | Tăng kích thước khoảng 200 KB. | Tăng kích thước khoảng 5,7 MB. |
Thời gian khởi chạy | Có thể phải đợi mô hình tải xuống trước khi sử dụng lần đầu. | Mẫu có sẵn ngay lập tức |
Dùng thử
- Hãy thử nghiệm với ứng dụng mẫu để xem ví dụ về cách sử dụng API này.
Trước khi bắt đầu
Trong tệp
build.gradle
cấp dự án, hãy nhớ thêm kho lưu trữ Maven của Google vào cả mụcbuildscript
vàallprojects
.Thêm các phần phụ thuộc cho thư viện Android của Bộ công cụ học máy vào tệp gradle cấp ứng dụng của mô-đun, thường là
app/build.gradle
. Chọn một trong các phần phụ thuộc sau đây dựa trên nhu cầu của bạn:Để gói mô hình với ứng dụng:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:image-labeling:17.0.9' }
Để sử dụng mô hình trong Dịch vụ Google Play:
dependencies { // ... // Use this dependency to use the dynamically downloaded model in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-image-labeling:16.0.8' }
Nếu chọn sử dụng mô hình trong Dịch vụ Google Play, bạn có thể định cấu hình ứng dụng để tự động tải mô hình xuống thiết bị sau khi cài đặt ứng dụng từ Cửa hàng Play. Để thực hiện việc này, hãy thêm nội dung khai báo sau vào tệp
AndroidManifest.xml
của ứng dụng:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="ica" > <!-- To use multiple models: android:value="ica,model2,model3" --> </application>
Bạn cũng có thể kiểm tra rõ ràng tình trạng có sẵn của mô hình và yêu cầu tải xuống thông qua ModuleInstallClient API của Dịch vụ Google Play.
Nếu bạn không bật tính năng tải mô hình xuống tại thời điểm cài đặt hoặc yêu cầu tải xuống rõ ràng, thì mô hình sẽ được tải xuống trong lần đầu tiên bạn chạy trình gắn nhãn. Các yêu cầu bạn đưa ra trước khi quá trình tải xuống hoàn tất sẽ không có kết quả.
Bây giờ, bạn đã sẵn sàng gắn nhãn hình ảnh.
1. Chuẩn bị hình ảnh đầu vào
Tạo đối tượngInputImage
từ hình ảnh của bạn.
Trình gắn nhãn hình ảnh chạy nhanh nhất khi bạn sử dụng Bitmap
hoặc nếu bạn sử dụng API camera2, thì nên sử dụng media.Image
YUV_420_888 khi có thể.
Bạn có thể tạo đối tượng InputImage
từ nhiều nguồn, mỗi nguồn được giải thích bên dưới.
Sử dụng media.Image
Để tạo đối tượng InputImage
từ đối tượng media.Image
, chẳng hạn như khi bạn chụp ảnh từ máy ảnh của thiết bị, hãy truyền đối tượng media.Image
và độ xoay của hình ảnh đến InputImage.fromMediaImage()
.
Nếu bạn sử dụng thư viện
CameraX, các lớp OnImageCapturedListener
và ImageAnalysis.Analyzer
sẽ tính toán giá trị xoay cho bạn.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Nếu không sử dụng thư viện máy ảnh cung cấp cho bạn độ xoay của hình ảnh, bạn có thể tính toán độ xoay đó từ độ xoay của thiết bị và hướng của cảm biến máy ảnh trong thiết bị:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Sau đó, truyền đối tượng media.Image
và giá trị độ xoay vào InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Sử dụng URI tệp
Để tạo đối tượng InputImage
từ URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đến InputImage.fromFilePath()
. Điều này hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT
để nhắc người dùng chọn một hình ảnh trong ứng dụng thư viện.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Sử dụng ByteBuffer
hoặc ByteArray
Để tạo đối tượng InputImage
từ ByteBuffer
hoặc ByteArray
, trước tiên, hãy tính độ xoay hình ảnh như mô tả trước đó cho dữ liệu đầu vào media.Image
.
Sau đó, hãy tạo đối tượng InputImage
bằng bộ nhớ đệm hoặc mảng, cùng với chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay của hình ảnh:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Sử dụng Bitmap
Để tạo đối tượng InputImage
từ đối tượng Bitmap
, hãy khai báo như sau:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Hình ảnh được biểu thị bằng đối tượng Bitmap
cùng với độ xoay.
2. Định cấu hình và chạy trình gắn nhãn hình ảnh
Để gắn nhãn cho các đối tượng trong hình ảnh, hãy truyền đối tượngInputImage
vào phương thức process
của ImageLabeler
.
Trước tiên, hãy lấy một thực thể của
ImageLabeler
.Nếu bạn muốn sử dụng trình gắn nhãn hình ảnh trên thiết bị, hãy khai báo như sau:
Kotlin
// To use default options: val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS) // Or, to set the minimum confidence required: // val options = ImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build() // val labeler = ImageLabeling.getClient(options)
Java
// To use default options: ImageLabeler labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS); // Or, to set the minimum confidence required: // ImageLabelerOptions options = // new ImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build(); // ImageLabeler labeler = ImageLabeling.getClient(options);
- Sau đó, hãy truyền hình ảnh đến phương thức
process()
:
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
labeler.process(image) .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() { @Override public void onSuccess(List<ImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
3. Nhận thông tin về các đối tượng được gắn nhãn
Nếu thao tác gắn nhãn hình ảnh thành công, danh sách các đối tượngImageLabel
sẽ được truyền đến trình nghe thành công. Mỗi đối tượng ImageLabel
đại diện cho một đối tượng được gắn nhãn trong hình ảnh. Mô hình cơ sở hỗ trợ hơn 400 nhãn.
Bạn có thể xem nội dung mô tả bằng văn bản của từng nhãn, chỉ mục trong số tất cả nhãn mà mô hình hỗ trợ và điểm tin cậy của kết quả so khớp. Ví dụ:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
Java
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
Mẹo cải thiện hiệu suất theo thời gian thực
Nếu bạn muốn gắn nhãn hình ảnh trong một ứng dụng theo thời gian thực, hãy làm theo các nguyên tắc sau để đạt được tốc độ khung hình tốt nhất:
- Nếu bạn sử dụng API
Camera
hoặccamera2
, hãy điều tiết các lệnh gọi đến trình gắn nhãn hình ảnh. Nếu một khung video mới xuất hiện trong khi trình gắn nhãn hình ảnh đang chạy, hãy thả khung đó. Hãy xem lớpVisionProcessorBase
trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ. - Nếu bạn sử dụng API
CameraX
, hãy nhớ đặt chiến lược áp lực ngược thành giá trị mặc định làImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Điều này đảm bảo rằng mỗi lần chỉ có một hình ảnh được phân phối để phân tích. Nếu có nhiều hình ảnh được tạo khi trình phân tích đang bận, thì các hình ảnh đó sẽ tự động bị loại bỏ và không được đưa vào hàng đợi để phân phối. Sau khi hình ảnh đang được phân tích được đóng bằng cách gọi ImageProxy.close(), hình ảnh mới nhất tiếp theo sẽ được phân phối. - Nếu bạn sử dụng kết quả của trình gắn nhãn hình ảnh để phủ hình ảnh đồ hoạ lên hình ảnh đầu vào, trước tiên, hãy lấy kết quả từ Bộ công cụ học máy, sau đó kết xuất hình ảnh và lớp phủ trong một bước. Việc này chỉ kết xuất một lần cho mỗi khung đầu vào trên bề mặt hiển thị. Hãy xem các lớp
CameraSourcePreview
vàGraphicOverlay
trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ. - Nếu bạn sử dụng API Camera2, hãy chụp ảnh ở định dạng
ImageFormat.YUV_420_888
. Nếu bạn sử dụng API Máy ảnh cũ, hãy chụp ảnh ở định dạngImageFormat.NV21
.