VNCoreMLRequest — é uma subclasse de VNRequest que permite executar modelos Core ML dentro do pipeline do Vision, combinando as vantagens dos detectores prontos do Vision (rostos, texto, objetos) com modelos ML personalizados para classificação e regressão. De acordo com a Apple Machine Learning Documentation (2024), o VNCoreMLRequest gerencia automaticamente o pré-processamento de imagem — redimensionamento, recorte e conversão de espaço de cor — de acordo com os requisitos do modelo Core ML.
Principais conclusões
VNCoreMLRequest é uma subclasse de VNRequest, adicionada no iOS 11 junto com Vision, que permite executar modelos Core ML no contexto do Vision. Ele gerencia todo o pré-processamento de imagem necessário pelo modelo Core ML: redimensionamento, recorte, normalização e conversão de espaço de cor.
Sem o VNCoreMLRequest, um desenvolvedor teria que converter manualmente UIImage/CGImage para MultiArray (MLMultiArray) ou PixelBuffer (CVPixelBuffer) do tamanho necessário. O VNCoreMLRequest automatiza esse processo: você passa um CGImage através do VNImageRequestHandler, e o VNCoreMLRequest o redimensiona para a entrada do modelo.
VNCoreMLRequest herda todos os recursos do VNRequest: completion handler, regionOfInterest, capacidade de executar múltiplas requisições simultaneamente, suporte para VNImageRequestHandler e VNSequenceRequestHandler.
import Vision
import CoreML
// 1. Carregar e envolver o modelo
guard let model = try VNCoreMLModel(
for: MobileNetV2().model)
else { return }
// 2. Criar VNCoreMLRequest
let request = VNCoreMLRequest(model: model) { req, _ in
guard let results = req.results
as? [VNClassificationObservation]
else { return }
for r in results.prefix(3) {
print("\\(r.identifier): \\(r.confidence)")
}
}
// 3. Executar através do handler
let handler = VNImageRequestHandler(cgImage: image, options: [:])
try handler.perform([request])
VNCoreMLRequest suporta modelos com diferentes tipos de entrada: imagens (Image Feature), MultiArray e Double. Para imagens, o Vision converte automaticamente CGImage para CVPixelBuffer do tamanho e espaço de cor necessários. Para outros tipos de dados de entrada, use Core ML diretamente sem Vision.
De acordo com a Apple WWDC 2023, o VNCoreMLRequest é usado em 40% de todos os aplicativos iOS que utilizam Core ML para processamento de imagens. Esta é a forma mais popular de integrar modelos ML em aplicativos iOS.
VNCoreMLModel é um wrapper que adapta o modelo Core ML (MLModel) para uso no Vision. Ele converte os dados de entrada e saída do modelo para um formato compreensível pelo Vision: imagem → CVPixelBuffer, resultado → VNObservation.
A inicialização do VNCoreMLModel verifica a compatibilidade do modelo com Vision: o modelo deve aceitar uma imagem como entrada (Image Feature) e retornar classificação (MLMultiArray ou Dictionary). Se o modelo for incompatível, o inicializador lança um erro.
import Vision
import CoreML
// Opção 1: De .mlmodel (compilado em tempo de construção)
let model1 = try VNCoreMLModel(
for: MyVisionModel().model)
// Opção 2: De .mlmodelc (compilado no dispositivo)
let compiledURL = Bundle.main.url(
forResource: "MyVisionModel",
withExtension: "mlmodelc")!
let model2 = try VNCoreMLModel(
for: MLModel(contentsOf: compiledURL))
VNCoreMLModel armazena em cache o modelo na memória após o primeiro carregamento. Recarregar o mesmo modelo retorna a instância em cache, o que acelera requisições subsequentes. No entanto, se o modelo pesar mais de 100 MB, o iOS pode descarregá-lo da memória quando os recursos estiverem escassos — neste caso, o VNCoreMLModel recarregará o modelo automaticamente.
Para modelos treinados com Create ML, o VNCoreMLModel funciona sem configuração adicional. O Create ML exporta modelos com os metadados corretos que o Vision reconhece automaticamente — basta passar o modelo para VNCoreMLModel(model:).
imageCropAndScaleOption é uma propriedade chave do VNCoreMLRequest que determina como o Vision transforma a imagem de origem para corresponder ao tamanho de entrada do modelo Core ML. Escolher a opção correta afeta diretamente a precisão da classificação.
.centerCrop recorta a imagem do centro para um quadrado e depois a redimensiona para o tamanho de entrada do modelo. Adequado para modelos treinados em objetos centralizados (a maioria dos classificadores ImageNet). .scaleFill estica a imagem para o tamanho de entrada sem preservar proporções. Rápido, mas distorce a geometria. .scaleFit redimensiona preservando proporções, adicionando letterbox (barras pretas) nas bordas.
import Vision
let request = VNCoreMLRequest(model: model)
// .centerCrop — para objetos centralizados (padrão)
request.imageCropAndScaleOption = .centerCrop
// .scaleFill — para texturas uniformes (sem distorção)
request.imageCropAndScaleOption = .scaleFill
// .scaleFit — quando as proporções do objeto importam
request.imageCropAndScaleOption = .scaleFit
Recomendações: para a maioria dos modelos de classificação, use .centerCrop — ele oferece o melhor equilíbrio entre precisão e desempenho. Se o modelo foi treinado em imagens com proporções preservadas (por exemplo, detecção de anomalias em fotos de documentos), escolha .scaleFit com letterbox.
De acordo com a Apple Developer Documentation 2024, a escolha incorreta de imageCropAndScaleOption pode reduzir a precisão do modelo em 15–25%. Por exemplo, .scaleFill para um rosto localizado na borda do quadro pode cortar parte dele com .centerCrop ou distorcer proporções com .scaleFill.
O principal ponto forte do VNCoreMLRequest é a capacidade de combiná-lo com outros VNRequest em uma única chamada perform(). Isso permite construir pipelines: primeiro detectar rostos (VNDetectFaceRectanglesRequest), depois classificar cada rosto através de um modelo Core ML personalizado (VNCoreMLRequest).
VNCoreMLRequest também suporta regionOfInterest — se você definir esta área, o Vision recortará a imagem para o retângulo especificado antes de passá-la ao modelo Core ML. Isso é crítico para pipelines: após a detecção facial, você passa o bounding box como regionOfInterest para o VNCoreMLRequest.
import Vision
// 1. Detecção facial
let faceRequest = VNDetectFaceRectanglesRequest()
// 2. Classificação de emoções via Core ML
guard let emotionModel = try VNCoreMLModel(
for: EmotionClassifier().model)
else { return }
let emotionRequest = VNCoreMLRequest(model: emotionModel)
try handler.perform([faceRequest, emotionRequest])
// 3. Definir regionOfInterest para cada rosto
for face in faceRequest.results as? [VNFaceObservation] ?? [] {
emotionRequest.regionOfInterest = face.boundingBox
try handler.perform([emotionRequest])
// Processar resultado da classificação de emoções
}
Limitação: regionOfInterest para VNCoreMLRequest faz sentido quando o modelo é treinado em imagens do mesmo tamanho e proporção. Se o modelo espera uma entrada estritamente quadrada (224x224), .centerCrop com regionOfInterest dará o melhor resultado.
De acordo com a Apple ML Research, o pipeline “detecção → classificação” através de regionOfInterest proporciona uma melhoria de precisão de 20–30% em comparação com a classificação da imagem inteira, porque o modelo ML recebe apenas a área relevante sem ruído de fundo.
| Tipo de pipeline | Requisição 1 (detecção) | Requisição 2 (ML) | Exemplo |
|---|---|---|---|
| Rosto → emoção | VNDetectFaceRectanglesRequest | VNCoreMLRequest | Detecção de humor |
| Objeto → marca | VNDetectObjectAtPointRequest | VNCoreMLRequest | Reconhecimento de logotipos |
| Texto → idioma | VNRecognizeTextRequest | VNCoreMLRequest | Classificação de idioma do texto |
| Cena → descrição | VNClassifyImageRequest | VNCoreMLRequest | Geração de tags |
VNCoreMLRequest retorna resultados como VNClassificationObservation (para modelos de classificação) ou VNCoreMLFeatureValueObservation (para regressão e outros tipos). O tipo de resultado depende dos dados de saída do modelo Core ML.
VNClassificationObservation contém identifier (nome da classe) e confidence. Modelos com saída softmax retornam um array dessas observações ordenadas por confidence decrescente. VNCoreMLFeatureValueObservation contém um MLFeatureValue arbitrário — pode ser MultiArray, Double, String ou Dictionary.
// Para modelos de classificação
if let classificationResults = request.results
as? [VNClassificationObservation] {
for result in classificationResults
where result.confidence > 0.5 {
print("\\(result.identifier): \\(result.confidence)")
}
}
// Para modelos de regressão (valores de características)
if let featureResults = request.results
as? [VNCoreMLFeatureValueObservation] {
for result in featureResults {
let value = result.featureValue
print("\\(result.featureName): \\(value)")
}
}
Filtragem por confidence: a Apple recomenda descartar resultados com confidence < 0,3 para classificadores gerais e < 0,7 para aplicações críticas. Para modelos treinados em conjuntos de dados balanceados, o confidence se correlaciona com a probabilidade de resposta correta, mas não a garante.
VNCoreMLFeatureValueObservation.featureName corresponde ao nome da camada de saída do modelo (por exemplo, “classLabel” ou “features”). Isso permite lidar com modelos com múltiplas saídas — cada saída é representada por uma observação separada com um featureName único.
VNCoreMLRequest é otimizado para funcionar em Neural Engine (A12+), GPU e CPU. O Vision seleciona automaticamente o melhor dispositivo para execução do modelo dependendo do seu tipo e tamanho. No entanto, o desempenho pode ser ainda mais melhorado com uma configuração adequada.
Modelos Core ML são carregados na memória no primeiro VNCoreMLRequest e permanecem lá até o aplicativo ser descarregado. Para modelos maiores que 200 MB, a Apple recomenda carregá-los sob demanda e descarregá-los através de MLModel.release(). O VNCoreMLModel gerencia o cache por si só, mas você pode controlar isso através de autoreleasepool.
Para processamento em lote de imagens, crie um VNCoreMLRequest e reutilize-o com diferentes VNImageRequestHandler. Não crie um novo VNCoreMLRequest para cada imagem — isso diminuirá o processamento devido ao carregamento repetido do modelo. Reutilizar a requisição proporciona um ganho de desempenho de até 40% ao processar 10+ imagens.
// Correto: requisição única para todas as imagens
let batchSize = 20
let batchRequest = VNCoreMLRequest(model: model)
for i in 0..<batchSize {
let handler = VNImageRequestHandler(
cgImage: images[i],
options: [:])
try handler.perform([batchRequest])
// batchRequest.results atualiza em cada chamada
}
Seleção de dispositivo: por padrão, o Vision seleciona Neural Engine para modelos compatíveis em dispositivos A12+. Se o modelo não suportar Neural Engine, o Vision usa GPU ou CPU. Você pode forçar a especificação do dispositivo através de MLModelConfiguration.computeUnits, mas a Apple recomenda deixar a seleção automática.
De acordo com o Apple Performance Benchmarks 2024, o VNCoreMLRequest no Neural Engine (iPhone 15 Pro) processa a classificação MobileNetV2 em 3–5 ms, na GPU — 8–12 ms, na CPU — 20–30 ms. A diferença se torna crítica para aplicações em tempo real que processam 30+ quadros por segundo.
Perguntas frequentes
Sim, Core ML pode ser usado diretamente através de MLModel.prediction() sem Vision. No entanto, o VNCoreMLRequest automatiza o pré-processamento de imagem (redimensionamento, recorte, conversão para CVPixelBuffer). Se o modelo aceitar não uma imagem, mas MultiArray ou Double — use Core ML diretamente. O VNCoreMLRequest é apenas para modelos com Image Feature como entrada.
Crie um novo VNCoreMLModel a partir do MLModel atualizado e um novo VNCoreMLRequest. A requisição antiga continuará usando a versão antiga do modelo. Para atualizações remotas de modelos, use MLModel.compileModel(at:) para compilar o modelo no dispositivo a partir de um arquivo .mlmodelc baixado do servidor.
VNCoreMLRequest suporta apenas modelos com uma única entrada Image Feature. Se o modelo tiver múltiplas entradas (por exemplo, imagem + texto), use Core ML diretamente através de MLModel. O Vision não pode passar parâmetros adicionais além da imagem.
O limite é 8192 x 8192 pixels para CGImage passado ao VNImageRequestHandler. No entanto, os modelos Core ML geralmente esperam entrada de 224x224, 299x299 ou 512x512. O Vision dimensiona automaticamente imagens grandes para o tamanho de entrada. Se a imagem original for muito grande, reduza-a previamente através de CGImage para economizar memória.
Sim, defina preferBackgroundProcessing = true no VNRequest. Isso permite que o Vision adie a execução da requisição se o sistema estiver em modo intensivo de recursos (por exemplo, carregamento de conteúdo). Além disso, use DispatchQueue.global(qos: .background) para chamar handler.perform().
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também