O ML Kit oferece um SDK otimizado para segmentação de selfies. Os recursos do Selfie Segmenter são vinculados de forma estática ao 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 cerca 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 conferir um exemplo de uso dessa API.
Antes de começar
Inclua as seguintes bibliotecas do Kit de ML no seu Podfile:
pod 'GoogleMLKit/SegmentationSelfie', '7.0.0'
Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o .
xcworkspace
. O Kit de ML tem suporte na versão 13.2.1 ou mais recente do Xcode.
1. Criar uma instância do Segmenter
Para realizar a segmentação em uma imagem de selfie, primeiro crie uma instância de Segmenter
com SelfieSegmenterOptions
e, opcionalmente, especifique as configurações de segmentação.
Opções do segmentador
Modo de segmentação
O Segmenter
opera em dois modos. Escolha a opção que melhor se adapta ao seu caso de uso.
STREAM_MODE (default)
Esse modo é projetado para streaming de frames de vídeo ou câmera. Nesse modo, o segmentador aproveita os resultados de frames anteriores para retornar resultados de segmentação mais suaves.
SINGLE_IMAGE_MODE (default)
Esse modo é projetado para imagens únicas não relacionadas. Nesse modo, o segmentador processa cada imagem de forma independente, sem suavização sobre os frames.
Ativar máscara de tamanho bruto
Pede ao segmentador para retornar a máscara de tamanho bruto que corresponde ao tamanho de 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 personalizada de redimensionamento ou se não for necessário redimensionar para seu caso de uso.
Especifique as opções de segmentação:
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Por fim, receba uma instância de Segmenter
. Transmita as opções especificadas:
let segmenter = Segmenter.segmenter(options: options)
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Preparar a imagem de entrada
Para segmentar selfies, siga estas etapas para cada imagem ou frame de vídeo.
Se você tiver ativado o modo de stream, precisará criar objetos VisionImage
usando
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.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
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 conferir a orientação da imagem:
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; } }
- Crie um objeto
VisionImage
usando o objetoCMSampleBuffer
e a orientação: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. 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 imagem de selfie de forma síncrona:
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.
Para realizar a segmentação de uma imagem de selfie de forma assíncrona, faça o seguinte:
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. Receber a máscara de segmentação
Para receber o resultado da segmentação, faça o seguinte:
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); }
Para conferir um exemplo completo de como usar os resultados de 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 consiga um resultado de segmentação preciso, a imagem precisa ter pelo menos 256 x 256 pixels.
- Se você fizer a segmentação de selfie em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Já que as imagens menores podem ser processadas mais rapidamente, reduza a latência capturando imagens em resoluções menores. No entanto, lembre-se dos requisitos de resolução acima e faça o rosto da pessoa ocupar o máximo possível da imagem.
- Uma imagem com foco inadequado também pode afetar 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 de segmentação
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 síncronos do frame de vídeo fornecido. Mantenha AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames 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 no exemplo de início rápido do Kit de ML.