はじめに
こんにちはsuzukiです。最近案件とは関係ないのですが、swiftでカメラを利用し文字列を認識できないか聞かれることがございました。
MITライセンスでCocoaPodsでインストール可能なTesseract-OCR-iOSを使って試したのですが、想像以上に難航したので記事にしようと思います。
導入準備
Xcode11.3
swift5
TesseractOCRiOS 5.0.1
ライブラリのインポート
CocoaPodsで簡単にライブラリの導入が完了します。Podfileに下記を記述しましょう。
1 2 3 | pod 'TesseractOCRiOS' #なくても動作可能ですが、私の環境では満足な精度にするためチュートリアルのコードを使うため導入しました。 pod 'GPUImage' |
上記を記入し
$pod install
します。
言語モデルのインポート
私の環境ではこちらのリンク先の.traineddataではエラーが発生してしまいました。色々な記事を確認しているとどうも学習データの文字の解析に使う文字情報のバージョンによってエラーが発生するようです。
とりあえず動く学習データということでチュートリアルのeng.traineddataとfra.traineddataをもとに作業しました。
公式の導入方法ではダウンロードした内容をtessdataフォルダの配下に配置しフォルダごとリファレンスフォルダーとしてアプリ内に配置しております。
カメラを利用する準備
info.Plistに下記の項目を追加しましょう。一部必須ではございません。
1 2 3 4 5 6 | <key>NSCameraUsageDescription</key> <string>画像撮影のため</string> <key>NSPhotoLibraryUsageDescription</key> <string>画像ライブラリ</string> <key>NSPhotoLibraryAddUsageDescription</key> <string>画像ライブラリに追加</string> |
レイアウト作成
ざっくりとしたレイアウトです。
・UIImageView:カメラで取得した画像を表示する
・UITextView:分析結果を表示する
・UIButton:UIImagePickerを表示
・UIButton:撮影した画像を分析を行う
カメラ撮影
続いてカメラ周りの処理をViewControllerに記述します。下記のデリゲートを忘れず記述してください。
・UIImagePickerControllerDelegate
・UINavigationControllerDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import UIKit //インポート import TesseractOCR class ViewController:UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate,G8TesseractDelegate{ @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var textView: UITextView! override func viewDidLoad() { super.viewDidLoad() } @IBAction func analyze(_ sender: Any) { } @IBAction func takePicture(_ sender: Any) { let sourceType:UIImagePickerController.SourceType = UIImagePickerController.SourceType.camera // カメラが利用可能か if UIImagePickerController.isSourceTypeAvailable( //カメラ起動 UIImagePickerController.SourceType.camera){ let cameraPicker = UIImagePickerController() cameraPicker.sourceType = sourceType cameraPicker.delegate = self self.present(cameraPicker, animated: true, completion: nil) } } // 撮影が完了時下記デリゲートが呼ばれる func imagePickerController(_ imagePicker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]){ if let pickedImage = info[.originalImage] as? UIImage { imageView.image = pickedImage } //閉じる処理 imagePicker.dismiss(animated: true, completion: nil) } } |
データ解析
最後に解析ボタンを押した際の動作を作成します。
UIImageのエクステンション
チュートリアルを参考にUIImageのExtensionを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | extension UIImage { func scaledImage(_ maxDimension: CGFloat) -> UIImage? { var scaledSize = CGSize(width: maxDimension, height: maxDimension) if size.width > size.height { scaledSize.height = size.height / size.width * scaledSize.width } else { scaledSize.width = size.width / size.height * scaledSize.height } UIGraphicsBeginImageContext(scaledSize) draw(in: CGRect(origin: .zero, size: scaledSize)) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return scaledImage } func preprocessedImage() -> UIImage? { let stillImageFilter = GPUImageAdaptiveThresholdFilter() stillImageFilter.blurRadiusInPixels = 15.0 let filteredImage = stillImageFilter.image(byFilteringImage: self) return filteredImage } } |
ボタンタップ時の動作
先ほどのエクステンションを利用しUIImageを加工後解析を行います
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @IBAction func analyze(_ sender: Any) { guard let image = imageView?.image else { return } let scaledImage = image.scaledImage(1000) ?? image let preprocessedImage = scaledImage.preprocessedImage() ?? scaledImage if let tesseract = G8Tesseract(language: "eng+fra"){ tesseract.delegate = self tesseract.image = preprocessedImage tesseract.recognize() self.textView.text = tesseract.recognizedText } } |
さいごに
認証の内容は期待した精度ではなかったです。UIImageの加工をしない場合はほぼ0%でしたが、UIImageの加工を追加することで50%ぐらいになりました。日本語+英語の文字認識をするため、今度日本語の学習データを使う方法があるか調査していきます。