Multi-model image pipelines
Share a decoded frame, crop regions, and compare embeddings.
Use your own .slab model and a runtime license. Model files carry their input sizes, preprocessing, labels, and backend preferences.
Faces.swift
Swift setup →Run inference off the main thread.
// Detect faces, crop each one without copying pixels, and compare the
// first two with a face embedding model.
import Foundation
import SlabSwift
func compareFaces(detectorPath: String, embedderPath: String, imagePath: String, license: String) throws -> Float? {
let config = try SlabConfig(license: license)
let detector = try SlabSession(config: config, path: detectorPath)
let embedder = try SlabSession(config: config, path: embedderPath)
// Preprocessing normally comes from the model file. Set it in code only
// when a model file does not carry it, or to override it.
detector.setResize(.letterbox, letterboxPad: 0)
detector.setColorBGR(true)
detector.setNormalization(scale: 1, mean: (127.5, 127.5, 127.5), std: (128, 128, 128))
embedder.setNormalization(scale: 1, mean: (127.5, 127.5, 127.5), std: (127.5, 127.5, 127.5))
// One decoded frame feeds both models.
let frame = try SlabFrame(contentsOf: imagePath)
let faces = try detector.run(SlabInput(frame: frame)).boxes(scoreThreshold: 0.5, iouThreshold: 0.4, maxBoxes: 32)
for face in faces {
print("face at \(face.rect), score \(face.score), \(face.landmarks.count) landmarks")
}
guard faces.count >= 2 else { return nil }
// A crop is a view into the original pixels, not a copy.
func embed(_ face: SlabBox) throws -> SlabOutput {
let output = try embedder.run(SlabInput(frame: try frame.crop(face.rect)))
output.normalizeL2()
return output
}
return try embed(faces[0]).cosineSimilarity(with: try embed(faces[1]))
}