実現したいこと・発生している問題
現在、AVFoundationを利用したカメラ機能を開発しております。
iPhone標準のカメラアプリにある、露出調整(明るさ調整)と同等の機能を実装したいと考えているのですが、
実装したものだと露出を上げるほどに被写体がブレる(かくつくような動き)ようになります。
「露出を変更する = シャッタースピードが変わる」という認識でいます。
その影響で被写体がブレる現象が発生していると考えているのですが、iPhone標準のカメラではブレる現象が発生しません。
※以下、iPhoneのカメラアプリの露出調整機能
質問内容
どような実装を行えば、被写体がブレる現象を抑えられますでしょうか?
「露出を上げすぎないことで対応するしかない」、「ISO感度と組み合わせて実装する」など、何かアドバイスなどあればご回答をお願いしたいです。
補足情報(FW/ツールのバージョンなど)
- 開発環境:Xcode11.2.1
- 言語 :Swift5
- 端末 :iPhoneX、iPadPro11インチ
参考にしたサイトなど
Swiftでカメラアプリを作成する(1)
AVCamManual: Extending AVCam to Use Manual Capture API
z-hao-wang / ios-pro-camera
該当のソースコード
【露出調整箇所抜粋】
以下はAppleのサンプルソースを元に実装。
「case 1」配下で露出調整を行なっているのですが、ここの処理に問題があると想定しております。
Swift
// MARK: - Slider Action @IBAction func changeSlider(_ sender: UISlider) { let tag: Int = sender.tag do { try currentDevice!.lockForConfiguration() defer { currentDevice!.unlockForConfiguration() } // 露出、または ISO感度を設定 switch tag { case 1: // 露出調整 let p = Float64(pow(sender.value, kExposureDurationPower)) // Apply power function to expand slider's low-end range let minDurationSeconds = Float64(max(CMTimeGetSeconds(currentDevice!.activeFormat.minExposureDuration), kExposureMinimumDuration)) let maxDurationSeconds = Float64(CMTimeGetSeconds(currentDevice!.activeFormat.maxExposureDuration)) let newDurationSeconds = Float64(p * (maxDurationSeconds - minDurationSeconds)) + minDurationSeconds // Scale from 0-1 slider range to actual duration currentDevice!.setExposureModeCustom(duration: CMTimeMakeWithSeconds( newDurationSeconds, preferredTimescale: 1000*1000*1000 ), iso: AVCaptureDevice.currentISO, completionHandler: nil) case 2: // ISO感度調整 currentDevice!.setExposureModeCustom(duration: AVCaptureDevice.currentExposureDuration, iso: sender.value, completionHandler: nil) default: break } } catch { print("(error.localizedDescription)") } }
【処理全体】
Swift
import UIKit import AVFoundation class ViewController: UIViewController { @IBOutlet weak var cameraView: UIView! // プレビュー配置先のView var captureSession = AVCaptureSession() // セッション管理用 var mainCamera: AVCaptureDevice? // メインカメラ用 var innerCamera: AVCaptureDevice? // インカメラ用意 var currentDevice: AVCaptureDevice? // 現在利用中のカメラ用 var photoOutput : AVCapturePhotoOutput? // キャプチャーの出力データを受け付けるオブジェクト var cameraPreviewLayer : AVCaptureVideoPreviewLayer?// プレビュー表示用のレイヤ @IBOutlet weak var brightnessSlider: UISlider! // 露出用スライダー @IBOutlet weak var isoSlider: UISlider! // ISO感度用スライダー // 露出制御時用の定数 let kExposureDurationPower : Float = 4 // Higher numbers will give the slider more sensitivity at shorter durations let kExposureMinimumDuration: Float64 = 1.0/2000 // Limit exposure duration to a useful range // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() // カメラ周り設定 self.setupCaptureSession() self.setupDevice() self.setupInputOutput() self.setupPreviewLayer() self.captureSession.startRunning() // スライダー初期設定 self.setupSlider() } // スライダー初期設定 func setupSlider() { // 露出用スライダー設定 brightnessSlider.minimumValue = 0.0 brightnessSlider.maximumValue = 1.0 self.brightnessSlider.value = 0.5 // ISO感度用スライダー設定 isoSlider.minimumValue = Float(currentDevice!.activeFormat.minISO) isoSlider.maximumValue = Float(currentDevice!.activeFormat.maxISO) self.isoSlider.value = (isoSlider.minimumValue + isoSlider.maximumValue) / 2 } // MARK: - Camera Setting // カメラの画質の設定 func setupCaptureSession() { captureSession.sessionPreset = AVCaptureSession.Preset.photo } // デバイスの設定 func setupDevice() { // カメラデバイスのプロパティ設定 let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified) // カメラデバイスの取得 let devices = deviceDiscoverySession.devices for device in devices { if device.position == AVCaptureDevice.Position.back { mainCamera = device } else if device.position == AVCaptureDevice.Position.front { innerCamera = device } } // 起動時のカメラを設定 currentDevice = mainCamera // 露出とISO感度の初期値設定 do { try mainCamera!.lockForConfiguration() defer { mainCamera!.unlockForConfiguration() } mainCamera!.exposureMode = .custom mainCamera!.setExposureModeCustom(duration: AVCaptureDevice.currentExposureDuration, iso: AVCaptureDevice.currentISO, completionHandler: nil) } catch { print("(error.localizedDescription)") } } // 入出力データの設定 func setupInputOutput() { do { // 入力を初期化 let captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice!) captureSession.addInput(captureDeviceInput) // 出力を初期化 photoOutput = AVCapturePhotoOutput() photoOutput!.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil) captureSession.addOutput(photoOutput!) } catch { print(error) } } // カメラプレビュー用のレイヤを設定 func setupPreviewLayer() { // プレビューレイヤを初期化 self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait self.cameraPreviewLayer?.frame = view.frame self.cameraPreviewLayer?.frame = self.cameraView.bounds self.cameraView.layer.insertSublayer(self.cameraPreviewLayer!, at: 0) } // MARK: - Slider Action @IBAction func changeSlider(_ sender: UISlider) { let tag: Int = sender.tag do { try currentDevice!.lockForConfiguration() defer { currentDevice!.unlockForConfiguration() } // 露出、または ISO感度を設定 switch tag { case 1: let p = Float64(pow(sender.value, kExposureDurationPower)) // Apply power function to expand slider's low-end range let minDurationSeconds = Float64(max(CMTimeGetSeconds(currentDevice!.activeFormat.minExposureDuration), kExposureMinimumDuration)) let maxDurationSeconds = Float64(CMTimeGetSeconds(currentDevice!.activeFormat.maxExposureDuration)) let newDurationSeconds = Float64(p * (maxDurationSeconds - minDurationSeconds)) + minDurationSeconds // Scale from 0-1 slider range to actual duration currentDevice!.setExposureModeCustom(duration: CMTimeMakeWithSeconds( newDurationSeconds, preferredTimescale: 1000*1000*1000 ), iso: AVCaptureDevice.currentISO, completionHandler: nil) case 2: currentDevice!.setExposureModeCustom(duration: AVCaptureDevice.currentExposureDuration, iso: sender.value, completionHandler: nil) default: break } } catch { print("(error.localizedDescription)") } } }
2020.06.22追記
発生している現象をGIFにしてみました。
露出を上げる前は問題なくカメラプレビューが表示されますが、
露出を上げると、カメラプレビュー自体がブレるような動きになってしまいます。
※iPhoneの標準カメラアプリではこの様な現象は発生しないため、何か特殊な処理が必要なのでしょうか。
まだ回答がついていません
会員登録して回答してみよう