Swift：射影変換を行う

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

#ソース
```swift: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
    }
    
}
```

#使い方

```swift:例
// 射影変換後の四角形の頂点
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])`メソッドの説明
- 一つ目の引数は射影変換したい点の配列です．
- 二つ目の引数は射影変換後の四角形の頂点の配列です．
- 三つ目の引数は射影変換前の四角形の頂点の配列です．
ここで，射影変換後の四角形の頂点と射影変換前の四角形の頂点は対応する順番に配列に格納する必要があります．

#使用例
<img width="600" alt="射影変換.png" src="https://qiita-image-store.s3.amazonaws.com/0/67153/ac5862b2-8b91-b057-6e0a-9dff47c7fda8.png">

いい感じ

#参考
[射影変換のパラメータを求める](http://mf-atelier.sakura.ne.jp/mf-atelier/modules/tips/index.php/program/algorithm/a6.html)