Переключение AVCaptureSessionPreset перед съемкой фотографии - PullRequest
0 голосов
/ 17 декабря 2018

Я создаю приложение фильтра камеры в реальном времени.

Я использую AVCaptureVideoDataOutput для передачи сэмплебуфера в MTKView для предварительного просмотра рендеринга и AVCapturePhotoOutput для захвата фотографий.

И мое приложениеимеет некоторые параметры формата изображения для захвата фотографий.(включая 16: 9 и 4: 3)

Я хочу сделать предварительный просмотр в полноэкранном режиме (16: 9, но все, что ближе к этому, тоже подойдет), хотя пользователи выбирают 4: 3 варианта.

И я планирую показать границы границ в предварительном просмотре, чтобы пользователи могли определить размер выходного фото.

Мне нужен предустановленный параметр 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()
    }
}

ошибка:

Ошибка домена = AVFoundationErrorDomain Code =-11800 "Операция не может быть завершена" UserInfo = {NSLocalizedFailureReason = Произошла неизвестная ошибка (-16800), NSLocalizedDescription = Операция не может быть завершена, NSUnderlyingError = 0x280013720 {Ошибка домена = NSOSStatusErrorDomain Code = -16800 "(null)"}}

Я думаю, это потому, что я вызываю capturePhoto метод до того, как сессия завершит обновление до новой предустановки.

, когда я вызываю self.photoOutput.capturePhoto метод 1 или 2 секунды послеcommitConfiguration(), это работает.

Итак, есть ли какой-нибудь способ, которым я могу знать о завершении обновлений AVCaptureSession или есть лучшее решение, имеющее дело с различным соотношением сторон между выводом видеоданных и выводом фотографий

1 Ответ

0 голосов
/ 18 декабря 2018

Лучший способ вывести другое соотношение сторон для видео и фото - это обработать данные вашего фотоизображения в делегате 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 ().

...