2017-10-20 189 views
5

Wzywam metodę delegowania AVFoundation do obsługi przechwytywania zdjęć, ale mam trudności z konwersją AVCapturePhoto, które generuje w UIImage z prawidłową orientacją. Chociaż poniższa procedura kończy się sukcesem, zawsze otrzymuję zorientowaną na prawo UIImage (UIImage.imageOrientation = 3). Nie mogę zapewnić orientacji podczas korzystania z UIImage (data: image) i próba pierwszego użycia funkcji photo.cgImageRepresentation()? TakeRetainedValue() również nie pomaga. Proszę o pomoc.Jak wygenerować UIImage z AVCapturePhoto z prawidłową orientacją?

Orientacja obrazu ma tu kluczowe znaczenie, ponieważ wynikowy obraz jest przesyłany do przepływu pracy 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 
     } 
    } 
} 

UPDATE 1: Reading Apple Photo Capture Programming Guide (z datą iOS11), udało mi się znaleźć jedną rzecz robiłem źle:

  1. Na każde wezwanie wychwytu (self.capturePhotoOutput .capturePhoto) należy skonfigurować połączenie z obiektem PhotoOutput i zaktualizować jego orientację, aby dopasować orientację urządzenia w momencie robienia zdjęcia. W tym celu utworzyłem rozszerzenie UIDeviceOrientation i użyłem go w funkcji snapPhoto(), którą stworzyłem, aby wywołać procedurę przechwytywania i czekać na wykonanie metody delegowania didFinishProcessingPhoto. Dodałem migawkę kodów, ponieważ ograniczniki próbek kodu nie wydają się wyświetlać ich poprawnie. enter image description here enter image description here

Aktualizacja 2 Link do pełnego projektu na GitHub: https://github.com/agu3rra/Out-Loud

+0

ciekawe, i ewentualnie [błąd w pliku] (http://bugreport.apple.com). Czy zdaje się ignorować orientację, jeśli zapisujesz te dane do pliku i używa inicjalizatora UIImage? – rickster

+0

Po dodaniu wywołania połączenia w procedurze snapPhoto() (UPDATE 1), nie ignoruje ona już orientacji, ale zwraca mi nieprawidłowe wartości orientacji w tworzonej przeze mnie UIImage. –

+1

Informacje o Twoim rozszerzeniu orientacji. Apple wydało aktualizację AVCam. Istnieje podobne rozszerzenie. 'extension UIDeviceOrientation { var videoOrientacja: AVCaptureVideoOrientation? { przełącznik siebie { przypadek .portrait: powrót .portrait przypadek .portraitUpsideDown: powrót .portraitUpsideDown case .landscapeLeft: powrót .landscapeRight case .landscapeRight: powrót .landscapeLeft default: powrót nil } } } ' –

Odpowiedz

1

Wewnątrz AVCapturePhoto Jestem całkiem pewien, że będzie znaleźć metadata przedmiot zwany także CGImageProperties.
Wewnątrz znajduje się słownik EXIF ​​dla orientacji. Kolejnym krokiem jest zrobienie orientacji i utworzenie obrazu zgodnie z tym.
Nie mam żadnych doświadczeń w korzystaniu z AVCapturePhotoOutput, ale niektóre z nich używają starego sposobu.
Zwróć uwagę, że słownik EXIF ​​jest inaczej odwzorowany w UIImageOrientation.
Oto article Napisałem dużo czasu temu, ale główna zasada jest nadal ważna.
Ten question wskaże ci niektóre implementacje, jest całkiem stary, jestem prawie pewien, że w najnowszej wersji wydali łatwiejszy interfejs API, ale nadal poprowadzi Cię do podjęcia problemu.

2

Ostateczna zmiana: wpadłem kilka eksperymentów z aplikacją i doszła do następujących wniosków:

  1. kCGImagePropertyOrientation nie wydaje się wpływać na orientację przechwyconego obrazu wewnątrz aplikacji i to tylko zależy od orientacji urządzenia, jeśli zaktualizujesz połączenie photoOutput za każdym razem, gdy chcesz wywołać metodę capturePhoto.Więc:

    func snapPhoto() {// przygotować i rozpocząć przechwytywanie obrazu rutynowe

    // 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. Przedstawiamy wygenerowanych obrazów na debugger pokazało mi, jak oni dostać generowane, więc może wywnioskować wymagany obrót (UIImageOrientation), aby wyświetlić go w pozycji pionowej. Innymi słowy: aktualizowanie UIImageOrientation mówi, w jaki sposób obraz powinien zostać obrócony, aby można było go zobaczyć we właściwej orientacji. Więc doszedłem do poniższej tabeli: Which UIImageOrientation to apply according to how the device was at the time of capture

  3. musiałem zaktualizować rozszerzenie UIDeviceOrientation do raczej nieintuicyjne postaci:

    przedłużenie UIDeviceOrientation { func getUIImageOrientationFromDevice() -> UIImageOrientation { // zwrot w oparciu CGImagePropertyOrientation on Device Orientation // Ta rozszerzona funkcja została określona na podstawie eksperymentów pokazujących, jak wyświetla się UIImage. przełącznik samo { przypadku UIDeviceOrientation.portrait, .faceUp: powrotu UIImageOrientation.right przypadku UIDeviceOrientation.portraitUpsideDown, .faceDown: powrotu UIImageOrientation.left przypadku UIDeviceOrientation.landscapeLeft: powrotu UIImageOrientation.up // to jest orientacja podstawy przypadku UIDeviceOrientation .landscapeRight: powrót UIImageOrientation.down przypadek UIDeviceOrientation.unknown: powrót UIImageOrientation.up } } }

  4. to jest jak moja ostateczna metoda delegat wygląda teraz. Wyświetla obraz w oczekiwanej orientacji.

    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)") 
    }