なすびのブログ

iOS・swiftの情報を気ままに書いていきます。

【swift4.2】iOSのVisionフレームワークで矩形検出をしてみる

はじめに

こんにちは、なすびです。
今回はよくある画像の矩形検出をしてみました。
画像処理といえばOpenCVを使うというイメージでしたがiOS11からVisionフレームワークでを思い出したので使ってみました。

画像から矩形を検出する

・VNImageRequestHandlerに矩形検出する画像を与える
・VNDetectRectanglesRequestのクロージャーで検出後の処理を行う

let image = UIImage(named: "sample")!
let handler = VNImageRequestHandler(cgImage: image.cgImage!, options: [:])
let request = VNDetectRectanglesRequest(completionHandler: {(request, error) in
    // 検出後の処理
})

try! handler.perform([request])

クロージャーの引数requestのresultsで検出結果としてVNRectangleObservationの配列で取得できます。

矩形の頂点座標を取得する

VNRectangleObservationからは矩形の頂点座標を取得することができます。

座標系

Visionで取得できる座標系は以下の図ようにUIKitの座標系とは異なるため、適宜変換してやる必要があります。
Vision座標系では与えた画像に対する割合で座標が表現されるため0.0〜1.0の間の値が渡されます。

f:id:nasubiblog:20181224132933p:plainf:id:nasubiblog:20181224132958p:plain

座標変換

Visionの座標からCGPoint、CGRectをUIKit上の座標に変換する関数です。
それぞれ差を取ってサイズをかけているだけです。
CGRectのy座標について、Vision座標では長方形の左下を原点とするのに対して、UIKit座標では長方形の左上を原点とするためrect.size.heightを引いています。

func convertPoint(_ point: CGPoint, to size: CGSize) -> CGPoint {
    return CGPoint(x: point.x * size.width,
                   y: (1.0 - point.y) * size.height)
}

func convertRect(_ rect: CGRect, to size: CGSize) -> CGRect {
    return CGRect(x: rect.origin.x * size.width,
                  y: (1.0 - rect.origin.y - rect.size.height) * size.height,
                  width: rect.width * size.width,
                  height: rect.height * size.height)
}

矩形を取得する

それでは矩形を検出します。
VNRectangleObservationのtopLeft〜bottomLeftで検出対象の頂点座標をそれぞれ取得取得します。(下記画像の赤線部分)
boundingBoxでは検出した長方形を包括する矩形を取得できます。(下記画像の青線部分)

let request = VNDetectRectanglesRequest(completionHandler: {(request, error) in
    guard let results = request.results as? [VNRectangleObservation] else {
        return
    }
    guard !results.isEmpty else {
        return
    }
    // 頂点座標取得
    let topLeft = self.convertPoint(results[0].topLeft, to: image.size)
    let topRight = self.convertPoint(results[0].topRight, to: image.size)
    let bottomRight = self.convertPoint(results[0].bottomRight, to: image.size)
    let bottomLeft = self.convertPoint(results[0].bottomLeft, to: image.size)
    // 検出対象を包括する矩形
    let bounds = self.convertRect(results[0].boundingBox, to: image.size)
}

f:id:nasubiblog:20181224145526p:plain
あとは取得した座標を使って画像を切り抜いたり、カメラ画像に矩形のラインを描画したりするだけです。
UIImageViewに表示する場合はさらにUIImageViewの中で表示されている画像の座標も合わせて計算してあげる必要がありますがそれはまた別の機会にでも書きます。

最後に

今回はiOS11から追加されたVisionフレームワークを使って矩形の検出を行いました。
以前までは画像処理に関する知識が必要だったりOpenCVを使わないとできないことがswiftで簡単にできるようになっています。
とはいえ複雑なことをやろうと思うとやっぱりOpenCVとか使わないといけなくなるとは思いますが。。。台形補正とかやろうとしたらやっぱりOpenCVの方がいいんかなー

他にもCoreMLなど機械学習フレームワークが出てたりするのでその辺も使っていくと簡単に面白いアプリが作れそうですね!
CoreMLも今度触ってみようかなー

今後もブログではswiftやiOSに関する内容を書くのでよかったら読者登録お願いします!

参考サイト

iOS VisionFramework 矩形検知(VNDetectRectanglesRequest)のTips - Qiita