"""把 search_results.json 渲染成结构化 markdown 综述。 用法: python3 research/gen_review_from_json.py \ --in research/search_results.json \ --out research/zed2i_arxiv_live_review.md """ from __future__ import annotations import argparse import json from collections import Counter from datetime import datetime # 主题中英文标签 + 项目阶段映射 TOPIC_META = { "stereo_matching": { "zh": "A. 双目立体匹配(被动深度)", "stage": "M2-3 / M3-4", "intro": "对应 ZED 双目深度算法的替换/超越路线。关注零样本泛化、Transformer 架构、神经几何编码。", }, "visual_inertial_slam": { "zh": "B. 视觉惯性 SLAM / VIO", "stage": "M2-1 / M3-3", "intro": "对标 ZED 内建 VIO 的替代方案。关注 IMU 融合、长时鲁棒、动态环境。", }, "gaussian_splatting_slam": { "zh": "C. 3D Gaussian Splatting SLAM(融合建图)", "stage": "M3-5 / M4", "intro": "把 3DGS 作为 SLAM 后端,实现实时定位+建图+渲染一体化。world model 训练的核心视觉表征。", }, "monocular_depth_foundation": { "zh": "D. 单目深度基础模型", "stage": "M3-4", "intro": "Depth Anything / Marigold / Metric3D / UniDepth 等通用深度模型,作为双目深度失效的兜底。", }, "indoor_rgbd_dataset": { "zh": "E. 室内 RGB-D 数据集与重建", "stage": "M4-1 / M4-2", "intro": "可参考的数据集设计、评测基准、室内几何重建方法。", }, "world_model_video": { "zh": "F. 视频世界模型(下游应用)", "stage": "M4-4", "intro": "本项目数据 pipeline 的最终下游:训练能预测未来视频/动作的 world model。", }, "zed_camera": { "zh": "G. ZED 相机相关应用工作", "stage": "全周期", "intro": "用 ZED 系列采集数据的应用论文,参考其采集协议、评测方式、参数配置。", }, "orbbec_gemini": { "zh": "H. Orbbec / Femto / Azure Kinect 相关工作", "stage": "国产化 / 替代硬件", "intro": "奥比中光、乐视/微视 Femto、微软 Azure Kinect 等 RGB-D 相机的应用论文。", }, "rgbd_indoor_reconstruction": { "zh": "I. RGB-D 室内重建", "stage": "M2-2 / M3-5", "intro": "RGB-D 输入下的室内场景重建,与本项目的房间级建图任务高度对齐。", }, "neural_stereo_depth": { "zh": "J. 神经立体深度(指定 RAFT/IGEV/Foundation 家族)", "stage": "M3-4", "intro": "针对 RAFT-Stereo / IGEV-Stereo / FoundationStereo 等核心立体匹配方法的衍生与改进。", }, } CN_KEYWORDS = [ # 中国机构关键词(用于粗筛"国产团队"论文) "tsinghua", "peking", "fudan", "shanghai jiao", "zhejiang", "ustc", "huazhong", "harbin", "tianjin", "wuhan", "xi'an jiaotong", "xi'an", "nanjing", "chinese academy", "cas ", "casia", "hkust", "cuhk", "hku", "polyu", "city university of hong kong", "alibaba", "tencent", "bytedance", "baidu", "huawei", "megvii", "sensetime", "ant group", "didi", "meituan", "xiaomi", "damo", "noah", "arc lab", "shanghai ai lab", ] def is_china_team(authors: list[str], summary: str) -> bool: """粗略判断是否有国产团队作者(依赖摘要中机构提及)。""" lower = (" ".join(authors) + " " + summary).lower() return any(k in lower for k in CN_KEYWORDS) def fmt_authors(authors: list[str], max_n: int = 4) -> str: if not authors: return "(作者信息缺失)" if len(authors) <= max_n: return ", ".join(authors) return ", ".join(authors[:max_n]) + f" 等 ({len(authors)} 人)" def render_paper(p: dict, idx: int) -> list[str]: aid = p.get("arxiv_id", "") title = p.get("title", "").strip().rstrip(".") pub = p.get("published", "")[:10] authors = p.get("authors", []) summary = p.get("summary", "") cats = p.get("categories", []) url = p.get("url") or f"https://arxiv.org/abs/{aid}" china_tag = " 🇨🇳" if is_china_team(authors, summary) else "" lines = [ f"#### {idx}. [{aid}]({url}) — {title}{china_tag}", f"- **发表**: {pub} | **分类**: {', '.join(cats[:3]) if cats else '-'}", f"- **作者**: {fmt_authors(authors)}", f"- **摘要**: {summary}", "", ] return lines def render_topic(topic: str, papers: list[dict]) -> list[str]: meta = TOPIC_META.get(topic, {"zh": topic, "stage": "-", "intro": ""}) out = [ f"## {meta['zh']}", f"**项目阶段**: {meta['stage']} | **论文数**: {len(papers)}", "", meta["intro"], "", ] # 按时间排序(新到旧) papers_sorted = sorted(papers, key=lambda p: p.get("published", ""), reverse=True) for i, p in enumerate(papers_sorted, 1): out.extend(render_paper(p, i)) return out def render_github_topic(topic: str, repos: list[dict]) -> list[str]: out = [f"### GitHub: {topic}(按 stars 排序)", ""] for r in repos: out.append( f"- [{r['name']}]({r['url']}) — ⭐ {r['stars']:,} | " f"{r.get('language', '-')} | {r.get('description', '')[:120]}" ) out.append("") return out def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--in", dest="inp", default="research/data/search_results.json") ap.add_argument("--out", default="research/spatial-memory/zed2i_arxiv_live_review.md") args = ap.parse_args() with open(args.inp, "r", encoding="utf-8") as f: data = json.load(f) arxiv = data.get("arxiv", {}) github = data.get("github", {}) meta = data.get("meta", {}) # 统计 total_papers = sum(len(v) for v in arxiv.values()) total_repos = sum(len(v) for v in github.values()) all_papers = [p for ps in arxiv.values() for p in ps] china_papers = [p for p in all_papers if is_china_team(p.get("authors", []), p.get("summary", ""))] cat_counter: Counter[str] = Counter() for p in all_papers: for c in p.get("categories", [])[:1]: cat_counter[c] += 1 lines: list[str] = [ "# ZED 2i 数据 Pipeline 实时 arXiv 综述(自动生成)", "", f"> **数据来源**:[`research/search_results.json`](search_results.json) 由 [`research/search_info.py`](search_info.py) 通过 HTTP 代理 `127.0.0.1:6984` 拉取自 arxiv.org / api.github.com。", f"> **生成时间**:{meta.get('generated_at', '-')}", f"> **检索代理**:{meta.get('proxy', '-')}", f"> **每主题最多**:{meta.get('max_results_per_topic', '-')} 篇", "", "## 0. 数据概览", "", f"- **arXiv 论文总数**:{total_papers}", f"- **arXiv 主题数**:{len(arxiv)}", f"- **疑似国产团队论文**:{len(china_papers)}(占比 {len(china_papers)*100//max(total_papers,1)}%;🇨🇳 标记,启发式判断)", f"- **GitHub 仓库总数**:{total_repos}", f"- **GitHub 主题数**:{len(github)}", "", "### 0.1 主类目分布(arXiv primary_category)", "", ] for cat, n in cat_counter.most_common(10): lines.append(f"- `{cat}`: {n}") lines.append("") lines.extend( [ "### 0.2 与本项目框架的映射", "", "| 项目阶段 | 主线主题 | 论文数 |", "|---|---|---|", ] ) for topic, m in TOPIC_META.items(): n = len(arxiv.get(topic, [])) lines.append(f"| {m['stage']} | {m['zh']} | {n} |") lines.append("") lines.extend( [ "---", "", "# 第一部分 · arXiv 论文(按主题分组,时间新→旧)", "", ] ) for topic in TOPIC_META.keys(): papers = arxiv.get(topic, []) if not papers: continue lines.extend(render_topic(topic, papers)) lines.append("---") lines.append("") lines.extend( [ "# 第二部分 · GitHub 仓库(按 stars 排序)", "", ] ) for topic, repos in github.items(): lines.extend(render_github_topic(topic, repos)) lines.extend( [ "---", "", "# 第三部分 · 关键洞察与项目对接建议", "", "## I.1 最值得关注的新论文(按相关性挑选)", "", "下方挑选每个主题中**与本项目最相关的 3 篇**(基于标题/摘要语义判断):", "", ] ) # 简单挑选每个主题前 3 篇放在洞察区 for topic in ["stereo_matching", "visual_inertial_slam", "gaussian_splatting_slam", "monocular_depth_foundation"]: papers = arxiv.get(topic, []) if not papers: continue meta = TOPIC_META[topic] lines.append(f"### {meta['zh']} → {meta['stage']}") for p in sorted(papers, key=lambda x: x.get("published", ""), reverse=True)[:3]: lines.append(f"- **[{p['arxiv_id']}]({p['url']})** {p['title']}({p['published'][:10]})") lines.append("") lines.extend( [ "## I.2 后续动作清单", "", "- [ ] 把上述每主题的 Top-3 论文加入 [`research/zed2i_stereo_vio_arxiv_review.md`](zed2i_stereo_vio_arxiv_review.md) 第 F 节的论文映射表", "- [ ] 对 🇨🇳 标记的论文重点核查机构归属,更新 G 节国产团队清单", "- [ ] 把 GitHub 仓库中 stars > 5k 的项目加入 [`plans/camera/github_opensource_projects.md`](../plans/camera/github_opensource_projects.md)", "- [ ] 每周重跑 [`research/search_info.py`](search_info.py) 增量更新", "", "## I.3 复现方法", "", "```bash", "# 通过 127.0.0.1:6984 代理拉取最新数据", "HTTPS_PROXY=http://127.0.0.1:6984 HTTP_PROXY=http://127.0.0.1:6984 \\", " python3 research/search_info.py \\", " --proxy http://127.0.0.1:6984 \\", " --max-results 10 --delay 5.0 \\", " --out research/search_results.json", "", "# 渲染为 markdown", "python3 research/gen_review_from_json.py \\", " --in research/search_results.json \\", " --out research/zed2i_arxiv_live_review.md", "```", "", "---", "", "**说明**:", "- 本文档由脚本自动生成,可重复执行覆盖", "- 🇨🇳 标记基于作者/摘要中是否包含中国机构关键词的启发式判断,**仅供参考,需人工复核**", "- 摘要截断到 400 字符以控制文档体积", "- 与 [`zed2i_stereo_vio_arxiv_review.md`](zed2i_stereo_vio_arxiv_review.md)(人工综述)互为补充:人工综述给方法论与映射,本文档给最新原始素材", ] ) with open(args.out, "w", encoding="utf-8") as f: f.write("\n".join(lines)) print(f"✅ 生成: {args.out}") print(f" arXiv: {total_papers} 篇,国产疑似: {len(china_papers)}") print(f" GitHub: {total_repos} 仓库") return 0 if __name__ == "__main__": raise SystemExit(main())