O ML Kit oferece um SDK otimizado para segmentação de selfies. Os recursos do Selfie Segmenter são vinculados estaticamente ao seu app no momento da criação. Isso vai aumentar o tamanho do app em até 24 MB, e a latência da API pode variar de ~7 ms a ~12 ms, dependendo do tamanho da imagem de entrada, conforme medido no iPhone X.
Faça um teste
- Teste o app de exemplo para ver um exemplo de uso dessa API.
Antes de começar
Inclua as seguintes bibliotecas do ML Kit no seu Podfile:
pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'
Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o .
xcworkspace
. O ML Kit é compatível com o Xcode versão 13.2.1 ou mais recente.
1. Criar uma instância de Segmenter
Para realizar a segmentação em uma selfie, primeiro crie uma instância de Segmenter
com SelfieSegmenterOptions
e, opcionalmente, especifique as configurações de segmentação.
Opções do segmentador
Modo segmentador
O Segmenter
opera em dois modos. Escolha a opção que corresponde ao seu caso de uso.
STREAM_MODE (default)
Esse modo foi criado para transmitir frames de vídeo ou câmera. Nesse modo, o segmentador usa resultados de frames anteriores para retornar resultados de segmentação mais suaves.
SINGLE_IMAGE_MODE (default)
Esse modo é projetado para imagens únicas que não estão relacionadas. Nesse modo, o segmentador processa cada imagem de forma independente, sem suavização nos frames.
Ativar máscara de tamanho bruto
Pede ao segmentador para retornar a máscara de tamanho bruto que corresponde ao tamanho da saída do modelo.
O tamanho da máscara bruta (por exemplo, 256 x 256) geralmente é menor que o tamanho da imagem de entrada.
Sem especificar essa opção, o segmentador vai redimensionar a máscara bruta para corresponder ao tamanho da imagem de entrada. Use essa opção se quiser aplicar uma lógica de reescala personalizada ou se a reescala não for necessária para seu caso de uso.
Especifique as opções do segmentador:
Swift
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
Objective-C
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Por fim, receba uma instância de Segmenter
. Transmita as opções especificadas:
Swift
let segmenter = Segmenter.segmenter(options: options)
Objective-C
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Preparar a imagem de entrada
Para segmentar selfies, faça o seguinte para cada imagem ou frame de vídeo.
Se você tiver ativado o modo de stream, precisará criar objetos VisionImage
a partir de
CMSampleBuffer
.
Crie um objeto VisionImage
usando um UIImage
ou um CMSampleBuffer
.
Se você usa um UIImage
, siga estas etapas:
- Crie um objeto
VisionImage
com oUIImage
. Especifique a.orientation
correta.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Se você usa um CMSampleBuffer
, siga estas etapas:
-
Especifique a orientação dos dados da imagem contidos no
CMSampleBuffer
.Para ver a orientação da imagem:
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; } }
- Crie um objeto
VisionImage
usando o objetoCMSampleBuffer
e a orientação: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. Processar a imagem
Transmita o objeto VisionImage
para um dos métodos de processamento de imagem do Segmenter
. É possível usar o método process(image:)
assíncrono ou o método results(in:)
síncrono.
Para realizar a segmentação em uma selfie de forma síncrona:
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.
Para realizar a segmentação de uma selfie de maneira assíncrona:
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. Receber a máscara de segmentação
Você pode receber o resultado da segmentação da seguinte forma:
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); }
Para um exemplo completo de como usar os resultados da segmentação, consulte a amostra do guia de início rápido do Kit de ML.
Dicas para melhorar a performance
A qualidade dos resultados depende da qualidade da imagem de entrada:
- Para que o Kit de ML tenha um resultado de segmentação preciso, a imagem precisa ter pelo menos 256 x 256 pixels.
- Se você fizer a segmentação de selfies em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Como as imagens menores podem ser processadas mais rapidamente, para reduzir a latência, capture imagens em resoluções mais baixas, mas tenha em mente os requisitos de resolução acima e faça o assunto ocupar o máximo possível da imagem.
- Uma imagem com foco inadequado também pode prejudicar a precisão. Se os resultados não forem aceitáveis, peça para o usuário recapturar a imagem.
Se você quiser usar a segmentação em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames:
- Use o modo segmentador
stream
. - Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem da API.
- Para processar frames de vídeo, use a API síncrona
results(in:)
do segmentador. Chame esse método da função captureOutput(_, didOutput:from:) do AVCaptureVideoDataOutputSampleBufferDelegate para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como "true" para limitar as chamadas ao segmentador. Se um novo frame de vídeo ficar disponível enquanto o segmentador estiver em execução, ele será descartado. - Se você usar a saída do segmentador para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Consulte as classes previewOverlayView e CameraViewController na amostra do guia de início rápido do Kit de ML para ver um exemplo.