Bạn có thể sử dụng Bộ công cụ học máy để phát hiện và theo dõi các đối tượng trên các khung hình của video.
Khi bạn truyền hình ảnh cho Bộ công cụ học máy, Bộ công cụ học máy sẽ trả về danh sách tối đa 5 đối tượng được phát hiện và vị trí của các đối tượng đó trong hình ảnh cho mỗi hình ảnh. Khi phát hiện các đối tượng trong luồng video, mỗi đối tượng đều có một mã nhận dạng mà bạn có thể dùng để theo dõi đối tượng trên các hình ảnh. Bạn cũng có thể tuỳ ý bật tính năng phân loại đối tượng thô, tính năng này sẽ gắn nhãn các đối tượng bằng phần mô tả danh mục chung.
Trước khi bắt đầu
- Nếu bạn chưa thực hiện, hãy thêm Firebase vào dự án Android.
- 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) trong mô-đun của bạn (thường là
app/build.gradle):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-object-detection-model:19.0.6' }
1. Định cấu hình trình phát hiện đối tượng
Để bắt đầu phát hiện và theo dõi các đối tượng, trước tiên, hãy tạo một thực thể của
FirebaseVisionObjectDetector, tuỳ ý chỉ định mọi chế độ cài đặt trình phát hiện mà bạn
muốn thay đổi so với chế độ mặc định.
Định cấu hình trình phát hiện đối tượng cho trường hợp sử dụng của bạn bằng đối tượng
FirebaseVisionObjectDetectorOptions. Bạn có thể thay đổi các chế độ cài đặt sau:Chế độ cài đặt trình phát hiện đối tượng Chế độ phát hiện STREAM_MODE(mặc định) |SINGLE_IMAGE_MODEỞ
STREAM_MODE(mặc định), trình phát hiện đối tượng chạy với độ trễ thấp nhưng có thể tạo ra kết quả không đầy đủ (chẳng hạn như hộp giới hạn hoặc nhãn danh mục không xác định) trong vài lần gọi đầu tiên của trình phát hiện. Ngoài ra, ởSTREAM_MODE, trình phát hiện sẽ gán mã theo dõi cho các đối tượng mà bạn có thể dùng để theo dõi các đối tượng trên các khung hình. Hãy sử dụng chế độ này khi bạn muốn theo dõi các đối tượng hoặc khi độ trễ thấp là quan trọng, chẳng hạn như khi xử lý luồng video theo thời gian thực.Ở
SINGLE_IMAGE_MODE, trình phát hiện đối tượng sẽ đợi cho đến khi có hộp giới hạn của đối tượng được phát hiện và nhãn danh mục (nếu bạn đã bật tính năng phân loại) thì mới trả về kết quả. Do đó, độ trễ phát hiện có thể cao hơn. Ngoài ra, ởSINGLE_IMAGE_MODE, mã theo dõi sẽ không được gán. Hãy sử dụng chế độ này nếu độ trễ không quan trọng và bạn không muốn xử lý kết quả một phần.Phát hiện và theo dõi nhiều đối tượng false(mặc định) |trueCó phát hiện và theo dõi tối đa 5 đối tượng hay chỉ phát hiện và theo dõi đối tượng nổi bật nhất (mặc định) hay không.
Phân loại đối tượng false(mặc định) |trueCó phân loại các đối tượng được phát hiện thành các danh mục thô hay không. Khi được bật, trình phát hiện đối tượng sẽ phân loại các đối tượng thành các danh mục sau: hàng thời trang, thực phẩm, đồ gia dụng, địa điểm, cây cối và không xác định.
API phát hiện và theo dõi đối tượng được tối ưu hoá cho 2 trường hợp sử dụng cốt lõi sau:
- Phát hiện và theo dõi trực tiếp đối tượng nổi bật nhất trong khung ngắm của camera
- Phát hiện nhiều đối tượng từ một hình ảnh tĩnh
Cách định cấu hình API cho các trường hợp sử dụng này:
Java
// Live detection and tracking FirebaseVisionObjectDetectorOptions options = new FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE) .enableClassification() // Optional .build(); // Multiple object detection in static images FirebaseVisionObjectDetectorOptions options = new FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() // Optional .build();Kotlin
// Live detection and tracking val options = FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE) .enableClassification() // Optional .build() // Multiple object detection in static images val options = FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() // Optional .build()Tạo thực thể của
FirebaseVisionObjectDetector:Java
FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(); // Or, to change the default settings: FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options);Kotlin
val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector() // Or, to change the default settings: val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options)
2. Chạy trình phát hiện đối tượng
Để phát hiện và theo dõi các đối tượng, hãy truyền hình ảnh đến phương thức FirebaseVisionObjectDetector
của thực thể processImage().
Đối với mỗi khung hình của video hoặc hình ảnh trong một chuỗi, hãy làm như sau:
Tạo đối tượng
FirebaseVisionImagetừ hình ảnh của bạn.-
Để tạo đối tượng
FirebaseVisionImagetừ đối tượngmedia.Image, chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy truyền đối tượngmedia.Imagevà độ xoay của hình ảnh đếnFirebaseVisionImage.fromMediaImage().Nếu bạn sử dụng thư viện CameraX, các lớp
OnImageCapturedListenervàImageAnalysis.Analyzersẽ tính toán giá trị xoay cho bạn, vì vậy, bạn chỉ cần chuyển đổi độ xoay thành một trong các hằng sốROTATION_của Bộ công cụ học máy trước khi gọiFirebaseVisionImage.fromMediaImage():Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Nếu không dùng thư viện máy ảnh cho biết độ xoay của hình ảnh, bạn có thể tính độ xoay đó từ độ xoay của thiết bị và hướng của cảm biến camera trong thiết bị:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Sau đó, hãy truyền đối tượng
media.Imagevà giá trị xoay đếnFirebaseVisionImage.fromMediaImage():Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImagetừ URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đếnFirebaseVisionImage.fromFilePath(). Điều này rất hữu ích khi bạn sử dụng ý địnhACTION_GET_CONTENTđể nhắc người dùng chọn một hình ảnh từ ứng dụng thư viện của họ.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Để tạo đối tượng
FirebaseVisionImagetừByteBufferhoặc mảng byte, trước tiên, hãy tính toán độ xoay của hình ảnh như mô tả ở trên cho dữ liệu đầu vàomedia.Image.Sau đó, hãy tạo đối tượng
FirebaseVisionImageMetadatachứa chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay của hình ảnh:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
Sử dụng bộ đệm hoặc mảng và đối tượng siêu dữ liệu để tạo đối tượng
FirebaseVisionImageobject:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Cách tạo đối tượng
FirebaseVisionImagetừ đối tượngBitmap:Hình ảnh do đối tượngJava
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmapbiểu thị phải ở vị trí thẳng đứng, không cần xoay thêm.
-
Truyền hình ảnh đến phương thức
processImage():Java
objectDetector.processImage(image) .addOnSuccessListener( new OnSuccessListener<List<FirebaseVisionObject>>() { @Override public void onSuccess(List<FirebaseVisionObject> detectedObjects) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });Kotlin
objectDetector.processImage(image) .addOnSuccessListener { detectedObjects -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }Nếu lệnh gọi đến
processImage()thành công, thì danh sáchFirebaseVisionObjects sẽ được truyền đến trình nghe thành công.Mỗi
FirebaseVisionObjectchứa các thuộc tính sau:Hộp giới hạn Rectcho biết vị trí của đối tượng trong hình ảnh.Mã theo dõi Một số nguyên xác định đối tượng trên các hình ảnh. Giá trị rỗng trong SINGLE_IMAGE_MODE. Danh mục Danh mục thô của đối tượng. Nếu trình phát hiện đối tượng không bật tính năng phân loại, thì giá trị này luôn là FirebaseVisionObject.CATEGORY_UNKNOWN.Độ tin cậy Giá trị độ tin cậy của tính năng phân loại đối tượng. Nếu trình phát hiện đối tượng không bật tính năng phân loại hoặc đối tượng được phân loại là không xác định, thì giá trị này là null.Java
// The list of detected objects contains one item if multiple object detection wasn't enabled. for (FirebaseVisionObject obj : detectedObjects) { Integer id = obj.getTrackingId(); Rect bounds = obj.getBoundingBox(); // If classification was enabled: int category = obj.getClassificationCategory(); Float confidence = obj.getClassificationConfidence(); }Kotlin
// The list of detected objects contains one item if multiple object detection wasn't enabled. for (obj in detectedObjects) { val id = obj.trackingId // A number that identifies the object across images val bounds = obj.boundingBox // The object's position in the image // If classification was enabled: val category = obj.classificationCategory val confidence = obj.classificationConfidence }
Cải thiện khả năng sử dụng và hiệu suất
Để có trải nghiệm người dùng tốt nhất, hãy làm theo các nguyên tắc sau trong ứng dụng của bạn:
- Việc phát hiện đối tượng thành công phụ thuộc vào độ phức tạp trực quan của đối tượng. Các đối tượng có số lượng ít tính năng trực quan có thể cần chiếm một phần lớn hơn trong hình ảnh để được phát hiện. Bạn nên hướng dẫn người dùng về cách chụp dữ liệu đầu vào hoạt động tốt với loại đối tượng mà bạn muốn phát hiện.
- Khi sử dụng tính năng phân loại, nếu bạn muốn phát hiện các đối tượng không thuộc các danh mục được hỗ trợ, hãy triển khai quy trình xử lý đặc biệt cho các đối tượng không xác định.
Ngoài ra, hãy xem [ứng dụng giới thiệu Material Design của Bộ công cụ học máy][showcase-link]{: .external } và bộ sưu tạo Material Design Mẫu cho các tính năng dựa trên học máy collection.
Khi sử dụng chế độ truyền trực tuyến trong ứ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:
Không sử dụng tính năng phát hiện nhiều đối tượng ở chế độ truyền trực tuyến, vì hầu hết các thiết bị sẽ không thể tạo ra tốc độ khung hình phù hợp.
Tắt tính năng phân loại nếu bạn không cần.
- Điều chỉnh tốc độ gọi đến trình phát hiện. Nếu có một khung video mới trở thành có sẵn trong khi trình phát hiện đang chạy, hãy thả khung đó.
- Nếu bạn dùng kết quả của trình phát hiện để phủ đồ 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. Bằng cách đó, bạn chỉ kết xuất vào bề mặt hiển thị một lần cho mỗi khung đầu vào.
-
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 Camera cũ, hãy chụp ảnh ở định dạng
ImageFormat.NV21