Bạn có thể 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 nhiều khung hình của video.
Khi bạn truyền hình ảnh của Bộ công cụ học máy, Bộ công cụ học máy sẽ trả về một danh sách tối đa 5 đối tượng được phát hiện và vị trí của chúng trong hình ảnh. Khi phát hiện trong luồng video, mỗi đối tượng đều có một mã nhận dạng mà bạn có thể sử dụng để theo dõi trên các hình ảnh. Bạn cũng có thể tùy ý bật đối tượng tương đối Phân loại để 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 làm như vậy, thêm Firebase vào dự án Android của bạn.
- Thêm các phần phụ thuộc của thư viện Android cho Bộ công cụ học máy vào mô-đun của bạn
Tệp Gradle (cấp ứng dụng) (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
, nếu muốn chỉ định bất kỳ chế độ cài đặt trình phát hiện nào
muốn thay đổi so với giá trị 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 tuỳ chọn sau cài đặt: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
Trong
STREAM_MODE
(mặc định), trình phát hiện đối tượng sẽ chạy với độ trễ thấp, nhưng có thể tạo ra kết quả không hoàn chỉnh (chẳng hạn như hộp giới hạn không xác định hoặc nhãn danh mục) ở vài trang đầu các lệnh gọi của trình phát hiện. Ngoài ra, trongSTREAM_MODE
, trình phát hiện gán ID theo dõi cho các đối tượng mà bạn có thể sử dụng để theo dõi đối tượng trên các khung. Sử dụng chế độ này khi bạn muốn theo dõi đối tượng hoặc khi độ trễ thấp là quan trọng, chẳng hạn như khi xử lý theo thời gian thực.Trong
SINGLE_IMAGE_MODE
, trình phát hiện đối tượng sẽ đợi cho đến vùng giới hạn của đối tượng được phát hiện và (nếu bạn đã bật trước khi trả về một nhãn phân loại kết quả. Do đó, độ trễ phát hiện có thể cao hơn. Ngoài ra, trongSINGLE_IMAGE_MODE
, mã theo dõi không được được chỉ định. Hãy sử dụng chế độ này nếu độ trễ không quan trọng và bạn thì 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 5 đối tượng hay chỉ phát hiện tối đa đối tượng đối tượng nổi bật (mặc định).
Phân loại đối tượng false
(mặc định) |true
Liệu có phân loại các đối tượng được phát hiện thành các danh mục tương đối hay không. Khi được bật, trình phát hiện đối tượng sẽ phân loại đố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, thực vật và những điều chưa biết.
API theo dõi và phát hiện đối tượng được tối ưu hoá cho hai mục đích sử dụng cốt lõi này trường hợp:
- Phát hiện trực tiếp và theo dõi đối tượng nổi bật nhất trong máy ảnh kính ngắm
- Phát hiện nhiều đối tượng trong một ả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+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()
Nhận một 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+KTX
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 FirebaseVisionObjectDetector
phương thức processImage()
của thực thể.
Đối với mỗi khung video hoặc hình ảnh trong một trình tự, hãy làm như sau:
Tạo một đối tượng
FirebaseVisionImage
từ hình ảnh của bạn.-
Cách tạo đối tượng
FirebaseVisionImage
qua Đố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.Image
và xoay thànhFirebaseVisionImage.fromMediaImage()
.Nếu bạn sử dụng Thư viện CameraX,
OnImageCapturedListener
và Các lớpImageAnalysis.Analyzer
tính toán giá trị xoay cho bạn, nên bạn chỉ cần chuyển đổi chế độ xoay vòng thành một Hằng sốROTATION_
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 không sử dụng thư viện máy ảnh cho phép bạn xoay hình ảnh, có thể tính toán kích thước này dựa trên hướng xoay của thiết bị và hướng của máy ảnh cảm biến 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 đó, hãy truyền đối tượng
media.Image
và thànhFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
qua URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đểFirebaseVisionImage.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 bức ảnh trong ứ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() }
- Cách tạo đối tượng
FirebaseVisionImage
quaByteBuffer
hoặc một mảng byte, trước tiên, hãy tính hình ảnh như mô tả ở trên cho đầu vàomedia.Image
.Sau đó, hãy tạo một đối tượng
FirebaseVisionImageMetadata
có chứa chiều cao, chiều rộng, định dạng mã hoá màu của hình ảnh, và xoay: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 vùng đệm hoặc mảng và đối tượng siêu dữ liệu để tạo một Đố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)
- Cách tạo đối tượng
FirebaseVisionImage
qua Đối tượngBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
đại diện phải thẳng đứng mà 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 đến
processImage()
thành công, một danh sáchFirebaseVisionObject
sẽ được chuyển đến trình nghe thành công.Mỗi
FirebaseVisionObject
chứa các thuộc tính sau:Hộp giới hạn Rect
cho biết vị trí của đối tượng trong hình ảnh.Mã theo dõi Số nguyên xác định đối tượng trên các hình ảnh. Rỗng trong SINGLE_IMAGE_MODE. Danh mục Danh mục tương đối của đối tượng. Nếu trình phát hiện vật thể không đã bật tính năng phân loại, chức năng này luôn FirebaseVisionObject.CATEGORY_UNKNOWN
.Mức độ tự tin Giá trị tin cậy của việc phân loại đối tượng. Nếu đối tượng trình phát hiện 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, 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 hữu 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 được đối tượng có thành công hay không còn phụ thuộc vào độ phức tạp trực quan của đối tượng. Đồ vật nhưng có một số ít các tính năng trực quan có thể cần chiếm phần lớn hơn hình ảnh cần phát hiện. Bạn cần cung cấp cho người dùng hướng dẫn về cách chụp đầ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 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 nằm trong rõ ràng vào các danh mục được hỗ trợ, triển khai cách xử lý đặc biệt đối với các trường hợp .
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][đường liên kết giới thiệu]{: .external } và Thiết kế Material Design Bộ sưu tập Mẫu cho các tính năng dựa trên công nghệ học máy.
Khi sử dụng chế độ truyền trực tuyến 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:
Đừng dùng tính năng phát hiện nhiều vật thể ở chế độ phát trực tuyến, vì hầu hết các thiết bị sẽ không dùng được có thể tạo đủ tốc độ khung hình.
Hãy tắt tính năng phân loại nếu bạn không cần.
- Điều tiết lệnh gọi đến trình phát hiện. Nếu một khung video mới trong khi trình phát hiện đang chạy, hãy thả khung hình.
- Nếu bạn đang sử dụng đầu ra 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à phủ lên trên trong một bước duy nhất. Khi làm vậy, bạn sẽ kết xuất lên giao diện màn hình 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 trong Định dạng
ImageFormat.YUV_420_888
.Nếu bạn sử dụng API Máy ảnh cũ, hãy chụp ảnh trong Định dạng
ImageFormat.NV21
.