ML Kit предоставляет оптимизированный SDK для сегментации селфи. Объекты Selfie Segmenter статически связываются с приложением во время сборки. Это увеличит размер приложения на 24 МБ, а задержка API может составить от 7 до 12 мс в зависимости от размера входного изображения (измерено на iPhone X).
Попробовать
- Попробуйте поработать с примером приложения, чтобы понять, как использовать этот API.
Подготовка
Добавьте в Podfile следующие библиотеки ML Kit:
pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'После установки или обновления Pods откройте проект Xcode, используя его файл .
xcworkspace. ML Kit поддерживается в Xcode версии 13.2.1 или более поздней.
1. Создайте экземпляр Segmenter
Чтобы выполнить сегментацию селфи, сначала создайте экземпляр Segmenter с помощью SelfieSegmenterOptions и при необходимости укажите настройки сегментации.
Параметры сегментатора
Режим сегментации
Segmenter работает в двух режимах. Выберите тот, который подходит для вашего случая.
STREAM_MODE (default)
Этот режим предназначен для потоковой передачи кадров из видео или камеры. В этом режиме сегментатор будет использовать результаты предыдущих кадров, чтобы возвращать более плавные результаты сегментации.
SINGLE_IMAGE_MODE (default)
Этот режим предназначен для отдельных изображений, не связанных между собой. В этом режиме сегментатор будет обрабатывать каждое изображение независимо, без сглаживания кадров.
Включить маску исходного размера
Запрашивает у сегментатора возврат маски необработанного размера, которая соответствует размеру выходных данных модели.
Размер необработанной маски (например, 256 x 256) обычно меньше размера входного изображения.
Если не указать этот параметр, сегментатор изменит масштаб необработанной маски в соответствии с размером входного изображения. Используйте этот вариант, если вы хотите применить собственную логику масштабирования или оно вам не требуется.
Укажите параметры сегментатора:
Swift
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
Objective-C
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Наконец, получите экземпляр Segmenter. Передайте указанные вами параметры:
Swift
let segmenter = Segmenter.segmenter(options: options)
Objective-C
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Подготовьте входное изображение
Чтобы сегментировать селфи, выполните следующие действия для каждого изображения или кадра видео.
Если вы включили режим потоковой передачи, то должны создать объекты VisionImage из CMSampleBuffer.
Создайте объект VisionImage, используя UIImage или CMSampleBuffer.
Если вы используете UIImage, выполните следующие действия:
- Создайте объект
VisionImageсUIImage. Убедитесь, что вы указали правильный.orientation.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Если вы используете CMSampleBuffer, выполните следующие действия:
-
Укажите ориентацию изображения, содержащегося в
CMSampleBuffer.Чтобы узнать ориентацию изображения:
Swift
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 } }
Objective-C
- (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; } }
- Создайте объект
VisionImage, используя объектCMSampleBufferи ориентацию:Swift
let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
Objective-C
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Обработка изображения
Передайте объект VisionImage в один из методов обработки изображений Segmenter. Вы можете использовать асинхронный метод process(image:) или синхронный метод results(in:).
Чтобы выполнить сегментацию изображения селфи синхронно:
Swift
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.
Objective-C
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
Чтобы выполнить сегментацию селфи-изображения асинхронно:
Swift
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
Objective-C
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. Получить маску сегментации
Результат сегментации можно получить следующим образом:
Swift
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 }
Objective-C
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); }
Полный пример использования результатов сегментации см. в кратком руководстве по ML Kit.
Советы по повышению эффективности
Качество результатов зависит от качества исходного изображения:
- Чтобы ML Kit получил точный результат сегментации, изображение должно быть размером не менее 256x256 пикселей.
- Если вы выполняете сегментацию селфи в приложении реального времени, вам также может потребоваться учитывать общие размеры входных изображений. Изображения меньшего размера обрабатываются быстрее, поэтому, чтобы сократить задержку, делайте снимки с более низким разрешением, но помните о требованиях к разрешению, указанных выше, и следите за тем, чтобы объект занимал как можно большую часть изображения.
- Плохая фокусировка изображения также может повлиять на точность. Если результаты неудовлетворительны, попросите пользователя сделать снимок заново.
Если вы хотите использовать сегментацию в приложении реального времени, следуйте этим рекомендациям, чтобы добиться наилучшей частоты кадров:
- Используйте режим сегментации
stream. - Попробуйте сделать снимки с более низким разрешением. Однако не забывайте о требованиях к размерам изображений, которые предъявляет этот API.
- Для обработки кадров видео используйте синхронный API
results(in:)сегментатора. Вызовите этот метод из функции captureOutput(_, didOutput:from:) делегата AVCaptureVideoDataOutputSampleBufferDelegate, чтобы синхронно получить результаты для заданного кадра видео. Для свойства alwaysDiscardsLateVideoFrames объекта AVCaptureVideoDataOutput должно быть задано значение true, чтобы ограничить количество вызовов сегментатора. Если во время работы сегментатора станет доступен новый кадр видео, он будет отброшен. - Если вы хотите наложить графику на входное изображение, сначала получите результат от ML Kit, а затем выполните рендеринг изображения и наложение за один шаг. Таким образом, вы выполняете рендеринг на поверхность отображения только один раз для каждого обработанного входного кадра. Пример можно найти в классах previewOverlayView и CameraViewController в кратком руководстве по ML Kit.