Files
hand-motion-pipeline/third_party/FoundationPose/run_ycb_video.py
T
liyang ae28d55f81 Update to 2026-09-17 pipeline snapshot; add weights, L20 assets and recording via Git LFS
Source: RGB-D -> Dyn-HaMR -> L20 retargeting -> FoundationPose -> reference repair -> SPIDER,
documented in docs/PIPELINE_LATEST.md and docs/SETUP_AND_WEIGHTS.md. Adds FoundationPose and
nvdiffrast upstream snapshots, requirements/pipeline_venv.txt and the FoundationPose weight
manifest/downloader.

Assets (Git LFS): weights/ (WiLoR detector, HandFlow denoiser, UniDepth-L), FoundationPose
checkpoints, HaMeR checkpoint, Dyn-HaMR HMP model and BMC constraints, L20 URDF/meshes, the
20260915_171525 D405 recording and the two box CADs. MANO models are not redistributed
(third_party/hamer/_DATA/data/mano/README.txt). Environments, caches and run outputs excluded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:43:37 +08:00

150 lines
5.1 KiB
Python

# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto. Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
from Utils import *
import json,uuid,joblib,os,sys,argparse
from datareader import *
from estimater import *
code_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(f'{code_dir}/mycpp/build')
import yaml
def get_mask(reader, i_frame, ob_id, detect_type):
if detect_type=='box':
mask = reader.get_mask(i_frame, ob_id)
H,W = mask.shape[:2]
vs,us = np.where(mask>0)
umin = us.min()
umax = us.max()
vmin = vs.min()
vmax = vs.max()
valid = np.zeros((H,W), dtype=bool)
valid[vmin:vmax,umin:umax] = 1
elif detect_type=='mask':
mask = reader.get_mask(i_frame, ob_id, type='mask_visib')
valid = mask>0
elif detect_type=='cnos': #https://github.com/nv-nguyen/cnos
mask = cv2.imread(reader.color_files[i_frame].replace('rgb','mask_cnos'), -1)
valid = mask==ob_id
else:
raise RuntimeError
return valid
def run_pose_estimation_worker(reader, i_frames, est:FoundationPose, debug=False, ob_id=None, device:int=0):
result = NestDict()
torch.cuda.set_device(device)
est.to_device(f'cuda:{device}')
est.glctx = dr.RasterizeCudaContext(device)
debug_dir = est.debug_dir
for i in range(len(i_frames)):
i_frame = i_frames[i]
id_str = reader.id_strs[i_frame]
logging.info(f"{i}/{len(i_frames)}, video:{reader.get_video_id()}, id_str:{id_str}")
color = reader.get_color(i_frame)
depth = reader.get_depth(i_frame)
H,W = color.shape[:2]
scene_ob_ids = reader.get_instance_ids_in_image(i_frame)
video_id = reader.get_video_id()
logging.info(f"video:{reader.get_video_id()}, id_str:{id_str}, ob_id:{ob_id}")
if ob_id not in scene_ob_ids:
logging.info(f'skip {ob_id} as it does not exist in this scene')
continue
ob_mask = get_mask(reader, i_frame, ob_id, detect_type=detect_type)
est.gt_pose = reader.get_gt_pose(i_frame, ob_id)
pose = est.register(K=reader.K, rgb=color, depth=depth, ob_mask=ob_mask, ob_id=ob_id, iteration=5)
logging.info(f"pose:\n{pose}")
if debug>=3:
tmp = est.mesh_ori.copy()
tmp.apply_transform(pose)
tmp.export(f'{debug_dir}/model_tf.obj')
result[video_id][id_str][ob_id] = pose
return result
def run_pose_estimation():
wp.force_load(device='cuda')
video_dirs = sorted(glob.glob(f'{opt.ycbv_dir}/test/*'))
res = NestDict()
debug = opt.debug
use_reconstructed_mesh = opt.use_reconstructed_mesh
debug_dir = opt.debug_dir
reader_tmp = YcbVideoReader(video_dirs[0])
glctx = dr.RasterizeCudaContext()
mesh_tmp = trimesh.primitives.Box(extents=np.ones((3)), transform=np.eye(4))
est = FoundationPose(model_pts=mesh_tmp.vertices.copy(), model_normals=mesh_tmp.vertex_normals.copy(), symmetry_tfs=None, mesh=mesh_tmp, scorer=None, refiner=None, glctx=glctx, debug_dir=debug_dir, debug=debug)
ob_ids = reader_tmp.ob_ids
for ob_id in ob_ids:
if use_reconstructed_mesh:
mesh = reader_tmp.get_reconstructed_mesh(ob_id, ref_view_dir=opt.ref_view_dir)
else:
mesh = reader_tmp.get_gt_mesh(ob_id)
symmetry_tfs = reader_tmp.symmetry_tfs[ob_id]
args = []
for video_dir in video_dirs:
reader = YcbVideoReader(video_dir, zfar=1.5)
scene_ob_ids = reader.get_instance_ids_in_image(0)
if ob_id not in scene_ob_ids:
continue
video_id = reader.get_video_id()
for i in range(len(reader.color_files)):
if not reader.is_keyframe(i):
continue
args.append((reader, [i], est, debug, ob_id, 0))
est.reset_object(model_pts=mesh.vertices.copy(), model_normals=mesh.vertex_normals.copy(), symmetry_tfs=symmetry_tfs, mesh=mesh)
outs = []
for arg in args:
out = run_pose_estimation_worker(*arg)
outs.append(out)
for out in outs:
for video_id in out:
for id_str in out[video_id]:
res[video_id][id_str][ob_id] = out[video_id][id_str][ob_id]
with open(f'{opt.debug_dir}/ycbv_res.yml','w') as ff:
yaml.safe_dump(make_yaml_dumpable(res), ff)
if __name__=='__main__':
parser = argparse.ArgumentParser()
code_dir = os.path.dirname(os.path.realpath(__file__))
parser.add_argument('--ycbv_dir', type=str, default="/mnt/9a72c439-d0a7-45e8-8d20-d7a235d02763/DATASET/YCB_Video", help="data dir")
parser.add_argument('--use_reconstructed_mesh', type=int, default=0)
parser.add_argument('--ref_view_dir', type=str, default="/mnt/9a72c439-d0a7-45e8-8d20-d7a235d02763/DATASET/YCB_Video/bowen_addon/ref_views_16")
parser.add_argument('--debug', type=int, default=0)
parser.add_argument('--debug_dir', type=str, default=f'{code_dir}/debug')
opt = parser.parse_args()
os.environ["YCB_VIDEO_DIR"] = opt.ycbv_dir
set_seed(0)
detect_type = 'mask' # mask / box / detected
run_pose_estimation()