Después de entrenar tu propio modelo con AutoML Vision Edge , puedes usarlo en tu aplicación para detectar objetos en imágenes.
Hay dos formas de integrar modelos entrenados desde AutoML Vision Edge. Puedes agrupar el modelo copiando los archivos del modelo en tu proyecto Xcode, o puedes descargarlo dinámicamente desde Firebase.
Opciones de agrupación de modelos | |
---|---|
Incluido en su aplicación |
|
Alojado con Firebase |
|
Antes de que empieces
Si desea descargar un modelo , asegúrese de agregar Firebase a su proyecto de Apple , si aún no lo ha hecho. Esto no es necesario cuando agrupa el modelo.
Incluye las bibliotecas TensorFlow y Firebase en tu Podfile:
Para agrupar un modelo con su aplicación:
Rápido
pod 'TensorFlowLiteSwift'
C objetivo
pod 'TensorFlowLiteObjC'
Para descargar dinámicamente un modelo desde Firebase, agregue la dependencia
Firebase/MLModelInterpreter
:Rápido
pod 'TensorFlowLiteSwift' pod 'Firebase/MLModelInterpreter'
C objetivo
pod 'TensorFlowLiteObjC' pod 'Firebase/MLModelInterpreter'
Después de instalar o actualizar los Pods de su proyecto, abra su proyecto Xcode usando su
.xcworkspace
.
1. Cargue el modelo
Configurar una fuente de modelo local
Para agrupar el modelo con su aplicación, copie el modelo y el archivo de etiquetas a su proyecto Xcode, teniendo cuidado de seleccionar Crear referencias de carpeta cuando lo haga. El archivo del modelo y las etiquetas se incluirán en el paquete de la aplicación.
Además, observe el archivo tflite_metadata.json
que se creó junto con el modelo. Necesitas dos valores:
- Las dimensiones de entrada del modelo. Esto es 320x320 por defecto.
- Las detecciones máximas del modelo. Esto es 40 por defecto.
Configurar una fuente de modelo alojada en Firebase
Para utilizar el modelo alojado de forma remota, cree un objeto CustomRemoteModel
, especificando el nombre que le asignó al modelo cuando lo publicó:
Rápido
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Google Cloud console.
)
C objetivo
FIRCustomRemoteModel *remoteModel = [[FIRCustomRemoteModel alloc]
initWithName:@"your_remote_model"];
Luego, inicie la tarea de descarga del modelo, especificando las condiciones bajo las cuales desea permitir la descarga. Si el modelo no está en el dispositivo, o si hay una versión más reciente del modelo disponible, la tarea descargará el modelo de forma asincrónica desde Firebase:
Rápido
let downloadProgress = ModelManager.modelManager().download(
remoteModel,
conditions: ModelDownloadConditions(
allowsCellularAccess: true,
allowsBackgroundDownloading: true
)
)
C objetivo
FIRModelDownloadConditions *conditions =
[[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
allowsBackgroundDownloading:YES];
NSProgress *progress = [[FIRModelManager modelManager] downloadModel:remoteModel
conditions:conditions];
Muchas aplicaciones inician la tarea de descarga en su código de inicialización, pero puede hacerlo en cualquier momento antes de necesitar usar el modelo.
Crea un detector de objetos a partir de tu modelo.
Después de configurar las fuentes de su modelo, cree un objeto Interpreter
de TensorFlow Lite a partir de una de ellas.
Si solo tiene un modelo empaquetado localmente, simplemente cree un intérprete a partir del archivo del modelo:
Rápido
guard let modelPath = Bundle.main.path(
forResource: "model",
ofType: "tflite"
) else {
print("Failed to load the model file.")
return true
}
let interpreter = try Interpreter(modelPath: modelPath)
try interpreter.allocateTensors()
C objetivo
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
ofType:@"tflite"];
NSError *error;
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
error:&error];
if (error != NULL) { return; }
[interpreter allocateTensorsWithError:&error];
if (error != NULL) { return; }
Si tiene un modelo alojado de forma remota, deberá comprobar que se haya descargado antes de ejecutarlo. Puede verificar el estado de la tarea de descarga del modelo utilizando el método isModelDownloaded(remoteModel:)
del administrador de modelos.
Aunque solo tiene que confirmar esto antes de ejecutar el intérprete, si tiene un modelo alojado de forma remota y un modelo empaquetado localmente, podría tener sentido realizar esta verificación al crear una instancia del Interpreter
: cree un intérprete a partir del modelo remoto si es descargado, y del modelo local en caso contrario.
Rápido
var modelPath: String?
if ModelManager.modelManager().isModelDownloaded(remoteModel) {
ModelManager.modelManager().getLatestModelFilePath(remoteModel) { path, error in
guard error == nil else { return }
guard let path = path else { return }
modelPath = path
}
} else {
modelPath = Bundle.main.path(
forResource: "model",
ofType: "tflite"
)
}
guard modelPath != nil else { return }
let interpreter = try Interpreter(modelPath: modelPath)
try interpreter.allocateTensors()
C objetivo
__block NSString *modelPath;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
[[FIRModelManager modelManager] getLatestModelFilePath:remoteModel
completion:^(NSString * _Nullable filePath,
NSError * _Nullable error) {
if (error != NULL) { return; }
if (filePath == NULL) { return; }
modelPath = filePath;
}];
} else {
modelPath = [[NSBundle mainBundle] pathForResource:@"model"
ofType:@"tflite"];
}
NSError *error;
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
error:&error];
if (error != NULL) { return; }
[interpreter allocateTensorsWithError:&error];
if (error != NULL) { return; }
Si solo tiene un modelo alojado de forma remota, debe desactivar la funcionalidad relacionada con el modelo (por ejemplo, atenuar u ocultar parte de su interfaz de usuario) hasta que confirme que el modelo se ha descargado.
Puede obtener el estado de descarga del modelo adjuntando observadores al Centro de notificaciones predeterminado. Asegúrese de utilizar una referencia débil a self
en el bloque de observador, ya que las descargas pueden tardar algún tiempo y el objeto de origen puede liberarse cuando finaliza la descarga. Por ejemplo:
Rápido
NotificationCenter.default.addObserver(
forName: .firebaseMLModelDownloadDidSucceed,
object: nil,
queue: nil
) { [weak self] notification in
guard let strongSelf = self,
let userInfo = notification.userInfo,
let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
as? RemoteModel,
model.name == "your_remote_model"
else { return }
// The model was downloaded and is available on the device
}
NotificationCenter.default.addObserver(
forName: .firebaseMLModelDownloadDidFail,
object: nil,
queue: nil
) { [weak self] notification in
guard let strongSelf = self,
let userInfo = notification.userInfo,
let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
as? RemoteModel
else { return }
let error = userInfo[ModelDownloadUserInfoKey.error.rawValue]
// ...
}
C objetivo
__weak typeof(self) weakSelf = self;
[NSNotificationCenter.defaultCenter
addObserverForName:FIRModelDownloadDidSucceedNotification
object:nil
queue:nil
usingBlock:^(NSNotification *_Nonnull note) {
if (weakSelf == nil | note.userInfo == nil) {
return;
}
__strong typeof(self) strongSelf = weakSelf;
FIRRemoteModel *model = note.userInfo[FIRModelDownloadUserInfoKeyRemoteModel];
if ([model.name isEqualToString:@"your_remote_model"]) {
// The model was downloaded and is available on the device
}
}];
[NSNotificationCenter.defaultCenter
addObserverForName:FIRModelDownloadDidFailNotification
object:nil
queue:nil
usingBlock:^(NSNotification *_Nonnull note) {
if (weakSelf == nil | note.userInfo == nil) {
return;
}
__strong typeof(self) strongSelf = weakSelf;
NSError *error = note.userInfo[FIRModelDownloadUserInfoKeyError];
}];
2. Prepare la imagen de entrada
A continuación, debe preparar sus imágenes para el intérprete de TensorFlow Lite.
Recorte y escale la imagen a las dimensiones de entrada del modelo, como se especifica en el archivo
tflite_metadata.json
(320x320 píxeles de forma predeterminada). Puedes hacer esto con Core Image o una biblioteca de terceros.Copie los datos de la imagen en un objeto
Data
(NSData
):Rápido
guard let image: CGImage = // Your input image guard let context = CGContext( data: nil, width: image.width, height: image.height, bitsPerComponent: 8, bytesPerRow: image.width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue ) else { return nil } context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height)) guard let imageData = context.data else { return nil } var inputData = Data() for row in 0 ..< 320 { // Model takes 320x320 pixel images as input for col in 0 ..< 320 { let offset = 4 * (col * context.width + row) // (Ignore offset 0, the unused alpha channel) var red = imageData.load(fromByteOffset: offset+1, as: UInt8.self) var green = imageData.load(fromByteOffset: offset+2, as: UInt8.self) var blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self) inputData.append(&red, count: 1) inputData.append(&green, count: 1) inputData.append(&blue, count: 1) } }
C objetivo
CGImageRef image = // Your input image long imageWidth = CGImageGetWidth(image); long imageHeight = CGImageGetHeight(image); CGContextRef context = CGBitmapContextCreate(nil, imageWidth, imageHeight, 8, imageWidth * 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image); UInt8 *imageData = CGBitmapContextGetData(context); NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0]; for (int row = 0; row < 300; row++) { for (int col = 0; col < 300; col++) { long offset = 4 * (row * imageWidth + col); // (Ignore offset 0, the unused alpha channel) UInt8 red = imageData[offset+1]; UInt8 green = imageData[offset+2]; UInt8 blue = imageData[offset+3]; [inputData appendBytes:&red length:1]; [inputData appendBytes:&green length:1]; [inputData appendBytes:&blue length:1]; } }
3. Ejecute el detector de objetos.
A continuación, pase la entrada preparada al intérprete:
Rápido
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
C objetivo
TFLTensor *input = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { return; }
[input copyData:inputData error:&error];
if (error != nil) { return; }
[interpreter invokeWithError:&error];
if (error != nil) { return; }
4. Obtener información sobre los objetos detectados.
Si la detección de objetos tiene éxito, el modelo produce como salida tres matrices de 40 elementos (o lo que se haya especificado en el archivo tflite_metadata.json
) cada una. Cada elemento corresponde a un objeto potencial. La primera matriz es una matriz de cuadros delimitadores; el segundo, una serie de etiquetas; y el tercero, una serie de valores de confianza. Para obtener los resultados del modelo:
Rápido
var output = try interpreter.output(at: 0)
let boundingBoxes =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 4 * 40)
output.data.copyBytes(to: boundingBoxes)
output = try interpreter.output(at: 1)
let labels =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 40)
output.data.copyBytes(to: labels)
output = try interpreter.output(at: 2)
let probabilities =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 40)
output.data.copyBytes(to: probabilities)
C objetivo
TFLTensor *output = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { return; }
NSData *boundingBoxes = [output dataWithError:&error];
if (error != nil) { return; }
output = [interpreter outputTensorAtIndex:1 error:&error];
if (error != nil) { return; }
NSData *labels = [output dataWithError:&error];
if (error != nil) { return; }
output = [interpreter outputTensorAtIndex:2 error:&error];
if (error != nil) { return; }
NSData *probabilities = [output dataWithError:&error];
if (error != nil) { return; }
Luego, puedes combinar las salidas de las etiquetas con tu diccionario de etiquetas:
Rápido
guard let labelPath = Bundle.main.path(
forResource: "dict",
ofType: "txt"
) else { return true }
let fileContents = try? String(contentsOfFile: labelPath)
guard let labelText = fileContents?.components(separatedBy: "\n") else { return true }
for i in 0 ..< 40 {
let top = boundingBoxes[0 * i]
let left = boundingBoxes[1 * i]
let bottom = boundingBoxes[2 * i]
let right = boundingBoxes[3 * i]
let labelIdx = Int(labels[i])
let label = labelText[labelIdx]
let confidence = probabilities[i]
if confidence > 0.66 {
print("Object found: \(label) (confidence: \(confidence))")
print(" Top-left: (\(left),\(top))")
print(" Bottom-right: (\(right),\(bottom))")
}
}
C objetivo
NSString *labelPath = [NSBundle.mainBundle pathForResource:@"dict"
ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:labelPath
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil || fileContents == NULL) { return; }
NSArray<NSString*> *labelText = [fileContents componentsSeparatedByString:@"\n"];
for (int i = 0; i < 40; i++) {
Float32 top, right, bottom, left;
Float32 labelIdx;
Float32 confidence;
[boundingBoxes getBytes:&top range:NSMakeRange(16 * i + 0, 4)];
[boundingBoxes getBytes:&left range:NSMakeRange(16 * i + 4, 4)];
[boundingBoxes getBytes:&bottom range:NSMakeRange(16 * i + 8, 4)];
[boundingBoxes getBytes:&right range:NSMakeRange(16 * i + 12, 4)];
[labels getBytes:&labelIdx range:NSMakeRange(4 * i, 4)];
[probabilities getBytes:&confidence range:NSMakeRange(4 * i, 4)];
if (confidence > 0.5f) {
NSString *label = labelText[(int)labelIdx];
NSLog(@"Object detected: %@", label);
NSLog(@" Confidence: %f", confidence);
NSLog(@" Top-left: (%f,%f)", left, top);
NSLog(@" Bottom-right: (%f,%f)", right, bottom);
}
}
Consejos para mejorar el rendimiento en tiempo real
Si desea etiquetar imágenes en una aplicación en tiempo real, siga estas pautas para lograr las mejores velocidades de fotogramas:
- Llamadas del acelerador al detector. Si hay un nuevo cuadro de video disponible mientras el detector está en ejecución, suelte el cuadro.
- Si está utilizando la salida del detector para superponer gráficos en la imagen de entrada, primero obtenga el resultado, luego renderice la imagen y superpóngala en un solo paso. Al hacerlo, renderiza en la superficie de visualización solo una vez por cada cuadro de entrada. Consulte las clases previaOverlayView y FIRDetectionOverlayView en la aplicación de muestra de presentación para ver un ejemplo.