AVCapturePhoto 在调用 cgImageRepresentation()!.takeUnretainedValue() 后逆时针旋转 90

AVCapturePhoto rotated 90 counterclockwise after calling `cgImageRepresentation()!.takeUnretainedValue()`

我正在开发一款应用程序,可以在 iOS 上使用 AVFoundation 拍摄方形照片。当我在photoOutput(_:, didFinishProcessingPhoto:, error:)中使用以下语句保存图像时,我相册中保存的图像方向正确。

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }

        PHPhotoLibrary.shared().performChanges({
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: photo.fileDataRepresentation()!, options: nil)
        }, completionHandler: nil)
    }
}

但是,当我用下面的代码来保存CGImage对象时,我发现保存在我的相册中的图像被逆时针旋转了90。

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    let image = photo.cgImageRepresentation()!.takeUnretainedValue()
    UIImageWriteToSavedPhotosAlbum(UIImage(cgImage: image), nil, nil, nil)
}

这是我的照片捕获管道配置。

private func initializeCapturePipeline() {
    captureSession = AVCaptureSession()
    captureSession?.sessionPreset = .photo
    captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
    videoInput = try! AVCaptureDeviceInput(device: captureDevice!)
    imageOutput = AVCapturePhotoOutput()
    captureSession?.addInput(videoInput!)
    captureSession?.addOutput(imageOutput!)
    imageOutput?.connection(with: .video)?.videoOrientation = .portrait
    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
    previewLayer?.videoGravity = .resizeAspectFill
    videoContainerView.layer.addSublayer(previewLayer!)
    captureSession?.startRunning()
}

我想知道是什么导致了这个问题?我该如何解决这个问题。

我的理解是两种情况下的底层图像数据是相同的,显示上的差异是由于 photo.cgImageRepresentation() 中缺少元数据造成的。照片应用程序了解图像元数据,并根据图像元数据中 "Orientation" 属性 的值自动应用必要的旋转 and/or 镜像。

使用 PHPhotoLibrary 函数生成带有嵌入元数据的图像副本。如 CGImagePropertyOrientation 中所述,iOS 正在保存方向为 .right 的对象。但是,直接从 AVCapturePhoto 对象创建的 CGImage 不包含关联的元数据。

有几个潜在的修复(这些尚未经过测试,但应该有助于找到有效的解决方案):

  1. 使用从照片元数据中获得的方向创建 UIImage
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    let image = photo.cgImageRepresentation()!.takeUnretainedValue()
    let orientation = CGImageProperyOrientation(rawValue: photo.metadata["Orientation"] as! UInt32)
    var uiImage: UIImage!
    if orientation == .right {
        uiImage = UIImage(cgImage: image, scale: 1.0, orientation: .right)
    } else {
        uiImage = UIImage(cgImage: image)
    }
    UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil)
}
  1. 使用 CGImageDestinationImage I/O framework 中的 CGImageDestinationAddImageAndMetadata 函数保存带有元数据的 CGImage

  2. 根据具体的用例,可能需要显式旋转图像数据。可以找到如何执行此操作的示例 here.