2017-10-20 34 views
5

J'appelle la méthode déléguée d'AVFoundation pour gérer une capture de photos, mais j'ai de la difficulté à convertir l'AVCapturePhoto qu'il génère en UIImage avec l'orientation correcte. Bien que la routine ci-dessous soit réussie, j'obtiens toujours un UIImage orienté vers le droit (UIImage.imageOrientation = 3). Je n'ai aucun moyen de fournir une orientation lors de l'utilisation de la UIImage (données: image) et en essayant d'utiliser d'abord photo.cgImageRepresentation() ?. takeRetainedValue() n'aide pas non plus. Veuillez aider.Comment générer un UIImage à partir de AVCapturePhoto avec une orientation correcte?

L'orientation de l'image est critique ici car l'image résultante est transmise à un flux de travail Vision Framework.

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { 
    // capture image finished 
    print("Image captured.") 
    if let imageData = photo.fileDataRepresentation() { 
     if let uiImage = UIImage(data: imageData){ 
      // do stuff to UIImage 
     } 
    } 
} 

MISE À JOUR 1: Lecture Apple Photo Capture Programming Guide (de date pour iOS11), j'ai réussi à trouver une chose que je faisais mal:

  1. Sur chaque appel de capture (self.capturePhotoOutput .capturePhoto) on doit établir une connexion avec l'objet PhotoOutput et mettre à jour son orientation pour correspondre à l'orientation de l'appareil au moment où la photo est prise. Pour ce faire, j'ai créé une extension de UIDeviceOrientation et l'ai utilisée sur la fonction snapPhoto() que j'ai créée pour appeler la routine de capture et attendre que la méthode de délégué didFinishProcessingPhoto soit exécutée. J'ai ajouté un instantané des codes car les délimiteurs d'exemples de code ne semblent pas les afficher correctement. enter image description here enter image description here

Update 2 Lien vers projet complet sur GitHub: https://github.com/agu3rra/Out-Loud

+0

Intéressant, et peut-être un [bug dans un fichier] (http://bugreport.apple.com). Cela semble-t-il également ignorer l'orientation si vous écrivez ces données dans un fichier et utilisez un initialiseur 'UIImage' basé sur un fichier? – rickster

+0

Après avoir ajouté l'appel de connexion sur la routine snapPhoto() (UPDATE 1), il n'ignore plus l'orientation, mais il me renvoie des valeurs d'orientation incorrectes sur l'UIImage que je crée. –

+1

À propos de votre extension d'orientation. Apple a publié AVCam mis à jour. Il y a une extension similaire. 'extension UIDeviceOrientation { var vidéoOrientation: AVCaptureVideoOrientation? {{ switch auto cas .portrait: retour .portrait cas .portraitUpsideDown: retour .portraitUpsideDown cas .landscapeLeft: retour .landscapeRight cas .landscapeRight: retour .landscapeLeft défaut: retour nul }} } ' –

Répondre

1

A l'intérieur du AVCapturePhoto Je suis sûr que vous trouverez un objet metadata de l'aussi appelé CGImageProperties.
A l'intérieur vous trouverez le dictionnaire EXIF ​​pour l'orientation la prochaine étape est juste de prendre l'orientation et de créer une image en fonction de cela.
Je n'ai pas d'expérience dans l'utilisation de AVCapturePhotoOutput mais j'en utilise quelques-unes à l'ancienne.
Faites attention à ce que le dictionnaire EXIF ​​soit mappé différemment dans UIImageOrientation.
Voici un article que j'ai écrit il y a beaucoup de temps, mais le principe principal est toujours valable.
Ce question vous montrera quelques implémentations, c'est assez vieux aussi, je suis assez sûr que dans la dernière version ils ont libéré l'API plus facile, mais il vous guidera toujours pour prendre le problème.

2

mise à jour finale: J'ai couru quelques expériences avec l'application et est parvenu aux conclusions suivantes:

  1. kCGImagePropertyOrientation ne semble pas influencer l'orientation de l'image capturée dans votre application et il ne varie selon l'orientation de l'appareil si vous mettez à jour votre connexion photoOutput chaque fois que vous êtes sur le point d'appeler la méthode capturePhoto.Alors:

    func SnapPhoto() {// préparer et de lancer la capture d'image de routine

    // if I leave the next 4 lines commented, the intented orientation of the image on display will be 6 (right top) - kCGImagePropertyOrientation 
    let deviceOrientation = UIDevice.current.orientation // retrieve current orientation from the device 
    guard let photoOutputConnection = capturePhotoOutput.connection(with: AVMediaType.video) else {fatalError("Unable to establish input>output connection")}// setup a connection that manages input > output 
    guard let videoOrientation = deviceOrientation.getAVCaptureVideoOrientationFromDevice() else {return} 
    photoOutputConnection.videoOrientation = videoOrientation // update photo's output connection to match device's orientation 
    
    let photoSettings = AVCapturePhotoSettings() 
    photoSettings.isAutoStillImageStabilizationEnabled = true 
    photoSettings.isHighResolutionPhotoEnabled = true 
    photoSettings.flashMode = .auto 
    self.capturePhotoOutput.capturePhoto(with: photoSettings, delegate: self) // trigger image capture. It appears to work only if the capture session is running. 
    

    }

  2. Affichage des images générées sur le débogueur m'a montré comment ils sont générés, donc je pourrait déduire la rotation requise (UIImageOrientation) pour l'afficher correctement. En d'autres termes: la mise à jour de UIImageOrientation indique comment l'image doit être pivotée pour que vous puissiez la voir dans l'orientation correcte. Alors je suis venu à la table suivante: Which UIImageOrientation to apply according to how the device was at the time of capture

  3. je devais mettre à jour mon extension UIDeviceOrientation à une forme plutôt unintuitive:

    l'extension UIDeviceOrientation { func getUIImageOrientationFromDevice() -> UIImageOrientation { // Rendement basé CGImagePropertyOrientation sur l'orientation du périphérique // Cette fonction étendue a été déterminée en fonction de l'expérimentation de l'affichage d'un UIImage. commutateur auto { cas UIDeviceOrientation.portrait, .faceUp: retour UIImageOrientation.right cas UIDeviceOrientation.portraitUpsideDown, .faceDown: retour UIImageOrientation.left cas UIDeviceOrientation.landscapeLeft: retour UIImageOrientation.up // ceci est l'orientation de base cas UIDeviceOrientation .landscapeRight: retour UIImageOrientation.down cas UIDeviceOrientation.unknown: retour UIImageOrientation.up }} }

  4. Voici comment ma méthode déléguée finale ressemble maintenant. Il affiche l'image dans l'orientation attendue.

    func photoOutput(_ output: AVCapturePhotoOutput, 
               didFinishProcessingPhoto photo: AVCapturePhoto, 
               error: Error?) 
    { 
        // capture image finished 
        print("Image captured.") 
    
        let photoMetadata = photo.metadata 
        // Returns corresponting NSCFNumber. It seems to specify the origin of the image 
        //    print("Metadata orientation: ",photoMetadata["Orientation"]) 
    
        // Returns corresponting NSCFNumber. It seems to specify the origin of the image 
        print("Metadata orientation with key: ",photoMetadata[String(kCGImagePropertyOrientation)] as Any) 
    
        guard let imageData = photo.fileDataRepresentation() else { 
         print("Error while generating image from photo capture data."); 
         self.lastPhoto = nil; self.controller.goToProcessing(); 
         return 
    
        } 
    
        guard let uiImage = UIImage(data: imageData) else { 
         print("Unable to generate UIImage from image data."); 
         self.lastPhoto = nil; self.controller.goToProcessing(); 
         return 
    
        } 
    
        // generate a corresponding CGImage 
        guard let cgImage = uiImage.cgImage else { 
         print("Error generating CGImage");self.lastPhoto=nil;return 
    
        } 
    
        guard let deviceOrientationOnCapture = self.deviceOrientationOnCapture else { 
         print("Error retrieving orientation on capture");self.lastPhoto=nil; 
         return 
    
        } 
    
        self.lastPhoto = UIImage(cgImage: cgImage, scale: 1.0, orientation: deviceOrientationOnCapture.getUIImageOrientationFromDevice()) 
    
        print(self.lastPhoto) 
        print("UIImage generated. Orientation:(self.lastPhoto.imageOrientation.rawValue)") 
        self.controller.goToProcessing() 
    } 
    
    
    func photoOutput(_ output: AVCapturePhotoOutput, 
            willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) 
            { 
        print("Just about to take a photo.") 
        // get device orientation on capture 
        self.deviceOrientationOnCapture = UIDevice.current.orientation 
        print("Device orientation: \(self.deviceOrientationOnCapture.rawValue)") 
    }