この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Amazon Recognition のCustom Labelsでオブジェクト検出のモデルを作成する場合、データセットの準備が必要です。 画像に対するアノテーションは、Custom LabelsのUIでも提供さていますが、今回は、これを、microsoft/VoTTで作成してみました。
microsoft/VoTTは、マイクロソフトによって開発されているアプリケーションで、ローカルでアノテーションの定義が可能なツールです。
Custom Labelsでは、Ground Truth の出力からデータセットが生成できますので、VoTT形式のデータをGround Truth 形式に変換する作業になります。
2 VoTT
動作確認のためにVoTTで簡単なアノテーションを定義しました。
この状態で、作業フォルダを確認すると、プロジェクトファイル(tmp0.vott)と各画像に対応したアノテーション定義ファイル(.json)が生成されています。なお、Source CorrectionとTarget Correctonは、同じになっています。
3 VoTT形式
VoTTが生成する、アノテーション情報の入ったJSONファイルは、以下のようになっています。 ここで、変換に必要なのは、対応する画像ファイル名(/tmp0/IMG_9281.jpg)や、アノテーションの座標情報(boundingBox)、ラベル情報(tags)です。
{
"asset": {
"format": "jpg",
"id": "7eb927d9136e5357a2a2e5e3bb745f7f",
"name": "IMG_9281.jpg",
"path": "file:/tmp0/IMG_9281.jpg",
"size": {
"width": 600,
"height": 800
},
"state": 2,
"type": 1
},
"regions": [
{
"id": "SBoWJxOx1",
"type": "RECTANGLE",
"tags": [
"AHIRU"
],
"boundingBox": {
"height": 516.9595926412616,
"width": 336.0018060420315,
"left": 166.73872591943956,
"top": 62.86957950065703
},
"points": [
{
"x": 166.73872591943956,
"y": 62.86957950065703
},
{
"x": 502.7405319614711,
"y": 62.86957950065703
},
{
"x": 502.7405319614711,
"y": 579.8291721419185
},
{
"x": 166.73872591943956,
"y": 579.8291721419185
}
]
}
],
"version": "2.1.0"
}
4 Ground Truth 形式
一方、変換したいGround Truthのファイル形式は、以下のようになっています。変換は、元データから取得したファイル名等を、該当する箇所に埋め込むだけです。
下記の例は、JSONが見やすいように、コロンの後ろに空白や改行や空白が入っていますが、実際に生成するファイルでは、空白や改行があるとエラーとなって読み込めませんので、注意が必要です。
{
"source-ref": "s3://data-set-creater/TMP/IMG_9281.jpg",
"boxlabel": {
"image_size": [
{
"width": 600,
"height": 800,
"depth": 3
}
],
"annotations": [
{
"class_id": 0,
"width": 336.0018060420315,
"top": 62.86957950065703,
"height": 516.9595926412616,
"left": 166.73872591943956
}
]
},
"boxlabel-metadata": {
"job-name": "7eb927d9136e5357a2a2e5e3bb745f7f",
"class-map": {
"0": "AHIRU"
},
"human-annotated": "yes",
"objects": {
"confidence": 1
},
"creation-date": "2020-04-08T14:32:32.252239",
"type": "groundtruth/object-detection"
}
}
変換で生成されたデータは、以下のように出力先フォルダに出力されます。 なお、manifest(output.manifest)ファイルは、各jsonファイルを1行として集めたテキストファイルです。Custon Labelsからは、このmanifest(output.manifest)ファイルが読みこまれます。
5 S3
出力フォルダのファイルのうち、画像ファイルとmanifest(output.manifest)を、S3にアップロードします。各jsonファイルの内容は、先のとおり、manifest(output.manifest)に集められていますので、ここでは必要ありません。
6 Custom Labels
S3に置いたmanifest(output.manifest)を指定してデータセットを作成します。注意書きにもありますように、画像等を置いたS3バケットには、Rekognitionからアクセスするためのパーミッションの追加が必要です。
データセットが作成された様子です。VoTTで 設定したアノテーションが、反映されていることを確認できます。
7 プログラム
変換のためのプログラムは、以下のとおりです。下記のパスを設定して利用できます。
- inputPath 入力フォルダ(VoTTのSource Correction/Target Correcton)
- outputPath 出力フォルダ(変換したファイルの出力先)
- s3Path 画像が置かれるS3のバケット及び、プレフィックス(Rekognitionからデータセットを作成する場合に使用される)
import json
import glob
import os
import shutil
import datetime
# 定義
inputPath = '/tmp/input'
outputPath = '/tmp/output'
manifest = 'output.manifest'
s3Path = 's3://my-dataset/TMP'
# 出力先フォルダ生成
os.makedirs(outputPath, exist_ok=True)
# マニュフェスト出力用
outputText = ''
srcFiles = glob.glob("{}/*.json".format(inputPath))
# class_idが、全データで共通となるラベルの一覧を管理する
labels = []
for srcFile in srcFiles:
basename = os.path.basename(srcFile)
dst_json = {}
with open(srcFile, 'r') as f:
src_json = json.load(f)
asset = src_json["asset"]
path = asset["path"]
id = asset["id"]
name = asset["name"]
size_width = asset["size"]["width"]
size_height = asset["size"]["height"]
dst_json["source-ref"] = "{}/{}".format(s3Path,name)
dst_json["boxlabel"] = {}
dst_json["boxlabel"]["image_size"] = []
dst_json["boxlabel"]["image_size"].append({
"width": size_width,
"height": size_height,
"depth": 3
})
dst_json["boxlabel"]["annotations"]=[]
regions = src_json["regions"]
for i,region in enumerate(regions):
tag = region["tags"][0]
if(labels.count(tag)==0):
labels.append(tag)
cls_id = len(labels)-1
else:
cls_id = labels.index(tag)
boundingBox = region["boundingBox"]
left = boundingBox["left"]
top = boundingBox["top"]
width = boundingBox["width"]
height = boundingBox["height"]
dst_json["boxlabel"]["annotations"].append({
"class_id": cls_id,
"width": int(width),
"top": int(top),
"height": int(height),
"left": int(left)
})
dst_json["boxlabel-metadata"] = {}
dst_json["boxlabel-metadata"]["job-name"] = id
dst_json["boxlabel-metadata"]["class-map"] = {}
for i,label in enumerate(labels):
dst_json["boxlabel-metadata"]["class-map"][i]= label
dst_json["boxlabel-metadata"]["human-annotated"] = "yes"
dst_json["boxlabel-metadata"]["objects"] = [{
"confidence": 1
}]
dt = datetime.datetime.now()
dst_json["boxlabel-metadata"]["creation-date"] = dt.isoformat(timespec='microseconds')
dst_json["boxlabel-metadata"]["type"] = "groundtruth/object-detection"
outFile = "{}/{}.json".format(outputPath, name.split('.')[0])
with open(outFile, 'w') as f:
json.dump(dst_json, f)
#outputText += str(dst_json) + '\n'
outputText += json.dumps(dst_json) + '\n'
outFile = "{}/{}".format(outputPath, name)
shutil.copyfile(path.replace('file:',''), outFile)
with open("{}/{}".format(outputPath, manifest), mode='w') as f:
f.write(outputText)
8 最後に
アノテーション作業は、大量になると結構大変な作業ですが、ローカルで作業できるVoTTは、非常に快適です。 VoTTでCustom Labelsのデータセットが作成できるのは、個人的には非常に嬉しいです。