导入谱面或谱面包,一键生成谱面配置预览图
输入 RPE(Re:PhiEdit)/ 官谱谱面 JSON、PEZ 或 ZIP 谱面包,输出纵向时间轴式的谱面预览图。 所有判定线的音符会映射到统一主时间轴,直观展示音符分布、密度变化、多押情况与 Hold 轨迹。
- 下落式时间轴:时间从下往上递增,默认按谱面长度自动调节每栏拍数(16–128,每 4 拍一档),超长谱面自动横向分栏
- 完整缓动支持:29 种标准缓动 + 缓动截取(easingLeft/Right)+ 贝塞尔曲线缓动,判定线 X 坐标按 4 层事件叠加计算
- Note 贴图渲染:Tap / Drag / Flick / Hold 使用原版贴图,多押自动切换高亮(HL)贴图
- Hold 渲染:Body 竖直拉伸、Head/End 贴图、运动轨迹曲线(仅持续期内存在实际位移时渲染),跨栏自动分段;Body 向 Head/End 贴图内侧延伸重叠拼接,消除接缝(与游戏内一致)
- 时间轴标记:拍线/小节线灰色网格、拍号、BPM 变化、Note 时值间隔标记、每 4 拍累计计数(白色标记文字)
- 轨道加深:每条 Note 轨道(栏内竖直区域,不含轨道间隔栏)叠加可配置透明度的加深底色,提高相邻轨道区分度
- 位置重合标注:同一开始时间的 Note 中,谱面原始 X 距离 ≤ 阈值(默认 75)的组,在其旁边标注 "×n"(Hold 仅以头部参与)
- Note 炸弹防御:同一时刻、同一位置且几何完全一致的重复 Note 默认最多实际绘制 4 个,并优先覆盖四种 Note 类型;统计、Combo 与数量标注仍使用完整 Note 列表
- 官谱 JSON 适配:自动识别官谱格式,支持每条判定线独立 BPM、官谱 Note 类型与事件;官谱坐标会按比例映射到 RPE 轨道坐标系,并自动启用分音拟合(启用
bpm_scale时跳过) - 底部信息栏:曲名、时长、难度、曲师、谱师、BPM 范围(最低~最高)与四类 Note 统计(按类型着色),双边框卡片样式,背景与主区共用整图模糊曲绘
- 可选背景与格式:曲绘高斯模糊(σ=15)作为整体背景;不指定背景时 PNG 输出透明底,也可输出 JPG 以减小文件体积
- Web UI 元数据编辑:加载谱面后可编辑谱面名称、谱师、难度、曲师,修改只作用于当前渲染任务
- 预览区底色:谱面预览区叠加可配置透明度的半透明黑色底色,压暗背景突出白色标记与 Note
- 自定义字体:所有渲染文字(拍号/BPM/时值/计数/信息栏)统一使用可配置的主字体(默认
resources/fonts/phi.ttf),信息栏中文在字体缺字时按字形回退到系统 CJK 字体 - 配置文件:所有渲染参数可通过
render_config.json覆盖默认值,无需修改代码
- Python ≥ 3.10
- matplotlib ≥ 3.7.0
- numpy ≥ 1.24.0
- Pillow ≥ 10.0.0
- Node.js ≥ 18(仅运行 Web UI 时需要)
pip install -r requirements.txt# JSON、PEZ、ZIP 均可直接作为输入
python -m rpe_render chart.json
python -m rpe_render chart.pez
# 指定背景曲绘与输出路径
python -m rpe_render chart.json --background art.png -o output.png
# JPG 输出(文件体积更小)
python -m rpe_render chart.json --background art.png -o output.jpg --format jpg
# 对 RPE 谱面显式开启实验性官谱分音拟合(官谱会自动启用)
python -m rpe_render chart.json --fit-official-divisions
# 默认自动选择每栏拍数;也可固定为 16~128 且为 4 的倍数
python -m rpe_render chart.json --column-beats auto
python -m rpe_render chart.json --column-beats 64
# 谱面按 N 倍 BPM 书写时(如实际 180 按 540 书写),整体缩放时值与 BPM
python -m rpe_render chart.json --bpm-scale 1/3
# 常用参数组合
python -m rpe_render chart.json --bg art.png -o out.jpg --format jpg --dpi 300 \
--preview-bg-alpha 0.4 --track-bg-alpha 0.75 --config render_config.json| 参数 | 说明 | 默认值 |
|---|---|---|
chart |
RPE JSON、PEZ 或 ZIP 谱面文件路径(必填) | - |
--config |
配置文件路径(JSON,覆盖 constants 默认值) | 当前目录下 render_config.json |
--background / --bg |
背景曲绘图片路径 | 无(透明背景) |
-o / --output |
输出图片路径 | output.png |
--format |
输出格式:png / jpg;省略时按输出扩展名推断 |
按扩展名推断 |
--dpi |
输出 DPI | 150 |
--notes-dir |
Note 贴图目录 | resources/notes |
--preview-bg-alpha |
谱面预览区半透明黑色底色透明度(0.0 关闭 ~ 1.0) | 0.55 |
--track-bg-alpha |
每条 Note 轨道区域额外加深透明度(0.0 关闭 ~ 1.0) | 0.75 |
--fit-official-divisions |
启用官谱分音拟合(实验性,仅 Tap/Hold) | 关闭 |
--column-beats |
每栏拍数:auto 智能选择,或指定 16–128 且为 4 的倍数 |
auto |
--bpm-scale |
BPM 缩放倍率:1/4、1/3、1/2、1、2、3、4 之一(支持分数与浮点写法) |
1(不缩放) |
输入为 PEZ/ZIP 时,程序会安全解包并定位曲绘;不会修改原始压缩包。
谱面包信息文件按以下顺序回退:info.txt → 与谱面 JSON 同名的 .txt → 包内唯一 .txt。
其中 Name、Level、Composer、Charter 会作为底部信息栏默认元数据,显式指定的元数据优先。
谱面 JSON 声明的曲绘不可用时,会依次回退到信息文件中的 Picture: 和包内唯一的 PNG/JPG/JPEG。
关于官谱适配与分音拟合: 官谱 JSON 的时间单位为各判定线 BPM 下的 1/32 拍,Note type 顺序为 Tap/Drag/Hold/Flick,且不同判定线可以使用不同 BPM。渲染器会将这些时间统一映射到主时间轴,并将官谱横向坐标按比例映射到 RPE 的
[-675, 675]范围。 官谱只支持 2 的整数次幂分音,12、20、24 等特殊分音通常由密集 Note 近似产生。官谱检测到后会自动执行拟合;RPE JSON 仍需通过--fit-official-divisions显式开启。 启用--bpm-scale的官谱会跳过拟合:官谱时值固定在 T 网格上,不存在特殊分音的量化漂移(N 倍 BPM 书写的谱面正是绕开了该限制),拟合没有收益,且高密度时值下拟合耗时可能失控。
RPE JSON原生支持任意分音,通常无需开启此选项。如果你的RPE JSON由官谱转换而来或者确认需要开启,请注意: 官谱拟合会以至少 3 个连续间隔为候选序列,并以 1/16 拍作为最大允许误差; 精确的 16/32/64 等原生分音会作为边界,避免不同节奏段互相吸附。该功能属于实验性启发式算法, 开启后应检查关键段落的渲染结果。
关于 BPM 缩放倍率: 部分谱面以 N 倍 BPM 书写(如实际 180 BPM 按 540 书写,以获得更细的时间粒度),渲染画布会按相同倍数增大,甚至因图过大而超时。对这类谱面指定
--bpm-scale 1/3可把全部音符时值、事件时间与 BPM 数值整体缩放回真实节奏:画布恢复正常大小,网格与各类标记按缩放后的 BPM 标注,信息栏时长(真实秒数)保持不变。仅缩放时间轴,Note 与判定线的空间位置不变。Web UI 与 API 的对应字段为bpm_scale。 官谱在启用缩放时自动跳过分音拟合(见上节),RPE 谱面的显式开关不受影响。
所有渲染参数(拍高、栏宽、颜色、透明度、贴图路径等)都可通过 JSON 配置文件覆盖,无需修改代码:
# 复制示例文件并编辑
cp render_config.example.json render_config.json
# 或显式指定路径
python -m rpe_render chart.json --config my_settings.json- 配置文件键名对应
constants.py中的常量名,大小写不敏感(如beat_height_px等价于BEAT_HEIGHT_PX) - 主字体通过
FONT_PATH配置(默认resources/fonts/phi.ttf,相对仓库根目录或绝对路径);文件缺失或加载失败时回退到系统默认字体并告警 - 未知键与类型不匹配的键会被忽略并发出警告;以
_开头的键视为注释 - 查找顺序:
--config参数 > 环境变量RPE_RENDER_CONFIG> 当前目录下render_config.json - 参数优先级:命令行参数 > 配置文件 > 代码默认值
FIT_OFFICIAL_DIVISIONS默认false,SMART_COLUMN_BEATS默认true,BPM_SCALE默认1,NOTE_BOMB_RENDER_LIMIT默认4SMART_COLUMN_BEATS开启时,渲染器会在 16–128 拍间以 4 拍为一档自动选择每栏拍数;关闭时使用固定的COLUMN_BEATS。命令行通过--column-beats auto|数值选择模式。- 测试假定默认配置运行(仓库内不创建
render_config.json时行为不变)
日志级别默认 WARNING,可通过环境变量开启调试:
RPE_RENDER_LOG_LEVEL=DEBUG python -m rpe_render chart.jsonfrom rpe_render.renderer import RenderConfig, render
render(RenderConfig(
chart_path="chart.json", # 也可以是已解包后的 JSON 路径
background_path="art.png", # 可选
output_path="preview.png",
notes_dir="resources/notes",
smart_column_beats=True, # 按谱面长度自动调节每栏拍数,默认开启
column_beats=64, # 关闭自动调节时使用,范围 16~128,步长 4
dpi=150,
preview_bg_alpha=0.55, # 预览区半透明黑底色透明度(0.0~1.0)
track_bg_alpha=0.75, # 轨道区域额外加深透明度(0.0~1.0)
fit_official_divisions=False, # 实验性官谱分音拟合,默认关闭
bpm_scale=1.0, # BPM 缩放倍率(1/4、1/3、1/2、1、2、3、4),1 不缩放
))PEZ/ZIP 等谱面包建议使用服务层接口,它会负责临时解包、曲绘定位和清理:
from rpe_render.service import render_source
image = render_source(
"chart.pez",
output_format="png",
fit_official_divisions=True,
metadata={"name": "自定义标题", "level": "AT 14"},
)
open("preview.png", "wb").write(image)库调用若要使用配置文件,在导入 rpe_render 渲染模块前调用:
from rpe_render import constants
constants.load_config("my_settings.json") # 覆盖默认常量
from rpe_render.renderer import RenderConfig, renderuvicorn rpe_render.api:app --host 127.0.0.1 --port 8000API 使用内存任务队列和本地临时目录。任务结果默认保留 30 分钟,服务重启时自动删除旧任务。
前端会先调用 POST /api/v1/charts/metadata 读取谱面元数据,再将用户编辑的
name、charter、level、composer 作为表单字段提交到 POST /api/v1/jobs。
任务还支持 format(png/jpg)、smart_column_beats、column_beats、bpm_scale、dpi、
tile_workers、preview_bg_alpha、track_bg_alpha、background_blur_sigma、background_brightness 与
fit_official_divisions。Web UI 将输出格式、每栏拍数与 BPM 缩放倍率作为基础配置,其余渲染参数位于高级设置。
FIT_OFFICIAL_DIVISIONS / --fit-official-divisions 可为 RPE 谱面显式开启实验性的分音拟合,
默认关闭;官谱检测到后会自动开启。它只调整 Tap/Hold 起始时间,Drag/Flick 不参与拟合。
POST /api/v1/charts/metadata 和 POST /api/v1/jobs 均使用 multipart/form-data,
字段 file 必填,支持 .json、.pez、.zip。
| 字段 | 类型/默认值 | 说明 |
|---|---|---|
format |
png |
png 或 jpg |
dpi |
150 |
72–600 |
tile_workers |
0 |
高 DPI 分块并发数,0 为自动,1–32 可手动指定;低 DPI 未达到分块阈值时不会启用线程池 |
preview_bg_alpha |
0.55 |
预览区黑色覆盖层透明度 |
track_bg_alpha |
0.75 |
轨道加深透明度 |
background_blur_sigma |
15 |
曲绘高斯模糊强度,0–100 |
background_brightness |
0.75 |
曲绘亮度,0–2 |
fit_official_divisions |
false |
实验性官谱分音拟合 |
smart_column_beats |
true |
自动选择每栏拍数,使画布比例尽量接近 16:9 |
column_beats |
64 |
关闭自动选择时的每栏拍数,16–128 且为 4 的倍数 |
name / charter / level / composer |
空 | 覆盖底部信息栏对应字段 |
POST /api/v1/charts/metadata 只解析并返回四项可编辑元数据,不创建渲染任务。
任务接口返回 202 和任务 ID,通过 GET /api/v1/jobs/{job_id} 轮询;完成后从
GET /api/v1/jobs/{job_id}/result 下载图片。
渲染失败时任务的 error 字段会保留异常类型和具体信息,Web UI 会在任务状态区域展示。
可配置环境变量:
| 变量 | 默认值 | 说明 |
|---|---|---|
RPE_RENDER_WORKERS |
1 |
并发渲染 worker 数 |
RPE_RENDER_TILE_WORKERS |
min(8, CPU 数 / RPE_RENDER_WORKERS) |
高 DPI 背景分块合成的并发线程数;块越多时越能利用多核,但会增加内存占用 |
RPE_MAX_QUEUE_SIZE |
32 |
最大排队/运行任务数 |
RPE_MAX_UPLOAD_BYTES |
268435456 |
上传文件上限 |
RPE_RESULT_TTL_SECONDS |
1800 |
结果保留时间 |
RPE_RATE_LIMIT_PER_MINUTE |
60 |
单 IP 每分钟创建任务数;设为 0 关闭 |
RPE_LOCAL_MODE |
false |
true 时关闭 IP 频率限制 |
RPE_CORS_ORIGINS |
localhost:5173 |
允许的前端来源,逗号分隔 |
- 支持
.pez、.zip和.json。 - 根目录只有一个 JSON 时直接使用;多个 JSON 时必须由信息文件的
Chart:声明。 - 信息文件按
info.txt→ 与谱面 JSON 同名的.txt→ 包内唯一.txt回退选择。 Name、Level、Composer、Charter用于补充官谱缺失的 JSONMETA;JSON 中非空字段优先。- 谱面包曲绘依次读取 JSON 的
META.background、信息文件的Picture:、包内唯一 PNG/JPG/JPEG 图片。 - 前一项声明不存在、路径无效或不是支持的图片格式时继续回退;全部不可用或包内存在多张未声明图片时返回“未找到曲绘”。
- 声明路径区分大小写,允许空格,禁止系统保留字符和目录穿越。
cd web/frontend
npm install
npm run dev设置 VITE_API_BASE_URL 指向 API 服务即可将前端部署到任意静态托管平台(包括 Vercel)。
docker compose up --build前端地址为 http://localhost:8080,API 地址为 http://localhost:8000。
.
├── rpe_render/
│ ├── api.py # FastAPI:上传、任务队列、元数据接口与结果下载
│ ├── cli.py # argparse 命令行解析
│ ├── service.py # 可复用服务层:谱面包加载 + 内存图片输出
│ ├── renderer.py # 主渲染协调器(编排各模块)
│ ├── division_fit.py # 实验性官谱非 2 次幂分音拟合
│ ├── package_loader.py # JSON/PEZ/ZIP 安全加载、解包与曲绘定位
│ ├── chart_parser.py # RPE JSON 解析与验证
│ ├── timeline.py # 分栏与像素坐标映射(纯计算)
│ ├── time_utils.py # TimeT 三元组 ↔ 拍数转换
│ ├── models.py # 数据模型(ChartData / NoteRenderInfo 等)
│ ├── note_renderer.py # Note 贴图加载、多押判定、炸弹防御与放置
│ ├── hold_renderer.py # Hold Body/Head/End/轨迹渲染
│ ├── grid_renderer.py # 网格线、拍号、BPM 标记
│ ├── marker_renderer.py # 时值间隔标记、累计计数标记
│ ├── info_bar.py # 底部信息栏
│ ├── background.py # 曲绘模糊背景、预览区/轨道加深覆盖层
│ ├── fonts.py # 自定义字体管理(FONT_PATH 加载、CJK 回退)
│ ├── constants.py # 所有可调常量集中管理
│ └── easing/ # 缓动函数、贝塞尔与事件求值
├── web/
│ ├── README.md # Web UI/API 快速说明
│ └── frontend/ # React + Vite 前端
├── tests/ # 计算、渲染、API 相关回归测试
├── resources/ # Note 贴图、字体、示例谱面与曲绘
├── render_config.example.json
├── requirements.txt
└── docker-compose.yml
架构遵循:
- 输入层:
package_loader负责格式识别、安全解包和曲绘定位,chart_parser负责结构验证与模型化; - 预处理层:可选的
division_fit只在显式开启时调整 Tap/Hold 起始时间; - 计算层:
timeline、easing、time_utils计算统一主时间轴、判定线姿态和像素几何; - 渲染层:
grid_renderer、note_renderer、hold_renderer、affected_area_renderer等绘制前景; - 输出层:
background合成曲绘/JPG,info_bar绘制元信息和统计; - 适配层:
cli面向命令行,service面向库调用和 API,api提供异步任务接口; - 所有可调参数集中在
constants.py,通过配置文件覆盖;默认关闭的实验性能力不会改变普通渲染路径。
JSON / PEZ / ZIP
│
▼
安全加载与谱面解析 ──► 官谱自动适配/分音拟合(RPE 可选开启)
│
▼
统一主时间轴与判定线姿态计算
│
▼
网格 / Hold / Note / 受影响区域 / 标记 / 信息栏
│
▼
透明前景 + 曲绘合成 ──► PNG 或 JPG
| 决策 | 方案 |
|---|---|
| 多押判定 | 映射到主谱面后的 displayBeat 相同才构成实际多押;浮点映射使用稳定舍入 |
| Hold Body | 始终竖直矩形拉伸;段内 Head/End 与 Body 同 X 对齐;跨栏分段各用段内 X(尾段/中段取本段起始时刻判定线 X 映射到本栏);Body 向两端延伸 HOLD_BODY_OVERLAP_PX(默认 1px)重叠拼接,消除接缝;轨迹曲线仅当持续期内存在实际位移(像素 X 范围 ≥ HOLD_TRAJECTORY_MIN_DISPLACEMENT_PX,默认 1px)时渲染 |
| 时值间隔标记 | 仅 Tap+Hold 跨类型混合排序,间隔 < 1/2 拍(小于八分音符,如 12 分、16 分)标记 N 分音符刻度(label = 4/间隔拍数);0.49 拍的默认阈值把整 1/2 拍排除在外 |
| 位置重合标注 | 仅同一主谱面实际开始时间的 Note 参与判定;组内按真实 X 距离 ≤ NOTE_OVERLAP_THRESHOLD_X(默认 75)聚类,在组最右 Note 旁标注 "×n";Hold 只以头部计入 |
| Note 炸弹防御 | 仅对同一实际开始时间、精确相同渲染位置且几何完全一致的重复 Note 生效;默认最多绘制 4 个,按类型轮询优先覆盖 Tap/Hold/Flick/Drag;原始 Note 仍用于数量标注与统计 |
| 判定线坐标 | 全部 4 层 moveXEvents/moveYEvents/rotateEvents 叠加;父线递归变换,rotateWithFather 控制角度继承 |
| BPM 因数 | displayBeat = localBeat × bpmfactor,Note、Hold、轨迹、标记和重合判断统一使用映射后的主谱面时间 |
| 忽略字段 | above/yOffset/size/speed/判定线视觉属性/extended 事件不参与预览 |
| isFake 音符 | 过滤不渲染 |
| 官谱分音拟合 | 官谱自动执行(启用 bpm_scale 时跳过),RPE 仅显式开启;以连续高密度 Tap/Hold 起始时间推断非 2 次幂分音,误差上限 1/16 拍;精确原生分音作为边界 |
python -m pytest tests/ -v测试分层:纯计算单元测试(time_utils/easing/parser/timeline/marker 等)→
渲染层集成测试(检查 Axes 状态)→ E2E 测试(真实谱面 resources/chart.json 出图验证)。
理论上支持现有的几乎所有 RPE 特性(包括分层事件、递归父子线和 BPM 因数;第五层事件自动忽略,也不考虑缩放等特殊视觉事件)。配置渲染以 Note 的实际落点和主谱面时间为准。
本项目采用 GNU General Public License v3.0 进行许可。
您可以在项目根目录下的 LICENSE 文件中查看完整的许可证文本,或访问 GNU 官网 获取更多信息。
本项目使用了以下第三方素材,其版权分别归属各自的权利人:
| 素材类型 | 来源 | 版权归属 |
|---|---|---|
| Note 贴图(Tap / Drag / Flick / Hold 等) | Phigros 游戏 | 南京鸽游网络有限公司(Pigeon Games) |
| 字体文件 | 思源黑体(Source Han Sans / Noto Sans CJK) | Adobe Systems Incorporated(依据 SIL Open Font License v1.1 授权) |
| 示例谱面 | 本项目内置 | @Temmie0125 |
| 示例曲绘 | 网络 | @nanakaria |
关于字体:本字体依据 SIL Open Font License v1.1 协议分发,您可以在遵守该协议的前提下自由使用、修改与再分发。
注意:示例曲绘仅用于展示本项目渲染效果,请勿用于其他用途。如版权所有者认为不妥,请联系作者移除。
本项目并非 Phigros 官方项目,也与南京鸽游网络有限公司(Pigeon Games)、Re:PhiEdit 及其开发者无任何官方合作或附属关系。
-
本项目是一个粉丝自制工具,仅供学习交流使用。
-
所有游戏素材(包括但不限于 Note 贴图等)的版权归其原始权利人所有。
-
使用本项目生成的预览图仅可用于谱面展示等合法用途,请勿用于任何侵犯第三方权益的场景。
本程序按“现状”提供,不提供任何明示或暗示的担保,包括但不限于对适销性、特定用途适用性和非侵权性的担保。在任何情况下,作者或版权持有人均不对因使用本程序而引起的任何索赔、损害或其他责任负责,无论这些责任是基于合同、侵权还是其他原因。
