Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Azure Cognitive SearchとComputer Vision APIを使って類似の画像を検索してみる

こんにちは、@shin0higuchiです😊

今回のテーマは、「Azure Cognitive Searchでの画像検索」ということで、
写真から似たものを探す類似検索
の仕組みを作ってみます。
背景として、アクロクエストではElasticsearchを使い込んでおり、テキスト検索を扱う機会は多いのですが、
近頃は画像による検索ニーズも高まってきており、手早く実現するにはどうするのが良いかを知りたいという課題感がありました。

どういったユースケースでニーズがあるかというと、例えば買い物時の商品検索です。
買い物をする時、「画像で類似検索ができたら」と思うことがあります。
棚に並んでいるものの名前がわからなくても、写真で探せたらとても便利ですよね?

今回は、Azure と 簡単なPythonコードで実装してみました。
大まかに下記の流れで進めます。

1. Blobストレージアカウント/コンテナを作成し、画像をアップロードする
2. Cognitive Searchのインデックスを作成し、Blobコンテナのデータを取り込む
3. Computer Vision API を準備する
4. 各APIを叩くための簡単なPython スクリプトを書く

前提:Azure Cognitive Search とは

Azureであらゆるデータを取り込み、大規模検索をするためのクラウドサービスです。
AI機能も組み込まれており、複雑な検索を簡単に実現できることが売りとなっています。
下図のように、Azure上の様々なデータソースからドキュメントを生成し、検索できるようにしてくれます。
構造化されていないデータや、PDFなどの文書まで簡単に検索できるのは嬉しいですね。

https://docs.microsoft.com/ja-jp/azure/search/media/cognitive-search-intro/cogsearch-architecture.png
azure.microsoft.com

1. Blobストレージアカウント/コンテナを作成し、画像をアップロードする

Blobコンテナの作成

Azure Portal からストレージアカウントを作成し、Blobコンテナを作成します。
ここでは、"images" という名前のコンテナを作成しています。
f:id:acro-engineer:20201108230909p:plain:w500


今回はサンプルとして、次の画像18枚をアップロードしてみます。(画像は全て PNG 画像となっています)
ホームセンターで探し物が見つからず歩き回ったことがあったので、そのイメージです。
(その時は所定の場所ではなく、入口前の特別コーナーにあったというオチでした)
f:id:acro-engineer:20201108224313p:plain:w500

f:id:acro-engineer:20201108230543p:plain:w500

2. Cognitive Searchのインデックスを作成し、Blobコンテナのデータを取り込む

ここからが、Cognitive Search の準備になります。
Cognitive Search はLuceneベースの検索エンジンとなっており、
「インデックス」と呼ばれるデータ構造で取り込んだデータを保持し、検索する形です。

3インデックスまでは無料プランで利用できるようなので、今回はそちらで試すことにします。

あとは、「データのインポート」のメニューから取り込み時の設定を行います。

データソース (Blobコンテナ) を選び...
f:id:acro-engineer:20201115024459p:plain:w800
エンリッチメントの追加を選択します。
f:id:acro-engineer:20201115024617p:plain:w800

するとOCR を有効にし、すべてのテキストを merged_content フィールドにマージする」といったチェックボックスや、
「画像の認知技術」というチェックボックスが表示されるので選択します。
ここで、内部的にはCognitive ServeicesのComputer Vision APIが利用されています。
f:id:acro-engineer:20201115024737p:plain:w400


最後に、それぞれのフィールドに対するAnalyzerを選択するとインデックスが作成されます。
なお、日本語Analyzerには「Lucene」と「Microsoft」の2種類がありましたが、その辺りの確認はしていません。今回は本題ではないので割愛します。
f:id:acro-engineer:20201115025206p:plain:w800



自動的に検索用のエンドポイントも作成されるので、実際にインデクシングされたドキュメントを確認してみます。
たとえばサンダルの画像は、次のようなJSONになっています。 OCRを有効化したので画像中の文字列を抽出してくれているほか、imageTagsを良い塩梅に振ってくれています。(imageCaption はイマイチ。物が単体で映っている画像には不適?)
画像中にある "HAMMER" という文字列を、OCRチェックボックスを選んでおくだけで抽出してくれるのは凄いですね。
f:id:acro-engineer:20201108235910j:plain

{
            "@search.score": 1,
            "content": "\n",
            "metadata_storage_content_md5": "v90y4cHYAZDVfbz0smxplw==",
            "metadata_storage_name": "16.jpg",
            "metadata_storage_path": "aHR0cHM6Ly9zYW1wbGUyMDIwMTEuYmxvYi5jb3JlLndpbmRvd3MubmV0L2ltYWdlcy8xNi5qcGc1",
            "merged_content": " HAMMER \n",
            "text": [
                "HAMMER"
            ],
            "layoutText": [
                "{\"language\":\"en\",\"text\":\"HAMMER\",\"lines\":[{\"boundingBox\":[{\"x\":67,\"y\":102},{\"x\":103,\"y\":91},{\"x\":106,\"y\":102},{\"x\":70,\"y\":112}],\"text\":\"HAMMER\"}],\"words\":[{\"boundingBox\":[{\"x\":68,\"y\":103},{\"x\":103,\"y\":92},{\"x\":107,\"y\":102},{\"x\":70,\"y\":112}],\"text\":\"HAMMER\"}]}"
            ],
            "imageTags": [
                "black",
                "shoe",
                "footwear",
                "sneaker"
            ],
            "imageCaption": [
                "{\"tags\":[\"table\",\"sitting\",\"keyboard\",\"computer\",\"desk\",\"phone\",\"mouse\",\"laying\",\"cat\"],\"captions\":[{\"text\":\"a close up of a keyboard\",\"confidence\":0.64379424287271192}]}"
            ],
            "imageCelebrities": []
        }

3. Computer Vision API を準備する

これで画像検索の準備はできました。
ただし、検索APIが受け付けるクエリは文字列形式であるという問題があります。
今回は、Azure Cognitive Serviceの一つであるComputer Vision API を用いて、検索したい画像のタグやキャプションを取得し、それをCognitive Searchに投げる作戦で行きます。

さて、Computer Vision APIについても、Azure Portalから作成しました。
特に難しい設定もないので楽チンです。
気になる料金ですが、無料のプランでもそれなりに遊んでみることができそうです(執筆時点では、"20 Calls per minute, 5K Calls per month" だそうです)。

f:id:acro-engineer:20201109000917p:plain

4. 各APIを叩くための簡単なPython スクリプトを書く

今回はGoogle Colaboratory上で動かしてみました(Azure じゃないんかい!という突っ込みが聞こえてきそうですが、特にGoogleにした理由はありません。好みの問題です。)
各サービスを呼び出すサンプルコードは、公式のドキュメントに載っているのでそちらを参考にするのが良いと思います。

4-1 ライブラリのインストール

Cognitive Search および Blob Storageを扱うためのライブラリをインストールします。

!pip install azure-search-documents
!pip install azure-storage-blob

4-2 Computer Vision API による解析

ここでは検索したい軍手の画像 (sample.jpg) を Computer Vision APIに食わせて、タグとキャプションを取得します。

import os
import requests
import sys
%matplotlib inline
import matplotlib.pyplot as plt
from io import BytesIO
from PIL import Image

# colaboratory上での画像パスは /content/sample.jpg
local_path = "/content/"
local_file_name = "sample.jpg"
image_path = os.path.join(local_path, local_file_name)
image_data = open(image_path, "rb").read()
image = Image.open(BytesIO(image_data))
plt.imshow(image)

sample.jpg(軍手の画像)が表示されることを確認し...
f:id:acro-engineer:20201115030225p:plain


Computer Vision APIを呼び出してsample.jpgの解析結果を受け取ります。

# Compouter Vision APIを呼び出し、画像解析結果(タグやキャプション)を得る
subscription_key = "<Computer Vision APIのキー (ポータルで確認する)>"
endpoint = "https://sample202011.cognitiveservices.azure.com/"
analyze_url = endpoint + "vision/v3.1/analyze"
headers = {'Ocp-Apim-Subscription-Key': subscription_key, 'Content-Type': 'application/octet-stream'}
params = {'visualFeatures': 'Categories,Description'}  # 今回はCategories使わないですが、使いたい場合もあると思うので。
response = requests.post(analyze_url, headers=headers, params=params, data=image_data)

# 結果をパース
analysis = response.json()
tags = analysis['description']['tags']
image_caption = analysis["description"]["captions"][0]["text"].capitalize()

# 解析結果の表示
print("tags: {}".format(tags))
print("caption: {}".format(image_caption))

結果が取れました。タグは 'handwear' で良い感じですね。キャプションは。。。うん、ユーモラスですね。嫌いじゃないです(違う)。
f:id:acro-engineer:20201115030343p:plain

4-3 Cognitive Search での検索

いよいよCognitive Searchで検索結果を取得してみます。
ここでは先ほどの画像解析で得られた 'handware' で検索をかけて見ましょう。

from azure.core.credentials import AzureKeyCredential
from azure.search.documents import SearchClient

# cognitive search の検索クライアントを定義
index_name = "azureblob-index";
endpoint = 'https://cognitive-search-sample.search.windows.net/'
key = '<Cognitive Searchのアクセスキー>'
credential = AzureKeyCredential(key)
client = SearchClient(endpoint=endpoint,
                      index_name=index_name,
                      credential=credential)

# 検索結果の上位5件のファイル名を取得する
results = client.search(search_text=tags, top=5) # 今回はタグ (handware)で検索

# 検索結果をBlobから取得するための設定
container_name = "images"
connect_str = "<コンテナの接続文字列>"
blob_service_client = BlobServiceClient.from_connection_string(connect_str)

# 検索結果のファイル名を使って、Blobから画像を取得して表示する
for result in results:
    blob_client = blob_service_client.get_blob_client(container=container_name, blob=result['metadata_storage_name'])
    image = Image.open(BytesIO(blob_client.download_blob().readall()))
    plt.imshow(image)
    print(result['metadata_storage_name'])
    plt.axis("off")
    plt.show()

f:id:acro-engineer:20201109002803p:plain

しっかり軍手が取得できていますね。
めでたしめでたし。

まとめ

  • Azure Cognitive Searchを用いて、類似した画像の検索を手軽に実装することができた。(サンプル探しも含めて2,30分くらい)
  • 実際にスマホで写真を撮るなら、背景に他の物が写るケースが多いと思うので、対応できるか要確認です
  • 今回はimageTagsを使いましたが、HAMMERのように画像中の文字列で検索するのも面白いかもしれません
  • 細かいスコアリングや、Analyzerの設定にはフォーカスしなかったが、もっと突き詰めると良い検索が実現できそう
  • ドキュメントを読み切れていないので、もっと良い使い方などはありそうです。お気づきの点があったら是非教えてください


以上です。お読みいただきありがとうございました。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

【データ分析】
Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のデータサイエンティストの求人 - Wantedlywww.wantedly.com