GoogleのML Kitを使って、オフラインで日本語テキスト認識するカメラメモアプリを作る（その2：テキスト抽出とファイルアップロード）

iOS 15ではOCR機能がOSに標準搭載されます（Live Text）。これはオフラインの機械学習モデルを使っているので、ネットワークに画像データが乗ったり、プライバシーの心配なく使えるのが魅力です。しかし、リリース時点では日本語に対応していません。

そこで使ってみたのがGoogleのML Kitになります。[まだベータ版なのですが、バージョン2から日本語にも対応しています](https://developers.google.com/ml-kit/vision/text-recognition/v2/ios?hl=ja)。また、ML Kitはオフライン動作も可能で、Google Cloudへの登録なども不要です。これを使えばLive Text的なことはできそうです。

[前回は画面の仕様と、NCMBのSwift SDKを初期化するところ](https://qiita.com/goofmint/items/67543a05aa1ab2230098)まで解説しました。今回は実際にML Kitを使ってテキストを抽出するのと、NCMBへ写真とメモをアップロードする処理を実装します。

## 写真を取得する

写真はImagePickerViewを通じて取得します。InputViewでボタンを押すと `showingPicker = true` にして ImagePickerViewを呼び出します。そして画像データが取得できたら（ `image` にデータが入ったら）、それを画面上に表示します。

`TextRecognizer.textRecognizer(options: JapaneseTextRecognizerOptions())` がML Kitの日本語抽出対応版になります。

```swift
struct InputView: View {
    // イメージピッカーの表示用フラグ
    @State var showingPicker = false
    // 選択した写真が入る
    @State var image: UIImage?
    // 認識したテキストが入る
    @State var recognizeText: String?
    // ML Kitのテキスト認識用オブジェクト
    @State var textRecognizer = TextRecognizer.textRecognizer(options: JapaneseTextRecognizerOptions())
    // アラートのタイトル
    @State var title: String?
    // アラートのメッセージ
    @State var message: String?
    // アラートの表示用フラグ
    @State private var showingAlert = false
    
    var body: some View {
        VStack {
            HStack {
                Button("写真を撮る", action: {
                    showingPicker = true
                })
                Spacer()
                Button("保存する", action: {
                    savePhoto()
                })
                .disabled(image == nil)
            }
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                Text(recognizeText ?? "")
            }
        }
        .sheet(isPresented: $showingPicker) {
            ImagePickerView(image: $image, sourceType: .library)
        }
        .onChange(of: image, perform: { value in
            getText()
        })
        .alert(isPresented: $showingAlert) {
            Alert(title: Text(title!), message: Text(message!))
        }
    }
}
```

## テキスト抽出する

ではML Kitを使った実装です。これは画像データが入ったタイミングで処理を行う `getText` メソッドにて処理を行います。実装としては `textRecognizer.process(vImage)` を行うだけで、結果にテキストが返ってきます。

```swift
// テキスト抽出を行います
func getText() -> Void {
    // 画像データがない場合は処理を行わない
    if image == nil {
        recognizeText = nil
        return
    }
    // 画像データをML Kit用に変換
    let vImage = VisionImage(image: image!)
    vImage.orientation = image!.imageOrientation
    // 抽出実行
    textRecognizer.process(vImage) { result, error in
        // エラー判定
        guard error == nil, let result = result else {
            // エラーの場合は何もしない
            return
        }
        // 認識されたテキストを抽出する
        recognizeText = result.text
    }
}
```

抽出結果には `blocks` というプロパティがあり、その中にフレームや、各フレームのサイズなどのプロパティがあります。おそらく、これらを使えば写真や動画のどの場所から抽出されたのかといった情報が取れたり、抽出部分に枠を表示することもできるでしょう。

## NCMBへアップロードする

撮影した写真と、抽出されたテキストをNCMBへアップロードします。これは `savePhoto` メソッドにて行います。まず、写真データをファイルストア（ストレージ）へアップロードします。

```swift
// ファイル名はランダムに生成
let fileName = "\(UUID()).jpg"
// ファイルストアのインスタンスを作成する
let file = NCMBFile(fileName: fileName)
// アップロード実行（JPEGとして）
var result = file.save(data: image!.jpegData(compressionQuality: 80)!)
// 結果判定
switch result {
case .success(_):
    break
case .failure(_):
    title = "エラー"
    message = "ファイルアップロードでエラーが発生しました"
    showingAlert = true
    return
}
```

ファイルがアップロードできたら、そのファイル名と抽出されたテキストを紐付けて `Memo` というクラス名（一般的なDBでいうテーブル名）で保存します。

```swift
// Memoクラス（一般的なDBでいうテーブル名）のインスタンスを作成
let memo = NCMBObject(className: "Memo");
// text/fileNameはDBでいうカラム相当
memo["text"] = recognizeText
memo["fileName"] = fileName
// 保存実行
result = memo.save()
// 結果の判定
switch result {
case .success(_):
    message = "メモしました"
    title = "保存完了"
    image = nil
    break
case .failure(_):
    message = "ファイルアップロードでエラーが発生しました"
    title = "エラー"
    break
}
showingAlert = true
```

これで画像からテキスト（日本語）を抽出し、NCMBのデータストアとファイルストアに保存する処理が完成しました。

## コード

今回のデモアプリのコードは[NCMBMania/Swift_CameraMemo_MLKit: Swift SDKによるカメラメモアプリにML Kitを追加した版です](https://github.com/NCMBMania/Swift_CameraMemo_MLKit)にアップロードしてあります。

## まとめ

今回はML Kitを使ってオフラインでの日本語抽出をし、さらにその対象になった写真データとテキストをNCMBのデータストアとファイルストアに保存しました。次回は保存されたデータを一覧表示、個別表示する部分を解説します。

[イントロダクション (Swift) : クイックスタート | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/introduction/quickstart_swift.html)