iMind Developers Blog

iMind開発者ブログ

Open Image Dataset V5を使ってみる

概要

Open Image Dataset V5をダウンロードして中身を確認する。

BoxやSegmentationの情報をplotしてみる。

Open Image Dataset V5とは

Googleが公開しているアノテーション付きの画像データ

  • 600カテゴリ、1585万のボックス
  • 350カテゴリ、278万のセグメンテーション
  • 2万弱のカテゴリ、3646万の画像単位のラベル

などかなりの量のデータが入っている。

アノテーションデータのダウンロード

下記のページからダウンロードできる。

https://storage.googleapis.com/openimages/web/download.html

とりあえずアノテーションやメタデータだけダウンロードしてみる。

# box
wget https://storage.googleapis.com/openimages/2018_04/train/train-annotations-bbox.csv
wget https://storage.googleapis.com/openimages/v5/validation-annotations-bbox.csv
wget https://storage.googleapis.com/openimages/v5/test-annotations-bbox.csv

# segmentation
wget https://storage.googleapis.com/openimages/v5/train-annotations-object-segmentation.csv
wget https://storage.googleapis.com/openimages/v5/validation-annotations-object-segmentation.csv
wget https://storage.googleapis.com/openimages/v5/test-annotations-object-segmentation.csv

# relationship
wget https://storage.googleapis.com/openimages/2019_01/train/challenge-2018-train-vrd.csv
wget https://storage.googleapis.com/openimages/v5/validation-annotations-vrd.csv
wget https://storage.googleapis.com/openimages/v5/test-annotations-vrd.csv

# image label
wget https://storage.googleapis.com/openimages/v5/train-annotations-human-imagelabels-boxable.csv
wget https://storage.googleapis.com/openimages/v5/validation-annotations-human-imagelabels-boxable.csv
wget https://storage.googleapis.com/openimages/v5/test-annotations-human-imagelabels-boxable.csv

# image id
wget https://storage.googleapis.com/openimages/2018_04/train/train-images-boxable-with-rotation.csv
wget https://storage.googleapis.com/openimages/2018_04/validation/validation-images-with-rotation.csv
wget https://storage.googleapis.com/openimages/2018_04/test/test-images-with-rotation.csv

# metadata
mkdir metadata
cd metadata
wget https://storage.googleapis.com/openimages/v5/class-descriptions-boxable.csv
wget https://storage.googleapis.com/openimages/2019_01/challenge-2018-relationships-description.csv
wget https://storage.googleapis.com/openimages/2019_01/challenge-2018-attributes-description.csv
wget https://storage.googleapis.com/openimages/2019_01/challenge-2018-relationship-triplets.csv
wget https://storage.googleapis.com/openimages/v5/classes-segmentation.txt
cd ../

続いてsegmentationの0〜Fまでのファイルをダウンロード。zipになっているのでsegmentationsというディレクトリを作ってそこに展開してみる。zipの中にはsegmentationした物体のpng画像が入っている。

# segmentations
mkdir segmentations
cd segmentations
mkdir train
mkdir validation
mkdir test
for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f
do
    unzip train-masks-$i.zip -d train/
    unzip validation-masks-$i.zip -d validation/
    unzip test-masks-$i.zip -d test/
done

画像データのダウンロード

画像のダウンロードはawscliを用いる。Pythonを使っていればpipからインストールできる。

pip install awscli

awscliが入ったら下記URLを参考にしながらダウンロードする。500GBを超えるなかなかの大物。

https://github.com/cvdfoundation/open-images-dataset#download-images-with-bounding-boxes-annotations

class-descriptions-boxable.csv

ダウンロードしたmetadataの中を確認してみる。

まずはclass-descriptions-boxable.csv。Boxでアノテートしてるクラスの一覧が入っている。

$ head metadata/class-descriptions-boxable.csv 

/m/011k07,Tortoise
/m/011q46kg,Container
/m/012074,Magpie
/m/0120dh,Sea turtle
/m/01226z,Football
/m/012n7d,Ambulance
/m/012w5l,Ladder
/m/012xff,Toothbrush
/m/012ysf,Syringe
/m/0130jx,Sink

csvで1列目がラベルのid、2列目がラベルの名称。

/m/011k07はTortoise(亀)らしい。

annotations-bbox.csvの中をgrepして実際に亀であることを確認してみる。

$ grep /m/011k07 test-annotations-bbox.csv | head -3

023b4802f26b54e6,xclick,/m/011k07,1,0.25,0.7647059,0.124168515,0.9179601,0,0,0,0,0
05373b7f9363f1b5,xclick,/m/011k07,1,0.29056048,0.77138644,0.28318584,0.73893803,0,0,0,0,0
061bad48d5f83750,xclick,/m/011k07,1,0.015086207,1,0.36611196,0.881997,0,0,0,0,0

1番目のカラムが画像のID。023b4802f26b54e6.jpg がファイル名になるので、中身を見てみる。

f:id:imind:20190616004122j:plain

うん、確かに亀だ。

classes-segmentation.txt

classes-segmentation.txtにはセグメンテーションのラベルの一覧が入っている。

$ head metadata/classes-segmentation.txt

/m/01_5g
/m/0c06p
/m/01lsmm
/m/01bqk0
/m/0l14j_
/m/0342h
/m/09j2d
/m/076bq
/m/01xqw
/m/01599

こちらは見ての通りラベルのIDだけ。名称はBoxと共通。

例えば1番上に表示されたID、/m/01_5gをBoxの方の一覧でgrepすれば名称が出てくる。

$ grep /m/01_5g metadata/class-descriptions-boxable.csv 

/m/01_5g,Chopsticks

challenge-2018-attributes-description.csv

challenge-2018-attributes-description.csv には下記のような5レコードが入っている。

$ cat metadata/challenge-2018-attributes-description.csv 

/m/02gy9n,Transparent
/m/05z87,Plastic
/m/0dnr7,(made of)Textile
/m/04lbp,(made of)Leather
/m/083vt,Wooden

プラスチック(Plastic)、織物(Textile)、革(Leather)など素材に関する情報が入っているようだ。

challenge-2018-train-vrd.csv の中に当該IDが振られた情報が入っている。

/m/02gy9n(Transparent)のデータを確認してみる。

$ grep /m/02gy9n challenge-2018-train-vrd.csv | head -2

82d16a22f703df5c,/m/04dr76w,/m/02gy9n,0.612,0.735,0.4189189,0.9504505,0.612,0.735,0.4189189,0.9504505,is
7da79ddcea564138,/m/04dr76w,/m/02gy9n,0.416875,0.468125,0,0.2532833,0.416875,0.468125,0,0.2532833,is

実際の画像を確認してみる。

f:id:imind:20190616014608j:plain f:id:imind:20190616014722j:plain

確かに瓶などの透明(Transparent)な物体が写っている。

challenge-2018-relationships-description.csv

challenge-2018-relationships-description.csv には下記のようなデータが入っている。

$ cat metadata/challenge-2018-relationships-description.csv

at,at
on,on (top of)
holds,holds
plays,plays
interacts_with,interacts with
wears,wears
is,is
inside_of,inside of
under,under
hits,hits

わかりやすそうなところで wears が振られている画像を見てみる。

$ grep wear challenge-2018-train-vrd.csv | head -2

23d725e3f329db33,/m/03bt1vf,/m/080hkjn,0.534375,0.875625,0.11333334,0.9825,0.49125,0.81375,0.31916666,0.9875,wears
6e61958358f360fe,/m/03bt1vf,/m/080hkjn,0.52086556,0.7658424,0.34774256,0.9990394,0.57341576,0.74806803,0.6676273,0.9990394,wears

f:id:imind:20190616015922j:plain

うん、確かにwearsだ。

Boxの矩形を表示

annotations-bbox.csvのレコードに書いてある座標から矩形を表示する。

2枚ほど犬(/m/0bt9lr)の画像に対して矩形を出してみる。

$ grep "/m/0bt9lr" train-annotations-bbox.csv | head -2

0000b9fcba019d36,xclick,/m/0bt9lr,1,0.165000,0.903750,0.268333,0.998333,1,1,0,0,0
0000cb13febe0138,xclick,/m/0bt9lr,1,0.000000,0.651875,0.000000,0.999062,1,1,0,0,0

カラムのヘッダはこんな内容。

ImageID,Source,LabelName,Confidence,XMin,XMax,YMin,YMax,IsOccluded,IsTruncated,IsGroupOf,IsDepiction,IsInside

ImageIDから画像のファイル名、XMin, XMax, YMin, YMaxで矩形が取れる。

# 犬の上2つの画像を読み込む
df = pd.read_csv('train-annotations-bbox.csv', nrows=10000)
df = df[df.LabelName == '/m/0bt9lr'].head(2)

f, ax_list = plt.subplots(1, 2, figsize=(12, 6))
for idx, (df_id, row) in enumerate(df.iterrows()):
    # 画像の読込み
    path = 'images/train/%s.jpg' % row.ImageID
    img = cv2.imread(path)
    # 座標の取得
    y, x = img.shape[:2]
    x_min = int(x * row.XMin)
    x_max = int(x * row.XMax)
    y_min = int(y * row.YMin)
    y_max = int(y * row.YMax)
    # 矩形の表示
    cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color=(0, 0, 255), thickness=5)
    # plot
    ax_list[idx].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

出力結果

f:id:imind:20190618020056p:plain

Segmentの抽出

segmentationのデータも確認してみる。

$ grep "/m/0bt9lr" train-annotations-object-segmentation.csv | head -2

ヘッダの情報も合わせて結果を表示するとこんな感じ。

MaskPath,ImageID,LabelName,BoxID,BoxXMin,BoxXMax,BoxYMin,BoxYMax,PredictedIoU,Clicks
871991d5e1d800a5_m0bt9lr_9f191f9e.png,871991d5e1d800a5,/m/0bt9lr,9f191f9e,0.213832,0.543402,0.318519,0.998942,0.83814,0.43900 0.98096 1;0.34233 0.98247 1;0.52251 0.72368 1;0.38990 0.33436 1;0.36837 0.98628 0;0.51072 0.55970 1;0.48918 0.91509 0;0.31680 0.99227 1;0.37546 0.95996 0
0abe18d0e7f90ff7_m0bt9lr_ee783a59.png,0abe18d0e7f90ff7,/m/0bt9lr,ee783a59,0.585625,0.868125,0.109765,0.616957,0.70112,0.77855 0.24412 1;0.60155 0.35340 1;0.67869 0.61283 1;0.83916 0.24329 1;0.75244 0.36364 1;0.60633 0.40386 1;0.78011 0.22287 0;0.74185 0.31033 0;0.64125 0.36710 1;0.67212 0.58310 1

ImageIDから画像名がわかり、MaskPathにセグメンテーション画像のパスが入っている。

確認の為、画像を2つほど加工せずに表示してみる。

# 適当な犬画像を2つほど取得
df = pd.read_csv('train-annotations-object-segmentation.csv', nrows=10000)
df = df[df.LabelName == '/m/0bt9lr'].head(10).tail(2)
df.head()

# 上下に並べて元画像とセグメンテーション画像を表示
f, ax_list = plt.subplots(2, 2, figsize=(8, 10))
for idx, (df_id, row) in enumerate(df.iterrows()):
    # 画像の読込み
    img_path = 'images/train/%s.jpg' % row.ImageID
    img = cv2.imread(img_path)
    # セグメンテーション画像の読込み
    seg_path = 'segmentations/train/%s' % row.MaskPath
    mask = cv2.imread(seg_path)
    print(img.shape, mask.shape)
    # 画像の表示
    ax_list[0][idx].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    ax_list[1][idx].imshow(cv2.cvtColor(mask, cv2.COLOR_BGR2RGB))

f:id:imind:20190618110541p:plain

画像のshapeを標準出力しているが、その結果は下記のようになる。左が元画像、右がマスク画像。

(1024, 768, 3) (1600, 1200, 3)
(683, 1024, 3) (800, 1200, 3)

どうやら元画像とマスク画像でshapeが違うらしい。

元画像とサイズを合わせて重ねて表示してみる。

f, ax_list = plt.subplots(1, 2, figsize=(12, 6))
for idx, (df_id, row) in enumerate(df.iterrows()):
    # 画像の読込み
    img_path = 'images/train/%s.jpg' % row.ImageID
    seg_path = 'segmentations/train/%s' % row.MaskPath
    img = cv2.imread(img_path)
    mask = cv2.imread(seg_path)
    # maskを元画像のサイズにリサイズ
    y, x = img.shape[:2]
    mask = cv2.resize(mask, (x, y))
    print(img.shape, mask.shape)
    # 重ねて表示
    dst = cv2.addWeighted(img, 0.5, mask, 0.5,gamma=1.0)
    ax_list[idx].imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB))

実行結果。かなりキレイに切り取ってありますね。

f:id:imind:20190618112302p:plain

昔、学習用データを作る為に延々とlabelmeで延々と切り抜きをしたことがあるけど、とても辛かった。これだけのデータを作った方々には頭が下がる。

Boxの情報とClicks(このポイントはセグメント内だよ)の情報もあるので一緒に表示してみる。

Clicksは下記のようにセミコロン区切りになっていて、X座標、Y座標、T(そのポイントがセグメント内なら1、違う場合は0)が入っている。

0.43900 0.98096 1; 0.34233 0.98247 1; 0.52251 0.72368 1;

これらの情報を画像に表示してみる。

df = pd.read_csv('train-annotations-object-segmentation.csv', nrows=10000)
df = df[df.LabelName == '/m/0bt9lr'].head(10).tail(2)
df.head()

f, ax_list = plt.subplots(1, 2, figsize=(12, 12))
for idx, (df_id, row) in enumerate(df.iterrows()):
    # 画像の読込み
    img_path = 'images/train/%s.jpg' % row.ImageID
    img = cv2.imread(img_path)
    # 矩形の表示
    y_size, x_size = img.shape[:2]
    x_min = int(x_size * row.BoxXMin)
    x_max = int(x_size * row.BoxXMax)
    y_min = int(y_size * row.BoxYMin)
    y_max = int(y_size * row.BoxYMax)
    cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color=(128, 128, 255), thickness=5)
    # clickのT=1の位置に赤circleを, 0に青circleを表示
    clicks = row.Clicks.split(';')
    for click in clicks:
        x, y, t = click.split(' ')
        x = int(x_size * float(x))
        y = int(y_size * float(y))
        color = (0, 0, 255) if int(t) == 1 else (255, 0, 0)
        cv2.circle(img, (x, y), radius=20, color=color, thickness=-1)
    # 重ねて表示
    ax_list[idx].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))

f:id:imind:20190618114334p:plain

改定履歴

Author: Masato Watanabe, Date: 2019-06-18, 記事投稿