PythonでGoogleのCloud Vision APIを利用して画像から日本語文字検出する

# PythonでGoogleのCloud Vision APIを利用して画像から日本語文字検出する

## はじめに
[サーバーレスWebアプリ Mosaic](https://mosaic.w2or3w.com "Mosaic")を開発して得た知見を振り返り定着させるための[ハンズオン記事](https://qiita.com/w2or3w/items/87b57dfdbcf218de91e2)を書き連ねていて、合計17記事を予定して現在13記事、あと4記事なのですが、少し飽きてきてしまいました。
飽きてきたというか、新しい機能の実装にも着手したくなってきてしまって。
で、着手してしまったわけです。文字検出の機能追加に。

文字検出？文字認識？OCR(Optical Character Recognition)って言うみたいですね。
AWSのRekognitionでも文字検出できるみたいなのですが、残念ながら日本語非対応らしく。(2020年1月現在。試してないのですが、まだ未サポートらしい。)
しかしGoogleのCloud Vision APIは日本語もサポートしているということで、こちらを利用することにしました。

## Cloud Vision API 有効化
ということで、早速やってきましょう。
Google Cloud Platform もしくは Google Developpers のコンソール > APIとサービス にアクセス。
https://console.cloud.google.com/apis
https://console.developers.google.com/apis

画面上部にある「＋APIとサービスを有効化」ボタンを押下。
![Screenshot 2020-01-11 at 11.55.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/b13453c5-4c66-dac9-9a6d-cb407731d698.png)
![Screenshot 2020-01-11 at 11.56.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/d581ffe2-3719-ced8-0615-e23922a5496d.png)

Cloud Vision API を探して、有効化。
![Screenshot 2020-01-11 at 11.56.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/3ed7381a-9008-2e41-e56c-6da0077000fe.png)
![Screenshot 2020-01-11 at 11.58.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/8bfb3031-13f9-ce66-530f-bf9dc805a741.png)

認証情報として、サービスアカウントを追加しますが、[こちらの記事](https://qiita.com/w2or3w/items/b66a4a8e45e6001a9c46)と重複しますので、ここでは割愛します。

## Lambda(Python3)でGoogle Vision APIを利用する
サービスアカウントでGoogleのAPIを呼ぶために必要なライブラリのインポートについても、先ほど同様、[こちらの記事](https://qiita.com/w2or3w/items/b66a4a8e45e6001a9c46)を参照ください。

ローカル画像ファイルをVision APIに渡して、顔とテキストを検出するコードは以下のような感じです。

```lambda_function.py
  : 
def detectFacesByGoogleVisionAPIFromF(localFilePath, bucket, dirPathOut):
    try:
        keyFile = "service-account-key.json"
        scope = ["https://www.googleapis.com/auth/cloud-vision"]
        api_name = "vision"
        api_version = "v1"
        service = getGoogleService(keyFile, scope, api_name, api_version)
        
        ctxt = None
        with open(localFilePath, 'rb') as f:
            ctxt = b64encode(f.read()).decode()
        
        service_request = service.images().annotate(body={
            "requests": [{
                "image":{
                    "content": ctxt
                  },
                "features": [
                    {
                        "type": "FACE_DETECTION"
                    }, 
                    {
                        "type": "TEXT_DETECTION"
                    }
                ]
            }]
        })
        response = service_request.execute()
        
    except Exception as e:
        logger.exception(e)

def getGoogleService(keyFile, scope, api_name, api_version):
    credentials = ServiceAccountCredentials.from_json_keyfile_name(keyFile, scopes=scope)
    return build(api_name, api_version, credentials=credentials, cache_discovery=False) 
```
このサンプルコードでは、`FACE_DETECTION`と`TEXT_DETECTION`を指定しています。
それ以外にも`LABEL_DETECTION`, `LANDMARK_DETECTION`, `LOGO_DETECTION` などが指定できますが、指定するとその分料金が加算されてきます。なので、目的が文字検出だけの場合には`TEXT_DETECTION`だけ指定するなどしたほうがいいですね。
![Screenshot 2020-01-12 at 14.58.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/265da686-cdac-b6ea-56ae-293fb8b5c4a4.png)
featuresに指定できるものや返されるjsonの詳細についてはここでは書きません。[Cloud Vision API ドキュメント](https://cloud.google.com/vision/docs?hl=ja)を参照ください。料金の詳細については[こちら](https://cloud.google.com/vision/pricing?hl=ja)。

### 本当はGoogle Driveにアップロードしたファイルに対してVisionしたかった
のですが、できませんでした。

```test.py
        imageUri = "https://drive.google.com/uc?id=" + fileID
        service_request = service.images().annotate(body={
            "requests": [{
                "image":{
                    "source":{
                      "imageUri": imageUri
                    }
                  },
                "features": [
                    {
                        "type": "FACE_DETECTION"
                    }, 
                    {
                        "type": "TEXT_DETECTION"
                    }
                ]
            }]
        })
        response = service_request.execute()
```
こんな感じでイケると思ったのですが、以下のようなエラーが返ってきてしまい、色々試したのですが結局できませんでした。

```response.json
{"responses": [{"error": {"code": 7, "message": "We're not allowed to access the URL on your behalf. Please download the content and pass it in."}}]}
{"responses": [{"error": {"code": 4, "message": "We can not access the URL currently. Please download the content and pass it in."}}]}ass it in."}}]}
```
Visionを呼び出すことは出来てるのですが、VisionからWeb上の画像(imageUri)にアクセスできてないみたいですね。S3のPublicバケット上の画像の直リンクを指定してもダメでした。謎です。誰か教えて下さい。

## 動作確認
TEXT_DETECTIONでは、`textAnnotations`と`fullTextAnnotation`という2つの要素が得られます。
textAnnotationsで検出された領域をブルー、fullTextAnnotationで検出された領域をグリーンで囲った結果は以下のような感じでした。
![faces-aaaaaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/f68b90dd-d70e-85da-66fa-1aeadd50dbeb.png)

まずまずの結果が得られていると思うのですが、日本語だからかどうなのか、ブルーの方は若干1文字の範囲がズレているような印象を受けますね。

また、下の画像のように1文字1文字が独立しているような場合に弱そうです。これも日本語特有の問題なのか、はたまた違ったfeaturesだったら検出できるのか、その他パラメータで調整できるのか、深追いはしていませんがとにかく期待した結果は得られていません。
将来的にAPIの学習が進んでこの画像にある文字が漏れなく検出できる日がくることを祈るしか無いのが辛いところです。
![faces-IMG_20190901_094815-200126131521-1387a884261.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394775/5e769f97-03bc-f4b3-b566-acab641aabc5.jpeg)
AWSのRekognitionが文字検出で日本語をサポートしたあかつきには、まずはこの画像で評価をしようと思っています！

## あとがき
記事としてまとめることは大切なことだし必要なことだと分かっているのですが、、やっぱり何か新しい機能を作っている時が一番楽しいですね。
その流れでこのVision APIの記事もサラサラと書くことができました。
記事の作成を後回しにすると面倒になってしまうので、記憶の新しいうちに、そしてテンションが高いうちに、さっさと書いてしまうことは大切なことかもしれないなと思った次第です。

今作ってるMosaicというサーバーレスWebアプリ、基本的にはAWSのインフラを利用しているのですが、ゆくゆくはGCPで同じものを構築するということもやりたいと思っています。2020年内には。
インフラはどちらかに統一して構築したほうが良いだろうなと思いますが、RekognitionやVisionAPIのようなWebAPIサービスは、どちらでも良いというか、やりたいことが実現できる方を選択することになりますね。
どちらもAPIを呼ぶだけなので大した事はなく、結局何のシステムもそうなのかもしれませんが、部品を組み合わせて作っている、プラモデルのような、そんな感じがより強くします。
全てを知っておく必要はないと思いますが、1つにこだわりすぎるのも良くないと思いますね。

機械学習された高精度のサービスをこんなに簡単に利用できるのは喜ばしい限りなのですが、それが期待する結果を返してくれなかった場合に手詰まりになってしまうのは辛いところですね。とはいえ、この先の研究分野で何かできるかというと手も足も出ないわけで、いつの日か学習が進んで期待する結果が出てくるのを正座して待つしかできないワタシです。