先日 SceneKitで内部パラメーターを取得する記事を書いたのですが、今回はAVFoundationで内部パラメーターを取得する話。
iOS 11から内部パラメーターの取得ができるようになったのですが、これまた情報がないので、ソースコードだけそっと置いておきます。
#ターゲット
- Swift 4でAVCaptureSessionをつかってリアルタイムの顔認識や画像処理をしたい人
- また、iOS 11から使えるようになったカメラの内部パラメーター(intrinsicMatrix)の取得をしたい人
- 他のパラメーター取得にも使えそう
#ポイント
VideoDataOutputへの設定
まずセッションにVideoDataOutput情報を追加しないと内部パラメーター含むコネクション情報が取得できず、nilエラーが起きます。
captureSession.addOutput(videoDataOutput)
をしてからコネクションに対してenable設定を行います。
let videoDataOutput = AVCaptureVideoDataOutput()
// ~~~ もろもろの設定
captureSession.addOutput(videoDataOutput)
videoDataOutput.connections.first?.isCameraIntrinsicMatrixDeliveryEnabled = true
このenable設定を行っておくことで、CMSampleBufferから内部パラメーターの取得をすることができます。
if let camData = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, nil) as? Data {
let matrix: matrix_float3x3 = camData.withUnsafeBytes { $0.pointee }
print(matrix)
}
(些事) Swift 3から Swift 4に移行した場合の注意点
キャプチャした際に呼び出されるcaptureOutput
ですが、Swift 3ではdidOutputSampleBuffer
がSwift 4ではdidOutput
になっているので注意が必要です。
(アプリは起動するのに写真が表示されない不具合が起きます)
func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)
#以下ソースコード
ViewController
class ViewController: UIViewController, AVCaptureDelegate {
@IBOutlet weak var imageView: UIImageView!
let avCapture = AVCapture()
override func viewDidLoad() {
super.viewDidLoad()
avCapture.delegate = self
}
func capture(image: UIImage) {
// 何かしらの画像処理
imageView.image = image
}
}
avCapture.swift
(本来ならもっとエラーハンドリングするべきなんですが。)
import UIKit
import AVFoundation
protocol AVCaptureDelegate {
func capture(image: UIImage)
}
class AVCapture:NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
var captureSession: AVCaptureSession!
var delegate: AVCaptureDelegate?
// DISMISS_COUNT に1回画像処理 (captureの実行)
var counter = 0
let DISMISS_COUNT = 5
override init(){
super.init()
captureSession = AVCaptureSession()
captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
let videoDevice = AVCaptureDevice.default(for: AVMediaType.video)
videoDevice?.activeVideoMinFrameDuration = CMTimeMake(1, 30)// 1/30秒 (1秒間に30フレーム)
let videoInput = try! AVCaptureDeviceInput.init(device: videoDevice!)
captureSession.addInput(videoInput)
let videoDataOutput = AVCaptureVideoDataOutput()
videoDataOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable as! String : Int(kCVPixelFormatType_32BGRA)]
videoDataOutput.alwaysDiscardsLateVideoFrames = true
// 先にセッションにOutput情報を追加
captureSession.addOutput(videoDataOutput)
// 内部パラメーターの配信を有効にする
// isCameraIntrinsicMatrixDeliverySupported がtrueの場合のみ
videoDataOutput.connections.first?.isCameraIntrinsicMatrixDeliveryEnabled = true
DispatchQueue.global(qos: .userInitiated).async {
self.captureSession.startRunning()
}
}
// 新しいキャプチャの追加で呼ばれる関数
func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if (counter % DISMISS_COUNT) == 0 {
let image = imageFromSampleBuffer(sampleBuffer: sampleBuffer)
delegate?.capture(image: image)
// カメラの内部パラメーターの入手
if let camData = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, nil) as? Data {
let matrix: matrix_float3x3 = camData.withUnsafeBytes { $0.pointee }
print(matrix)
}
}
counter += 1
}
// SampleBufferからUIImageの作成
func imageFromSampleBuffer(sampleBuffer :CMSampleBuffer) -> UIImage {
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
// イメージバッファのロック
CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
// 画像情報を取得
let base = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)!
let bytesPerRow = UInt(CVPixelBufferGetBytesPerRow(imageBuffer))
let width = UInt(CVPixelBufferGetWidth(imageBuffer))
let height = UInt(CVPixelBufferGetHeight(imageBuffer))
// ビットマップコンテキスト作成
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitsPerCompornent = 8
let bitmapInfo = CGBitmapInfo(rawValue: (CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) as UInt32)
let newContext = CGContext(data: base, width: Int(width), height: Int(height), bitsPerComponent: Int(bitsPerCompornent), bytesPerRow: Int(bytesPerRow), space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! as CGContext
// イメージバッファのアンロック
CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
// 画像作成
let imageRef = newContext.makeImage()!
let image = UIImage(cgImage: imageRef, scale: 1.0, orientation: UIImageOrientation.right)
return image
}
}
#参考記事
該当 Apple ドキュメント:isCameraIntrinsicMatrixDeliveryEnabled
ソースコード元: [iOS] AVFoundation+OpenCVで劇画調カメラを作ってみた
[コピペで使える]swift3/swift4でリアルタイム顔認識をする方法