2017-10-20 209 views
5

我打电话给AVFoundation的委托方法来处理照片捕获,但我很难将其生成的AVCapturePhoto转换为正确方向的UIImage。虽然下面的例程是成功的,但我总是得到一个正确的UIImage(UIImage.imageOrientation = 3)。我没有办法在使用UIImage(data:image)时尝试先使用photo.cgImageRepresentation()?. takeRetainedValue()也没有帮助。请协助。如何以正确的方向从AVCapturePhoto生成UIImage?

由于生成的图像被送入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 
     } 
    } 
} 

更新1: 阅读苹果的Photo Capture Programming Guide(过时的iOS11的),我还是设法找到一件事我做错了:

  1. 每次捕捉调用(self.capturePhotoOutput .capturePhoto)必须设置与PhotoOutput对象的连接并更新其方向以匹配拍摄照片时设备的方向。为此,我创建了UIDeviceOrientation的扩展,并将其用于我创建的snapPhoto()函数以调用捕获例程并等待didFinishProcessingPhoto委托方法执行。我添加了代码的快照,因为这里的代码示例分隔符似乎没有正确显示它们。 enter image description here enter image description here

更新2 链接到完整的项目在GitHub上:https://github.com/agu3rra/Out-Loud

+0

有趣,和可能的[错误到文件(http://bugreport.apple.com)。如果将数据写入文件并使用基于文件的'UIImage'初始化程序,它是否似乎忽略方向? – rickster

+0

在snapPhoto()例程(UPDATE 1)上添加了连接调用之后,它不再忽略方向,但它在我创建的UIImage上返回了不正确的方向值。 –

+1

关于您的方向扩展。苹果已经发布了AVCam更新。有一个类似的扩展。 'extension UIDeviceOrientation {var VideoOrientation:AVCaptureVideoOrientation? { 开关自{ 情况.portrait:返回.portrait 情况.portraitUpsideDown:返回.portraitUpsideDown 情况.landscapeLeft:返回.landscapeRight 情况.landscapeRight:返回.landscapeLeft 默认:返回零 } } }' –

回答

1

AVCapturePhoto里面我敢肯定你会发现的也叫CGImageProperties一个metadata对象。
在里面你会发现EXIF字典的定位方向,下一步就是采取方向并根据它创建一个图像。
我没有使用AVCapturePhotoOutput的经验,但我有一些使用旧的方式。
请注意,EXIF字典在UIImageOrientation中的映射方式不同。
这是一个article我写了很多时间以前,但主要原则仍然有效。
这个question会告诉你一些实现,它也很老,我很确定在最新版本中他们发布了更简单的API,但它仍然会指导你解决这个问题。

2

最后更新: 我跑了一些实验用的应用程序,并来到了以下结论:

  1. kCGImagePropertyOrientation似乎并不影响您的应用程序,它里面所拍摄的图像的方向只每次您要调用capturePhoto方法时,如果更新photoOutput连接,则会随着设备方向而变化。所以:

    FUNC snapPhoto(){// 准备和启动图像捕获例程

    // 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. 查看在调试器所生成的图像呈现出我,他们是如何被生成的,所以我可以推断出所需的旋转(UIImageOrientation)以使其直立显示。换句话说:更新UIImageOrientation会告诉您如何旋转图像,以便您以正确的方向查看图像。所以我就下表: Which UIImageOrientation to apply according to how the device was at the time of capture

  3. 我不得不更新我的UIDeviceOrientation扩展到一个相当直观的形式:

    扩展UIDeviceOrientation { FUNC getUIImageOrientationFromDevice() - > UIImageOrientation {// 返回基于CGImagePropertyOrientation on Device Orientation //这个扩展函数是根据UIImage如何显示的实验确定的。 开关自{ 情况UIDeviceOrientation.portrait,.faceUp:返回UIImageOrientation.right 情况UIDeviceOrientation.portraitUpsideDown,.faceDown:返回UIImageOrientation.left 情况UIDeviceOrientation.landscapeLeft:返回UIImageOrientation.up //这是基本方向 情况UIDeviceOrientation .landscapeRight:返回UIImageOrientation.down 情况下UIDeviceOrientation.unknown:返回UIImageOrientation.up }} }

  4. 这是我最后的委托方法现在的样子。它以预期的方向显示图像。

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