Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Bonjour,
Pouvez vous m’aidez à trouver la solution à cette erreur

Bonjour Popi,

Il faut certainement mettre as? CIImage plutôt que as! CIImage.
Ça te permet de faire un cast optionnel et de ne pas planter ton programme si la valeur que le programme essaye de récupérer est null. C’est pour ça que ton programme plante. En mettant le as?, ton programme continuera son exécution car il considérera la variable filtreData comme null.

Et, au passage, je déconseille de copier/coller du code trouvé sur internet ou sur des forums sans savoir explicitement ce qu’il fait :wink:
Parce que, vue la ligne d’après, si c’est toi qui avait codé ces lignes, tu aurais mis un as?

Bonjour,
merci pour cette réponse. en fait j’ai déja essayé cette option et j’ai toujours le même message d’erreur

Ce n’est pas comme ça qu’il faut faire. Dés qu’une fonction peut échouer et fournir une valeur null pour indiquer que quelque chose s’est mal passée, il faut employer un if let pour la sécuriser.

Peux-tu montrer le code de la fonction configuerAvecFiltre() en entier, sans le message d’erreur cachant les instructions ?

A ce moment là, tu peux faire un
if let filtreData = filtreChoisi.value(...) as? CIImage { sur la ligne 32
et rajouter une } après le if let qui encadre cgImage

1 « J'aime »

Personnellement, je ne suis pas fan du code mélangeant des fonctions systèmes bas niveau, gestion des interfaces graphiques et interaction avec l’utilisateur.

CoreImage est un sacré foutoir, avec ses paramètres interminables et ces noms de fonction stockées dans des variables textes.

Ton problème vient probablement d’un nom de filtre mal recopié.

Je pense qu’il ne faut pas utiliser CoreImage « brut de décoffrage », mais l’utiliser pour créer des fonctions graphiques plus simple d’emploi.

A titre d’exemple, j’ai repris le filtre CoreImage le plus utilisé dans les tutoriels internet (Sepia), pour l’encapsuler dans une fonction.

    // Fabrication imageSepia
    func filtreSepia(_ image:UIImage, _ intensite:CGFloat) -> UIImage? {
        var imageSepia:UIImage?
        
        // Création image source
        let ciImageSource = CIImage(image: image)
        // Création du Filtre CoreImage
        if let filtre = CIFilter(name: "CISepiaTone") {
            print ("Création Filtre Sepia - OK")
            // Paramétres du Filtre Sepia
            filtre.setValue(ciImageSource, forKey: kCIInputImageKey)
            filtre.setValue(intensite, forKey: kCIInputIntensityKey)
            print ("Paramétrage Filtre - OK")
            // Récupération de l'image CIImage? en sortie du filtre
            // et conversion en UIImage?
            if let sortieFiltre = filtre.outputImage {
                print ("sortie filtre - OK")
                imageSepia = UIImage(ciImage: sortieFiltre)
                print ("création UIImage Sepia - OK")
            }
        }
        // On retourne le résultat
        return imageSepia
    }

Le code est un peu long, à cause des commentaires et des print() ajoutés pour suivre l’évolution du processus du traitement graphique.

Utilisation :

        if let imageSepia = filtreSepia(image, 0.8) {
            imageView2.image = imageSepia
        }

Les paramètres sont l’image à filtrer et l’intensité de l’effet Sepia (de 0.0 à 1.0). Le résultat est une UIImage?. Si quelque chose s’est mal passé pendant le filtrage, elle contient la valeur nul, d’où le besoin d’utiliser un if let avant d’utiliser l’image.


Pour tester, j’ai réalisé une mini-application avec deux imagesView et un chat (pris sur un site d’images libre de droit).

Le code :

import UIKit
import CoreImage


class ViewController: UIViewController {

    @IBOutlet weak var imageView1: UIImageView!
    @IBOutlet weak var imageView2: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Tentative chargement image
        if let image = UIImage(named: "miaou.jpg") {
            // Affichage image
            imageView1.image = image
            // Tentative de création image Sepia
            if let image2 = filtreSepia(image, 0.8) {
                // Affichage image Sepia
                imageView2.image = image2
            }
        }
    }

    // Fabrication imageSepia
    func filtreSepia(_ image:UIImage, _ intensite:CGFloat) -> UIImage? {
        // J'ai effacé le corps de la fonction pour améliorer la lisibilité
        // et éviter la redondance avec la fonction présentée plus haut
    }
    
}

Mise en pratique :

Grâce aux instructions print() l’application affiche les étapes du traitement graphique, dans la console d’XCode, en bas à droite de l’écran.

1 « J'aime »

L’utilisation systématique de if let peut nuire à la lisibilité du code. C’est pourquoi quelque années après l’apparition du if let, le Swift a été doté d’un nouvel outil : l’opérateur ?. C’est une variante de if let, limitée à une seule ligne de code.

Si je tente de faire une opération sur un objet qui n’existe pas, c’est le plantage assuré. D’où le besoin de if let pour vérifier que l’objet existe réellement.

if let filtre = CIFilter(name: "CISepiaTone") {
     filtre.setValue(ciImageSource, forKey: kCIInputImageKey)
     filtre.setValue(intensite, forKey: kCIInputIntensityKey)
     .....

La création d’un filtre peut échouer si le nom ne correspond a aucun filtre existant, c’est pourquoi la fonction CIFilter() retourne une valeur optionnelle de type CIFilter?.

Le if let permet de :
a) vérifier qu’un filtre a bien été créé
b) créer une variable normale (sans valeur optionnelle intégré) utilisable entre les parenthèses du if let.

On peut aussi garder la variable CIFilter? sous sa forme optionnelle, sans la convertir.

    let filtre = CIFilter(name: "CISepiaTone")
    filtre?.setValue(ciImageSource, forKey: kCIInputImageKey)
    filtre?.setValue(intensite,     forKey: kCIInputIntensityKey)

L’opérateur ? indique à Swift : ATTENTION, j’essaye d’utiliser un objet qui n’existe pas forcément. Le langage fait alors sa petite cuisine interne, vérifiant si l’opération est possible avant de l’effectuer.

Voici une version simplifiée de ma fonction Sepia, utilisant cette syntaxe :

// Fabrication imageSepia
func filtreSepia2(_ image:UIImage, _ intensite:CGFloat) -> UIImage? {
    var imageSepia:UIImage?
    
    let ciImageSource = CIImage(image: image)
    let filtre = CIFilter(name: "CISepiaTone")
    filtre?.setValue(ciImageSource, forKey: kCIInputImageKey)
    filtre?.setValue(intensite,     forKey: kCIInputIntensityKey)
    if let ciSortieFiltre = filtre?.outputImage {
        imageSepia = UIImage(ciImage: ciSortieFiltre)
    }
    return imageSepia
}

Il n’y a plus qu’un seul if let, ce qui améliore la lisibilité. J’ai aussi effacé les commentaires et les print() pour alléger le code.

Comment cela fonctionne ?
A l’initialisation, la variable imageSepia? contient la valeur null. Toutes les variables optionnelles ont la valeur null à la création.

Le contenu de cette variable n’est modifié que si la fonction arrive à fabriquer une UIImage à partir du filtre.

    if let ciSortieFiltre = filtre?.outputImage {
        imageSepia = UIImage(ciImage: ciSortieFiltre)
    }

Si quelque chose tourne mal, la variable n’est pas modifiée, gardant la valeur null reçue à sa création.


EDIT : Je convertis directement l’image CIImage en une UIImage. J’ai vu que tu ajoute une étape de conversion supplémentaire en passant par un contexte graphique. Plusieurs tutos internet font la même chose.

Peut-être que la conversion directe fait perdre des informations au passage ? Ou peut-être que certaines personnes aiment bien compliquer les choses, même pour de la pédagogie ? Peut-être que la conversion CIImage -> UIImage n’existait pas au début, et qu’il fallait passer par une étape intermédiaire incluant une image CGI ?

Je ne sais vraiment pas. En tout cas, la conversion en une seule étape fonctionne bien, dans le cadre de mon test.

C’est ça! le bug n’apparait plus merci!