LoginSignup
23
20

More than 5 years have passed since last update.

[iOS]リアルタイムで画像処理をする時の カメラの内部パラメーターの取得方法

Last updated at Posted at 2018-08-06

先日 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でリアルタイム顔認識をする方法

23
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
20