Quando você transmite uma imagem para o Kit de ML, ele detecta até cinco objetos nela junto com a posição de cada um. Ao detectar objetos em streams de vídeo, cada um tem um ID exclusivo que pode ser usado para rastrear o objeto de frame a frame.
É possível usar um modelo personalizado de classificação de imagens para classificar os objetos detectados. Consulte Modelos personalizados com o Kit de ML para orientações sobre requisitos de compatibilidade de modelos, onde encontrar modelos pré-treinados e como treinar seus próprios modelos.
Há duas maneiras de integrar um modelo personalizado. Você pode empacotar o modelo colocando-o na pasta de recursos do app ou fazer o download dele dinamicamente do Cloud Storage. A tabela a seguir compara as duas opções.
| Modelo agrupado | Modelo hospedado |
|---|---|
O modelo faz parte do arquivo .ipa do app, o que
aumenta o tamanho dele. |
O modelo não faz parte do arquivo .ipa do seu app. Ele é hospedado com o upload para o Cloud Storage. Recomendamos usar o
Cloud Storage para
Firebase. |
| O modelo estará disponível imediatamente, mesmo quando o dispositivo Android estiver off-line | O app precisa incluir código para fazer o download do modelo sob demanda. |
| Não é necessário ter um projeto do Firebase | Requer um projeto do Firebase (se você estiver usando o Cloud Storage para Firebase). |
| Você precisa republicar o app para atualizar o modelo | Enviar atualizações do modelo sem republicar o app |
| Sem teste A/B integrado | Teste A/B com a Configuração remota do Firebase |
Faça um teste
- Consulte o app de início rápido do Vision para um exemplo de uso do modelo agrupado e o app de início rápido do AutoML para um exemplo de uso do modelo hospedado.
- Consulte o app de demonstração do Material Design para uma implementação completa dessa API.
Antes de começar
Inclua as bibliotecas do kit de ML no seu Podfile:
pod 'GoogleMLKit/ObjectDetectionCustom', '8.0.0'Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o
.xcworkspace. O Kit de ML é compatível com a versão 13.2.1 ou superior do Xcode.Se você quiser fazer o download de um modelo usando o Cloud Storage para Firebase, adicione o Firebase ao seu projeto do iOS, caso ainda não tenha feito isso. Essa etapa não é necessária para empacotar o modelo.
1. Carregar o modelo
Configurar uma fonte de modelo local
Para agrupar o modelo e o app:
Copie o arquivo de modelo (geralmente terminado em
.tfliteou.lite) para seu projeto do Xcode, selecionandoCopy bundle resourcesao fazer isso. O arquivo de modelo será incluído no pacote de apps e estará disponível para o Kit de ML.Crie o objeto
LocalModel, especificando o caminho para o arquivo do modelo:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
Configurar uma fonte de modelo hospedada remotamente
Para usar o modelo hospedado remotamente, faça o download do arquivo modelo para o armazenamento local do dispositivo usando sua própria lógica de app e carregue-o como um modelo local. Recomendamos usar o Cloud Storage para Firebase para hospedar um modelo. Para detalhes da implementação, consulte o guia de migração do Firebase ML para o Cloud Storage.
2. Configurar o detector de objetos
Depois de configurar as origens do modelo, configure o detector de objetos para seu
caso de uso com um objeto CustomObjectDetectorOptions. É possível mudar as
seguintes configurações:
| Configurações do detector de objetos | |
|---|---|
| Modo de detecção |
STREAM_MODE (padrão) | SINGLE_IMAGE_MODE
No No |
| Detectar e rastrear vários objetos |
false (padrão) | true
Se for preciso detectar e rastrear até cinco objetos ou apenas o objeto mais proeminente (padrão). |
| Classificar objetos |
false (padrão) | true
Se é preciso ou não classificar os objetos detectados usando o modelo de classificador personalizado fornecido. Para usar seu modelo de classificação personalizada, defina isso como |
| Limite de confiança de classificação |
Pontuação mínima de confiança dos rótulos detectados. Se não for definido, qualquer limite de classificador especificado pelos metadados do modelo será usado. Se o modelo não tiver metadados ou se eles não especificarem um limite de classificação, será usado um limite padrão de 0,0. |
| Número máximo de rótulos por objeto |
Número máximo de rótulos por objeto que o detector vai retornar. Se não for definido, o valor padrão de 10 será usado. |
Se você tiver apenas um modelo agrupado localmente, basta criar um detector de objetos usando
o objeto LocalModel:
Swift
let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3
Objective-C
MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3;
Se você tiver um modelo hospedado remotamente, será necessário verificar se foi feito o download dele antes de executá-lo.
Embora você só precise confirmar isso antes de executar o detector de objetos, se tiver um modelo hospedado remotamente e um agrupado localmente, talvez seja interessante realizar essa verificação ao instanciar o ObjectDetector: crie um detector do modelo remoto se ele tiver sido baixado e do modelo local caso contrário.
Swift
// Path where your download logic saves the model let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") let model: LocalModel if FileManager.default.fileExists(atPath: localModelURL.path) { // Use the downloaded model model = LocalModel(path: localModelURL.path) } else { // Fall back to bundled model guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return } model = LocalModel(path: bundledModelPath) } let options = CustomObjectDetectorOptions(localModel: model) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3 let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; MLKLocalModel *model; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Use the downloaded model model = [[MLKLocalModel alloc] initWithPath:localModelPath]; } else { // Fall back to bundled model NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"]; model = [[MLKLocalModel alloc] initWithPath:bundledModelPath]; } MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:model]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3; MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
Se você tiver apenas um modelo hospedado remotamente, desative o recurso relacionado ao modelo (por exemplo, ocultando ou esmaecendo parte da IU) até confirmar que o download do modelo foi concluído.
Swift
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") if FileManager.default.fileExists(atPath: localModelURL.path) { // Model is already cached, initialize immediately self.initializeDetector(with: localModelURL) } else { // Model is not yet available, show loading UI and start download self.showLoadingUI() let storage = Storage.storage() let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite") modelRef.write(toFile: localModelURL) { url, error in self.hideLoadingUI() if let error = error { // Handle download error self.showErrorUI() } else if let modelURL = url { // Download success, initialize detector self.initializeDetector(with: modelURL) } } } func initializeDetector(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true self.objectDetector = ObjectDetector.objectDetector(options: options) // Enable ML features in UI self.enableMLFeatures() }
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath]; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Model is already cached, initialize immediately [self initializeDetectorWithURL:localModelURL]; } else { // Model is not yet available, show loading UI and start download [self showLoadingUI]; FIRStorage *storage = [FIRStorage storage]; FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"]; [modelRef writeToFile:localModelURL completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { [self hideLoadingUI]; if (error != nil) { // Handle download error [self showErrorUI]; } else { // Download success, initialize detector [self initializeDetectorWithURL:URL]; } }]; } - (void)initializeDetectorWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; self.objectDetector = [MLKObjectDetector objectDetectorWithOptions:options]; // Enable ML features in UI [self enableMLFeatures]; }
A API de detecção de objeto e rastreamento de objetos é otimizada para estes dois casos de uso principais:
- Detecção ao vivo e rastreamento do objeto mais proeminente no visor da câmera.
- A detecção de vários objetos em uma imagem estática.
Para configurar a API para esses casos de uso:
Swift
// Live detection and tracking let options = CustomObjectDetectorOptions(localModel: localModel) options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3 // Multiple object detection in static images let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableMultipleObjects = true options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3
Objective-C
// Live detection and tracking MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3; // Multiple object detection in static images MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableMultipleObjects = YES; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3;
3. Preparar a imagem de entrada
Crie um objeto VisionImage usando um UIImage ou um CMSampleBuffer.
Se você usa um UIImage, siga estas etapas:
- Crie um objeto
VisionImagecom oUIImage. Especifique a.orientationcorreta.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
VisionImageusando o objetoCMSampleBuffere 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];
4. Criar e executar o detector de objetos
Crie um novo detector de objetos:
Swift
let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
Em seguida, use o detector:
De forma assíncrona:
Swift
objectDetector.process(image) { objects, error in guard error == nil, let objects = objects, !objects.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[objectDetector processImage:image completion:^(NSArray
*_Nullable objects, NSError *_Nullable error) { if (objects.count == 0) { // Handle the error. return; } // Show results. }]; De forma síncrona:
Swift
var objects: [Object] do { objects = try objectDetector.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray
*objects = [objectDetector resultsInImage:image error:&error]; // Show results or handle the error.
5. Receber informações sobre objetos rotulados
Se a chamada para o processador de imagem for bem-sucedida, ela transmitirá uma lista de
Object para o gerenciador de conclusão ou retornará a lista, caso você tenha chamado o método assíncrono ou síncrono.
Cada Object contém as seguintes propriedades:
frame |
Um CGRect que indica a posição do objeto na
imagem. |
||||||
trackingID |
Um número inteiro que identifica o objeto nas imagens ou "nil" em SINGLE_IMAGE_MODE. | ||||||
labels |
|
Swift
// objects contains one item if multiple object detection wasn't enabled. for object in objects { let frame = object.frame let trackingID = object.trackingID let description = object.labels.enumerated().map { (index, label) in "Label \(index): \(label.text), \(label.confidence), \(label.index)" }.joined(separator: "\n") }
Objective-C
// The list of detected objects contains one item if multiple object detection // wasn't enabled. for (MLKObject *object in objects) { CGRect frame = object.frame; NSNumber *trackingID = object.trackingID; for (MLKObjectLabel *label in object.labels) { NSString *labelString = [NSString stringWithFormat:@"%@, %f, %lu", label.text, label.confidence, (unsigned long)label.index]; } }
Garantir uma ótima experiência do usuário
Para a melhor experiência do usuário, siga estas diretrizes no aplicativo:
- A detecção de objeto bem-sucedida depende da complexidade visual do objeto. Para serem detectados, objetos com poucos recursos visuais podem precisar ocupar uma parte maior da imagem. Forneça aos usuários orientações sobre como capturar entradas que funcionem bem com o tipo de objeto que você quer detectar.
- Ao usar a classificação, se você quiser detectar objetos que não se enquadrem nas categorias aceitas, implemente o tratamento especial para objetos desconhecidos.
Além disso, confira o app de demonstração do Kit de ML com Material Design e a coleção de Material Design de Padrões para recursos com tecnologia de machine learning.
Como melhorar o desempenho
Se você quiser usar a detecção de objetos em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames:Ao usar o modo de streaming em um aplicativo em tempo real, não use a detecção de vários objetos, porque a maioria dos dispositivos não será capaz de produzir taxas de frames adequadas.
- Para processar frames de vídeo, use a API síncrona
results(in:)do detector. Chame esse método da funçãoAVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:)para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha oAVCaptureVideoDataOutputalwaysDiscardsLateVideoFramescomotruepara limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, ele será descartado. - Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML. Em seguida, renderize a imagem e faça a sobreposição de uma só vez. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada quadro de entrada processado. Consulte updatePreviewOverlayViewWithLastFrame na amostra do guia de início rápido do Kit de ML para conferir um exemplo.