当前位置: 首页 > news >正文

从赛季数据到模板图库:深入解析 tft_fetch_assets.py和TFT 截图识别的资源构建链路

摘要

在TFT 阵容顾问项目里,tft_fetch_assets.py 不仅一个“下载图片”的脚本,如果把整个项目串起来看,该文件承担的是模板资源构建器的角色。它上游连接 tft_data_manager.py 生成的赛季数据,下游直接服务 tft_screen_capture.py 的 OpenCV 模板匹配,是截图识别链路里非常关键的一层。

一. 从整个项目的运行流程理解tft_fetch_assets.py文件的作用

tft_data_manager.py ↓ 生成 tft_champion_db.json / tft_item_db.json ↓ tft_fetch_assets.py ↓ 下载并标准化 tft_assets/champions/*.png 与 tft_assets/items/*.png ↓ tft_screen_capture.py ↓ 模板匹配识别英雄、装备、星级、站位 ↓ tft_converter.py / tft_web_ui.py

tft_fetch_assets.py 并不是一个孤立的辅助脚本,而是整个识别系统的“资源准备层”

它的核心职责如下:

1. 从本地赛季数据库中读取英雄和装备列表

2. 从 DDragon 和 CommunityDragon 下载正确的 TFT 图片资源

3. 统一命名与尺寸,构建标准模板库

4. 输出到 tft_assets/,让后续识别模块可以直接加载

二. "数据驱动"的数据脚本

我们项目的资源脚本没有采用直接写死列表的形式,如:

CHAMPIONS = ["Draven", "Leona", "Braum"] ITEMS = ["Deathblade", "BlueBuff"]

这种写法短期看很方便,但一到赛季更新就很难维护。tft_fetch_assets.py 采用的是更稳定的方案:所有下载目标都来自 tft_data_manager.py 生成的 JSON 数据库

核心代码如下:

def load_champion_db() -> dict: p = DB_DIR / "tft_champion_db.json" if not p.exists(): print(f" ✗ 找不到 {p},请先运行: python tft_data_manager.py") return {} db = json.loads(p.read_text(encoding="utf-8")) print(f" 英雄数据库: {len(db)} 个英雄 (来自 {p})") return db def load_item_db() -> dict: p = DB_DIR / "tft_item_db.json" if not p.exists(): print(f" ✗ 找不到 {p},请先运行: python tft_data_manager.py") return {} db = json.loads(p.read_text(encoding="utf-8")) filtered = {k: v for k, v in db.items() if _item_is_valid(k)} print(f" 装备数据库: {len(filtered)} 个装备 (来自 {p},原始 {len(db)} 条,已过滤)") return filtered

我们在这段代码中采用了以下的工程思想:
脚本不负责定义资源,而负责消费标准数据。

这样做有两个直接好处:

1. 赛季更新时,只需要先更新数据库,资源下载逻辑基本不变

2. 文件命名、业务 ID、模板库 key 可以天然保持一致

三. 代码文件是如何获取上层数据的?

在 tft_data_manager.py 里,我们会从 CommunityDragon 或 DDragon 拉取赛季数据,再整理成统一格式。例如英雄和装备数据会被整理成这样的字典结构:

champ_db[api] = { "id" : api, "short_id": short, "name_en" : c.get("name", short), "cost" : c.get("cost", 0), "traits" : traits, "stats" : c.get("stats", {}), } item_db[api] = { "id" : api, "name_en" : item.get("name", api), "desc" : item.get("desc", ""), "unique" : item.get("unique", False), "composition": item.get("composition", []), }

tft_fetch_assets.py 并不是随便抓图片,而是基于上游已经整理好的标准业务主键 apiName 来抓图。

例如:

1. 英雄模板最终会命名成 TFT16_Draven.png

2. 装备模板最终会命名成 TFT_Item_Deathblade.png

这和后面识别模块加载模板时使用的 key 是一一对应的。

四. 采取"双源回退"的下载策略

我们采用了如下的资源获取策略:优先 DDragon,失败后回退 CommunityDragon

""" 下载策略(按优先级): 英雄: 1. DDragon /img/tft-champion/{apiName}.png 2. CommunityDragon /game/assets/characters/{lc_id}/hud/{lc_id}_square.*.png 装备: 1. DDragon /img/tft-item/{apiName}.png 2. CommunityDragon /game/assets/items/icons2d/{lc_id}.png """

这个设计考虑了两个资源源站的不同特点:

1. DDragon 路径更稳定,资源命名更规整

2. CommunityDragon 更贴近游戏内部资源结构,适合作为回退来源

项目通过这样的下载策略构建了一条稳定的模板获取链路。

核心代码如下:

def fetch_champions(ver: str, failed_log: list): champ_db = load_champion_db() if not champ_db: return out = ASSETS_DIR / "champions" out.mkdir(parents=True, exist_ok=True) cdn = f"https://ddragon.leagueoflegends.com/cdn/{ver}" for api_name, entry in champ_db.items(): dest = out / f"{api_name}.png" if dest.exists() and dest.stat().st_size > 500: continue short_id = entry.get("short_id", api_name) data = None for dd_name in _ddragon_champ_candidates(api_name, short_id): data = dl(f"{cdn}/img/tft-champion/{dd_name}.png") if data: break if not data: for url in _cdragon_champ_urls(api_name, short_id): data = dl(url) if data: break if data: dest.write_bytes(resize_image(data, ICON_SIZE)) else: failed_log.append(("champion", api_name, str(dest)))

按数据库遍历英雄,用业务 ID 构建模板文件名,优先下载 DDragon 的 TFT 专属头像,下载不到再回退 CommunityDragon。

可以看到脚本使用的是 /img/tft-champion/,而不是普通的 /img/champion/,这是因为普通英雄立绘和 TFT 游戏内实际头像风格并不一样,模板匹配时必须保证模板和截图中的目标形态尽量一致。这个选择,直接决定了后面的 OpenCV 匹配是否稳定。

五. 对英雄名称做候选兼容

实际使用第三方资源站时,不仅需要考虑“有没有图”,还要考虑“命名是否完全一致”

这里我们写了一个候选名函数:

def _ddragon_champ_candidates(api_name: str, short_id: str) -> List[str]: candidates = [ api_name, f"TFT_{short_id}", ] _CASE_FIXES = { "Chogath" : "ChoGath", "Kaisa" : "KaiSa", "Leblanc" : "LeBlanc", "Monkeyking": "Wukong", } lc = short_id.lower() for dd, db in _CASE_FIXES.items(): if lc == db.lower(): candidates.insert(0, f"TFT16_{dd}") return candidates

候选名函数的作用:

1. 兼容历史命名格式

2. 兼容大小写差异

3. 兼容某些特殊英雄的资源命名

六. CommunityDragon 回退路径也做了兼容封装

除了 DDragon 候选名,脚本还把 CommunityDragon 的回退路径统一生成了出来:

def _cdragon_champ_urls(api_name: str, short_id: str) -> List[str]: lc_tft = f"tft16_{short_id.lower()}" lc_base = short_id.lower() urls = [] for lc in [lc_tft, lc_base]: base = f"{_CDN_CHARS}/{lc}/hud/{lc}" urls += [ f"{base}_square.tft_set16.png", f"{base}_square.png", f"{base}_square.tft_set15.png", ] return urls

七. 装备模板下载

相比英雄,装备数据更复杂,所以脚本专门做了一层过滤:

def _item_is_valid(api_name: str) -> bool: if _SKIP_RE.search(api_name): return False if api_name.startswith("TFT4_") and "Ornn" not in api_name: return False if api_name.startswith("TFT9_") and "Ornn" not in api_name: return False return api_name.startswith(( "TFT_Item_", "TFT5_Item_", "TFT16_Item_", "TFT16_TheDarkin", "TFT4_Item_Ornn", "TFT9_Item_Ornn", ))

经过过滤,模板库只保留或许识别真正会用到的资源.

八. 完整的命令行设计

主入口部分支持这些命令:

ap.add_argument("--champs", action="store_true", help="仅下载英雄头像") ap.add_argument("--items", action="store_true", help="仅下载装备图标") ap.add_argument("--verify", action="store_true", help="检查完整性") ap.add_argument("--list-missing", action="store_true", help="列出缺失文件") ap.add_argument("--version", default=None, help="指定 DDragon 版本(默认自动获取)") ap.add_argument("--db-dir", default=".", help="JSON 数据库目录(默认当前目录)") ap.add_argument("--assets-dir", default=None, help="模板输出目录(默认 ./tft_assets)")

把资源构建流程工具化,让它可以反复执行,按需执行,校验执行.

九. 总结

tft_fetch_assets.py是一条面向截图识别的 TFT 模板资源构建流水线。

具体实现了以下的功能:

1. 从标准数据库读取资源目标

2. 通过双源策略下载正确素材

3. 兼容命名差异与路径差异

4. 标准化尺寸与输出结构

5. 为 OpenCV 模板匹配准备可直接使用的模板库

tft_fetch_assets.py 是一份资源构建脚本。它把“数据、下载、过滤、兼容、标准化、落盘、校验”这几个步骤组织成了一条完整链路,让模板资源不再依赖人工维护,把模板资源构建设计成一个可重复、可维护、可直接接入识别链路的过程。

http://www.jsqmd.com/news/636411/

相关文章:

  • 猫抓浏览器扩展:3分钟掌握网页视频音频资源一键下载完整指南
  • 低成本DIY家庭监控:基于ESP32-CAM和OV2640的无线视频流方案实战
  • 在jupyter里面画图,并且显示中文字体
  • 别再弯腰插拔了!用闲置MicroUSB线和CH340N芯片,5分钟自制桌面TTL调试神器
  • 提示词工程(Prompt Engineering)-周红伟
  • 大数据分析与挖掘实战平台 实训报告
  • Harness Engineering(驾驭工程)-2026年最强的智能体-周红伟
  • 基于llama.cpp部署私有大模型
  • 民办本科自动化,大三下,蓝桥杯刚考砸,会的不多,想学嵌入式还来得及找到工作吗?求前辈真实建议,骂醒我也行
  • **发散创新:基于LLM的智能代码助手在Python开发中的实战落地**在现代软件工程
  • GD32F103C8T6驱动W25Q32 SPI Flash保姆级教程(含源码与接线图)
  • 《OpenNAS - 从零开始写一个开源NAS系统》04 - ZFS存储池的管理
  • 2026企业AI Agent落地秘籍:少走3年弯路,抢占数字化风口!
  • 别再死记硬背C#语法了!用5个机器视觉小例子带你快速上手(Visual Studio 2022版)
  • 【R语言实战】批量单因素Logistic回归:从数据清洗到变量初筛的自动化流程
  • 手把手教你用GPT-oss:20b:CSDN平台图文教程,小白也能快速部署
  • 信息学奥赛实战解析:N进制回文数的高精度运算与优化策略
  • vivado hls的应用(题外话之AI编程)
  • AI Harness 学习清单(AI生成)
  • 微信小程序实战:打造优雅的互动消息列表(评论 / 点赞 / 关注
  • 如何5分钟上手Translumo:Windows平台最强的实时屏幕翻译神器
  • 从零开始掌握时序逻辑电路:状态机设计与FPGA实战解析
  • 解决403 Forbidden:Pixel Script Temple API访问权限配置详解
  • 实验19:Gazebo:三维物理仿真平台
  • 从 Rule-Based 到 LLM-Based:企业自动化流程的重塑
  • **基于Python与Unity的数字孪生系统开发实战:从建模到实时交互的全流程解析**在工业4.0浪潮中,**数字孪生(Digit
  • 全球化字体技术架构:Noto字体项目的企业级多语言解决方案
  • 斯坦福AI软件工程课:Claude Code开发者亲授
  • Ubuntu 配置 Claude Code + MiniMax湛
  • 这是我的第一篇文章