Elasticsearch イメージ検索プラグイン

Elasticsearch Image Plugin — インストールから検索までの手順

Miyuki Endo
8 min readMar 31, 2014

Elasticsearch Image Plugin (elasticsearch-image) とは

Elasticsearchをベースに直感的な画像検索を行うためのコンテンツベースのプラグインです。
Googleの画像検索と同様に、アップロードした画像に類似した画像を結果として返す機能を実現できます。

インストール

事前にElasticsearchがインストールされていること。
elasticsearchのホームディレクトリに移動し実行。

$ bin/plugin -install com.github.kzwang/elasticsearch-image/1.2.0

インストールされたことを確認する。

$ curl -XGET 'localhost:9200/_nodes?plugin=true&pretty'

“plugins”リスト内に次の情報が返ってくれば問題なし。

{
“name” : “image”,
“version” : “1.2.0",
“description” : “Elasticsearch Image Plugin”,
“jvm” : true,
“site” : false
}

マッピング

作成するインデックスイメージは次の通り。

  • Index: photo
    -> Type: Scenery
$ curl -XPOST 'localhost:9200/photo' -d '{
"mappings":{
"scenery": {
"properties": {
"my_img": {
"type": "image",
"feature": {
"CEDD": {
"hash": "BIT_SAMPLING"
},
"JCD": {
"hash": ["BIT_SAMPLING", "LSH"]
},
"FCTH": {}
},
"metadata": {
"jpeg.image_width": {
"type": "string",
"store": "yes"
},
"jpeg.image_height": {
"type": "string",
"store": "yes"
}
}
},
"file_name": {
"type": "string",
"store": "yes"
}
}
}
}
}'

マッピングを確認する。

$ curl -XGET 'localhost:9200/photo/scenery/_mapping?pretty'

次のように設定した内容が返ってくれば問題ない。

{
“photo” : {
“mappings” : {
“scenery” : {
“properties” : {
“file_name” : {
“type” : “string”,
“store” : true
},
“my_img” : {
“type” : “image”,
“feature” : {
“FCTH” : { },
…(略)

画像フォーマット

javax.imageio でサポートされているJPEG, PNG, GIFなどの標準的なフォーマットが対象だが、imageIOのプラグインによりフォーマットを追加することも可能。

データ登録の事前準備

base64encodeを使ってデータを登録するため、Elasticsearch公式のクライアントelasticsearch-pyを使いpythonでコーディングすることにする。

1. elasticsearch-pyのインストール

$ pip install elasticsearch

2. インデックス・検索対象となる画像を用意 (今回はとりあえず150件用意した)

データ登録

pythonを使用してデータ登録を行う。

$ pythonimport base64
import os
import glob
from elasticsearch import Elasticsearch
es = Elasticsearch()
image_files = glob.glob('…画像が置いてあるDIR…/*.jpg')
for i, f in enumerate(image_files):
file = open(f, 'rt').read()
efile = base64.b64encode(file)
fname = os.path.basename(f)
#base64encode済みのイメージデータとファイル名をセット
d = {"my_img":efile, "file_name":fname}
es.index(index="photo", doc_type="scenery", id=i, body=d)

検索

投入したデータを検索してみる。せっかくなのでいくつかの検索を実行してみることにする。

  • id=0 を指定
$ curl -XGET 'localhost:9200/photo/scenery/0?pretty'
id=0 を指定した検索結果 — 1件 (結果途中まで)
  • file_name を指定
$ curl -XPOST 'localhost:9200/photo/scenery/_search?pretty' -d '{
"query":{
"match":{
"file_name":"...ファイル名..."
}
}
}'
file_name を指定した検索結果 — 複数件 (結果途中まで)

上記クエリでは複数件の結果が返ってきてしまった。matchクエリーはデフォルト”OR”検索となるため、例えば「abc.jpg」と検索すると、Elasticsearchの内部処理で正規化、トークナイズされ「abc」「jpg」の2単語に分割されOR検索がされている様子。(mappingで対象フィールドのanalyze設定を変えている場合はまた違う結果になると思うが。)このような場合に検索側で対応するには”operator”で”and”をきちんと指定する必要がある。

$ curl -XPOST 'localhost:9200/photo/scenery/_search?pretty' -d '{
"query":{
"match":{
"file_name":{
"query":"...ファイル名...",
"operator":"and"
}
}
}
}'
file_name をAND検索した結果 — 1件 (結果途中まで)

正確に指定したファイル名のデータ1件だけが返ってきた。

次に本来の目的であるImageを使用した検索の確認をしてみる。

$ curl -XPOST 'localhost:9200/photo/scenery/_search?pretty=true' -d '{
"query": {
"image": {
"my_img": {
"feature": "CEDD",
"image": "… base64 encoded image to search …",
"hash": "BIT_SAMPLING",
"boost": 2.1,
"limit": 2
}
}
}
}'
画像を検索条件に指定した検索 — limit指定 (結果途中まで)

どの画像データが検索結果として返ってきたのかは画像を1つずつ見てみないと分からないが、なんとなく検索できているよう。

limit=2を指定すると、shard単位で2件ずつなのでTOTAL10件が返ってくる。
shard単位ではなくて全体でn件取得したい場合は、searchのパラメータ「size」を指定すると想定通りのイメージ検索ができそう。

$ curl -XPOST 'localhost:9200/photo/scenery/_search?pretty' -d '{
"size":5,
"query": {
"image": {
"my_img": {
"feature": "CEDD",
"image": "… base64 encoded image to search …",
"hash": "BIT_SAMPLING",
"boost": 2.1
}
}
}
}'
画像を検索条件に指定した検索 — size指定 (結果途中まで)

上記検索では、”hits”→”total”はヒット総件数となっているが、実際に返ってきたデータは5件だけとなった。結果の内容を見たところ、limitによるshard単位の件数制限の結果と比較しても元画像に類似した結果が返ってきているようで使えそうだ。

LICENSE

ライセンスは、Apache License Ver2.0 なので商用利用、修正、配布は許可されている。派生成果物には本ライセンスのコピーを含めること。

--

--