Bộ công cụ học máy cung cấp một SDK được tối ưu hoá để phân đoạn ảnh tự chụp. Các thành phần của Selfie Segmenter được liên kết tĩnh với ứng dụng của bạn tại thời điểm tạo bản dựng. Việc này sẽ làm tăng kích thước ứng dụng lên đến 24 MB và độ trễ API có thể thay đổi từ khoảng 7 mili giây đến khoảng 12 mili giây tuỳ thuộc vào kích thước hình ảnh đầu vào, như được đo trên iPhone X.
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
Đưa các thư viện Bộ công cụ học máy sau vào Podfile:
pod 'GoogleMLKit/SegmentationSelfie', '7.0.0'
Sau khi bạn cài đặt hoặc cập nhật các Pods của dự án, hãy mở dự án Xcode bằng .
xcworkspace
. Bộ công cụ học máy được hỗ trợ trong Xcode phiên bản 13.2.1 trở lên.
1. Tạo một thực thể của Segmenter
Để phân đoạn hình ảnh tự chụp, trước tiên, hãy tạo một thực thể của Segmenter
bằng SelfieSegmenterOptions
và tuỳ ý chỉ định chế độ cài đặt phân đoạn.
Tuỳ chọn trình phân đoạn
Chế độ phân đoạn
Segmenter
hoạt động ở hai chế độ. Hãy nhớ chọn một mẫu phù hợp với trường hợp sử dụng của bạn.
STREAM_MODE (default)
Chế độ này được thiết kế để truyền trực tuyến khung hình từ video hoặc máy ảnh. Ở chế độ này, trình phân đoạn sẽ tận dụng kết quả từ các khung trước đó để trả về kết quả phân đoạn mượt mà hơn.
SINGLE_IMAGE_MODE (default)
Chế độ này được thiết kế cho các hình ảnh đơn lẻ không liên quan. Ở chế độ này, trình phân đoạn sẽ xử lý từng hình ảnh một cách độc lập, không làm mượt các khung hình.
Bật mặt nạ kích thước thô
Yêu cầu trình phân đoạn trả về mặt nạ kích thước thô khớp với kích thước đầu ra của mô hình.
Kích thước mặt nạ thô (ví dụ: 256x256) thường nhỏ hơn kích thước hình ảnh đầu vào.
Nếu bạn không chỉ định tuỳ chọn này, trình phân đoạn sẽ điều chỉnh lại tỷ lệ của mặt nạ thô cho phù hợp với kích thước hình ảnh đầu vào. Cân nhắc sử dụng tuỳ chọn này nếu bạn muốn áp dụng logic điều chỉnh theo tỷ lệ tuỳ chỉnh hoặc không cần điều chỉnh theo tỷ lệ cho trường hợp sử dụng của mình.
Chỉ định các tuỳ chọn của trình phân đoạn:
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Cuối cùng, hãy lấy một thực thể của Segmenter
. Truyền các tuỳ chọn bạn đã chỉ định:
let segmenter = Segmenter.segmenter(options: options)
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Chuẩn bị hình ảnh đầu vào
Để phân đoạn ảnh chân dung tự chụp, hãy làm như sau cho mỗi hình ảnh hoặc khung hình video.
Nếu đã bật chế độ truyền trực tuyến, bạn phải tạo các đối tượng VisionImage
từ CMSampleBuffer
.
Tạo đối tượng VisionImage
bằng UIImage
hoặc CMSampleBuffer
.
Nếu bạn sử dụng UIImage
, hãy làm theo các bước sau:
- Tạo đối tượng
VisionImage
bằngUIImage
. Hãy nhớ chỉ định đúng.orientation
.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Nếu bạn sử dụng CMSampleBuffer
, hãy làm theo các bước sau:
-
Chỉ định hướng của dữ liệu hình ảnh có trong
CMSampleBuffer
.Cách lấy hướng hình ảnh:
func imageOrientation( deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position ) -> UIImage.Orientation { switch deviceOrientation { case .portrait: return cameraPosition == .front ? .leftMirrored : .right case .landscapeLeft: return cameraPosition == .front ? .downMirrored : .up case .portraitUpsideDown: return cameraPosition == .front ? .rightMirrored : .left case .landscapeRight: return cameraPosition == .front ? .upMirrored : .down case .faceDown, .faceUp, .unknown: return .up } }
- (UIImageOrientation) imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation cameraPosition:(AVCaptureDevicePosition)cameraPosition { switch (deviceOrientation) { case UIDeviceOrientationPortrait: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
- Tạo đối tượng
VisionImage
bằng đối tượng và hướngCMSampleBuffer
:let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Xử lý hình ảnh
Truyền đối tượng VisionImage
vào một trong các phương thức xử lý hình ảnh của Segmenter
. Bạn có thể sử dụng phương thức process(image:)
không đồng bộ hoặc phương thức results(in:)
đồng bộ.
Cách đồng bộ hoá quá trình phân đoạn trên ảnh chân dung tự chụp:
var mask: [SegmentationMask] do { mask = try segmenter.results(in: image) } catch let error { print("Failed to perform segmentation with error: \(error.localizedDescription).") return } // Success. Get a segmentation mask here.
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
Cách phân đoạn ảnh chân dung tự chụp theo cách không đồng bộ:
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. Lấy mặt nạ phân đoạn
Bạn có thể nhận được kết quả phân đoạn như sau:
let maskWidth = CVPixelBufferGetWidth(mask.buffer) let maskHeight = CVPixelBufferGetHeight(mask.buffer) CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) var maskAddress = CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( to: Float32.self, capacity: maskBytesPerRow * maskHeight) for _ in 0...(maskHeight - 1) { for col in 0...(maskWidth - 1) { // Gets the confidence of the pixel in the mask being in the foreground. let foregroundConfidence: Float32 = maskAddress[col] } maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size }
size_t width = CVPixelBufferGetWidth(mask.buffer); size_t height = CVPixelBufferGetHeight(mask.buffer); CVPixelBufferLockBaseAddress(mask.buffer, kCVPixelBufferLock_ReadOnly); size_t maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer); float *maskAddress = (float *)CVPixelBufferGetBaseAddress(mask.buffer); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { // Gets the confidence of the pixel in the mask being in the foreground. float foregroundConfidence = maskAddress[col]; } maskAddress += maskBytesPerRow / sizeof(float); }
Để biết ví dụ đầy đủ về cách sử dụng kết quả phân đoạn, vui lòng xem mẫu bắt đầu nhanh của Bộ công cụ học máy.
Mẹo cải thiện hiệu suất
Chất lượng của kết quả phụ thuộc vào chất lượng của hình ảnh đầu vào:
- Để Bộ công cụ học máy nhận được kết quả phân đoạn chính xác, hình ảnh phải có kích thước tối thiểu là 256x256 pixel.
- Nếu thực hiện phân đoạn ảnh chân dung tự chụp trong một ứng dụng theo thời gian thực, bạn cũng nên cân nhắc các kích thước tổng thể của hình ảnh đầu vào. Hình ảnh nhỏ hơn có thể được xử lý nhanh hơn, vì vậy, để giảm độ trễ, hãy chụp ảnh ở độ phân giải thấp hơn, nhưng hãy lưu ý đến các yêu cầu về độ phân giải ở trên và đảm bảo rằng đối tượng chiếm nhiều không gian hình ảnh nhất có thể.
- Độ nét của hình ảnh cũng có thể ảnh hưởng đến độ chính xác. Nếu bạn không nhận được kết quả chấp nhận được, hãy yêu cầu người dùng chụp lại hình ảnh.
Nếu bạn muốn sử dụng tính năng phân đoạ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:
- Sử dụng chế độ phân đoạn
stream
. - Cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, hãy lưu ý đến các yêu cầu về kích thước hình ảnh của API này.
- Để xử lý khung hình video, hãy sử dụng API đồng bộ
results(in:)
của trình phân đoạn. Gọi phương thức này từ hàm captureOutput(_, didOutput:from:) của AVCaptureVideoDataOutputSampleBufferDelegate để đồng bộ nhận kết quả từ khung video đã cho. Giữ alwaysDiscardsLateVideoFrames của AVCaptureVideoDataOutput ở trạng thái true để điều tiết các lệnh gọi đến trình phân đoạn. Nếu có khung video mới trong khi trình phân đoạn đang chạy, khung video đó sẽ bị loại bỏ. - Nếu bạn sử dụng kết quả của trình phân đoạn để 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. Bằng cách này, bạn chỉ kết xuất một lần cho mỗi khung đầu vào đã xử lý trên bề mặt hiển thị. Hãy xem các lớp previewOverlayView và CameraViewController trong mẫu bắt đầu nhanh của Bộ công cụ học máy để biết ví dụ.