【问题标题】:Switching AVCaptureSessionPreset before capturing photo拍摄照片前切换 AVCaptureSessionPreset
【发布时间】:2018-12-17 19:20:39
【问题描述】:

我正在制作实时相机滤镜应用。

我使用AVCaptureVideoDataOutput 将samplebuffer 传递给MTKView 用于渲染预览,AVCapturePhotoOutput 用于捕获照片。

我的应用有一些用于拍摄照片的纵横比选项。 (包括16:9和4:3)

尽管用户选择了 4:3 选项,但我想做的是以全屏尺寸(即 16:9,但更接近此尺寸的任何东西也可以)进行预览。

我打算在预览中显示边框线,以便用户可以确定照片输出大小。

我需要 16:9 预设选项,例如 1280*720 用于预览和照片预设选项以拍摄照片。

我想出了几个主意。

  1. 拥有两个具有不同预设的AVCaptureSessions --> 不利于性能

  2. 使用 1280*720 预设拍摄并将照片输出裁剪为 4:3 纵横比 --> 低分辨率照片

  3. 在调用 photoOutput.capturePhoto 方法之前切换预设。 --> 预览立即冻结,因为AVCaptureSession 必须更新

我决定选择 3,但它给了我一个错误。

(如果有更好的方法,请告诉我)

这是我的代码。

@IBAction func takePhoto(_ sender: UIButton) {
    captureSessionQueue.async {
        var photoSettings = AVCapturePhotoSettings()
        photoSettings.isHighResolutionPhotoEnabled = true

        // switch preset from .hd1280*720 to .photo
        self.session.beginConfiguration()
        if self.session.canSetSessionPreset(.photo) {
            self.session.sessionPreset = .photo
        }
        self.session.commitConfiguration()

        self.photoOutput.capturePhoto(with: photoSettings, delegate: self)

        self.session.beginConfiguration()
        self.session.sessionPreset = .hd1280*720
        self.session.commitConfiguration()
    }
}

错误是,

Error Domain=AVFoundationErrorDomain Code=-11800 "该操作可能 未完成” UserInfo={NSLocalizedFailureReason=未知错误 发生 (-16800), NSLocalizedDescription=操作不能 完成,NSUnderlyingError=0x280013720 {错误 Domain=NSOSStatusErrorDomain Code=-16800 "(null)"}}

我认为这是因为我在会话完成更新到新预设之前调用了capturePhoto 方法。

当我在commitConfiguration() 之后 1 或 2 秒调用 self.photoOutput.capturePhoto 方法时,它会起作用。

那么,有什么方法可以让我知道AVCaptureSession 更新的完成情况,还是有更好的解决方案来处理视频数据输出和照片输出之间的不同纵横比?

【问题讨论】:

    标签: swift avfoundation avcapturesession


    【解决方案1】:

    为视频和照片输出不同纵横比的最佳方法是在 AVCapturePhotoCaptureDelegate 委托中处理您的照片图像数据。您可以执行以下操作。在 didFinishProcessingPhoto 函数中,您可以裁剪图像并从中创建 jpeg 数据。您还需要将照片元数据复制到此 jpeg 数据中。然后在 didFinishCaptureFor 函数中,您可以将这些数据保存到照片库中。

    class MyPhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
            // crop image
            if let cgImage = photo.cgImageRepresentation()?.takeUnretainedValue() {
                let cropRect : CGRect = calculateAspectRatioCrop(cgImage:cgImage, aspectRatio:16.0/9.0)
                if let cgCroppedImage = cgImage.cropping(to:cropRect) {
                    let image = UIImage(cgImage:cgCroppedImage)
                    if let photoData = image.jpegData(compressionQuality: 0.9) {
                        // add meta data from original image to cropped image data
                        self.photoData = addImageProperties(imageData: photoData, properties: photo.metadata as NSDictionary)
                    }
                }
            }
        }
    
        func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
            if let error = error {
                print("Error capturing photo: \(error)")
                return
            }
    
            guard let photoData = photoData else {
                print("No photo data resource")
                return
            }
            savePhotoData()
        }
    
        func calculateAspectRatioCrop(cgImage : CGImage, aspectRatio: CGFloat) -> CGRect {
            var width = CGFloat(cgImage.width)
            var height = CGFloat(cgImage.height)
            // should be cropped vertically or horizontally?
            if aspectRatio > width/height {
                height = width / aspectRatio
            } else {
                width = height * aspectRatio
            }
            return CGRect(x: (CGFloat(cgImage.width) - width)/2, y: (CGFloat(cgImage.height) - height)/2, width: width, height: height)
        }
    
        func addImageProperties(imageData: Data, properties: NSDictionary?) -> Data? {
            // create an imagesourceref
            if let source = CGImageSourceCreateWithData(imageData as CFData, nil) {
                // this is of type image
                if let uti = CGImageSourceGetType(source) {
                    // create a new data object and write the new image into it
                    let destinationData = NSMutableData()
                    if let destination = CGImageDestinationCreateWithData(destinationData, uti, 1, nil) {
                        // add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
                        CGImageDestinationAddImageFromSource(destination, source, 0, properties)
                        if CGImageDestinationFinalize(destination) == false {
                            return nil
                        }
                        return destinationData as Data
                    }
                }
            }
            return nil
        }
    
        private var photoData : Data? = nil
    }
    

    我让你填写 savePhotoData() 函数。

    【讨论】:

    • 感谢回复@Spads 我会尝试这种方式并更新结果!但是一个问题。如果我裁剪照片图像数据,照片的分辨率不会降低吗?
    • 你无法避免这种情况。只要您将sessionPreset 设置为AVCaptureSession.Preset.photo,您就可以从您的iPhone(大多数手机上为4032x2268)拍摄最大的16:9 照片。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 2014-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多