t2m-gpt-vsl-code / build_3view_csv.py
Tri1's picture
T2M-GPT VSL adaptation: Python sources only (82 files, no checkpoints or data)
8e5456b verified
Raw
History Blame Contribute Delete
4.42 kB
#!/usr/bin/env python3
"""Build a 3-view (front/left/right) Full_TriVis CSV that `prepare_vsl_data.py` can pack.
`Full_TriVis/split_lab_front.csv` covers only the front view: 24,151 clips.
`Full_TriVis/Label/Dataset_full_lab.csv` is the master label file and holds all three
views of the same sentences -- 72,453 rows = 24,151 x 3, scene `lab` throughout -- which
matches the 72,453 lab `.npz` files under `Full_TriVis/skeleton/` exactly.
The split is INHERITED, not redrawn. `splits.json` used `split_key = Sign_sentence`, so
assigning each master row the split of its `Sign_sentence` keeps every view of a sentence
in the same split. That preserves the text-disjoint property (a sentence never spans
splits), and it keeps the new test set a strict superset of the old one, so front-only
results stay directly comparable -- the front subset of the new test split IS the old test
split.
npz path = "Full_TriVis/skeleton/" + Sentence_video_path with .mp4 -> .npz.
"""
import argparse
import collections
import csv
import os
REPO = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
COLS = ['split', 'npz_path', 'Sentence', 'Sign_sentence', 'Category', 'group', 'view',
'scene', 'ID_sentence', 'Sentence_video_path']
def main():
ap = argparse.ArgumentParser()
ap.add_argument('--front-csv', default=os.path.join(REPO, 'Full_TriVis',
'split_lab_front.csv'))
ap.add_argument('--master-csv', default=os.path.join(REPO, 'Full_TriVis', 'Label',
'Dataset_full_lab.csv'))
ap.add_argument('--skeleton-root', default='Full_TriVis/skeleton')
ap.add_argument('--repo-root', default=REPO)
ap.add_argument('--views', default='front,left,right')
ap.add_argument('--out', default=os.path.join(REPO, 'Full_TriVis',
'split_lab_3view.csv'))
ap.add_argument('--check-exists', action='store_true',
help='stat every npz and drop the missing ones (slow but safe)')
args = ap.parse_args()
want = set(args.views.split(','))
with open(args.front_csv, newline='', encoding='utf-8') as f:
front = list(csv.DictReader(f))
split_of = {r['Sign_sentence']: r['split'] for r in front}
print(f'{len(front)} front rows -> {len(split_of)} distinct Sign_sentence with a split')
with open(args.master_csv, newline='', encoding='utf-8') as f:
master = list(csv.DictReader(f))
print(f'{len(master)} master rows, views {sorted({r["view"] for r in master})}')
rows, missing_split, missing_file = [], 0, 0
for r in master:
if r['view'] not in want:
continue
sp = split_of.get(r['Sign_sentence'])
if sp is None:
missing_split += 1
continue
npz = os.path.join(args.skeleton_root,
r['Sentence_video_path'].replace('.mp4', '.npz'))
if args.check_exists and not os.path.exists(os.path.join(args.repo_root, npz)):
missing_file += 1
continue
rows.append({'split': sp, 'npz_path': npz, 'Sentence': r['Sentence'],
'Sign_sentence': r['Sign_sentence'], 'Category': r['Category'],
'group': r['group'], 'view': r['view'], 'scene': r['scene'],
'ID_sentence': r['ID_sentence'],
'Sentence_video_path': r['Sentence_video_path']})
with open(args.out, 'w', newline='', encoding='utf-8') as f:
w = csv.DictWriter(f, fieldnames=COLS)
w.writeheader()
w.writerows(rows)
print(f'wrote {args.out}: {len(rows)} rows '
f'(no split {missing_split}, missing npz {missing_file})')
print(' split x view:')
c = collections.Counter((r['split'], r['view']) for r in rows)
for sp in ('train', 'val', 'test'):
line = ' '.join(f'{v} {c[(sp, v)]}' for v in sorted(want))
print(f' {sp:<6} {line} total {sum(c[(sp, v)] for v in want)}')
# leak check, the same one splits.json reports
by = collections.defaultdict(set)
for r in rows:
by[r['Sign_sentence']].add(r['split'])
span = sum(1 for v in by.values() if len(v) > 1)
print(f' leakage_check: {"PASS" if span == 0 else "FAIL"}: {span} Sign_sentence '
f'values span multiple splits')
if __name__ == '__main__':
main()