scripts/perturb_sync_mapping.py creates a new, non-overwriting coordinate-perturbed mapping for image-resampling experiments and always records physical_clock_updated: false. A RIFEm PRBS sweep found that a shared +0.15 source-coordinate perturbation improves export P95 from 0.1607 to 0.1212 frame, but independently combining apparent per-camera zero points worsens it to 0.1752. The response is therefore nonlinear/non-separable under the beacon protocol; no correction is admitted into final synchronization. The full falsification record is experiments/rifem_alpha_response_sweep_v098_2026_08_01.json.
The official RIFEm arbitrary-time model is now available through camera_sync_audit.rife_vfi:rife_arbitrary_interpolate. It requires explicit CAMERA_SYNC_RIFE_CODE_DIR, CAMERA_SYNC_RIFE_MODEL_DIR, and CAMERA_SYNC_RIFE_SHA256; sync-export writes the resulting code/model provenance into its manifest. The inspected official code is MIT, its checkout is pinned to commit 5d8adb…c086, and the extracted weights are pinned to SHA-256 9f9e…d3cb; see experiments/rifem_asset_manifest_v097_2026_08_01.json.
On Joker captured midpoints, RIFEm improves MAE/PSNR to 0.811 / 38.59 dB versus linear 1.188 / 33.91 dB; on an independent Corgi camera it reaches 0.537 / 43.50 dB versus 0.973 / 35.84 dB. It also improves the PRBS export loop to MAE/P95/max 0.0833/0.1607/0.1655 frames. This is promising image resampling evidence, but it does not meet the raw controlled-beacon mapping P95 0.0227 frame and remains validation-required rather than a timing authority.
camera_sync_audit.motion_vfi:dis_bidirectional_interpolate is a CPU-capable, no-weight external interpolator for sync-export. It estimates DIS flow in both directions, suppresses inconsistent/occluded vectors, then forward-splats both source images at the requested fractional time. A textured 8-pixel translation regression test reduces mean pixel error from 15.32 for linear blending to <0.05; this verifies the implementation on a motion case, not physical timing.
The same frozen PRBS beacon rejects the plugin for timing delivery: its post-export MAE/P95/max is 0.1398/0.2386/0.2495 frame, worse than all existing baselines. Conversely, a 20-triplet captured-midpoint benchmark from Joker camera 001.mp4 gives plugin/linear MAE 1.036/1.188 and PSNR 36.53/33.91 dB at 320 pixels wide. It therefore remains requires_independent_interpolator_validation: useful for dynamic-content resampling, never to upgrade a synchronization authority or claim subframe image accuracy. The preferred learned external candidate is RIFE because it natively accepts arbitrary time, but model weights must be separately acquired, version-pinned, and validated before use.
sync-export now consumes the original video/matrix-image root and final_sync_mapping.npz, then writes common-valid frame-major images. nearest is the formal frame-quantized path; built-in linear and cubic are explicitly marked diagnostic because pixel interpolation is not motion- or exposure-aware. An external arbitrary-time interpolator can be supplied as module:function, but its result requires independent validation.
python -m camera_sync_audit sync-export F:\capture `
F:\sync_result\final_sync_mapping.npz --out F:\aligned --method nearestFor a deterministic motion-compensated diagnostic baseline, OpenCV users can
pass --interpolator camera_sync_audit.motion_vfi:dis_bidirectional_interpolate.
It uses bidirectional DIS optical flow, consistency-weighted forward splatting,
and an uncovered-pixel fallback. It remains diagnostic until independently
validated on the target exposure and motion regime.
On the frozen 4-camera PRBS beacon, the range-safe mapping has 147/150 common-valid reference frames (frames 3–149) and retains the pre-export P95 error of 0.0227 frame. Re-estimation after the frozen v0.9.6 export produced MAE/P95 0.0872/0.1762 for nearest, 0.0933/0.1952 for linear, and 0.0923/0.1915 for cubic. Thus the interpolation sign is verified, but simple pixel blending does not preserve exposure-integrated subframe phase; use nearest for formal frame selection or a separately validated motion-aware arbitrary-time model.
Every observable sync-finalize / sync-auto run now also writes final_sync_mapping.npz and final_sync_mapping.json. The NPZ contains reference_frame, per-camera sample_coordinate/lower_index/upper_index/alpha/valid, and all_application_cameras_valid. Downstream reconstruction can therefore sample frames directly instead of reimplementing offset signs, drift anchors or counter discontinuities.
Hardware event series are composed exactly through their common clock. Missing source counters become invalid reference frames and are never interpolated across. If the event series is unavailable, the system falls back to declared mapping segments, exposure phase and gated drift. The final report hashes both the input event-series NPZ and the delivered mapping NPZ.
Frozen end-to-end materialization results are: Joker .videoidx 1716/1720 common valid frames across 40 cameras; controlled-beacon matrix images 147/150 across 4 cameras with P95 0.0227 frame; hardware timestamps 793/800 across 6 cameras, with exact preservation of all 4,786 valid event-derived coordinates. Full evidence is in experiments/frame_mapping_delivery_summary_2026_08_01.json.
sync-auto now accepts a directory containing one video or one matrix-image directory per camera and writes a policy-safe final_sync.json plus final_sync.csv. It automatically discovers .videoidx; it can also run a controlled beacon, hardware exposure timestamps, declared electronic common-source audio/LTC, and the natural-image diagnostic. Physical clocks always override image estimates, and ambient audio is withheld unless propagation/pipeline semantics are explicitly declared.
python -m camera_sync_audit sync-auto F:\capture `
--beacon-manifest F:\capture\beacon_manifest.json `
--beacon-roi-json F:\capture\beacon_rois.json `
--camera-fps 60 --out F:\capture\sync_resultFor an array with .videoidx, the minimal command is:
python -m camera_sync_audit sync-auto F:\capture --out F:\capture\sync_resultThe output convention is camera_frame_coordinate = reference_frame_coordinate + shift. Each camera row includes the applicable sub-frame shift, nearest integer index, fractional residual, drift per 1000 reference frames, uncertainty, discontinuity segments, source authority, and cross-source conflict status. A verified 4-camera × 150-frame matrix-image beacon run achieved strict reference-relative MAE/P95/max 0.0134/0.0227/0.0237 frames with 100% integer agreement. Joker raw videos produced 40/40 authoritative frame mappings from .videoidx; Corgi visual-only input was correctly withheld as unobservable.
On the tested Windows MKL environment, run the full suite with MKL_THREADING_LAYER=SEQUENTIAL, MKL_NUM_THREADS=1, and OMP_NUM_THREADS=1; the current result is 92 passed. Machine evidence is in experiments/final_sync_system_summary_2026_08_01.json.
The Corgi cross-scene follow-up now explicitly segments the moving dog/person before sparse reconstruction. This removes static-background dominance (245 shared COLMAP identities fall to 35 dynamic identities, while median 33-frame motion rises to 167–263 pixels), but does not create an absolute clock. Deterministic dynamic-only COLMAP support, dog silhouettes, single-frame DINOv2 identity and 64/128-frame instance-pooled DINO sequences all fail frozen label-free admission. The DINO sequence injection-response P95 is 0.579–0.729 frames against a fixed <0.20 gate.
The supported boundary is therefore explicit: dynamic identity may feed Track3D residual refinement after a trustworthy integer basin is known; it must not override hardware trigger, shared counter/.videoidx, electronic common-source LTC/audio or a calibrated visible beacon. Full evidence is in experiments/dynamic_instance_identity_summary_2026_08_01.json.
这是一个独立的多相机时间同步审计项目。推荐的主流程是估计每台相机的全序列整体偏移;另保留可选的分段诊断能力。
主流程可以估计:
- 每台相机相对于统一参考相机的连续亚帧偏移;
- 实际重排图像时使用的整数帧索引修正;
- 与已知
sync.json的规范无关定量误差。
扩展诊断还可以检测:
- 相机从采集开始就存在的任意整数帧偏移;
- 采集中途发生的一次或多次丢帧、重复帧或插入帧;
- 随时间累积的偏移,即疑似帧率或时钟漂移;
- 哪台相机更可能是异常源,以及实际对齐时每一段应修正多少帧。
它检测的是时间轴/帧索引错位,不是相机内参、外参或画面空间位置标定误差。原始的 audit_r2.py 保持不变,新项目不依赖其中的 TDGS 模型或 CUDA。
一次全序列互相关只能得到一个主偏移。如果相机在第 300 帧丢了 4 帧、在第 520 帧又重复了 2 帧,全局相关可能只返回其中一个折中结果。
本项目采用以下流程:
- 从每个相机提取抗曝光变化的亮度运动、边缘运动和空间分布特征。
- 在 1/3/7/15 帧尺度建立整段运动模型,兼顾快速动作和缓慢动作。
- 使用 FFT 标准化互相关计算所有相机两两之间的任意范围时延。
- 把相机作为节点、两两时延作为边,用 Huber-IRLS 求解循环一致的全局相机偏移图。
- 从全图选出高质量稀疏跟踪图,在整个序列上滑窗重复求解。
- 用中位数规范固定多数相机,防止一台相机跳变时所有正常相机被连带误判。
- 对偏移轨迹做带惩罚的最优分段,同时拟合每千帧漂移量。
- 在粗跳变附近进行逐帧边界细化,同时给出参考时间轴和异常相机本地帧号。
- 输出 JSON、CSV 和可视化 HTML,并给出置信度、图残差和边界命中警告。
两两相关的计算不是 O(序列长度 × 搜索偏移) 的逐帧暴力枚举;交叉乘积使用 FFT,长序列也可以处理。
cd F:\project\audit
python -m pip install -e .依赖为 NumPy、SciPy、Pillow 和 ImageIO。读取 MP4/MOV 等视频时由 imageio-ffmpeg 解码。
支持“每台相机一个图片目录”:
capture/
cam01/
000001.png
000002.png
cam02/
000001.png
000002.png
cam03/
...
也会自动识别重建数据集中常见的“每个时刻一个目录”布局,并在读取时虚拟转置为相机序列,不复制图片:
capture/
000001/
cam01.png
cam02.png
000002/
cam01.png
cam02.png
也支持“每台相机一个视频文件”:
capture/
cam01.mp4
cam02.mp4
cam03.mp4
图片按文件名中的数字自然排序。不同相机可以有少量帧数差异,但必须拥有足够的公共序列。
如果背景运动、屏幕闪烁或曝光变化很强,建议提供每相机独立的归一化 ROI:
{
"cam01": [0.10, 0.20, 0.85, 0.90],
"cam02": [0.05, 0.15, 0.80, 0.88]
}顺序是 [x0, y0, x1, y1],坐标范围为 0 到 1。
先运行自带的已知错位演示:
python -m camera_sync_audit demo --out sync_audit_demo演示中的 cam02 在约第 300 帧发生 +4 帧跳变,随后发生 -2 帧跳变。打开:
sync_audit_demo/report.html
分析真实采集:
python -m camera_sync_audit align F:\data\capture `
--out F:\data\capture_sync_report `
--max-offset 300 `
--roi-json F:\data\capture_rois.json使用秒为单位的已知同步文件验证(真值只参与验证,不参与偏移求解):
python -m camera_sync_audit align F:\data\capture `
--out F:\data\capture_sync_report `
--sync-json F:\data\sync.json `
--fps 60程序会自动尝试 sync.json 的两种常见符号约定,并选择相对指定参考相机 RMSE 较低的约定。预测值和真值各自只减去该参考相机一次;主要 MAE/RMSE/P95 不再额外消除中位数偏置,因为那会把参考相机自身的误差隐藏掉。报告仍以 gauge_aligned_*_secondary 保留二次规范对齐值,仅供诊断。
自然动作在周期运动或相似姿态下可能形成内部自洽的错误峰。如果原视频存在全局曝光/照明变化,可先生成不读取同步标签的亮度报告,再让 align 尝试一条非传播的 q25 曝光边:
python scripts/luma_exposure_alignment_experiment.py `
--dataset F:\data\capture `
--cache F:\data\capture_luma.npz `
--out F:\data\capture_luma.json
python -m camera_sync_audit align F:\data\capture\videos `
--intri F:\data\capture\intri.yml `
--extri F:\data\capture\extri.yml `
--luma-exposure-report F:\data\capture_luma.json `
--out F:\data\capture_sync_report只有原始 q25、全片和分窗都不命中边界、质量至少 0.20、相对主锚修正为 0.50–3.00 帧,并且整个序列候选数恰好为 1 时才应用;候选为 0 或多于 1 时逐位回退原推荐值。冻结规则在 Corgi/Hair/Yoga 三个连续标签序列上分别得到 1/4/0 个候选:仅 Corgi 0010–0011 被修正,Hair 和 Yoga 的正式接口均验证为逐位不变。该选项仍是自然场景精修,不具备受控信标的物理可观测性保证。
v0.8.0 修复了旧验证代码对已经参考相对化的误差再次减中位数的问题。按严格参考相对口径重算,v0.6.1 在三个连续标签序列的 72 路相机中由内部证据准入 61 路,pooled MAE 0.287、RMSE 0.424、P95 0.875、最大 1.385 帧;Corgi/Hair/Yoga 的准入 P95 分别为 1.005/0.733/1.028 帧。旧 0.261/0.393/0.732 只相当于当前的二次 gauge-aligned 诊断,不能作为主精度。自然场景输出仍只是带拒绝机制的图像模型估计;需要稳定高精度时应使用共同可见信标、电子共源音频/LTC 或共享硬件触发。
如果采集现场允许所有相机看到同一块屏幕或 LED 面板,推荐使用可见伪随机信标。该模式把每帧亮度建模为已知码序列经过曝光时间积分后的采样,同时拟合每台相机的码相位和曝光长度;相位差直接给出连续亚帧偏移。它不依赖场景动作、跨视角外观或相机标定。
先生成播放视频和清单:
python scripts/generate_sync_beacon.py `
--out F:\data\sync_beacon `
--display-fps 120 --length 4095播放 sync_beacon.mp4 时应关闭播放器补帧,并让每台相机的 ROI 主要覆盖信标内部矩形。采集开始时间的最大差异必须小于码周期的一半。随后运行:
python -m camera_sync_audit beacon-align F:\data\capture `
--manifest F:\data\sync_beacon\beacon_manifest.json `
--camera-fps 60 `
--roi-json F:\data\beacon_rois.json `
--out F:\data\beacon_alignment输出 beacon_alignment.json 和可复用的 beacon_traces.npz。只有相关性不低于 0.70、且相对于第二候选峰的间隔不低于 0.05 的相机才标记为 alignment_usable=true。受控 4 路 PNG 端到端基准位于 benchmarks/beacon_e2e/,P95 为 0.0200 帧;另一个 3 路 H.264 解码端到端基准位于 benchmarks/beacon_video_e2e/,P95 为 0.0201 帧。两者都是合成采集链验证,不代表无信标自然场景或物理相机阵列也能达到同一数值。
滚动快门相机可把 ROI 分成水平条带,中心行相位用于同步,行相位斜率给出整帧读出时间:
python -m camera_sync_audit beacon-align F:\data\capture `
--manifest F:\data\sync_beacon\beacon_manifest.json --camera-fps 60 `
--roi-json F:\data\beacon_rois.json --row-bands 8 `
--out F:\data\beacon_alignment_rs长序列可增加 --drift-window 128 --drift-step 64,报告线性模型 offset(frame) = offset_at_frame_zero + drift * frame,其中漂移同时以帧/帧和帧/千帧输出。线性模型只有在完整序列相关性相对常量模型至少提高 0.005 时才会被选择;否则只保留漂移诊断并继续使用常量相位。条带数应使每条带仍有足够像素;漂移窗口不得短于 48 帧。
v0.7.1 提供 audio-align。当每路视频内嵌同一个电子音频源或 LTC 时,程序把音轨重采样为单声道,使用 FFT 计算每个重叠区间精确归一化的 Pearson 相关,并对主峰做三点分数样本拟合。默认 8 kHz 分析采样率在 60 fps 下每个音频样本为 0.0075 帧;峰相关性、与远端次优峰的间隔和搜索边界共同决定一条边能否准入。多机默认由 3 个最长音轨组成多锚图,再用 Huber IRLS 求全局偏移;静音或损坏的最长音轨不会再拖垮整个阵列,报告同时保存连通分量和每条边的图残差。
python -m camera_sync_audit audio-align F:\data\capture `
--video-fps 60 --sample-rate 8000 `
--max-offset-seconds 10 --max-audio-seconds 120 `
--audio-graph multi_anchor --audio-anchors 3 `
--out F:\data\audio_alignment输出为 audio_alignment.json;加 --save-traces 可保存解码后的 audio_traces.npz。--audio-graph all 计算全对图,适合相机数较少、需要最大冗余的阵列;star 只保留兼容和快速诊断用途。长片或独立音频采样钟可增加:
python -m camera_sync_audit audio-align F:\data\capture `
--video-fps 60 --max-offset-seconds 10 `
--drift-window-seconds 10 --drift-step-seconds 5 `
--drift-search-radius-seconds 0.05 `
--out F:\data\audio_alignment_drift漂移分支至少需要 3 个通过相关与唯一性门控的局部窗口;只有线性残差相对常量模型至少改善 25%,且观测区间累计漂移至少 0.01 帧时才采用线性模型。4 路 AAC-in-MP4 合成媒体端到端基准位于 benchmarks/audio_video_e2e/:多锚图 6/6 边准入、4/4 路连通,MAE 0.000122、P95 0.000245、最大误差 0.000246 帧,最大图残差 0.00144 帧;开启漂移诊断后 0/6 边误选线性模型。该基准是电子共源合成音频,不包含真实麦克风、相机音视频管线时延或声传播。
普通环境声记录的是声波到达各麦克风的时刻,不等同于同一曝光时刻:声源到麦克风每相差 1 米约引入 2.9 ms,在 60 fps 下约 0.175 帧。若要把音频结果作为相机时钟真值,应使用分配到所有设备的电子共源音频/LTC,或根据已知声源/麦克风几何校正传播时间。Corgi、Hair、Yoga 三套公开 4DV 视频经 ffprobe 检查均只有 HEVC 视频流,因此不能验证音频通道。
汉服花瓣真实 36 机位 AAC 的全对图有 103/630 条边通过门控、36/36 路连通,图残差 P95 仅 0.027 帧;然而音频时钟与 .videoidx 的零整数映射相差 MAE 1.220、P95 1.923 帧。低图残差只表示音轨彼此自洽,不能消除声传播或每机音频/视频管线延迟。由于该拍摄没有证明电子共源输入,也没有 AV-to-exposure 标定,机器对照报告固定写 audio_exposure_correction_usable=false,位于 experiments/videostar_hanfu_audio_sidecar_comparison.json。
v0.8.0 新增 videoidx-align,用于同一 VisBulletTime/VideoStar 采集目录内的 *.mp4.videoidx。当前二进制布局是从本地样本反向分析得到,并未找到厂商公开格式规范;程序把每个 36 字节 INDX 记录的最后一个 uint32 明确标为 source_frame_counter_candidate,不会把推断字段伪装成已确认的厂商语义。
python -m camera_sync_audit videoidx-align F:\data\capture `
--reference auto --save-mapping `
--out F:\data\capture_videoidx输出 videoidx_alignment.json、可直接给 align --sync-json 使用的静态秒映射,以及可选的 videoidx_frame_mapping.npz。若相机名仅是补零差异,还会生成去前导零的映射。报告检查计数器跳变/倒退、PTS 步长、包偏移单调性和包边界,并把丢帧后的映射压缩为多个整数段。
若同一采集还运行了自然图像对齐,可显式生成“计数器整数权威 + 图像小数诊断”报告:
python -m camera_sync_audit videoidx-fuse `
F:\data\capture_videoidx\videoidx_alignment.json `
F:\data\sync_report\global_alignment.json `
--out F:\data\counter_anchored_alignmentcounter_anchored_alignment.json 的 application_index_shift_vs_reference_subframe 始终来自 .videoidx 整数计数器;图像整数分歧与折返到 [-0.5,0.5) 的小数残差单独记录为 diagnostic_only,不会修改可应用映射。
已对两套独立的真实 36 机位采集做只读验证:双人舞 100 fps 的 36/36 路共享起始计数、无计数跳变,汉服花瓣 120 fps 的 36/36 路同样如此;后者 PTS 的 8333/8334 tick 交替来自微秒整数化。所有 72 个 sidecar 的包偏移严格递增且未越过对应 MP4。这个证据能给出采集侧的精确整数帧对应,但记录里没有帧内曝光相位,所以报告固定写 subframe_status=unobservable。双人舞 MP4 虽含 AAC 轨道,样本全零,也不能作为亚帧时基。
双人舞 .btprj 与备份逐字节相同;可读内容主要是相机 IP、序列号和视频路径,没有 sync/trigger/exposure/timecode/clock 字段。配套 INI 的四元组数值形态与二维画面校正一致,但无字段语义,不能解释为时间。厂商页面说明 Z CAM E2 同步线可通过 Cat 6 菊花链做多机同步录制;这只能指导下一轮搭建,不能证明历史拍摄确实连接了同步线或曝光沿达到某个误差。
v0.9.0 把 PHYSICAL_VALIDATION_PROTOCOL.md 的真值契约实现成可执行接口。每路 CSV 必须来自清单声明的同一硬件时钟,帧号和可选源计数器严格递增;measured_exposure_interval 读取曝光起止并计算中点,exposure_midpoint 直接读取曝光中点。只有这两种语义可进入亚帧曝光验收;input_trigger_only 会保留帧级诊断,但固定标记曝光亚帧不可观测。
python -m camera_sync_audit hardware-align F:\data\capture\truth_manifest.json `
--out F:\data\capture\hardware_truth
python -m camera_sync_audit hardware-validate `
F:\data\capture\truth_manifest.json `
F:\data\capture_sync_report\global_alignment.json `
--out F:\data\capture\hardware_validationhardware-align 输出 hardware_alignment.json 与逐帧无损 hardware_alignment_series.npz,利用共享源计数器建立整数段和掉帧映射,再用曝光事件相位得到连续偏移;无共享计数器时,直接在共同硬件时钟上插值。所有相机必须存在严格共同的评价时间区间,否则 hardware-validate 拒绝比较,避免拿各自不同的时间中点掩盖漂移。验证只做一次参考相机相对化,不再减中位数,并报告静态 MAE/P95/最大误差、覆盖率、时间戳不确定度和全观测区间累计漂移误差。
6 路、800 帧、120 fps 的确定性硬件时间戳端到端基准位于 benchmarks/hardware_truth_e2e/,覆盖整数错位、亚帧曝光相位、线性漂移和一个源计数器缺口。解析真值相对解析模型的最大误差为 0.000122 帧;冻结估计报告的 MAE/P95/最大误差为 0.0160/0.0325/0.0350 帧,最大累计漂移误差为 0.01596 帧。它证明文件契约、符号、分段、CLI 和验证器闭环,不是物理相机实拍;报告因此始终写 stage_c_complete=false,直到完整重复采集矩阵通过。
同时提供 EasyVolcap/OpenCV 格式的 intri.yml 与 extri.yml 后,程序会在运动能量粗对齐附近增加标定几何证据:
python -m camera_sync_audit align Z:\dataset\4DV_dataset\corgi-release\corgi-release\videos `
--out F:\project\audit\corgi_calibrated_alignment `
--intri Z:\dataset\4DV_dataset\corgi-release\corgi-release\optimized\intri.yml `
--extri Z:\dataset\4DV_dataset\corgi-release\corgi-release\optimized\extri.yml `
--sync-json Z:\dataset\4DV_dataset\corgi-release\corgi-release\optimized\sync.json `
--fps 60 `
--max-offset 30 --min-correlation 0.05固定相机的 COLMAP 文本模型也可直接使用。模型目录必须包含 cameras.txt 和 images.txt,且每个相机只有一个注册位姿:
python -m camera_sync_audit align F:\data\frame_major_images `
--out F:\data\alignment `
--colmap-model F:\data\sparse_text\0 `
--max-offset 30 --min-correlation 0.05如果现有模型是 COLMAP 二进制格式,可先用 colmap model_converter --output_type TXT 转换;原始二进制模型不会被修改。
COLMAP 图像名与输入相机名会先做精确匹配;仅当两边都是十进制数字且去前导零后一一对应时,才允许例如 001 → 1 的安全别名。存在碰撞或缺失会报错,不按文件位置猜配。
Vexel/VisMoment 相机阵列导出的 Calibration JSON 可以直接用于原始 ZCam 视频,读取器会保留
OpenCV 径向畸变并按输入相机的自然排序关联标定条目:
python -m camera_sync_audit align F:\data\zcam_videos `
--out F:\data\zcam_alignment `
--vexel-calibration F:\data\calib0.3343.json `
--max-offset 30 --min-correlation 0.05该 JSON 的相机条目是位置式的,因此运行前必须确认条目顺序与视频自然排序一致;报告会记录 标定文件绝对路径以便审计。
高分辨率稠密运动图用于研究型空间定位时,可独立、逐相机生成缓存,避免24路数组同时驻留内存,并支持中断续跑:
python scripts/extract_motion_map_cache.py F:\data\capture\videos `
--out F:\data\motion_maps_w192 --map-width 192 `
--max-frames 1233 --workers 2 --resume每台相机完成后立即写入 .npy 和 partial_index.json;完整结束后生成兼容 --reuse-motion-map-cache 的 index.json。恢复时会核对输入路径、分辨率、帧窗、数组形状、通道和帧数,不会静默复用参数不一致或损坏的缓存。Corgi/Hair 的192宽受控消融表明该分辨率对 Hair 有明显收益、对 Corgi 无收益,增加到3000体素后两者均饱和,因此这个研究分支尚未进入默认推荐。
为了验证系统是否真的响应已知帧错位,可以用 --frame-start 固定对照时间窗,再用
--source-offset-json 对指定相机做虚拟重索引。原始图片不会被复制、改名或覆盖。映射值
n 表示该相机的本地帧 t 实际读取 frame_start + n + t;因此相对于未注入对照,报告中
预期新增的 index_shift_vs_reference 是 -n。
{
"cam03": -5,
"cam12": 4
}python -m camera_sync_audit align F:\data\capture `
--out F:\data\injected_alignment `
--frame-start 15 --max-frames 270 `
--source-offset-json F:\data\source_offsets.json `
--colmap-model F:\data\sparse_text\0这两个参数主要用于受控实验。严谨的注入评测应保持所有参数和参考相机相同,并比较
“注入报告减去未注入对照报告”;这样不会把原数据本身已有的曝光相位或未知同步误差算成
本次注入误差。没有 sync.json 或其他时间真值时,该实验验证的是已知扰动恢复能力,不能
替代原数据绝对同步精度评估。
标定模式包含四条互补证据:原始多尺度运动能量;反畸变后归一化视线坐标中的前景速度/面积轨迹;由 R/T 构造的轮廓代理点对极代价;以及不依赖人或动物类别的稠密对极平面运动能量。最后一条会把每个运动像素的世界射线按相机基线归入共同的对极平面,在正确时间偏移下比较两台相机的平面能量分布。
默认 --fusion-mode auto 不使用 sync.json 调权,而是根据各分支的相关质量、搜索边界、相机覆盖率和图循环残差自动计算权重。任何分支相对原始运动能量的影响都受限幅保护。系统同时检查运动像素比例与最强分支可靠性;证据不足时报告 observability.status=unobservable,偏移仅用于诊断,不应应用到数据。
首次运行会生成 foreground_tracks.npz/.json 和 motion_maps/。调参时可同时复用三类缓存:
python -m camera_sync_audit align Z:\dataset\capture\videos `
--reuse-feature-cache F:\report\feature_models.npz `
--reuse-spatial-cache F:\report\foreground_tracks.npz `
--reuse-motion-map-cache F:\report\motion_maps `
--intri Z:\dataset\capture\intri.yml --extri Z:\dataset\capture\extri.yml `
--out F:\report_tunedsync.json 的 Corgi/EasyVolcap 语义为:
actual_time(camera, frame_index) = frame_index / fps - sync[camera]
matching_index_c = reference_index + fps * (sync[c] - sync[reference])
因此它记录的是连续时间偏移(秒),不是“第几帧丢了”的事件表。标定文件和 sync.json 完全分开使用:intri/extri 进入求解,sync.json 只在结果完成后计算误差。
安装为命令后也可以使用:
camera-sync-audit align F:\data\capture --out F:\data\report| 参数 | 含义 | 建议 |
|---|---|---|
--max-offset |
搜索的最大绝对帧偏移 | 必须大于可能的累计错位;命中边界时应加大 |
--window-size |
局部建模窗口 | 动作稀疏时加大;需要精确定位跳变时减小 |
--window-step |
偏移轨迹采样间隔 | 越小定位越准、计算越多 |
--min-overlap |
相关计算最少有效转移数 | 通常 30–80 |
--min-correlation |
接受相关证据的基准 | 视角差异大时可降到 0.1–0.15 |
--change-penalty |
新增分段的惩罚 | 假事件多时调大,漏掉真实小跳变时调小 |
--tracking-lag-penalty |
滑窗 lag 突变的连续性代价 | 周期动作产生远距离假峰时调大 |
--reference |
最终对齐参考相机 | 默认自动选择稳定且图连接好的相机 |
--frame-start |
从每台相机第几帧开始读取(从 0 计) | 固定对照时间窗或跳过片头 |
--source-offset-json |
测试用的每相机虚拟读取偏移 | 仅用于受控注入;不要当作检测输入 |
--resize-width |
特征提取宽度 | 240–480 通常足够,不影响帧级时间精度 |
--motion-map-width |
稠密运动图宽度 | 默认 64;首次提取后可复用缓存 |
--epipolar-bins |
对极平面角度分箱数 | 默认 64;过小会丢失空间细节,过大更稀疏 |
--fusion-mode |
多分支融合方式 | 默认 auto;manual 仅用于消融研究 |
--min-observability |
最强证据可靠性下限 | 低于阈值时拒绝推荐偏移 |
--min-camera-confidence |
逐相机推荐值的内部置信度下限 | 默认 0.20;低于时保留诊断估计但推荐值为 null |
--max-branch-mad |
允许的四个核心分支 MAD;无论是否有明显占优分支都执行 | 默认 0.50 帧;可选姿态不会改变核心准入 |
--branch-dominance-ratio |
最强分支直接胜出的相对可靠度门槛 | 默认 1.50;选择过程不读取同步标签 |
--luma-exposure-report |
可选的标签无关亮度报告 | 只允许一条唯一、稳定的 q25 边;歧义时整分支回退 |
分段发现阶段的定位精度由窗口大小和步长决定,随后会在附近逐帧细化。报告中的 near_frame 仍是图像证据给出的估计位置,建议回看其前后若干原始帧确认。
报告里的帧位置使用从 0 开始的数组索引;如果你的文件序号从 1 开始,查看文件时相应加 1。
输出目录包含:
report.html:结论、轨迹、事件和两两相关的可视化报告;report.json:完整机器可读结果;camera_summary.csv:每相机总体结论;pairwise_offsets.csv:完整相机时延矩阵;offset_timeline.csv:每个滑窗的偏移和置信度;events.csv:检测到的跳变事件。feature_models.npz/.json:运动模型缓存,调参复跑时可跳过视频解码。foreground_tracks.npz/.json:低分辨率动态前景轨迹缓存;motion_maps/:双通道稠密运动图缓存;energy_pairwise.csv、epipolar_energy_pairwise.csv、track_motion_pairwise.csv:各证据分支的两两时延与图残差。
全局对齐报告同时保留两类值:原始/融合估计用于诊断,recommended_index_shift_vs_reference_subframe 才是证据准入后可应用的亚帧修正。顶层 evidence_admission 给出覆盖率、推荐参考相机和拒绝原因;有 sync.json 时,recommended_sync_validation 评估推荐方法的全覆盖结果,admitted_sync_validation 只评估在看标签之前已准入的相机。
v0.9.1 在 diagnostics.subframe_health(标定模式还包括 subframe_health_by_branch)中输出亚帧数值健康度:lag_subframe 小数部分的10箱直方图、χ²和 p 值,恰好落在半帧边界的比例,以及新生成边的原始三点抛物线顶点和裁剪比例。样本不足、低质量边过滤视图和警告原因都会显式记录。这些字段固定为 diagnostic_only=true:真实同步阵列也可能正确集中在整数相位,不能仅凭非均匀分布拒绝结果,必须再结合可观测性或独立分支证据。
复用缓存:
python -m camera_sync_audit analyze F:\data\capture `
--reuse-feature-cache F:\data\report\feature_models.npz `
--out F:\data\report_tuned每个细化后的事件同时包含 near_local_frame(异常相机自身应检查的位置)、near_reference_frame(参考相机时间轴位置)和 coarse_window_frame(细化前的滑窗估计)。
三个符号必须区分:
global_content_index = local_frame_index + clock_offset
camera_b[t + pair_lag] 匹配 camera_a[t]
camera_frame_index = reference_frame_index + index_shift_vs_reference
例如某相机的 clock_offset 从 +3 跳到 +7,表示它的本地帧号现在对应晚 4 帧的内容,通常意味着中途丢了约 4 帧。真正重排文件时,应使用 alignment_segments_vs_reference,而不是直接使用中心化时钟值。
- 至少三台相机才能通过“多数一致”判断哪台相机异常;只有两台时只能确定相对错位。
- 场景中必须存在能被多数视角看到的共同运动。长时间完全静止的窗口会被标记为低置信度。
- 多台相机在同一时刻以相同方式丢帧,对纯图像方法不可观测,因为它们彼此仍然同步。
- 连续偏移是图像运动/几何目标的亚帧估计;它不自动等于真实曝光相位。滚动快门、曝光相位和音视频时钟需要时间戳、触发或共同光信号验证。
- 自然场景亚帧误差的 66%–90% 是逐相机的加性偏置,不是随机的成对噪声(Corgi/Hair/Yoga 极平面分支分别为 90.1%/77.4%/65.8%)。这类误差天然循环一致,图残差看起来很小也不代表准确;加密图拓扑或换鲁棒核不会降低它。该偏置由"场景内容 × 视角"产生,跨序列不相关(Pearson
-0.07…-0.19),因此也不能标定一次后复用。详见RESEARCH_PLAN.md的结构解剖一节。 audio-align对齐的是音轨采样时间;普通麦克风还包含声传播和设备内部音频/视频管线延迟。电子共源 LTC/音频也必须实测其相对曝光的固定管线延迟。.videoidx的共享候选计数器可证明整数帧对应关系,但未编码帧内曝光相位;反向分析字段必须与独立触发/光电真值交叉验证。- 小数部分峰锁、半帧端点堆积和抛物线裁剪可以暴露数值退化,但不是准确性的充分判据;Corgi 的最佳极平面分支也会触发峰锁警告,因此这些指标不会单独改变准入结果。
- 如果相机帧率本身不同,应先按时间戳统一名义帧率;报告的漂移项可以发现问题,但不会在本版本中重采样视频。
物理验收的接线、采集矩阵、真值 CSV/JSON 契约、盲测纪律和 <0.05 帧 P95 门槛见 PHYSICAL_VALIDATION_PROTOCOL.md。
python -m unittest discover -s tests -v当前73项测试覆盖大于 ±1/±2 的固定错位、符号约定、多个分段跳变、音频周期歧义、静音参考容错、多边图桥接、硬件曝光真值、增量缓存恢复、不同相机裁剪比例、计数器整数锚定、稠密光流描述子、空间能量图、亚像素 tracklet 积分、多视角身份链、FGW 身份、响应曲线反演、Track3D 原始帧坐标恢复与准入门、冲突感知闭环身份图与留一相机三角化、标定轨迹连续时延、连续小数延迟细化和亚帧健康诊断,以及避免把单台异常错误传播给其他正常相机。
标定的人体采集可以显式启用 RTMPose 关节分支。它默认只使用手腕和脚踝做时间细化,并降低静态关节/时段的权重;这是可选增强,不会在普通场景中自动启用。
python -m pip install -e ".[pose]"
python -m camera_sync_audit align F:\data\videos `
--intri F:\data\intri.yml --extri F:\data\extri.yml `
--human-pose --pose-joints extremities `
--pose-motion-weighting 0.85 `
--out F:\data\sync_report姿态结果缓存为 human_pose_tracks.npz/.json,求解时可以不重新解码。重复 --reuse-pose-cache 可把多个时间窗口作为独立边放入同一个 Huber-IRLS 图:
python -m camera_sync_audit align F:\data\videos `
--intri F:\data\intri.yml --extri F:\data\extri.yml `
--reuse-pose-cache F:\cache\window_1\human_pose_tracks.npz `
--reuse-pose-cache F:\cache\window_2\human_pose_tracks.npz `
--out F:\data\sync_report_multiwindow也可以让程序从全序列运动模型中自动选择互不重叠的高活动窗口:
python -m camera_sync_audit align F:\data\videos `
--intri F:\data\intri.yml --extri F:\data\extri.yml `
--human-pose --pose-auto-windows 2 --pose-window-length 220 `
--out F:\data\sync_report_auto_windows完整的研究门槛、文献、公开数据结果、失败路线与下一轮计划见 RESEARCH_PLAN.md。
v0.9.2 还提供实验性的 assign_track_identities / paired_track_views API,用单路统计和视图内轨迹结构建立不依赖时间初值的跨视角身份。盲匹配在 Corgi/Hair 上接近语义 oracle;进一步的共享3D速度残差把两者 P95 降到 0.9995/0.8871。第三序列 Bar 的原始细化会恶化,但最终轮信赖域边界门控将其整条拒绝并回退,同时明确列出不可观测相机17。由于 Bar 尚未被真正改善,该能力暂不接默认 align;机器可读证据见 experiments/trajectory_identity_summary_2026_08_01.json 和 experiments/shared_motion_spline_summary_2026_08_01.json。
成对极线导数的低成本替代也已完成反证:直接差分和5帧中值平滑版本都在冻结序列上恶化,因此不会进入 CLI。完整负结果见 experiments/pairwise_epipolar_velocity_summary_2026_08_01.json。
三窗口 multistart 留出选择也已完成预声明审计。Corgi 三窗赢家不一致(track_motion / semantic_absolute / track_motion),Hair 也不一致(epipolar_energy / global_fused / track_motion);跨窗排名间隔分别只有 0.0111 / 0.0667,均低于 0.10。第三窗口中,内部留出目标偏爱的 track_motion 事后 P95 达 1.2498 / 1.5922 帧,证明当前自重建速度损失会偏爱自洽但错误的时钟。该分支只保留诊断用途,不得自动覆盖 align 的核心结果;机器可读证据见 experiments/shared_motion_multistart_summary_2026_08_01.json。
进一步把相机拆成互不相交的两组并分别重建 3D,也未解决该退化:六窗口赢家仍随窗口变化,Hair 两窗继续错误选择 track_motion。这条反证说明仅做 camera holdout 不足,下一版必须同时 hold out 相机与时间,测试由早期区间拟合的运动模型能否预测后期区间;结果见 experiments/shared_motion_split_group_summary_2026_08_01.json。
相机+时间双重留出的 3D→3D 预测仍偏爱平滑零偏移,在 6 个强标签窗口中有 4 个选择 zero,因此也被否定。验证端改成原始 2D 关节点直接重投影后,六窗不再选择 zero / point_geometry / track_motion,且事后最优候选始终位于内部前四;但最大选择遗憾仍有 0.5204 帧,只能用作坏分支筛除诊断。进一步的逐相机局部曲线在 Hair 略改善、Corgi 明显恶化,已终止为自动细化候选。证据分别见 experiments/shared_motion_camera_time_holdout_summary_2026_08_01.json、experiments/shared_motion_heldout_reprojection_summary_2026_08_01.json 和 experiments/heldout_reprojection_refinement_summary_2026_08_01.json。
Joker 40 相机外部验证进一步固定了硬件优先级:.videoidx 证明 40 路的 1720 个源计数器完全一致、整数偏移全为 0;300 帧纯图像结果为 MAE 0.2468、P95 0.7814、整数一致率 87.5%。标定分支没有纠正误判,MAD 门控降至 23/40 覆盖率后整数一致率仍为 86.96%。因此共享计数器必须覆盖图像给出的整数部分,图像小数部分仍只能诊断;证据见 experiments/joker_videoidx_image_summary_2026_08_01.json。v0.9.3 同时修复了极平面 motion-map 错误要求两相机具有相同工作高度的问题,现支持真实阵列中不同裁剪比例的相机。
Joker 的预计算低分辨率稠密光流还完成了 36 路长时序实验。每个流场被压缩成 40 维幅值、形变、方向谱与空间能量描述子,只用这些描述子估计,.videoidx 的零整数映射和随机注入均只在事后评分。300 帧仍失败;600 帧连续细化达到零偏移 MAE 0.0250、P95 0.0484,899 帧离散相关达到 MAE 0.0253、P95 0.0549。全特征连续分支的三组随机亚帧注入响应为 MAE 0.0651–0.0843、P95 0.1471–0.1730;通道消融后,以全特征粗定位、仅空间能量连续细化的双分支把三种子平均 MAE 降至 0.0495,但 P95 仍为 0.1091–0.1376,且连续零偏 P95 恶化到 0.1914。因此该路线证明长时序稠密运动能显著降低宽基线视角偏置,也证明当前尾部小数响应尚未达到 <0.05 帧验收;它保留为实验诊断,不进入默认估计器。冻结证据见 experiments/joker_dense_flow_summary_2026_08_01.json。
第二轮对双分支做了严格的训练/留出审计:只在种子 20260801 上选择固定凸融合权重和分支分歧降权尺度,两个未参与选择的种子得到注入响应 MAE 0.0437/0.0578、P95 0.1065/0.1128。三个重叠 600 帧窗口还发现末段运动可观测性崩溃:中位边相关从约 0.68 降至 0.45,图残差 P95 从约 0.70 升至 5.32 帧;该坏窗可由内部指标拒绝,但选择好窗没有进一步降低留出 P95。Fisher-z 通道聚合的留出平均 P95 为 0.1180,白化典型相关则恶化至训练 P95 0.2196,后者已终止。证据见 experiments/joker_dense_flow_fusion_summary_2026_08_01.json。
第三轮依据 SyncTrack4D 与 WACV 2026 Pose Aggregation 的原始论文,测试了保留空间结构的轻量替代:将每帧光流压成 12×32 残差能量图,对 4×10 阵列的 59 条相邻边做仿射 ECC,再在已配准时空体中搜索小数延迟。59/59 条边均成功配准,中位 ECC 0.972,但 0.1 帧信赖域的注入 P95 仍为 0.263,扩大信赖域继续恶化。这证明平均运动区域的二维配准不能替代跨视角动态点身份;该分支终止。新的双线性流采样与亚像素 tracklet 积分器已在 5 台真实相机、3 个时间窗上探测:32 帧 tracklet 的最差完整存活率 98.44%、平均 99.74%,因此下一轮可以直接进入“持久轨迹 + 视角不变外观 + 关系几何”匹配。论文哈希、缓存定义和实验结果见 experiments/dense_tracks_ecc_summary_2026_08_01.json。
第四轮用本地 DINOv2 ViT-L/14 检验“身份”和“时钟”是否能由同一表示解决。相邻相机 2-3/3-4/4-5 得到 57/38/62 个互相最近邻,匹配点极线误差中位数仅为随机配对的 0.576%/3.776%/0.070%,证明外观身份线索有效;但固定身份后的二维积分轨迹极线搜索最佳注入 P95 仍为 0.296 帧。96 帧 DINO 全局或流加权动态池化序列在三次插值下的跨视角注入 P95 为 0.438–0.677 帧,而同相机正对照为 0.066 帧。两种时间目标均被拒绝且未接入 CLI;下一步只保留 DINO 身份,时间表示转向共同 3D 或显式视角不变姿态运动。完整证据见 experiments/dino_tracklet_summary_2026_08_01.json。
第五轮将 DINO 身份与留一相机 3D 重建连接,并用官方 TAPIR 替换低分辨率光流 Euler 积分。在相同 4 相机、31 条多视角身份、33 帧条件下,位置残差 λ=0 的注入 MAE/P95 从 0.279/0.490 降至 0.0648/0.1472 帧,证明原始图像点跟踪是正向变量。但逐轨迹偏移中位数、首帧几何分位过滤、9 相机传递身份和 96 帧长轨迹均恶化;后两者 P95 分别为 0.887/0.484 帧。当前分支仍未超过稠密流融合约 0.11 帧的留出 P95,不接入默认 CLI;下一版必须使用环一致全局身份/FGW 与周期性重锚,不能依赖成对匹配传递闭包。冻结证据见 experiments/dino_tapir_multiview_3d_summary_2026_08_01.json。
第六轮实现了按 DINO+极线置信度排序、禁止同相机双身份并记录冗余闭环边的全局身份图。局部 9 相机的 10 条近邻边产生 410 条准入边、88 个三视角以上组件,但只有 4 个组件得到真实闭环确认;使用全部组件时位置/速度 P95 为 1.124/0.727 帧,证明贪心冲突消解仍不能替代 FGW/多图匹配。另在帧 1/33/65 建立三个互不重叠的 33 帧 DINO+TAPIR 重锚窗口,单窗 P95 为 0.147/0.238/0.650,三窗中位响应为 MAE 0.0589、P95 0.1540,没有优于最佳单窗;无标签指标也不能安全识别所有坏窗。两条分支均只保留研究接口,证据见 experiments/dino_global_reanchor_summary_2026_08_01.json。
第七轮实现了真正的平衡 FGW Frank–Wolfe 指派,而不是此前的离散近似。合成未知置换可精确恢复,但 Joker 上无论锚相机 2 还是阵列中心 medoid 相机 5,加入二维轨迹关系项都未改善时差:锚 2 的外观/FGW 位置 P95 为 0.283/0.472,锚 5 的四个关系权重 P95 为 0.363–0.518。因此二维关系代价即使更低,也不能作为时间身份质量代理,该路线终止。
第八轮直接使用 Joker COLMAP 的共享 point3D_id 观测像素启动 TAPIR,并以速度置信度筛选动态点。四相机共有 1,236 个三视角以上共享点,1,214 个与速度缓存精确对齐;冻结 confidence>=0.7、192 点、几何 q75、逐轨迹运动中位数和 λ=1 后,训练注入 MAE/P95 为 0.0658/0.1251 帧。再仅用 ±0.2/±0.4 注入拟合逐相机单调响应反演,在互斥的 ±0.1/±0.3/±0.5 共24例上,MAE/P95 从 0.0799/0.1791 降到 0.0389/0.0986 帧。这是当前自然图像 3D 轨迹路线最强的独立注入结果,但仍只有 Joker 单场景、身份来自预计算 COLMAP、且没有物理曝光真值,所以保持 diagnostic_only,不接默认 CLI。冻结证据见 experiments/fgw_sparse3d_response_summary_2026_08_01.json。
第九轮把该分支封装为可直接读取帧矩阵目录或逐相机 MP4 的端到端研究命令,支持每机不同源帧起点、可选速度缓存、确定性 TAPIR、原始帧坐标恢复、留一相机 3D、响应校准、留出注入和最终参考相机 JSON。固定种子重复运行的 TAPIR 数组逐元素完全一致,3D 结果也完全一致;Joker 的确定性留出增量校准 MAE/P95 为 0.0306/0.0912 帧。但注入曲线只标定变化量增益,不能识别 baseline 零点,正式输出不会再把 baseline 直接送入反函数。
首次跨场景运行从 Corgi 四路原始 MP4 和无标签粗起点自动重建 333 个 COLMAP 点,得到 245 个三视角以上精确身份;这些点的33帧中位位移却只有 3.59–5.24 像素,明显被静态背景主导。其校准注入 P95 0.435、留出校准 P95 0.700,固定 <0.20 内部门在读标签前即拒绝;事后严格验证 MAE/P95 为 0.984/2.132 帧。动态掩膜 SIFT 整数探针同样选错。结论是“任意稀疏 COLMAP 身份”不能替代动态实例身份;该分支现在能安全输出或拒绝,但尚未具备跨场景 4DV 级能力。完整证据见 experiments/track3d_e2e_crossscene_summary_2026_08_01.json。