TensorFlow 2 による物体検出」で、アノテーション作成ツール「VoTT」を使用して、動画から教師データや検証用データとして使用するアノテーションデータを作成しました。今回は出力されたアノテーションデータについて、詳しく調べます。

【実行環境】

  • tensorflow 2.6.0
  • tensorflow-addons 0.14.0
  • tensorflow-datasets 4.4.0
  • tensorflow-estimator 2.6.0
  • tensorflow-gpu 2.6.0
  • tensorflow-hub 0.12.0
  • tensorflow-metadata 1.2.0
  • tensorflow-model-optimization 0.6.0
  • tensorflow-text 2.6.0

TFRecordファイルの可視化

アノテーション作成ツール「VoTT」で、フォルダ「TensorTest-TFRecords-export」にエクスポートしたTFRecordファイルの構造について、次のスクリプト「TFRecordToImg.py」により可視化します。ファイル「signalmp4.tfrecord」は、アノテーション作成ツール「VoTT」でエクスポートしたTFRecordファイルです。詳細については「TFRecords と tf.Example の使用法」の「TFRecord ファイルの読み込み」を参照してください。

TFRecordToImg.py

1
2
3
4
5
6
7
8
9
10
11
12
import tensorflow as tf
import numpy as np
import IPython.display as display
 
loaded_ds_train = tf.data.TFRecordDataset("signalmp4.tfrecord")
print(loaded_ds_train)
 
for raw_record in loaded_ds_train.take(1):
#    print(repr(raw_record))
    example = tf.train.Example()
    example.ParseFromString(raw_record.numpy())
    print(example)

スクリプト「TFRecordToImg.py」を実行した結果を次に示します。

  • 9行目の「image/encoded」で、 画像のバイナリデータが定義されます。
  • 49行目の「image/object/bbox/xmax」,58行目の「image/object/bbox/xmin」,67行目の「image/object/bbox/ymax」,76行目の「image/object/bbox/ymin」で、 アノテーションした座標位置が定義されます。アノテーションした数だけ値が含みます。
  • 85行目の「class/label で、タグ名に付与された番号「0」「1」が定義されます。
  • 94行目の「class/text」で、タグの名前「signalA_red」「signalB_green」が定義されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
2021-10-08 08:18:33.422875: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX AVX2
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-10-08 08:18:34.861523: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1510] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 1335 MB memory:  -> device: 0, name: NVIDIA GeForce GT 1030, pci bus id: 0000:01:00.0, compute capability: 6.1
<TFRecordDatasetV2 shapes: (), types: tf.string>
2021-10-08 08:18:34.966740: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:185] None of the MLIR Optimization Passes are enabled (registered 2)
features {
  feature {
    key: "image/encoded"
    value {
      bytes_list {
        value: "\377\330\37 ・・・・・(シリアライズされた画像データ)
      }
    }
  }
  feature {
    key: "image/filename"
    value {
      bytes_list {
        value: "signal.mp4#t=0.2.jpg"
      }
    }
  }
  feature {
    key: "image/format"
    value {
      bytes_list {
        value: "jpg"
      }
    }
  }
  feature {
    key: "image/height"
    value {
      int64_list {
        value: 480
      }
    }
  }
  feature {
    key: "image/key/sha256"
    value {
      bytes_list {
        value: "TqXFCKZWbnYkBUP4/rBv1Fd3e+OVScQBZDav2mXSMw4="
      }
    }
  }
  feature {
    key: "image/object/bbox/xmax"
    value {
      float_list {
        value: 0.6279257535934448
        value: 0.4789402186870575
      }
    }
  }
  feature {
    key: "image/object/bbox/xmin"
    value {
      float_list {
        value: 0.48531702160835266
        value: 0.37633150815963745
      }
    }
  }
  feature {
    key: "image/object/bbox/ymax"
    value {
      float_list {
        value: 0.3261205554008484
        value: 0.645285964012146
      }
    }
  }
  feature {
    key: "image/object/bbox/ymin"
    value {
      float_list {
        value: 0.2047913372516632
        value: 0.515455961227417
      }
    }
  }
  feature {
    key: "image/object/class/label"
    value {
      int64_list {
        value: 0
        value: 1
      }
    }
  }
  feature {
    key: "image/object/class/text"
    value {
      bytes_list {
        value: "signalA_red"
        value: "signalB_green"
      }
    }
  }
  feature {
    key: "image/object/difficult"
    value {
      int64_list {
        value: 0
        value: 0
      }
    }
  }
  feature {
    key: "image/object/truncated"
    value {
      int64_list {
        value: 0
        value: 0
      }
    }
  }
  feature {
    key: "image/object/view"
    value {
      bytes_list {
        value: "Unspecified"
        value: "Unspecified"
      }
    }
  }
  feature {
    key: "image/source_id"
    value {
      bytes_list {
        value: "signal.mp4#t=0.2.jpg"
      }
    }
  }
  feature {
    key: "image/width"
    value {
      int64_list {
        value: 640
      }
    }
  }
}
 
Process finished with exit code 0

アノテーション画像の取得

アノテーション作成ツール「VoTT」で、フォルダ「Target」に出力されたjsonファイルを用いて、入力した動画ファイルから作成したアノテーション画像を、次のスクリプト「cutimg.py」により取得します。

cutimg.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import json
import os
import fnmatch
import cv2
 
JSON_DIR = 'vott-json/'
IMG_DIR = 'vott-json/'
CUT_IMAGE = 'cut_images/'
IMAGE_FORMAT = '.jpg'
 
 
class Check():
 
    def filepath_checker(self, dir):
 
        if not (os.path.exists(dir)):
            print('No such directory > ' + dir)
            exit()
 
    def directory_init(self, dir):
 
        if not (os.path.exists(dir)):
            os.makedirs(dir, exist_ok=True)
 
 
def save_frame_sec(video_path, sec, result_path):
    cap = cv2.VideoCapture(video_path)
 
    if not cap.isOpened():
        return
 
    os.makedirs(os.path.dirname(result_path), exist_ok=True)
 
    fps = cap.get(cv2.CAP_PROP_FPS)
 
    cap.set(cv2.CAP_PROP_POS_FRAMES, round(fps * sec))
 
    ret, frame = cap.read()
 
    if ret:
        cv2.imwrite(result_path, frame)
        return frame
 
 
def main():
    check = Check()
 
    # jsonファイルを格納したディレクトリが存在するかチェック
    check.filepath_checker(JSON_DIR)
 
    # 'CUTした画像の格納場所を準備'
    check.directory_init(CUT_IMAGE)
 
    # jsonを解析し、画像とアノテーション座標から切り出しをしていく
    count = 0
    for jsonName in fnmatch.filter(os.listdir(JSON_DIR), '*.json'):
 
        # jsonを開く
        with open(JSON_DIR + jsonName) as f:
            result = json.load(f)
 
            # 画像のファイル名の取得
            imgName = result['asset']['name']
            print('jsonName = {}, imgName = {} '.format(jsonName, imgName))
 
            # img = cv2.imread(IMG_DIR + imgName)
 
            if  'timestamp' not in result['asset'] :
                print('<timestamp is NULL>  imgName = ', imgName)
                continue
 
            img = save_frame_sec(IMG_DIR + 'signal.mp4', result['asset']['timestamp'], 'temp/result_10sec.jpg')
 
            if img is None:
                print('cv.imread Error')
                exit()
 
            # アノテーションした数だけループ
            for region in result['regions']:
 
                height = int(region['boundingBox']['height'])
                width = int(region['boundingBox']['width'])
                left = int(region['boundingBox']['left'])
                top = int(region['boundingBox']['top'])
 
                cutImage = img[top: top + height, left: left + width]
                # アノテーション中に誤って1点クリックしてしまった情報は避ける
                if height == 0 or width == 0:
                    print('<height or width is 0>  imgName = ', imgName)
                    continue
 
                # 書き出す前にリサイズする場合はコメントアウトを外しましょう
                # cutImage = cv.resize(cutImage, (300,300))
 
                # 「cut_images/cat0000.jpg」と連番でファイルを書き出す
                filename =region['tags'][0]
                cv2.imwrite(CUT_IMAGE + filename + "{0:04d}".format(count + 1) + IMAGE_FORMAT, cutImage)
                print("{0:04d}".format(count + 1))
                count += 1
 
 
if __name__ == "__main__":
    main()

スクリプト「cutimg.py」を実行した結果を次に示します。

jsonName = 001475cdcbe31e6b07da7f7b59f53cdc-asset.json, imgName = signal.mp4#t=8.933333 
0001
0002
jsonName = 01c6357f4521d5ce878d8c95d02b3474-asset.json, imgName = signal.mp4#t=4.133333 
0003
0004
jsonName = 01e94c050e6819a2962903bbedd2d5e8-asset.json, imgName = signal.mp4#t=27.8 

・・・

0229
jsonName = fce141e62a7ccfbdcab982e3b24fe07a-asset.json, imgName = signal.mp4#t=32.866667 
0230
0231
jsonName = fd4633d1b9b6441bce9d327cf3f1a034-asset.json, imgName = signal.mp4#t=14.133333 
0232

Process finished with exit code 0

取得したアノテーション画像を次に示します。アノテーション作成ツール「VoTT」でアノテーション範囲を指定した画像が表示されます。