DID-M3Dをカスタムデータで動かしてみる!ための調査備忘録。
概要
画像からの2D物体検出と3D物体検出何が違うの?という単なる興味本位から。
単眼3D検出技術「[2207.08531] DID-M3D: Decoupling Instance Depth for Monocular 3D Object Detection (arxiv.org)」のOSSをカスタムデータで動かすための調査メモ。
論文は、単眼での難しさが深度推定にあることとし、物体の深度を、物体のの視覚的な表面の深度(視覚的な深度)と物体の属性の深度(属性の深度)の組み合わせで再定式化することを提案し、精度向上を果たしている。
論文内では、KITTIデータセットで評価を行っている。
OSSは、arxivにもあるようにGitHub - SPengLiang/DID-M3D: [ECCV 2022] DID-M3D: Decoupling Instance Depth for Monocular 3D Object Detection.に公開されている。
このOSSをカスタームデータで動かすための調査メモです。学習方法やコードについては書きませんので、悪しからず。あくまでコードリーディングしての事前調査の内容なので、KITTIデータセット、DataLoader読み解いて終わるかもしれません。漏れもあるかもしれません。
推論処理
環境構築
今回コードリーディングが中心なので試してはいませんが、試すときの備忘録として。
https://github.com/SPengLiang/DID-M3D?tab=readme-ov-file#installation
推論だけで全部必要かわかりませんが、入れておけば間違いないと思います。
推論処理(eval実行処理)
推論処理はtools/train_val.pyのmain( )にあり、以下のコードのようにevaluateパラメータで設定されている。実体(testerクラスのtest( ))は、lib/helpers/tester_helper.pyの中にある。
if args.evaluate:
tester = Tester(cfg['tester'], cfg['dataset'], model, val_loader, logger)
tester.test()
return
画像など推論処理を行っている部分は、test( )内の以下の部分。model( )への入力が独自で作ることができれば、カスタムデータで推論処理を動かすことができそう。
for batch_idx, (inputs, calibs, coord_ranges, _, info) in enumerate(self.data_loader):
# load evaluation data and move data to current device.
if type(inputs) != dict:
inputs = inputs.to(self.device)
else:
for key in inputs.keys(): inputs[key] = inputs[key].to(self.device)
calibs = calibs.to(self.device)
coord_ranges = coord_ranges.to(self.device)
outputs = self.model(inputs,coord_ranges,calibs,K=50,mode='test')
dets = extract_dets_from_outputs(outputs=outputs, K=50)
dets = dets.detach().cpu().numpy()
model( )への入力は、dataloaderから取得したinputs, calibs, coord_rangeとKとmodeで、Kは最大検出数、modeは推論処理の指定であるため、inputs, calibs, coord_rangeの正体がわかればよい。引数の使い方は、lib/models/DID.pyのforword( )を参照。
inputs, calibs, coord_rangeは、dataloaderで生成されているため、lib/datasets/kitti.pyから中身を確認。
inputs
ファイルから画像を読み込み、リサイズ、正規化、チャンネル変換(C,H,W)した画像データ。
calibs
キャリブレーションファイル、KITTIデータセットのleftカメラパラメータファイルのP2(3x4の射影行列) [ref: https://hirotaka-hachiya.hatenablog.com/entry/2018/02/10/163432]を抽出している。
coord_range
推論時には, [0.0, 0.0]のnumpyでよい。学習時のオーグメンテンションで画像中心がずれる時などズレを入れている。
出力のoutputは、 ['heatmap', 'offset_2d', 'size_2d', 'offset_3d', 'size_3d', 'train_tag', 'heading', 'vis_depth', 'att_depth', 'ins_depth', 'vis_depth_uncer', 'att_depth_uncer', 'ins_depth_uncer']と沢山。論文とおおよそ同じ構造。これらをextract_dets_from_outputs( )でマージしている。
後処理
lib/helpers/tester_helper.pyの推論部分の続きのコードを以下に示す。decode_detections( )でoutput、キャリブレーションファイル、検出閾値から後処理を行っている。infoは、dataloader作成時に付加される {'img_id': index, 'img_size': img_size, 'bbox_downsample_ratio': img_size/features_size}の情報。
# get corresponding calibs & transform tensor to numpy
calibs = [self.data_loader.dataset.get_calib(index) for index in info['img_id']]
info = {key: val.detach().cpu().numpy() for key, val in info.items()}
cls_mean_size = self.data_loader.dataset.cls_mean_size
dets = decode_detections(dets = dets,
info = info,
calibs = calibs,
cls_mean_size=cls_mean_size,
threshold = self.cfg['threshold']
)
results.update(dets)
decode_detections( )は、バッチ数ループ、検出数ループで構成されdets(以下のコードでは、results)にセットしている。長いので、コメントだけ残してます。scoreは閾値処理されるものと結果にセットされるものが違うかもしれません(取得している場所が違うので、、時間あったら読もうと思います)。
results = {}
for i in range(dets.shape[0]): # batch
preds = []
for j in range(dets.shape[1]): # max_dets
cls_id = int(dets[i, j, 0])
score = dets[i, j, 1]
if score < threshold: continue
# 2d bboxs decoding -> bbox, score
# heading angle decoding -> alpha, ry
# dimensions decoding -> dimensions
# positions decoding -> locations
preds.append([cls_id, alpha] + bbox + dimensions.tolist() + locations.tolist() + [ry, score])
results[info['img_id'][i]] = preds
return results
まとめ
DID-M3Dでカスタムデータを推論させるには、事前準備としてキャリブレーションファイル(データ?)を準備し、画像入力、画像の正規化など前処理をDataloaderを参考に実装し、推論処理、後処理はOSSを利用すれば出来そうであった。
この記事が気に入ったらサポートをしてみませんか?