LoginSignup
9
8

More than 3 years have passed since last update.

Swift:射影変換を行う

Last updated at Posted at 2018-08-01

はじめに

画像処理で射影変換が必要になったのだが,それだけのためにOpenCVを使うのもなんだかなぁと思い,Swiftで射影変換を行うコードを実装した.
実装にはAccelerate.frameworkを使ったため,非常に高速である.

ソース

Homography.swift
import Foundation
import Accelerate

class Homography {

    public func transeform(points: [CGPoint], to: [CGPoint], from: [CGPoint]) -> [CGPoint] {
        let td: [Double] = [Double(to[0].x), Double(to[1].x), Double(to[2].x), Double(to[3].x),
                            Double(to[0].y), Double(to[1].y), Double(to[2].y), Double(to[3].y)]

        let fd: [Double] = [Double(from[0].x), Double(from[1].x), Double(from[2].x), Double(from[3].x),
                            Double(from[0].y), Double(from[1].y), Double(from[2].y), Double(from[3].y)]

        let vector = la_matrix_from_double_buffer(td, 8, 1, 1, la_hint_t(LA_NO_HINT), la_attribute_t(LA_DEFAULT_ATTRIBUTES))

        let e: [Double] = [fd[0], fd[4], 1.0,   0.0,   0.0, 0.0, -td[0] * fd[0], -td[0] * fd[4],
                           fd[1], fd[5], 1.0,   0.0,   0.0, 0.0, -td[1] * fd[1], -td[1] * fd[5],
                           fd[2], fd[6], 1.0,   0.0,   0.0, 0.0, -td[2] * fd[2], -td[2] * fd[6],
                           fd[3], fd[7], 1.0,   0.0,   0.0, 0.0, -td[3] * fd[3], -td[3] * fd[7],
                             0.0,   0.0, 0.0, fd[0], fd[4], 1.0, -td[4] * fd[0], -td[4] * fd[4],
                             0.0,   0.0, 0.0, fd[1], fd[5], 1.0, -td[5] * fd[1], -td[5] * fd[5],
                             0.0,   0.0, 0.0, fd[2], fd[6], 1.0, -td[6] * fd[2], -td[6] * fd[6],
                             0.0,   0.0, 0.0, fd[3], fd[7], 1.0, -td[7] * fd[3], -td[7] * fd[7]]

        let i = invert(matrix: e)

        let matrix = la_matrix_from_double_buffer(i, 8, 8, 8, la_hint_t(LA_NO_HINT), la_attribute_t(LA_DEFAULT_ATTRIBUTES))

        let product = la_matrix_product(matrix, vector)

        let n: la_count_t = la_matrix_rows(product)
        let m: la_count_t = la_matrix_cols(product)

        var a = [Double](repeating: 0.0, count: Int(n * m))

        la_matrix_to_double_buffer(&a, m, product)

        var results = [CGPoint]()
        for p in points {
            let x = (a[0] * Double(p.x) + a[1] * Double(p.y) + a[2]) / (a[6] * Double(p.x) + a[7] * Double(p.y) + 1.0)
            let y = (a[3] * Double(p.x) + a[4] * Double(p.y) + a[5]) / (a[6] * Double(p.x) + a[7] * Double(p.y) + 1.0)
            results.append(CGPoint(x: x, y: y))
        }
        return results
    }

    private func invert(matrix: [Double]) -> [Double] {
        var inMatrix = matrix
        var N = __CLPK_integer(sqrt(Double(matrix.count)))
        var pivots = [__CLPK_integer](repeating: 0, count: Int(N))
        var workspace = [Double](repeating: 0.0, count: Int(N))
        var error: __CLPK_integer = 0

        withUnsafeMutablePointer(to: &N) { (clpk_integer) -> Void in
            dgetrf_(clpk_integer, clpk_integer, &inMatrix, clpk_integer, &pivots, &error)
            dgetri_(clpk_integer, &inMatrix, clpk_integer, &pivots, &workspace, clpk_integer, &error)
        }
        return inMatrix
    }

}

使い方

// 射影変換後の四角形の頂点
let toPoints = [CGPoint(x: bounds.minX + 20, y: bounds.minY + 20),
                CGPoint(x: bounds.maxX - 20, y: bounds.minY + 20),
                CGPoint(x: bounds.maxX - 20, y: bounds.maxY - 20),
                CGPoint(x: bounds.minX + 20, y: bounds.maxY - 20)]

// 射影変換前の四角形の頂点
let fromPoints = [CGPoint(x: bounds.minX + 60, y: bounds.minY + 60),
                  CGPoint(x: bounds.maxX - 30, y: bounds.minY + 40),
                  CGPoint(x: bounds.maxX - 50, y: bounds.maxY - 50),
                  CGPoint(x: bounds.minX + 80, y: bounds.maxY - 30)]

// 射影変換前の四角形内の点
let targetPoint = CGPoint(x: 200, y: 150)

let homography = Homography()

// 射影変換後の四角形内の点
let result = homography.transeform(points: [targetPoint], to: toPoints, from: fromPoints)[0]

.transform(points: [CGPoint], to: [CGPoint], from: [CGPoint])メソッドの説明
- 一つ目の引数は射影変換したい点の配列です.
- 二つ目の引数は射影変換後の四角形の頂点の配列です.
- 三つ目の引数は射影変換前の四角形の頂点の配列です.
ここで,射影変換後の四角形の頂点と射影変換前の四角形の頂点は対応する順番に配列に格納する必要があります.

使用例

射影変換.png

いい感じ

参考

射影変換のパラメータを求める

9
8
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
9
8