ライブラリを分解して組み合わせて、車輪の再発明をするーードキュメントスキャナ

# 置かれた書類を検知して真っ直ぐにする機能を作ります

# 置かれた書類を自動検知して真っ直ぐにしたい
置かれた書類をカメラで撮影すると、斜めになることがあります。
それを自動で真っ直ぐに直してくれる機能があればいいですよね。

で、iOSではVisionKitというフレームワークですでに実現できます。

<img src="https://camo.qiitausercontent.com/51e8a02d1386a68613329d868173f9045c568064/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3233353235392f64383238333436322d663265302d363537332d383835302d6163373163353966383863312e676966"> 

![Mar-01-2022 08-45-00](https://user-images.githubusercontent.com/23278992/156078070-37eefb44-936d-4a4a-9c3b-1c203a631ad0.gif)



https://qiita.com/john-rocky/items/501763f1ee8e8517f615

もっというなら、iPhoneのデフォルトカメラにもOCR付きのドキュメントスキャナが付いています。すごい。

# 既存のものを組み合わせてあえて再発明。

画像変形ライブラリと、Visionの四角形検出を組み合わせれば、ドキュメントスキャナっぽいものが再発明できそうです。

https://qiita.com/john-rocky/items/2a91187581ab062e44d2

<img src="https://camo.qiitausercontent.com/f53a99243cee65ad895a3d67649e24d5007069b6/68747470733a2f2f757365722d696d616765732e67697468756275736572636f6e74656e742e636f6d2f32333237383939322f3135353838363030382d65636631643763622d656561302d343932332d396534322d6439343939633439313165322e676966">

https://qiita.com/john-rocky/items/e5e08f17304f3a0116b1

<img src="https://camo.qiitausercontent.com/0f20e7dc3a2228d24455124f5e30d2b4d2ed952c/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3233353235392f31316630663332342d376262352d393066342d633065382d3964366237396435333635392e706e67">


# ライブラリのコアを見つける

上記の画像変形ライブラリのコアは、画像の4点を受け取って、整形された四角形の画像を返すFunctionです。
CIPerspectiveCorrectionというフィルターで4点から整形された画像を作っていることがわかります。

```swift
private func getCroppedImage(image: CIImage, topL: CGPoint, topR: CGPoint, botL: CGPoint, botR: CGPoint) -> CIImage {
    let rectCoords = NSMutableDictionary(capacity: 4)
        
    rectCoords["inputTopLeft"] = topL.toVector(image: image)
    rectCoords["inputTopRight"] = topR.toVector(image: image)
    rectCoords["inputBottomLeft"] = botL.toVector(image: image)
    rectCoords["inputBottomRight"] = botR.toVector(image: image)
        
    guard let coords = rectCoords as? [String : Any] else {
        return image
    }
    return image.applyingFilter("CIPerspectiveCorrection", parameters: coords)
}
```

```swift
extension CGPoint {
    func toVector(image: CIImage) -> CIVector {
        return CIVector(x: x, y: image.extent.height-y)
    }
}
```

このFunctionに、Visionで検知した四角形の座標を与えれば、自動検知による切り抜きができそうです。

```swift
private func getDocumentImage(image:UIImage) -> UIImage? {
    let ciImage = CIImage(image: image)!
    let request = VNDetectRectanglesRequest()
    let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
    try! handler.perform([request])
    guard let result = request.results?.first else { return nil }
        
    let topLeft = CGPoint(x: result.topLeft.x, y: 1-result.topLeft.y)
    let topRight = CGPoint(x: result.topRight.x, y: 1-result.topRight.y)
    let bottomLeft = CGPoint(x: result.bottomLeft.x, y: 1-result.bottomLeft.y)
    let bottomRight = CGPoint(x: result.bottomRight.x, y: 1-result.bottomRight.y)

        
    let deNormalizedTopLeft = VNImagePointForNormalizedPoint(topLeft, Int(ciImage.extent.width), Int(ciImage.extent.height))
    let deNormalizedTopRight = VNImagePointForNormalizedPoint(topRight, Int(ciImage.extent.width), Int(ciImage.extent.height))
    let deNormalizedBottomLeft = VNImagePointForNormalizedPoint(bottomLeft, Int(ciImage.extent.width), Int(ciImage.extent.height))
    let deNormalizedBottomRight = VNImagePointForNormalizedPoint(bottomRight, Int(ciImage.extent.width), Int(ciImage.extent.height))

    let croppedImage = getCroppedImage(image: ciImage, topL: deNormalizedTopLeft, topR: deNormalizedTopRight, botL: deNormalizedBottomLeft, botR: deNormalizedBottomRight)
    let safeCGImage = context.createCGImage(croppedImage, from: croppedImage.extent)
    let croppedUIImage = UIImage(cgImage: safeCGImage!)
    return croppedUIImage
}
```

できました。

![Mar-01-2022 08-45-00](https://user-images.githubusercontent.com/23278992/156078070-37eefb44-936d-4a4a-9c3b-1c203a631ad0.gif)

# ライブラリのコードを見てみると発見がある
ライブラリのコードを見てみたり、組み合わせてみたりすると、発見があるかも。

コードGitHubにアップロードしました。

https://github.com/john-rocky/AutoDocumentScanner/tree/main

SemanticImageという画像フィルター集にも入れました。

https://github.com/john-rocky/SemanticImage

<img src="https://user-images.githubusercontent.com/23278992/156080423-ec99d475-56bb-4787-9348-4c7b5d463b93.jpg" , width=400> 
<img src="https://user-images.githubusercontent.com/23278992/156080447-9751c793-952e-4ba6-bf47-336778934383.JPG", width=400>

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)