会計フリー周りのエンジニアをしているよーだ(@rtryoda)です。この記事は freee Developers Advent Calendar 2019 の11日目です。最近各ベンダーやOSSのOCRエンジンをgoで触る機会があったので、実行方法と結果をまとめました。
OCRとは
OCRとは光学的文字認識(Optical Character Recognition)のことで、画像などに記されている文字を読み取りテキストデータに変換することです。例えば以下のような画像に対してOCRを実行すると"あいうえお 12345"
と認識されることを期待します。
※各OCRの実行にはこちらの画像を使用します。
今回試すOCRエンジン一覧
OCRエンジン | 日本語対応 | クライアントライブラリ(go) |
---|---|---|
Google Cloud Vision API | ○ | googleapis/google-cloud-go |
Azure Computer Vision | ○ | - |
Amazon Rekognition Image | × | aws/aws-sdk-go |
Tesseract | ○ | otiai10/gosseract |
ざっくり触ってみた感想
- Google Cloud Vision API
- SDKもあってドキュメントも充実していて触りやすい
- 日本語に対応していて精度も高くて驚いた
- Azure Computer Vision
- SDKがなくREST APIで実行する必要があり、goでmaltipartで画像送るのが少し面倒くさい
- 日本語に対応しているが精度もGoogle Cloud Vision APIやTesseractには及ばず
- 正確な数値は計測していないが4つの中では一番レスポンスが早かった
- Amazon Rekognition Image
- goのSDKがあり他のAWSの機能と使い方が似ていて手軽に触れた
- 4つの中では信頼度やポリゴンなど一番詳細な情報が返ってくる
- 日本語対応していないのが厳しい
- Tesseract
- ローカルにインストールすれば手軽に触れて便利
- オプション多かったり独自の学習ができて拡張性が高い
- 返り値の信頼度を見て精度高められないか検討したが、自信満々に間違えることもあり信頼度はあまり使えなかった
- gosseractのインターフェースがよく出来ていて実装は一番シンプルになった
実行方法
Google Cloud Vision API
Google Cloud Vision APIはgoのSDKが公開されているのでこちらを利用します。
$ go get -u cloud.google.com/go/vision/apiv1
また事前に認証キーが含まれるJSONファイルを取得しファイルのパスを環境変数に指定します。
$ export GOOGLE_APPLICATION_CREDENTIALS=xxxxx
実装は公式サンプルを参考にしました。
(各OCRの実装はエラー処理を省いています)
package main import ( vision "cloud.google.com/go/vision/apiv1" "context" "fmt" "os" ) func main() { // クライアントの作成 ctx := context.Background() client, _ := vision.NewImageAnnotatorClient(ctx) // 画像の読み込み file, _ := os.Open("sample.jpg") defer file.Close() image, _ := vision.NewImageFromReader(file) // 実行 annotations, _ := client.DetectTexts(ctx, image, nil, 10) for _, annotation := range annotations { fmt.Printf( "locale:%s, description:%s, bounding_poly:%+v\n", annotation.Locale, annotation.Description, annotation.BoundingPoly, ) } }
実行結果
locale:ja, description:あいうえお 12345 , bounding_poly:vertices:<x:22 y:19 > vertices:<x:314 y:19 > vertices:<x:314 y:35 > vertices:<x:22 y:35 > locale:, description:あいうえお, bounding_poly:vertices:<x:22 y:19 > vertices:<x:115 y:19 > vertices:<x:115 y:35 > vertices:<x:22 y:35 > locale:, description:12345, bounding_poly:vertices:<x:263 y:20 > vertices:<x:314 y:21 > vertices:<x:314 y:35 > vertices:<x:263 y:34 >
戻り値のEntityAnnotation
のメンバにはConfidence(信頼度)
がありますが、DetectText
では値が入らないことに注意が必要です。
Azure Computer Vision
Azure Computer VisionはgoのSDKがないのでREST APIを利用します。まず、認証キーとエンドポイントを取得し環境変数に設定します。
$ export COMPUTER_VISION_SUBSCRIPTION_KEY=xxxxx $ export COMPUTER_VISION_ENDPOINT=xxxxx
実装は公式サンプルを参考にしました。
package main import ( "bytes" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "os" ) func main() { // 環境変数の読み込み subscriptionKey := os.Getenv("COMPUTER_VISION_SUBSCRIPTION_KEY") endpoint := os.Getenv("COMPUTER_VISION_ENDPOINT") // 画像の読み込み var buf bytes.Buffer w := multipart.NewWriter(&buf) fw, _ := w.CreateFormFile("data", "image") file, _ := os.Open("sample.jpg") io.Copy(fw, file) file.Close() w.Close() // REST APIの実行 uri := endpoint + "/vision/v2.1/ocr?language=unk&detectOrientation=true" req, _ := http.NewRequest("POST", uri, &buf) req.Header.Add("Content-Type", w.FormDataContentType()) req.Header.Add("Ocp-Apim-Subscription-Key", subscriptionKey) client := &http.Client{} resp, _ := client.Do(req) defer resp.Body.Close() data, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(data)) }
実行結果
{ "language": "ja", "textAngle": 0, "orientation": "Up", "regions": [ { "boundingBox": "22,19,95,17", "lines": [ { "boundingBox": "22,19,95,17", "words": [ { "boundingBox": "22,19,16,17", "text": "あ" }, { "boundingBox": "42,21,16,14", "text": "い" }, { "boundingBox": "62,19,14,17", "text": "う" }, { "boundingBox": "81,19,17,17", "text": "え" }, { "boundingBox": "100,19,17,16", "text": "お" } ] } ] }, { "boundingBox": "262,20,53,16", "lines": [ { "boundingBox": "262,20,53,16", "words": [ { "boundingBox": "262,20,20,15", "text": "12" }, { "boundingBox": "305,21,10,15", "text": "5" } ] } ] } ] }
Azure Computer VisionもConfidence
を返しません。また、REST APIなので実行結果をgoで扱う場合はJSON文字列を構造体に変換する必要があります。
var result struct { Language string Orientation string Regions []struct { BoundingBox string Lines []struct { BoundingBox string Words []struct { BoundingBox string Text string } } } } json.Unmarshal(data, &result)
Amazon Rekognition Image
Amazon Rekognition ImageはgoのSDKが公開されているのでこちらを利用します。
$ go get -u github.com/aws/aws-sdk-go
また、認証キーを環境変数に設定する必要があります。
$ export AWS_ACCESS_KEY_ID=xxxx $ export AWS_SECRET_ACCESS_KEY=xxx
実装はGithubのREADMEを参考にしました。
package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/rekognition" "io/ioutil" "os" ) func main() { // ファイル読み込み file, _ := os.Open("sample.jpg") defer file.Close() bytes, _ := ioutil.ReadAll(file) // セッション、クライアント、インプットの作成 awsSession := session.Must(session.NewSession()) svc := rekognition.New(awsSession, aws.NewConfig().WithRegion("ap-northeast-1")) input := &rekognition.DetectTextInput{Image: &rekognition.Image{Bytes: bytes}} // Rekognitionの実行 output, err := svc.DetectText(input) if err != nil { panic(err) } fmt.Printf("%+v\n", output) }
実行結果
{ TextDetections: [ { Confidence: 58.572227478027344, DetectedText: "$y", Geometry: { BoundingBox: { Height: 0.42103537917137146, Left: 0.07299134880304337, Top: 0.24968594312667847, Width: 0.14152440428733826 }, Polygon: [ { X: 0.07299134880304337, Y: 0.24968594312667847 }, { X: 0.21451574563980103, Y: 0.421069860458374 }, { X: 0.1993957757949829, Y: 0.8421052694320679 }, { X: 0.05787137523293495, Y: 0.6707213521003723 } ] }, Id: 0, Type: "LINE" }, { Confidence: 99.96682739257812, DetectedText: "12345", Geometry: { BoundingBox: { Height: 0.2631579041481018, Left: 0.7885196208953857, Top: 0.3333333432674408, Width: 0.16012084484100342 }, Polygon: [ { X: 0.7885196208953857, Y: 0.3333333432674408 }, { X: 0.9486404657363892, Y: 0.3333333432674408 }, { X: 0.9486404657363892, Y: 0.5964912176132202 }, { X: 0.7885196208953857, Y: 0.5964912176132202 } ] }, Id: 1, Type: "LINE" }, { Confidence: 58.572227478027344, DetectedText: "$y", Geometry: { BoundingBox: { Height: 0.4213235080242157, Left: 0.07250755280256271, Top: 0.2631579041481018, Width: 0.21235120296478271 }, Polygon: [ { X: 0.07250755280256271, Y: 0.2631579041481018 }, { X: 0.2145015150308609, Y: 0.42105263471603394 }, { X: 0.1993957757949829, Y: 0.8421052694320679 }, { X: 0.06042296066880226, Y: 0.6666666865348816 } ] }, Id: 2, ParentId: 0, Type: "WORD" }, { Confidence: 99.96682739257812, DetectedText: "12345", Geometry: { BoundingBox: { Height: 0.2631579041481018, Left: 0.7885196208953857, Top: 0.3333333432674408, Width: 0.16012084484100342 }, Polygon: [ { X: 0.7885196208953857, Y: 0.3333333432674408 }, { X: 0.9486404657363892, Y: 0.3333333432674408 }, { X: 0.9486404657363892, Y: 0.5964912176132202 }, { X: 0.7885196208953857, Y: 0.5964912176132202 } ] }, Id: 3, ParentId: 1, Type: "WORD" } ] }
Amazon Rekognition ImageはConfidence
を返しますが、日本語非対応のため日本語が読み取れていないことが分かります。
Tesseract
TesseractはオープンソースのOCRエンジンです。Tesseractにはgo向けのラッパーである otiai10/gosseract があるのでこちらを利用します。まずは、tesseractのインストールをします。
$ brew install tesseract
デフォルトでは英語データしか用意されていないので、日本語データを追加する必要があります。Githubのリポジトリからjpn.traineddata
をダウンロードし、/usr/local/share/tessdata
に置きます。以下のコマンドを実行し、jpn
が表示されれば日本語データの準備完了です。
$ tesseract --list-langs List of available languages (6): eng jpn
次にgosseractのインストールを行います。
$ go get -t github.com/otiai10/gosseract
実装はgosseractのREADMEを参考にしました。
package main import ( "fmt" "github.com/otiai10/gosseract" ) func main() { // クライアント作成 client := gosseract.NewClient() defer client.Close() // クライアント設定 client.SetLanguage("eng", "jpn") //jpnを指定するにはjpn.traineddataが必要 client.SetImage("sample.jpg") // textのみ取得 text, _ := client.Text() fmt.Printf("%s\n", text) // Level(BLOCK, PARA, TEXTLINE, WORD, SYMBOL)を指定して詳細を取得 boxes, _ := client.GetBoundingBoxes(gosseract.RIL_SYMBOL) for _, box := range boxes { fmt.Printf("%+v\n", box) } }
実行結果
あ い う え お ⑫③④⑤ {Box:(22,19)-(48,36) Word:あ Confidence:99.56348419189453 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(53,22)-(58,32) Word:い Confidence:99.56478881835938 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(62,19)-(76,36) Word:う Confidence:99.54214477539062 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(81,19)-(97,35) Word:え Confidence:99.55460357666016 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(100,19)-(116,35) Word:お Confidence:99.29891204833984 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(263,21)-(282,35) Word:⑫ Confidence:99.04163360595703 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(274,21)-(292,35) Word:③ Confidence:98.84930419921875 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(283,21)-(303,35) Word:④ Confidence:99.01544952392578 BlockNum:0 ParNum:0 LineNum:0 WordNum:0} {Box:(305,21)-(314,35) Word:⑤ Confidence:98.99661254882812 BlockNum:0 ParNum:0 LineNum:0 WordNum:0}
Text
メソッドで文字列のみを取得する他に、GetBoundingBoxes
メソッドで引数に指定したレベルの詳細を取得することもできます。また、今回の例では12
を⑫
として認識していますが、ホワイトリストやブラックリストを指定することで認識文字を制限できます。
client.SetBlacklist("①②③④⑤⑫") text, _ := client.Text() fmt.Printf("%s\n", text) // 実行結果 // あ い う え お 12345
他にもTesseractはフォントを指定して再学習をさせたりと拡張性が高いことが特徴です。
おわりに
この記事ではサンプル画像のみの実行でしたが、私の担当している業務で扱う画像を想定した検証ではGoogle Cloud Vision APIが日本語含め飛び抜けて認識精度が良い結果になりました。
また、Azure以外はgo向けのクライアントライブラリがありgoの人気を改めて実感しました。goでOCRを実行する際の参考にしていただければ幸いです。
明日はApple製品をこよなく愛する@_kemuridamaです!よろしくお願いします!