LoginSignup
3
5

More than 1 year has passed since last update.

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

Posted at

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

置かれた書類を自動検知して真っ直ぐにしたい

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

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

Mar-01-2022 08-45-00

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

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

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

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

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

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)
}
extension CGPoint {
    func toVector(image: CIImage) -> CIVector {
        return CIVector(x: x, y: image.extent.height-y)
    }
}

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

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

ライブラリのコードを見てみると発見がある

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

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

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


🐣


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

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

Twitter
Medium
GitHub

3
5
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
3
5