Google Cloud Vision APIでPDFにOCRをかけてみた

給与明細にOCRをかけて自分の給与を記録したい

会社から発行される給与明細はPDFファイル。しかもフォント埋め込みで埋め込まれてるフォントが有償?恐らく通常手に入らないRICOHのフォント。 データを抜き出せないかとGoogleDriveでPDFからGoogleDocumentsに変換してみたり、各種ツールでwordとかテキストに変換してみようとしたが一切うまくいかない。 そもそもコピペすらできない(コピペしようとしてもフォントが無いから?????になる)

ということでOCRをかけてみた。

OCRの機能差

調べてみたところ、AzureやAWSにもOCRを使う機能はある。 しかし認識率についてはGoogleがトップであるようだったので、GCPを利用してみることにする。(普段AWSばかり触っているのでたまにはGCPも触ってみようと思った、というのも理由)

Google Cloud Vision API (Go言語)

ということでGo言語でGoogle Cloud Vision APIを利用してみた。 と言ってもほぼサンプルのままで動作する。

事前準備

先にGoogle Cloud Storageに対象となるpdfファイルを置いておく必要がある。 またバケットに対して読み取り、書き込みの権限も付与してある必要がある。

なお今回はローカルで動作させるのでサービスアカウントのキーを作成しておく。

参考: https://cloud.google.com/vision/docs/quickstart-client-libraries?hl=ja

事前に環境変数として発行したキーをexportしておく。

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/key.json"

ソースコード

ほぼサンプルのまま。 結果は dst_uri := "gs://path/to/result.json" にjsonとして保存される。

// Sample vision-quickstart uses the Google Cloud Vision API to label an image.
package main

import (
	"context"
	"fmt"
	"os"
    "io"

	vision "cloud.google.com/go/vision/apiv1"
    visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1"
)

// detectAsyncDocument performs Optical Character Recognition (OCR) on a
// PDF file stored in GCS.
func detectAsyncDocumentURI(w io.Writer, gcsSourceURI, gcsDestinationURI string) error {
	ctx := context.Background()

	client, err := vision.NewImageAnnotatorClient(ctx)
	if err != nil {
		return err
	}

	request := &visionpb.AsyncBatchAnnotateFilesRequest{
		Requests: []*visionpb.AsyncAnnotateFileRequest{
			{
				Features: []*visionpb.Feature{
					{
						Type: visionpb.Feature_DOCUMENT_TEXT_DETECTION,
					},
				},
				InputConfig: &visionpb.InputConfig{
					GcsSource: &visionpb.GcsSource{Uri: gcsSourceURI},
					// Supported MimeTypes are: "application/pdf" and "image/tiff".
					MimeType: "application/pdf",
				},
				OutputConfig: &visionpb.OutputConfig{
					GcsDestination: &visionpb.GcsDestination{Uri: gcsDestinationURI},
					// How many pages should be grouped into each json output file.
					BatchSize: 2,
				},
			},
		},
	}

	operation, err := client.AsyncBatchAnnotateFiles(ctx, request)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "Waiting for the operation to finish.")

	resp, err := operation.Wait(ctx)
	if err != nil {
		return err
	}

	fmt.Fprintf(w, "%v", resp)

	return nil
}

func main() {

	var w io.Writer
	w = os.Stdout

	src_uri := "gs://path/to/pdf/test.pdf"
	dst_uri := "gs://path/to/result.json"

	detectAsyncDocumentURI(w, src_uri, dst_uri)
}

敗北

OCRをかけたは良いが、給与明細が表になっているために罫線などが邪魔をして結局うまく読み込めなかった… 手入力するか…