| #!/bin/bash |
| |
| |
| |
| |
| set -euo pipefail |
|
|
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| EVAL_DIR="${SCRIPT_DIR}" |
| REPO_ROOT="${REPO_ROOT:-$(cd "${EVAL_DIR}/../.." && pwd)}" |
| ENV_DIR="${REPO_ROOT}/env" |
| |
| [ -f "${REPO_ROOT}/env/eval_infer_alignment_env.sh" ] && source "${REPO_ROOT}/env/eval_infer_alignment_env.sh" |
|
|
| cd "${REPO_ROOT}" || exit 1 |
| export PYTHONPATH="${REPO_ROOT}:${PYTHONPATH:-}" |
|
|
| |
| if ! python3 - <<'PY' |
| import ctypes |
| ctypes.CDLL("libGL.so.1") |
| print("ok") |
| PY |
| then |
| echo "[eval_v2] FATAL: libGL.so.1 missing. Install libgl1-mesa-glx or use a headless OpenCV build." >&2 |
| exit 2 |
| fi |
|
|
| CKPT="${CKPT:?Set CKPT=/path/to/epoch-0.safetensors}" |
| CKPT_DIR="$(dirname "${CKPT}")" |
| if [ ! -f "${CKPT}" ]; then |
| echo "[eval_v2] FATAL: CKPT 不是可读文件: ${CKPT}" >&2 |
| exit 1 |
| fi |
| DATASET="${DATASET:-${DATASET_BASE_PATH:-${REPO_ROOT}/data/Context-as-Memory-Dataset}}" |
| if [ ! -d "${DATASET}" ]; then |
| echo "[eval_v2] FATAL: DATASET 不是目录: ${DATASET}" >&2 |
| exit 1 |
| fi |
| export CAMERA_INJECT_MODE="${CAMERA_INJECT_MODE:-pre_qkv}" |
|
|
| METADATA_NAME="${METADATA_NAME:-metadata_full.csv}" |
| METADATA_PATH="${METADATA_PATH:-${DATASET}/${METADATA_NAME}}" |
| SAMPLING_ACTION_DIR="${SAMPLING_ACTION_DIR:-${ENV_DIR}}" |
| LOOP_EXTRA_META=() |
| if [ -f "${METADATA_PATH}" ]; then |
| LOOP_EXTRA_META=(--dataset_metadata_path "${METADATA_PATH}") |
| else |
| echo "[eval_v2] WARN: METADATA_PATH 不存在,loop 将用 frames 随机采样(与 ep0 有 metadata 时不一致): ${METADATA_PATH}" >&2 |
| fi |
| LOOP_EXTRA_ARGS=() |
| |
| |
| |
| if [ "${MULTI_CTX_4CHUNK_FOV_HISTORY:-1}" = "1" ]; then |
| LOOP_EXTRA_ARGS+=(--multi_ctx_4chunk_fov_history) |
| fi |
| if [ "${MULTI_CTX_4CHUNK_FOV_LAST_TARGET:-1}" = "1" ]; then |
| LOOP_EXTRA_ARGS+=(--multi_ctx_4chunk_fov_last_target) |
| fi |
| if [ "${MULTI_CTX_4CHUNK_FOV_CONTEXT_RT:-1}" = "1" ]; then |
| LOOP_EXTRA_ARGS+=(--multi_ctx_4chunk_fov_context_rt) |
| fi |
| |
| |
| if [ "${RUN_LEFT_RIGHT_2CHUNK:-0}" != "1" ]; then |
| LOOP_EXTRA_ARGS+=(--skip_left_right_2chunk) |
| fi |
|
|
| EVALS_ROOT="${EVALS_ROOT:-${CKPT_DIR}/evals_v2/static_consistency}" |
| IN_DOMAIN="${EVALS_ROOT}/in_domain" |
| OPEN_DOMAIN_ROOT="${EVALS_ROOT}/open_domain" |
| mkdir -p "${IN_DOMAIN}" |
|
|
| |
| MEM_ARGS=() |
| eval "$(python3 "${ENV_DIR}/memory_baseline_runtime.py" bash-export "${CKPT}")" |
| |
| _default_ctx=1 |
| [[ "${CKPT}" =~ (ctx_20|context_k20|ctx20) ]] && _default_ctx=20 |
| [[ "${CKPT}" =~ (ctx_5|context_k5|ctx5) ]] && _default_ctx=5 |
| [ -n "${CONTEXT_FRAMES_MEM_OVERRIDE:-}" ] && _default_ctx="${CONTEXT_FRAMES_MEM_OVERRIDE}" |
| CONTEXT_FRAMES_EFFECTIVE="${CONTEXT_FRAMES:-$_default_ctx}" |
| SEED_EFFECTIVE="${SEED:-42}" |
|
|
| echo "[eval_v2] CONTEXT_FRAMES_EFFECTIVE=${CONTEXT_FRAMES_EFFECTIVE} CAMERA_INJECT_MODE=${CAMERA_INJECT_MODE} SAMPLING_ACTION_DIR=${SAMPLING_ACTION_DIR}" |
| echo "[eval_v2] MEM_ARGS=${MEM_ARGS[*]:-none}" |
| [ "${#LOOP_EXTRA_META[@]}" -gt 0 ] && echo "[eval_v2] loop dataset_metadata_path=${METADATA_PATH}" |
|
|
| |
| python3 "${EVAL_DIR}/tools/verify_static_eval_prereqs.py" remainder-mem-args "${MEM_ARGS[@]}" |
| |
| python3 "${EVAL_DIR}/tools/verify_static_eval_prereqs.py" wan-base |
|
|
| |
| python3 "${ENV_DIR}/run_replay_loop_two_chunk.py" \ |
| --ckpt "${CKPT}" \ |
| --context_frames "${CONTEXT_FRAMES_EFFECTIVE}" \ |
| --sampling_action_dir "${SAMPLING_ACTION_DIR}" \ |
| --dataset_base "${DATASET}" \ |
| "${LOOP_EXTRA_META[@]}" \ |
| --output_dir "${IN_DOMAIN}/loop_closure" \ |
| --num_samples "${NUM_SAMPLES_LOOP:-8}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| --sigma_shift "${SIGMA_SHIFT:-5}" \ |
| --num_inference_steps "${NUM_INFERENCE_STEPS:-50}" \ |
| --cfg_scale "${CFG_SCALE:-5.0}" \ |
| "${LOOP_EXTRA_ARGS[@]}" \ |
| "${MEM_ARGS[@]}" |
|
|
| |
| |
| |
| CHUNK_FRAMES_EFFECTIVE="${CHUNK_FRAMES:-81}" |
| NUM_CHUNKS_LONG="${NUM_CHUNKS_LONG:-3}" |
| MIN_SPAN=$(( CHUNK_FRAMES_EFFECTIVE * NUM_CHUNKS_LONG )) |
| NUM_SAMPLES_STATIC="${NUM_SAMPLES_STATIC:-6}" |
| COMBO_BASE="${IN_DOMAIN}/action_combos_random_symmetric" |
| COMBO_OUT="${IN_DOMAIN}/combo_revisit_in_domain" |
| mkdir -p "${COMBO_BASE}" "${COMBO_OUT}" |
|
|
| if [ "${STATIC_USE_OPEN_DOMAIN_COMBO:-0}" = "1" ]; then |
| echo "[eval_v2] STATIC_USE_OPEN_DOMAIN_COMBO=1 -> legacy single combo + external first frame" |
| COMBO_DIR="${IN_DOMAIN}/action_combo_rot_trans_rev" |
| python3 "${EVAL_DIR}/actions/build_action_combo.py" \ |
| --exp_dir "${ENV_DIR}" \ |
| --out_dir "${COMBO_DIR}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --translation_delta "${TRANSLATION_DELTA:-0.1}" >/dev/null |
| FIRST_FRAME_IMG="${FIRST_FRAME_IMG:-${REPO_ROOT}/assets/first_frame.png}" |
| python3 "${EVAL_DIR}/static/run_combo_revisit_fixed_first.py" \ |
| --ckpt "${CKPT}" \ |
| --first_frame_image "${FIRST_FRAME_IMG}" \ |
| --output_dir "${IN_DOMAIN}/combo_revisit_fixed_first" \ |
| --prompt "${PROMPT:-A scene.}" \ |
| --action_combo_dir "${COMBO_DIR}" \ |
| --context_frames "${CONTEXT_FRAMES_EFFECTIVE}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --sigma_shift "${SIGMA_SHIFT:-5}" \ |
| --num_inference_steps "${NUM_INFERENCE_STEPS:-50}" \ |
| --cfg_scale "${CFG_SCALE:-5.0}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| "${MEM_ARGS[@]}" |
| else |
| SAMPLES_TSV="${IN_DOMAIN}/_static_eval_samples.tsv" |
| python3 "${EVAL_DIR}/tools/emit_dataset_samples.py" \ |
| --dataset "${DATASET}" \ |
| --num_samples "${NUM_SAMPLES_STATIC}" \ |
| --min_frames "${MIN_SPAN}" \ |
| --seed "${SEED_EFFECTIVE}" > "${SAMPLES_TSV}" |
| _idx=0 |
| _seed_base="${SEED_EFFECTIVE}" |
| while IFS=$'\t' read -r _vn _st; do |
| [ -z "${_vn:-}" ] && continue |
| _combo_sub="${COMBO_BASE}/${_vn}_start${_st}" |
| mkdir -p "${_combo_sub}" |
| python3 "${EVAL_DIR}/actions/build_action_combo.py" \ |
| --random_symmetric \ |
| --combo_seed "$((_seed_base + _idx))" \ |
| --out_dir "${_combo_sub}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --yaw_min "${COMBO_YAW_MIN:-20}" \ |
| --yaw_max "${COMBO_YAW_MAX:-55}" \ |
| --translation_min "${COMBO_TRANS_MIN:-0.05}" \ |
| --translation_max "${COMBO_TRANS_MAX:-0.18}" >/dev/null |
| python3 "${EVAL_DIR}/static/run_combo_revisit_fixed_first.py" \ |
| --ckpt "${CKPT}" \ |
| --dataset_base "${DATASET}" \ |
| --video_name "${_vn}" \ |
| --start_frame "${_st}" \ |
| --output_dir "${COMBO_OUT}/${_vn}_start${_st}" \ |
| --action_combo_dir "${_combo_sub}" \ |
| --context_frames "${CONTEXT_FRAMES_EFFECTIVE}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --sigma_shift "${SIGMA_SHIFT:-5}" \ |
| --num_inference_steps "${NUM_INFERENCE_STEPS:-50}" \ |
| --cfg_scale "${CFG_SCALE:-5.0}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| "${MEM_ARGS[@]}" |
| _idx=$((_idx + 1)) |
| done < "${SAMPLES_TSV}" |
| python3 "${EVAL_DIR}/metrics/aggregate_combo_closure_metrics.py" \ |
| --root "${COMBO_OUT}" \ |
| --output_json "${EVALS_ROOT}/metrics/combo_closure_summary.json" |
| fi |
|
|
| if [ "${RUN_GEOMETRY_DIAG:-0}" = "1" ] && [ "${STATIC_USE_OPEN_DOMAIN_COMBO:-0}" = "1" ]; then |
| echo "[eval_v2] WARN: RUN_GEOMETRY_DIAG=1 但 STATIC_USE_OPEN_DOMAIN_COMBO=1 时不会生成 _static_eval_samples.tsv / long_horizon_gt_replay,点云段 (F) 无输入。若要 3D 诊断请关掉 STATIC_USE_OPEN_DOMAIN_COMBO 或设 RUN_BASIC_REPLAY_GT=1 后自行对齐 mp4 与数据集 pose。" >&2 |
| fi |
|
|
| |
| LONG_ROOT="${IN_DOMAIN}/long_horizon_gt_replay" |
| mkdir -p "${LONG_ROOT}" |
| if [ "${STATIC_USE_OPEN_DOMAIN_COMBO:-0}" != "1" ] && [ -f "${IN_DOMAIN}/_static_eval_samples.tsv" ]; then |
| while IFS=$'\t' read -r _vn _st; do |
| [ -z "${_vn:-}" ] && continue |
| _out="${LONG_ROOT}/${_vn}_start${_st}" |
| mkdir -p "${_out}" |
| python3 "${EVAL_DIR}/basic/check_dataset_gt_for_replay.py" \ |
| --dataset "${DATASET}" \ |
| --video "${_vn}" \ |
| --start_frame "${_st}" \ |
| --num_chunks "${NUM_CHUNKS_LONG}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" || continue |
| python3 "${EVAL_DIR}/basic/replay_gt_error.py" \ |
| --ckpt "${CKPT}" \ |
| --dataset_base "${DATASET}" \ |
| --video_name "${_vn}" \ |
| --start_frame "${_st}" \ |
| --num_chunks "${NUM_CHUNKS_LONG}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --context_frames "${CONTEXT_FRAMES_EFFECTIVE}" \ |
| --sigma_shift "${SIGMA_SHIFT:-5}" \ |
| --num_inference_steps "${NUM_INFERENCE_STEPS:-50}" \ |
| --cfg_scale "${CFG_SCALE:-5.0}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| --output_dir "${_out}" \ |
| --write_csv |
| done < "${IN_DOMAIN}/_static_eval_samples.tsv" |
| python3 "${EVAL_DIR}/metrics/aggregate_long_horizon_fid_fvd.py" \ |
| --root "${LONG_ROOT}" \ |
| --dataset_base "${DATASET}" \ |
| --device "${DEVICE:-cuda}" \ |
| --output_json "${EVALS_ROOT}/metrics/long_horizon_fid_fvd_summary.json" |
| |
| if [ "${RUN_TEMPORAL_ADJ:-0}" = "1" ]; then |
| python3 "${EVAL_DIR}/metrics/aggregate_long_horizon_temporal_adjacency.py" \ |
| --root "${LONG_ROOT}" \ |
| --output_json "${EVALS_ROOT}/metrics/long_horizon_temporal_adjacency_summary.json" |
| fi |
| fi |
|
|
| |
| |
| METRIC_SAMPLE_NUM="${METRIC_SAMPLE_NUM:-5}" |
| WRITE_VIZ="${WRITE_VIZ:-1}" |
| python3 "${EVAL_DIR}/metrics/aggregate_revisit_metrics.py" \ |
| --evals_root "${IN_DOMAIN}" \ |
| --num_samples "${METRIC_SAMPLE_NUM}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| --device "${DEVICE:-cuda}" \ |
| --output_dir "${EVALS_ROOT}/metrics/revisit_subset" \ |
| $( [ "${WRITE_VIZ}" != "0" ] && echo "--write_viz" || true ) |
|
|
| |
| |
| |
| |
| if [ -n "${MULTIVIEW_FIRSTFRAME_DIR:-}" ] && [ -d "${MULTIVIEW_FIRSTFRAME_DIR}" ]; then |
| mkdir -p "${OPEN_DOMAIN_ROOT}" |
| MULTIVIEW_FIRSTFRAME_LIST="${OPEN_DOMAIN_ROOT}/_multiview_firstframes_from_dir.txt" |
| _mv_prompt_args=() |
| if [ -n "${MULTIVIEW_PROMPT:-}" ]; then |
| _mv_prompt_args=(--prompt "${MULTIVIEW_PROMPT}") |
| fi |
| python3 "${EVAL_DIR}/tools/build_multiview_firstframe_list_from_dir.py" \ |
| --image_dir "${MULTIVIEW_FIRSTFRAME_DIR}" \ |
| --output "${MULTIVIEW_FIRSTFRAME_LIST}" \ |
| "${_mv_prompt_args[@]}" |
| fi |
| if [ -n "${MULTIVIEW_FIRSTFRAME_LIST:-}" ] && [ -f "${MULTIVIEW_FIRSTFRAME_LIST}" ]; then |
| mkdir -p "${OPEN_DOMAIN_ROOT}" |
| MV_OUT="${OPEN_DOMAIN_ROOT}/multiview_revisit" |
| MV_ACTION_DIR="${MULTIVIEW_ACTION_DIR:-${COMBO_BASE}/multiview_shared_combo}" |
| mkdir -p "${MV_ACTION_DIR}" "${MV_OUT}" |
| python3 "${EVAL_DIR}/actions/build_action_combo.py" \ |
| --random_symmetric \ |
| --combo_seed "${SEED_EFFECTIVE}" \ |
| --out_dir "${MV_ACTION_DIR}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --yaw_min "${COMBO_YAW_MIN:-20}" \ |
| --yaw_max "${COMBO_YAW_MAX:-55}" \ |
| --translation_min "${COMBO_TRANS_MIN:-0.05}" \ |
| --translation_max "${COMBO_TRANS_MAX:-0.18}" >/dev/null |
| python3 "${EVAL_DIR}/tools/verify_static_eval_prereqs.py" multiview-preflight \ |
| --ckpt "${CKPT}" \ |
| --firstframe-list "${MULTIVIEW_FIRSTFRAME_LIST}" \ |
| --action-combo-dir "${MV_ACTION_DIR}" \ |
| --runner "${EVAL_DIR}/static/run_combo_revisit_fixed_first.py" \ |
| -- "${MEM_ARGS[@]}" |
| python3 "${EVAL_DIR}/static/run_multiview_revisit_from_firstframes.py" \ |
| --ckpt "${CKPT}" \ |
| --firstframe_list "${MULTIVIEW_FIRSTFRAME_LIST}" \ |
| --action_combo_dir "${MV_ACTION_DIR}" \ |
| --output_root "${MV_OUT}" \ |
| --chunk_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --context_frames "${CONTEXT_FRAMES_EFFECTIVE}" \ |
| --sigma_shift "${SIGMA_SHIFT:-5}" \ |
| --num_inference_steps "${NUM_INFERENCE_STEPS:-50}" \ |
| --cfg_scale "${CFG_SCALE:-5.0}" \ |
| --seed "${SEED_EFFECTIVE}" \ |
| --extra_args "${MEM_ARGS[@]}" |
| if [ "${RUN_MULTIVIEW_AGGREGATE_METRICS:-1}" = "1" ] && [ -d "${MV_OUT}" ]; then |
| python3 "${EVAL_DIR}/metrics/aggregate_multiview_open_domain_metrics.py" \ |
| --multiview_root "${MV_OUT}" \ |
| --ref_view "${MULTIVIEW_REF_VIEW:-0}" \ |
| --device "${DEVICE:-cuda}" \ |
| --output_json "${MV_OUT}/multiview_closure_and_cross_view.json" |
| fi |
| fi |
|
|
| |
| |
| |
| if [ "${RUN_GEOMETRY_DIAG:-0}" = "1" ] && [ -f "${IN_DOMAIN}/_static_eval_samples.tsv" ]; then |
| GEOM_ROOT="${EVALS_ROOT}/geometry_diagnostics" |
| mkdir -p "${GEOM_ROOT}" |
| _geom_depth_args=(--depth_mode "${GEOMETRY_DEPTH_MODE:-pseudo}") |
| if [ -n "${GEOMETRY_DEPTH_DIR:-}" ]; then |
| _geom_depth_args+=(--depth_dir "${GEOMETRY_DEPTH_DIR}") |
| fi |
| if [ -n "${GEOMETRY_DEPTH_KEY:-}" ]; then |
| _geom_depth_args+=(--depth_key "${GEOMETRY_DEPTH_KEY}") |
| fi |
| if [ "${GEOMETRY_DEPTH_IS_INVERSE:-0}" = "1" ]; then |
| _geom_depth_args+=(--depth_is_inverse) |
| fi |
| if [ -n "${GEOMETRY_DEPTH_SCALE:-}" ]; then |
| _geom_depth_args+=(--depth_scale "${GEOMETRY_DEPTH_SCALE}") |
| fi |
| if [ -n "${MIDAS_DEVICE:-}" ]; then |
| _geom_depth_args+=(--midas_device "${MIDAS_DEVICE}") |
| fi |
| _geom_count=0 |
| while IFS=$'\t' read -r _vn _st; do |
| [ -z "${_vn:-}" ] && continue |
| _run="${LONG_ROOT}/${_vn}_start${_st}" |
| _gen="${_run}/replay_gt_gen_only.mp4" |
| [ ! -f "${_gen}" ] && continue |
| python3 "${EVAL_DIR}/geometry/render_multiview_pointcloud_offline.py" \ |
| --gen_video "${_gen}" \ |
| --dataset_base "${DATASET}" \ |
| --video_name "${_vn}" \ |
| --start_frame "${_st}" \ |
| --num_frames "${CHUNK_FRAMES_EFFECTIVE}" \ |
| --output_dir "${GEOM_ROOT}/${_vn}_start${_st}" \ |
| "${_geom_depth_args[@]}" |
| _geom_count=$((_geom_count + 1)) |
| done < "${IN_DOMAIN}/_static_eval_samples.tsv" |
| if [ "${_geom_count}" -eq 0 ]; then |
| echo "[eval_v2] WARN: RUN_GEOMETRY_DIAG=1 但未渲染任何点云(${LONG_ROOT} 下缺少 replay_gt_gen_only.mp4)。长时程 (D) 可能未跑或全部样本被 skip。" >&2 |
| else |
| echo "[eval_v2] geometry diagnostics: ${_geom_count} run(s) -> ${GEOM_ROOT}" |
| fi |
| python3 "${EVAL_DIR}/metrics/aggregate_geometry_consistency.py" \ |
| --root "${GEOM_ROOT}" \ |
| --output_json "${EVALS_ROOT}/metrics/geometry_consistency_summary.json" |
| fi |
|
|
| echo "Done. static_consistency root: ${EVALS_ROOT}" |
|
|
|
|