Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした