Go言語とGoogle Cloud Vision APIで画像認識

こんにちは!最近、Google Cloud Platformを周りにオススメしている金子です。

先週、Googleが画像認識や分類を行えるCloud Vision APIをベータとしてついに公開したので、今回はCloud Vision APIをGo言語で使用する方法を書かせていただきます。

Cloud Vision API

Cloud Vision APIは画像上のテキスト読み取りや顔の位置検知だけでなく、対象となる物がどのような種類かの検知や人の感情を推測することもできます。

ov-gopher

テキストの検知も多言語に対応しているため、日本語も問題なく認識することができます。

Cloud Vision API — 準備編

Cloud Vision APIを利用するためにVision APIの有効化と、Go言語で実行するためにService accountの作成をする必要があります。この準備は公式ドキュメントに記載があるのと、他にもBrowser Keyを生成してcurl経由にてAPIリクエストする方法も記載してあります。

公式ドキュメント→Getting Started

Vision APIを有効化

Vision APIを使用するためにCloud Developer ConsoleのAPI ManagerからCloud Vision APIを有効化します。

01-vision-search

Google Developer Console

Cloud Developer ConsoleからAPI Managerを開き、Cloud Vision APIを選択してください。


画像の通り、上の方に「Enable」ボタンがあるのでそちらを押して下さい。

02-vision-enable

押すだけでは料金は発生しないので安心してください。料金体系の詳細はこちらをご覧ください。

Credentialsの準備

次に、Cloud Vision APIを認証する準備を行います。Cloud Vision APIへ認証するためにCredentialsページでService accountを作成します。

03-vision-credentials

Credentials Page

「Create Credentials」を押してから「Service account key」を選択してください。


04-vision-serviceaccount

「Service account」をNew service accountを設定し、「Name」には適当に文字を入れてください。「Key type」はJSONにして「Create」を押すと、下記のようなJSONファイルのダウンロードが開始します。

{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "some_number",
"private_key": "-----BEGIN PRIVATE KEY-----\n....
=\n-----END PRIVATE KEY-----\n",
"client_email": "visionapi@project-id.iam.gserviceaccount.com",
"client_id": "...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/visionapi%40project-id.iam.gserviceaccount.com"
}

これを使用してCloud Vision APIのOAuth2認証をします。Goから読み込むためと、各種SDKでも使用できるように環境変数にJSONファイルのパスを設定しておいてください。

例として、~/.credentialsディレクトリにダウンロードしたservice_account.jsonファイルを置き、そのファイルを設定します。

$ mkdir ~/.credentials
$ mv service_account.json ~/.credentials/
$ export GOOGLE_APPLICATION_CREDENTIALS=~/.credentials/service_account.json

ソースコードで文字列として指定するのも良いですが、上記はGoogleのSDKでは基本となるのでその通りに設定しましょう。

<h2>Cloud Vision API — 実装編</h2>

今回作成したGoファイルはGitHubにホスティングしています。

<ul>
<li><a href=”https://github.com/kaneshin/go-cloud-vision-api">kaneshin/go-cloud-vision-api</a></li>
</ul>

<img src=”https://developers.eure.jp/wp-content/uploads/2016/02/project-1.png" alt=”project-1" width=”172" height=”100" class=”aligncenter size-full wp-image-3412" />

また、検証で使用したGoのバージョンは<code>1.5.3</code>と<code>1.6</code>で検証済みです。

<h3>セットアップ: go get</h3>

OAuth2認証をするために、<code>oauth2/google</code>と<code>context</code>パッケージが必要となります。

$ go get -u golang.org/x/net/context
$ go get -u golang.org/x/oauth2/google
$ go get -u google.golang.org/api/vision/...

GitHubにあげたリポジトリにはglide.lockをコミットしているので<code>glide install</code>でも取得可能です。

<h3>使用方法</h3>

リポジトリのものはCLIとして提供しています。main.goを<code>go run</code>で使用することもできますし、<code>go get</code>したのなら<code>go-cloud-vision-api</code>コマンドを使うことが可能です。

$ go-cloud-vision-api lenna.jpg
# go run main.go lenna.jpg
[
{
"faceAnnotations": [
{
"angerLikelihood": "VERY_UNLIKELY",
"blurredLikelihood": "VERY_UNLIKELY",
"boundingPoly": {
"vertices": [
{
"x": 143,
"y": 43
},
{
"x": 245,
"y": 43
},
{
"x": 245,
"y": 163
},
{
"x": 143,
"y": 163
}
]
},
....
"headwearLikelihood": "UNLIKELY",
"joyLikelihood": "VERY_UNLIKELY",
"landmarkingConfidence": 0.5350582,
"landmarks": [
{
"position": {
"x": 197.90556,
"y": 102.932,
"z": 0.00083794753
},
"type": "LEFT_EYE"
},
....
{
"position": {
"x": 220.39294,
"y": 132.60187,
"z": 54.00494
},
"type": "CHIN_RIGHT_GONION"
}
],
"panAngle": 34.809193,
"rollAngle": 5.9516206,
"sorrowLikelihood": "VERY_UNLIKELY",
"surpriseLikelihood": "VERY_UNLIKELY",
"tiltAngle": -10.011973,
"underExposedLikelihood": "VERY_UNLIKELY"
}
]
}
]

レスポンス結果(JSON)をそのまま標準出力に出しています。Credentialsの情報などが間違っている場合はエラーが出力されます。

<h3>Goでの実装</h3>

<code>”google.golang.org/api/vision/v1"</code>のリファレンスはgodoc.orgにホスティングされています。

<ul>
<li><a href=”https://godoc.org/google.golang.org/api/vision/v1">vision — godoc.org</a></li>
</ul>

Go言語側で必要な実装の大枠は5段階なので、その順番に解説をしていきます。

<ul>
<li>OAuth2認証準備</li>
<li><code>vision.Service</code>の生成</li>
<li><code>vision.AnnotateImageRequest</code>の生成</li>
<li><code>Service</code>からRequest送信</li>
<li>Responseの処理</li>
</ul>

これから記述する実装のベースは<a href=”https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go">kaneshin/go-cloud-vision-api/main.go</a>にあります。

※以降出てくるコードは見易いようにpanicを使用などしています。

<h4>OAuth2認証準備</h4>

認証は先ほど作成したService accountのJSONファイルを<code>golang.org/x/oauth2/google</code>パッケージに渡すことによって簡単に認証することができます。

fn := os.Getenv("GOOGLE_APPLICATION_CREDENTIALS")
b, err := ioutil.ReadFile(fn)
if err != nil {
panic(err)
}
config, err := google.JWTConfigFromJSON(b, vision.CloudPlatformScope)
if err != nil {
panic(err)
}
<p align=right>
ref: <a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go#L25-L38">main.go#L25-L38</a>
</p>
<code>JWTConfigFromJSON</code>を使用せず、<code>ConfigFromJSON</code>を使用して認証することも可能ですが、<code>JWTConfigFromJSON</code>を利用する方が簡単です。
<code>ConfigFromJSON</code>を使う例は<a href="https://developers.google.com/google-apps/calendar/quickstart/go">Google Calendar APIのQuick Start</a>の「Step 3: Set up the sample」にサンプルコードがあります。
<h4><code>vision.Service</code>の生成</h4>
先ほど作成した<code>config</code>を用いて、HTTPクライアントを取得します。そのHTTPクライアントを<code>vision</code>のサービスに設定することによって使用することが可能になります。
client := config.Client(context.Background())
srv, err := vision.New(client)
if err != nil {
panic(err)
}
return srv
<p align=right>
ref: <a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go#L39-L45">main.go#L39-L45</a>
</p>
<h4><code>vision.AnnotateImageRequest</code>の生成</h4>
画像をbase64にエンコードする必要があります。今回のサンプルコードでは引数にファイルパスを渡すため、Goコードの方でエンコードします。
encoded, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
content := base64.StdEncoding.EncodeToString(encoded)
このbase64エンコード文字列と、どの検知システムを使うかを設定します。
req := &vision.AnnotateImageRequest{
// Apply image which is encoded by base64.
Image: &vision.Image{
Content: content,
},
// Apply features to indicate what type of image detection.
Features: []*vision.Feature{
{
MaxResults: 10,
Type: "FACE_DETECTION",
},
},
}
return req
<p align=right>
ref: <a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go#L48-L68">main.go#L48-L68</a>
</p>
<code>vision.Feature</code>の<code>Type</code>には下記の値を渡すことができます。
<ul>
<li><code>"TYPE_UNSPECIFIED"</code>: タイプを特定してない、不特定の検知を行う。</li>
<li><code>"FACE_DETECTION"</code>: 顔(複数可)の位置や感情などを検知/推測する。</li>
<li><code>"LANDMARK_DETECTION"</code>: 物体の検知/推測を行う。</li>
<li><code>"LOGO_DETECTION"</code>: 企業のロゴマークを検知する。</li>
<li><code>"LABEL_DETECTION"</code>: ラベルにある多言語の文字を検知する。</li>
<li><code>"TEXT_DETECTION"</code>: OCRで多言語の文字を検知する。</li>
<li><code>"SAFE_SEARCH_DETECTION"</code>: アダルトコンテンツなどの画像を検知する。</li>
<li><code>"IMAGE_PROPERTIES"</code>: 画像が適切(アダルトコンテンツなどではない)であるかを推測する。</li>
</ul>
今回のサンプルでは<code>"FACE_DETECTION"</code>のみをサポートしています。
<h4><code>Service</code>からRequest送信</h4>
<code>Service</code>が持つHTTPクライアントから送信することになりますが、バッチリクエストでまとめて実行します。
// To call multiple image annotation requests.
batch := &vision.BatchAnnotateImagesRequest{
Requests: []*vision.AnnotateImageRequest{req},
}
// Execute the "vision.images.annotate".
res, err := srv.Images.Annotate(batch).Do()
if err != nil {
panic(err)
}
<p align=right>
ref: <a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go#L93-L103">main.go#L93-L103</a>
</p>
<h4>Responseの処理</h4>
Responseの中身にある各アノテーションに画像認識をした情報が入っています。
body, err := json.MarshalIndent(res.Responses, "", "  ")
if err != nil {
panic(err)
}
fmt.Println(string(body))
<p align=right>
ref: <a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/main.go#L105-L111">main.go#L105-L111</a>
</p>
各アノテーションは下記のように定義されています。
type AnnotateImageResponse struct {
FaceAnnotations []*FaceAnnotation `json:"faceAnnotations,omitempty"`
LabelAnnotations []*EntityAnnotation `json:"labelAnnotations,omitempty"`
LandmarkAnnotations []*EntityAnnotation `json:"landmarkAnnotations,omitempty"`
LogoAnnotations []*EntityAnnotation `json:"logoAnnotations,omitempty"`
SafeSearchAnnotation *SafeSearchAnnotation `json:"safeSearchAnnotation,omitempty"`
TextAnnotations []*EntityAnnotation `json:"textAnnotations,omitempty"`
}
<h2>Cloud Vision API - 実行編</h2>
使用する画像は画像処理でお馴染みのレナさんを使用し、その結果もコミットしておきました。
<a href="https://developers.eure.jp/wp-content/uploads/2016/02/lenna.jpg"><img src="https://developers.eure.jp/wp-content/uploads/2016/02/lenna.jpg" alt="lenna" width="400" height="225" class="aligncenter size-full wp-image-3410" /></a>
<code>lenna.jpg</code>という命名の場合
$ go run main.go lenna.jpg
結果は長くなるので、リンクだけ貼っておきます。
<ul>
<li><a href="https://github.com/kaneshin/go-cloud-vision-api/blob/master/result.json">kaneshin/go-cloud-vision-api/result.json</a></li>
</ul>
<h3>画像のベストプラクティス</h3>
<img src="https://developers.eure.jp/wp-content/uploads/2016/02/pkg.png" alt="pkg" width="83" height="120" class="aligncenter size-full wp-image-3417" />
<a href="https://cloud.google.com/vision/docs/image-best-practices">Image Best Practices - Cloud Vision API</a>に書いてあるので、認識させる画像はなるべくそれに準拠させるようにしましょう。
<h4>画像の形式</h4>
Cloud Vision APIがサポートしている画像の形式です。
<ul>
<li>JPEG</li>
<li>PNG8/24</li>
<li>GIF/Animated GIF (first frame only)</li>
<li>BMP</li>
<li>WEBP</li>
<li>RAW</li>
<li>ICO</li>
</ul>
<h4>画像データの入力</h4>
今回のサンプルではbase64だけで説明をしましたが、Cloud Storage経由でも認識が可能です。
<em>Cloud Storage</em>
"image":{
"source": {
"gcs_image_uri":"gs://bucket-name/path_to_image_object"
}
},
<em>base64</em>
"image":{
"content": "base64-encoded data"
},
画像のサイズ
適切に画像を認識させる推奨サイズが存在します。
  • FACE_DETECTION: 1600 x 1200
  • LANDMARK_DETECTION: 640 x 480
  • LOGO_DETECTION: 640 x 480
  • LABEL_DETECTION: 640 x 480
  • TEXT_DETECTION: 1024 x 768
  • SAFE_SEARCH_DETECTION: 640 x 480
使用リミット
リミットも存在しているので、これを超すリクエストは送らないようにしましょう。
  • MB per image: 4 MB
  • MB per request: 8 MB
  • Requests per second: 10
  • Requests per feature per day: 700,000
  • Requests per feature per month: 20,000,000
  • Images per second: 8
  • Images per request: 16
おわりに
今回Cloud Vision APIを使用した理由の一つに、画像を高精度に読み取り・解析することができれば弊社が提供しているPairsサービスの品質向上へと繋げることが可能になります。
実際、APIを使用してみたところ顔認識も精度が高いですし、ラベル検知も使える場面があるので好印象です。
他にも、このAPIを使用したWebサービスも思いついたので時間があるときにでもサービスを作ろうかなと思っています。
まずは自分で撮影した大量の画像を分析して自動で分類したいものです。