[SWIFT] [Vison / Core ML] Explication du code de l'application d'apprentissage automatique iOS qui effectue la classification des images

Quel est cet article?

Je voulais comprendre le code source de l'exemple de code "Classification des images dans Vision et Core ML" par Apple, alors je l'ai organisé.

environnement

macOS 10.15.7 Xcode 12.1 Swift 5.3

Code source

Afficher le code du contrôleur

Le projet qui peut effectivement être téléchargé n'a pas pu être construit tel quel avec le Xcode actuel (2020.10.29). Par conséquent, après avoir effectué l'optimisation par Xcode, j'ai modifié certaines parties. Ce qui suit est la partie implémentation omise.

ImageClassificationViewController.swift


class ImageClassificationViewController: UIViewController {
    // IBOutlets
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var cameraButton: UIBarButtonItem!
    @IBOutlet weak var classificationLabel: UILabel!
    
    // Image Classification
    //Configuration du modèle
    lazy var classificationRequest: VNCoreMLRequest = {
        let modelURL = Bundle.main.url(forResource: "MobileNetV2", withExtension: "mlmodelc")!
        let model = try! VNCoreMLModel(for: MobileNetV2(contentsOf: modelURL).model)
        
        let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
            self?.processClassifications(for: request, error: error)
        })
        request.imageCropAndScaleOption = .centerCrop
        return request
    }()
    
    //Exécuter la demande
    func updateClassifications(for image: UIImage) {...}
    
    ///Mettre à jour l'affichage à l'écran avec le résultat de la classification
    //Faire un classement
    func processClassifications(for request: VNRequest, error: Error?) {...}
    }
    
    //Procédure photographique
    @IBAction func takePicture() {...}    
    func presentPhotoPicker(sourceType: UIImagePickerController.SourceType) {...}
}

extension ImageClassificationViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    //Sélection par sélecteur d'images
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {...}
}

La classe «ImageClassificationViewController» a «4 propriétés» et «5 méthodes». La mise en œuvre est la suivante.

classificationRequest, propriété

Crée et renvoie une "requête" de type VNCoreMLRequest. Maintenant, faites attention au mot-clé «paresseux». Autrement dit, la valeur est calculée uniquement lors de l'accès. En d'autres termes, "créer une instance de modèle -> créer une demande" est exécuté à chaque appel.

classificationRequest, propriété


lazy var classificationRequest: VNCoreMLRequest = {
    do {
        //Utiliser la classe MobileNet générée automatiquement par Core ML
        //En réécrivant cette partie, vous pouvez utiliser n'importe quel modèle Core ML
        let modelURL = Bundle.main.url(forResource: "MobileNetV2", withExtension: "mlmodelc")
        let model = try VNCoreMLModel(for: MobileNetV2(contentsOf: modelURL!).model)
            
        let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in
            self?.processClassifications(for: request, error: error)
        })
        request.imageCropAndScaleOption = .centerCrop
        return request
    } catch {
        fatalError("Failed to load Vision ML model: \(error)")
    }
}()

updateClassifications (pour :) méthode

Tout d'abord, un prétraitement (orientation et format) est effectué sur l'image de type ʻUIImage` reçue. Ensuite, après avoir créé un gestionnaire de demandes, la demande est transmise au gestionnaire et exécutée. Notez que l'exécution du gestionnaire est gérée en arrière-plan.

updateClassifications()Méthode


func updateClassifications(for image: UIImage) {
    classificationLabel.text = "Classifying..."
    
    let orientation = CGImagePropertyOrientation(image.imageOrientation)
    guard let ciImage = CIImage(image: image) else { fatalError("Unable to create \(CIImage.self) from \(image).") }
    
    DispatchQueue.global(qos: .userInitiated).async {
        let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
        try! handler.perform([self.classificationRequest])
    }
}

processClassifications (pour la méthode: _ :)

Exécute le traitement sur le résultat de la classification. Tout le code est traité dans la file d'attente principale. Sur l'écran de l'interface utilisateur, le résultat de la classification est affiché sous la forme d'une chaîne de caractères telle que «(0,37) cliff, drop, drop-off» ».

processClassifications()Méthode


func processClassifications(for request: VNRequest, error: Error?) {
    DispatchQueue.main.async {
        guard let results = request.results else {
            self.classificationLabel.text = "Unable to classify image.\n\(error!.localizedDescription)"
            return
        }
        //Le type de résultats est toujours une collection de VNClassificationObservation
        let classifications = results as![VNClassificationObservation]
    
        if classifications.isEmpty {
            self.classificationLabel.text = "Nothing recognized."
        } else {
            //Les classifications sont affichées par ordre décroissant de précision
            let topClassifications = classifications.prefix(2)
            let descriptions = topClassifications.map { classification in
               //Format d'affichage de la classification: "(0.37) cliff, drop, drop-off"
               return String(format: "  (%.2f) %@", classification.confidence, classification.identifier)
            }
            self.classificationLabel.text = "Classification:\n" + descriptions.joined(separator: "\n")
        }
    }
}

méthode takePicture ()

Acquérir l'image à classer. Laissez l'utilisateur sélectionner un appareil photo ou une photothèque à partir de l'affichage des alertes.

takePicture()Méthode


@IBAction func takePicture() {
    //Afficher la sélection du sélecteur uniquement si la caméra est disponible
    guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
        presentPhotoPicker(sourceType: .photoLibrary)
        return
    }
    
    let photoSourcePicker = UIAlertController()
    let takePhoto = UIAlertAction(title: "Take Photo", style: .default) { [unowned self] _ in
        self.presentPhotoPicker(sourceType: .camera)
    }
    let choosePhoto = UIAlertAction(title: "Choose Photo", style: .default) { [unowned self] _ in
        self.presentPhotoPicker(sourceType: .photoLibrary)
    }
    
    photoSourcePicker.addAction(takePhoto)
    photoSourcePicker.addAction(choosePhoto)
    photoSourcePicker.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
    
    present(photoSourcePicker, animated: true)
}

presentPhotoPicker (_: didFinishPickerMediaWithInfo :) méthode

Affichez l'appareil photo ou la photothèque.

presentPhotoPicker()Méthode


func presentPhotoPicker(sourceType: UIImagePickerController.SourceType) {
    let picker = UIImagePickerController()
    picker.delegate = self
    picker.sourceType = sourceType
    present(picker, animated: true)
}

imagePickerController (_: didFinishPickingMediaWithInfo :) méthode

Définissez l'image sélectionnée par le sélecteur sur la propriété du membre.

imagePickerController()Méthode


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
   picker.dismiss(animated: true)
    
    //Créer l'image d'origine renvoyée par cette méthode
    let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
    imageView.image = image
    updateClassifications(for: image)
}

Organiser le flux des procédures

  1. Méthode takePicture ()méthode presentPhotoPicker (_: didFinishPickerMediaWithInfo:) ` Obtenez une image.

  2. Méthode ʻimagePickerController (_: didFinishPickingMediaWithInfo :) Passez "l'image sélectionnée" à la méthode ʻupdateClassifications (for:)pour démarrer le processus de classification.

  3. Méthode ʻupdateClassifications (for:) Créez un gestionnaire de requêtes et exécutez-le. Le gestionnaire est traité comme une file d'attente d'arrière-plan. À ce stade, transmettez la propriétéclassificationRequest` au gestionnaire.

  4. Propriété classificationRequest ʻUpdateClassifications (for :) La propriété est calculée en étant référencée à partir de la méthode, et la requête créée est retournée. Ici, la requête est passée à la méthode processClassifications (for: _:)`.

  5. Méthode processClassifications (for: _:) L'affichage du résultat de la classification est traité de manière asynchrone dans la file d'attente principale.

Recommended Posts

[Vison / Core ML] Explication du code de l'application d'apprentissage automatique iOS qui effectue la classification des images