Bạn có thể sử dụng ML Kit để 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 chuyển hình ảnh Bộ công cụ ML, Bộ công cụ ML sẽ trả về, đối với mỗi hình ảnh, danh sách tối đa năm đối tượng được phát hiện và vị trí của chúng trong hình ảnh. Khi phát hiện đối tượng trong luồng video, mọi đối tượng đều có ID mà bạn có thể sử dụng để theo dõi đối tượng trên các hình ảnh. Bạn cũng có thể tùy ý kích hoạt phân loại đối tượng thô, gắn nhãn các đối tượng bằng mô tả danh mục rộng.
Trước khi bắt đầu
- Nếu bạn chưa có, hãy thêm Firebase vào dự án Android của bạn .
- Thêm các phần phụ thuộc cho thư viện ML Kit Android vào tệp Gradle mô-đun (cấp ứng dụng) 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. 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 phiên bản FirebaseVisionObjectDetector
, tùy ý chỉ định bất kỳ cài đặt trình phát hiện nào bạn muốn thay đổi so với 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 cài đặt sau:Cài đặt dò tìm đối tượng Chế độ phát hiện STREAM_MODE
(mặc định) |SINGLE_IMAGE_MODE
Trong
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ư các hộp giới hạn hoặc nhãn danh mục không xác định) trong một số lần gọi đầu tiên của trình phát hiện. Ngoài ra, trongSTREAM_MODE
, trình phát hiện sẽ gán ID theo dõi cho các đối tượng mà bạn có thể sử dụng để theo dõi các đối tượng trên các khung. 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 trong thời gian thực.Trong
SINGLE_IMAGE_MODE
, trình phát hiện đối tượng sẽ đợi cho đến khi 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 phân loại) có sẵn trước khi trả về kết quả. Kết quả là độ trễ phát hiện có khả năng cao hơn. Ngoài ra, trongSINGLE_IMAGE_MODE
, ID theo dõi không được chỉ định. Sử dụng chế độ này nếu độ trễ không quá nghiêm trọng và bạn không muốn xử lý một phần kết quả.Phát hiện và theo dõi nhiều đối tượng false
(mặc định) |true
Phát hiện và theo dõi tối đa năm đối tượng hay chỉ đối tượng nổi bật nhất (mặc định).
Phân loại đồ vật false
(mặc định) |true
Có phân loại các đối tượng được phát hiện thành các loại 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 loại sau: hàng thời trang, thực phẩm, hàng gia dụng, địa điểm, thực vật và không xác định.
API theo dõi và phát hiện đối tượng được tối ưu hóa cho hai 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 kính ngắm máy ảnh
- Phát hiện nhiều đối tượng từ một hình ảnh tĩnh
Để đị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+KTX
// 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()
Lấy một phiên bản của
FirebaseVisionObjectDetector
:Java
FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(); // Or, to change the default settings: FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options);
Kotlin+KTX
val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector() // Or, to change the default settings: val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options)
2. Chạy trình dò tìm đối tượng
Để phát hiện và theo dõi các đối tượng, hãy chuyển hình ảnh tới phương thức processImage()
của phiên bản FirebaseVisionObjectDetector
.
Đối với từng khung hình của video hoặc hình ảnh trong một chuỗi, hãy thực hiện như sau:
Tạo đối tượng
FirebaseVisionImage
từ hình ảnh của bạn.Để tạo đối tượng
FirebaseVisionImage
từ đối tượngmedia.Image
, chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy chuyển đối tượngmedia.Image
và góc xoay của hình ảnh tớiFirebaseVisionImage.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, vì vậy bạn chỉ cần chuyển đổi phép xoay thành một trong các hằng sốROTATION_
của ML Kit 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+KTX
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 bạn không sử dụng thư viện camera cung cấp cho bạn góc quay của hình ảnh, bạn có thể tính toán nó từ góc quay 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+KTX
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 đó, chuyển đối tượng
media.Image
và giá trị xoay choFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp tớiFirebaseVisionImage.fromFilePath()
. Điều này hữu ích khi bạn sử dụng ý địnhACTION_GET_CONTENT
để nhắc người dùng chọn 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+KTX
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Để tạo đối tượng
FirebaseVisionImage
từByteBuffer
hoặc mảng byte, trước tiên hãy tính toán góc xoay hình ảnh như mô tả ở trên cho đầu vàomedia.Image
.Sau đó, tạo một đối tượng
FirebaseVisionImageMetadata
chứa chiều cao, chiều rộng, định dạng mã hóa 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+KTX
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
FirebaseVisionImage
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin+KTX
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Để tạo đối tượng
FirebaseVisionImage
từ đối tượngBitmap
:Hình ảnh được đại diện bởi đối tượngJava
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
phải thẳng đứng, không cần xoay thêm.
Truyền hình ảnh vào 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+KTX
objectDetector.processImage(image) .addOnSuccessListener { detectedObjects -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Nếu lệnh gọi tới
processImage()
thành công, danh sáchFirebaseVisionObject
sẽ được chuyển tới người nghe thành công.Mỗi
FirebaseVisionObject
chứa các thuộc tính sau:hộp giới hạn Rect
biểu thị vị trí của vật thể trong ảnh.ID 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. Loại Phạm trù 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ì đây luôn là FirebaseVisionObject.CATEGORY_UNKNOWN
.Sự tự tin Giá trị tin cậy của 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ì đâ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+KTX
// 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ó ít đặc điểm hình ảnh có thể cần chiếm phần lớn hơn của hình ảnh để được phát hiện. Bạn nên cung cấp cho người dùng hướng dẫn về cách thu thập thông tin đầu vào phù hợp với loại đối tượng bạn muốn phát hiện.
- Khi sử dụ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 thực hiện xử lý đặc biệt đối với 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 ML Kit][showcase-link]{: .external } và Mẫu thiết kế Material Design cho bộ sưu tập các tính năng hỗ trợ máy học .
Khi sử dụng chế độ phát trực tuyến trong ứng dụng 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 trong chế độ phát 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.
Vô hiệu hóa phân loại nếu bạn không cần nó.
- Van tiết lưu gọi tới máy dò. Nếu có khung hình video mới trong khi trình phát hiện đang chạy, hãy thả khung hình đó xuống.
- Nếu bạn đang sử dụng đầu ra của bộ dò để phủ đồ họa lên hình ảnh đầu vào, trước tiên hãy lấy kết quả từ Bộ công cụ ML, sau đó kết xuất hình ảnh và lớp phủ trong một bước duy nhất. Bằng cách đó, bạn chỉ hiển thị trên bề mặt hiển thị một lần cho mỗi khung hình đầ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 máy ảnh cũ hơn, hãy chụp ảnh ở định dạng
ImageFormat.NV21
.