如何在iOS 11 中将JPEG/RAW 图像数据保存到相机胶卷,无法访问已处理的图像数据

How to save JPEG/RAW image data to camera roll in iOS 11, can't access processed image data

我正在尝试编写一个可以拍摄 RAW 和 JPEG 图像并将它们保存到相机胶卷的照片应用程序。函数 jpegPhotoDataRepresentation 和 dngPhotoDataRepresentation 似乎是我找到的所有示例的关键,但是这两个函数在 iOS 11 中已弃用,"capturePhoto" 之后的保存函数现在是

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

我能够找到的工作 RAW iOS11 应用程序的主要示例是: https://ubunifu.co/swift/raw-photo-capture-sample-swift-4-ios-11 这有效,但它只拍摄 RAW 并且保存起来很笨拙,因为它不在相机胶卷上。

我已经更改了我的照片设置以允许使用这条线进行原始和处理后的捕获

photoSettings = AVCapturePhotoSettings(rawPixelFormatType: availableRawFormat.uint32Value, processedFormat: [AVVideoCodecKey : AVVideoCodecType.jpeg])

但是一旦我真正拍摄了照片,我就不知道如何访问处理过的格式数据。 fileDataRepresentation 似乎是访问 dng 内容的唯一方法,但是没有办法单独获取 jpeg?我从 Apple pre-iOS11 找到的代码建议使用 PHPhotoLibrary 并添加资源,但这需要一个数据表示,除了 dng 文件之外我无法访问它,当保存到库是白色的,因为库无法处理 RAW 文件。这是我的 photoOutput 代码,以防有帮助。

 func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

    let dir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! as String
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyyMMddHHmmss"
    formatter.locale = Locale.init(identifier: "en_US_POSIX")
    let filePath =  dir.appending(String(format: "/%@.dng", formatter.string(from: Date())))
    let dngFileURL = URL(fileURLWithPath: filePath)

    let dngData = photo.fileDataRepresentation()!
    do {
        try dngData.write(to: dngFileURL, options: [])
    } catch {
        print("Unable to write DNG file.")
        return
    }

    PHPhotoLibrary.shared().performChanges( {
        let creationRequest = PHAssetCreationRequest.forAsset()
        let creationOptions = PHAssetResourceCreationOptions()
        creationOptions.shouldMoveFile = true


        //dngData is the problem, this should be the jpeg representation
        creationRequest.addResource(with: .photo, data: dngData, options: nil)
        //This line works fine, the associated file is the correct RAW file, but the jpeg preview is garbage
        creationRequest.addResource(with: .alternatePhoto, fileURL: dngFileURL, options: creationOptions)

    }, completionHandler: nil)

}

好的,跟进之前的评论和 Capturing Photos in RAW Format 上的 Apple 文档:

如您所知,如果您想拍摄 RAW 并将其保存在照片库中,您需要将 DNG+ 处理后的版本一起保存在同一资产中,以便不支持 RAW 的照片库客户端仍然有资产的可读版本。 (这包括照片应用程序本身......)同时保存 RAW+processed 意味着在捕获中指定它。

如果您请求的是 RAW+ 处理过的捕获(其中处理过的是 JPEG,或者更好的是 HEIF),您每次拍摄都会得到两张照片。这意味着您的 didFinishProcessingPhoto 回调被调用 两次 :一次用于传送 JPEG(或 HEIF),另一次用于传送 RAW。

由于您需要将资产的 RAW+ 处理版本一起添加到照片中,因此您应该等到捕获输出提供两个版本后再尝试创建照片资产。您会注意到 Apple doc 中的代码片段在 didFinishProcessingPhoto 回调中存储了两个版本的数据:

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    if photo.isRawPhoto {
        // Save the RAW (DNG) fileDataRepresentation to a URL
    } else {
        // Hold JPEG/HEIF fileDataRepresentation in a property
    }
}

然后,当 didFinishCaptureFor 回调触发时,他们确保它们具有两个版本,并将它们一起添加到照片库中。

请注意,当您同时添加照片的 DNG 和 JPEG 或 HEIF 版本时...

  • JPEG/HEIF 需要是主要 photo 资源,DNG 是 alternatePhoto 资源。
  • 您可以直接从内存中的 Data 添加 JPEG/HEIF 资源,但需要从文件 URL 添加 DNG。

所以照片库部分是这样的(同样,在 didFinishCaptureFor 回调中):

PHPhotoLibrary.shared().performChanges({
    // Add the compressed (HEIF) data as the main resource for the Photos asset.
    let creationRequest = PHAssetCreationRequest.forAsset()
    creationRequest.addResource(with: .photo, data: compressedData, options: nil)

    // Add the RAW (DNG) file as an altenate resource.
    let options = PHAssetResourceCreationOptions()
    options.shouldMoveFile = true
    creationRequest.addResource(with: .alternatePhoto, fileURL: rawURL, options: options)
}, completionHandler: self.handlePhotoLibraryError)

您可以像 here 那样制作 CGImage 扩展,然后从该 cgImage 获取像素缓冲区。

if let cgImage = photo.cgImageRepresentation() {
    let pixelBuffer = cgImage.pixelBuffer()
}