MLKitを使った簡易的な翻訳アプリ

# はじめに
こんにちは。watnowで主にiOSアプリをダラダラ作っているKUROです。
今回はFirebaseが提供している[MLKit](https://firebase.google.com/docs/ml-kit/ios/recognize-text)という文字認識のAPIを使って文字を認識し、認識した文字を翻訳できるアプリを作りたいと思います。
# 今回作るもの
今回作るものは下記のようにカメラに映った文字認識して翻訳するアプリです。![RPReplay_Final1703471238.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089991/88d114e5-e476-b7df-c8ef-d82a71fe1050.gif)



# 使用した技術
MLKit
CropViewController
UIKit
ひらがなAPI

# 実際に作る
## 下準備
### ライブラリの導入
MLKitとCropViewControllerはCocoaPodsを使って導入しました。
CocoaPodsでのライブラリ導入方法は[こちらの記事](https://qiita.com/s11y/items/3090290cb72434852460)を参考にしました。

### UIを作る
今回はカメラを起動し、その場で撮影した画像から翻訳範囲を切り取り、翻訳をするアプリを作成するので、シンプルなUIを目指しました。
パーツとしては撮影した画像を表示するUIImageViewと翻訳を実施するUIButton、カメラを起動するUIButton、翻訳結果を表示するUILabelの4つのパーツを配置します。
![スクリーンショット 2023-12-25 9.17.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089991/ca4b6024-cbcb-4114-d260-46bce5cbba1c.png)

### ライブラリのインポート
UIKitはもちろん、今回使用する`MLKit`、日本語と文字を認識するための`MLKitTextRecognitionJapanese`と`MLKitTextRecognitionCommon`、カメラを使用するための`AVFoundation`そして撮影した画像を切り取るための`CropViewController`
```swift:ViewController
import UIKit
import MLKit
import MLKitTextRecognitionJapanese
import MLKitTextRecognitionCommon
import AVFoundation
import CropViewController
```

## ついに本題へ
下準備も終えたので、本題のコードを書いていきましょう。

### コードを書いていく！
まずは変数を宣言。
```swift:ViewController
//カメラやCropViewControllerのDelegateを継承しておくのを忘れずに
class ViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate,CropViewControllerDelegate {
//パーツの宣言
@IBOutlet var imageView: UIImageView!
@IBOutlet var result: UILabel!

//翻訳結果を一時的に入れる変数
var kanziResult: String = ""
//日本語を認識するためのクラス
let japaneseOptions = JapaneseTextRecognizerOptions()
let japaneseTextRecognizer = TextRecognizer.textRecognizer(options:JapaneseTextRecognizerOptions())
```
次にカメラの起動処理の部分を書いていきます。
`takePhoto`関数でカメラを起動し、`imagePuckerController`関数でカメラを閉じた際に`CropViewController`を起動し、画像を切り取りできるようにしています。
```swift:ViewController
//カメラを起動するボタンのIBAction
@IBAction func takePhoto() {
    let picker = UIImagePickerController()
    picker.sourceType = .camera
    picker.delegate = self
    present(picker, animated: true, completion: nil)
}
//カメラを閉じた後の処理を記述
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true)
    guard let image = info[.originalImage] as? UIImage else{
    print("Image not found.")
    return
    }
    let cropController = CropViewController(croppingStyle: .default, image: image)
    cropController.delegate = self
    cropController.customAspectRatio = CGSize(width: 100, height: 100)
    
    //今回は使わないボタン等を非表示にする。
    cropController.aspectRatioPickerButtonHidden = true
    cropController.resetAspectRatioEnabled = true
    cropController.rotateButtonsHidden = true
    
    //cropBoxのサイズを固定する。
    cropController.cropView.cropBoxResizeEnabled = true
    //pickerを閉じたら、cropControllerを表示する。
    picker.dismiss(animated: true) {
        self.present(cropController, animated: true, completion: nil)
    }
}
//撮影した画像を切り取ったものに置き換える
func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
    updateImageViewWithImage(image, fromCropViewController: cropViewController)
}
//ImageViewに切り取った画像を表示させる
func updateImageViewWithImage(_ image: UIImage, fromCropViewController cropViewController: CropViewController) {
    imageView.image = image
    cropViewController.dismiss(animated: true, completion: nil)
}
```
カメラを起動して画像を撮影できたらいよいよ翻訳していきましょう。
今回は画像内の文字をひらがなに変えてくれる、ひらがなAPIを使用しました。基本的には[こちらの記事](https://qiita.com/sventouz/items/318d8370ab489724c454)を参考にし、ひらがなに変える元の文字を`MLKit`で抽出しました。
```swift:ViewController
@IBAction func honyaku() {
    let image = VisionImage(image: imageView.image!)
    //ここで、画像からテキストを認識しています。
    japaneseTextRecognizer.process(image) { result, error in
        guard error == nil, let result = result else { return }
        self.kanziResult = result.text
        print("resultText: \(result.text)")
    }
    //ここからAPIにHTTPリクエストを送っていきます。
    var request = URLRequest(url: URL(string: "https://labs.goo.ne.jp/api/hiragana")!)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    //POSTするデータをURLRequestに持たせる
    let postData = PostData(app_id: "6937c927439be919b9c5add398fcd7a18307788779ce23b4181eb3a9c5435b6f", request_id: "record003", sentence: kanziResult, output_type: "hiragana")
    guard let uploadData = try? JSONEncoder().encode(postData) else {
        print("json生成に失敗しました")
        return
    }
    request.httpBody = uploadData
    //APIへPOSTしてresponseを受け取る
    let task = URLSession.shared.uploadTask(with: request, from: uploadData) {
        data, response, error in
        if let error = error {
            print ("error: \(error)")
            return
        }
        guard let response = response as? HTTPURLResponse,
              (200...299).contains(response.statusCode) else {
            print ("server error")
            return
        }
        if response.statusCode == 200 {
            guard let data = data, let jsonData = try? JSONDecoder().decode(Hiragana.self, from: data) else {
                print("json変換に失敗しました")
                return
            }
            print(jsonData.converted)
            DispatchQueue.main.async {
                self.result.text = jsonData.converted
            }
        } else {
            print("サーバエラー ステータスコード: \(response.statusCode)\n")
        }
    }
    task.resume()
}
}
//APIから受け取る結果の構造体
struct Hiragana:Codable {
    var request_id: String
    var output_type: String
    var converted: String
}
//APIにPOSTする際の構造体
struct PostData: Codable {
    var app_id:String
    var request_id: String
    var sentence: String
    var output_type: String
}
```
ここまでできたら、一度実行してみてください。おそらく、もうすでに翻訳ができていると思います。
ただ、一つ気づくと思います。結果のLabelに収まらなくね？と。そうなった場合は以下のコードを追加してください。自動的に結果をLabelに収めてくれます。
```swift:ViewController
override func viewDidLoad() {
    result.adjustsFontSizeToFitWidth = true
    super.viewDidLoad()
}
```
### 完成！
![スクリーンショット 2023-12-25 11.21.23.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089991/ddcee040-75b1-e85d-b5ac-ceb2f6437d32.jpeg)

# 終わりに
今回はMLKitを使った翻訳アプリを作ってみました。
僕が所属するwatnowではこのように簡単なモバイルアプリから、1万ユーザーを超えるバスアプリまで色々なアプリやサービスを作っています。
もし、少しでも興味がありましたら是非Twitter覗いてみてください。
ここまで読んでいただきありがとうございました。