mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
6069 字
16 分钟
从 SAM2 到 GT-free 多模型选择:视频目标分割实验开发笔记

从 SAM2 到 GT-free 多模型选择:视频目标分割实验开发笔记#

写在前面#

这次实验最初只是一个标准的视频目标分割任务:给定视频首帧的目标掩码,把同一个目标逐帧追踪并分割出来。项目做到后半程,问题却逐渐从“如何得到更精细的掩码”变成了另一个更关键的问题:

在多个外观相似的实例之间,模型是否一直盯着最初指定的那个对象?

这个转变直接改变了后续的研究路线。我先后跑通了 SAM2.1-large、SAM2Long、TCP 时序后处理与 SAM3,又从简单投票一路尝试到手工 GT-free 选择器和机器学习选择器。最终得到的主要结论是:

核心结论

在这组 VOS 实验中,模型间的主要差距不是“掩码边界差几个像素”,而是少数分歧点之后“是否继续跟对了目标”。因此,相比像素级融合,更有效的方向是以视频或物体为单位进行轨迹选择。

项目代码:

Cccy666
/
sam_mixed_segmentation
Waiting for api.github.com...
00K
0K
0K
Waiting...

一页看完实验结果#

单模型 Benchmark#

以下成绩来自 25 个测试视频,指标为 CodaBench 报告的 J&F_new

模型J&F观察
SAM2-tiny57.47最小模型
SAM2.1-large + SAM2Long + TCP58.39TCP 未带来正收益
SAM2.1-large + SAM2Long58.52长记忆树整体低于 baseline
SAM2.1-large58.90大模型 baseline
SAM360.59最强单模型

最反直觉的结果是:SAM2Long 的整体成绩低于普通 SAM2.1-large。继续拆解到单个物体后才发现,长记忆并非普遍无效,而是呈现明显的“双刃剑”特征——部分视频提升 30 分以上,少数困难视频却直接损失 60 分以上。

多模型选择#

方法J&F相对 SAM3是否可在推理时复现
始终使用 SAM360.59-
多数投票61.39+0.80
GT-free v561.33+0.74
GT-free v862.75+2.16
视频级 oracle65.20+4.61
物体级 oracle67.42+6.83

这里必须区分两类结果:

  • GT-free v8 只使用推理时可取得的掩码、轨迹和模型间一致性特征,可以自动运行;
  • oracle 使用了平台返回的真实成绩来选择最优模型,相当于“看过答案后再选”,只能用于估计多模型选择的理论空间,不能包装成可部署算法。

报告截稿时,视频级 oracle 提交取得 J&F=65.2,对应 submission ID 812523(2026-06-25),在当时榜单中位列第 1,领先第 2 名约 2.7 分。这个名次是一次有用的上限实验,但不属于自动选择器的实际能力。

任务与数据#

视频目标分割(Video Object Segmentation,VOS)的输入不是一句类别文本,而是首帧中指定目标的像素级掩码。模型必须在后续视频中持续输出同一实例的掩码。

任务看似只是把单图分割沿时间轴展开,实际还要处理:

  • 目标运动、尺度变化与运动模糊;
  • 遮挡、出画与重新出现;
  • 相机运动与光照变化;
  • 多个相似实例交叉时的身份保持;
  • 多目标掩码之间的互斥和覆盖关系。

测试集采用 MOSE 风格、DAVIS 式调色板标注:

dataset/
├── JPEGImages/
│ └── {video_id}/
│ ├── 00000.jpg
│ ├── 00001.jpg
│ └── ...
└── Annotations/
└── {video_id}/
└── 00000.png

其中首帧标注是 P 模式调色板 PNG,像素值 0 表示背景,1, 2, ... 表示不同物体。测试集共有:

数据规模
视频数25
每段帧数约 81-126
标注物体数共 64 个
单物体视频14 个
单视频最多物体数7 个
自拍图片5 张,4096×3072
自拍视频2 段,均不少于 5 秒

需要注意,“标注物体数”和“画面中的物体数”不是一回事。例如鸟群视频里可能出现几十只飞鸟,但评测只要求追踪首帧指定的几只。这正是 identity switch 最容易发生的场景。

评价指标与监督约束#

平台使用 VOS 标准指标 J&F

J=MGMGJ = \frac{|M \cap G|}{|M \cup G|}

其中 MM 是预测掩码,GG 是真实掩码。FF 是边界 F-measure,用于评价轮廓对齐程度,最终分数为:

J&F=J+F2J\&F = \frac{J + F}{2}

这次实验有一个决定后续方法设计的硬约束:

没有逐帧 GT

测试集只提供首帧标注。平台导出的 score/*.txt 只有每个物体整段聚合后的 J&F,没有逐帧真值或逐帧得分。因此,任何可以在真实推理阶段运行的自动选择器,都不能把 GT、平台分数或未来帧真实标签当作输入。

这意味着我能知道“某条完整轨迹最后得了多少分”,却不知道“究竟从哪一帧开始跟错”。监督信号既稀疏,又只有 64 个物体,后面的 ML 实验会明显受到这一点限制。

Baseline:先把完整工程管线跑通#

标准 SAM2 推理流程如下:

flowchart LR A["首帧 P 模式标注"] --> B["按 object id 拆成二值 mask"] B --> C["add_new_mask 注册目标"] D["JPEG 帧序列"] --> E["init_state"] C --> F["propagate_in_video"] E --> F F --> G["逐帧、逐物体 logits"] G --> H["阈值化并处理重叠"] H --> I["合成为 object-id 图"] I --> J["复制首帧调色板"] J --> K["submission/{video_id}/*.png"]

1. 首帧提示#

从调色板 PNG 中读取所有非零 id,并为每个物体构造二值掩码:

annotation = np.array(Image.open(first_mask_path))
object_ids = [x for x in np.unique(annotation) if x != 0]
for object_id in object_ids:
binary_mask = annotation == object_id
predictor.add_new_mask(
inference_state=state,
frame_idx=0,
obj_id=int(object_id),
mask=binary_mask,
)

2. 全序列传播#

init_state 负责载入视频帧,propagate_in_video 返回每一帧、每个目标的 mask logits。这里真正需要关心的不只是“有没有输出”,还包括:

  • 帧名与原图是否一一对应;
  • 输出尺寸是否与输入严格一致;
  • 多物体传播时 id 是否稳定;
  • 目标暂时消失时是否应该输出空掩码;
  • 多个掩码重叠时采用什么覆盖规则。

3. 多物体冲突裁决#

实验中采用“小 id 先画,大 id 后覆盖”的确定性规则,首帧则直接用 GT 覆盖,保证条件帧不被模型输出污染:

label_map = np.zeros((height, width), dtype=np.uint8)
for object_id in sorted(object_masks):
label_map[object_masks[object_id]] = object_id
if frame_idx == 0:
label_map = first_frame_annotation.copy()

这个规则不一定是最优的,但它可重复、容易检查,而且不会因为字典遍历顺序不同而生成不同提交。

4. 提交格式是隐藏的工程难点#

输出文件不仅要“看起来像一张分割图”,还必须同时满足:

  • P 模式 PNG;
  • 沿用首帧标注的调色板;
  • 文件名、帧数、分辨率全部与输入一致;
  • 每个像素保存的是物体 id,而不是 RGB 颜色;
  • 文件夹层级完全符合平台要求。
output = Image.fromarray(label_map, mode="P")
output.putpalette(first_frame_palette)
output.save(output_path)

如果把可视化后的 RGB 彩图直接当作提交,肉眼看上去可能完全正常,平台评分却会异常。对这类 Benchmark,格式验证和模型推理同样重要。

三条改进路线#

SAM2Long:用记忆树缓解错误累积#

SAM2Long 在 SAM2 上引入 memory tree,维护多条候选记忆路径,观察完整段视频后再回溯选择路径,试图缓解单路记忆中的误差累积。

实验采用官方 VOS 推荐参数:

num_pathway = 3
iou_thre = 0.1
uncertainty = 2

它有一个结构性限制:记忆树的路径评分按单物体、单状态设计,因此多目标视频必须逐物体单独传播。

这会带来两项代价:

  1. 推理次数随物体数线性增长,本实验 64 个物体需要 64 次独立传播;
  2. 失去 SAM2 多物体联合传播时潜在的跨物体互斥约束。

一开始我怀疑第二点是 SAM2Long 崩溃的主因,但单物体视频 ukt9w7oi 上同样出现了灾难性失败。这个反例直接推翻了“主要是互斥约束丢失”的猜测。逐帧动画进一步显示,真正的问题是:记忆树在早期选错路径后,长记忆把错误身份稳定地固化了下来。

TCP:只处理掩码,不处理身份#

TCP(Temporal Consistency Postprocessing)是一个基于 scipy 的纯后处理模块:

  • 空间维度:填补小孔洞;
  • 空间维度:删除面积小于 64 px 的孤立碎块;
  • 时间维度:移除孤立帧闪烁;
  • 时间维度:补齐 1 帧短遮挡;
  • 可选:3 帧多数投票。

对应开关包括:

ENABLE_TCP
PP_FILL_HOLES
PP_MIN_AREA
PP_FLICKER
PP_TEMPORAL_VOTE

TCP 可以让边界更干净、短期轨迹更平滑,却不能把“跟到另一只鸟”的掩码重新变回正确目标。只要错误掩码本身连续、平滑、形状合理,后处理甚至看不出它是错的。这也是它最终没有提升 Benchmark 的原因。

SAM3:批量注册所有物体#

SAM3 通过 HuggingFace transformers 与 ModelScope 权重完成推理,使用 Sam3TrackerVideoModel / Sam3TrackerVideoProcessor 的视频接口。

一个关键实现细节是:首帧必须一次性批量注册全部物体。若按 object 逐次注册,条件帧上可能出现部分物体没有 mask memory,随后传播报错:

maskmem_features ... cannot be empty

SAM3 原生支持多物体联合传播,在本数据集上取得 J&F=60.59,成为最强单模型,也因此被选为 GT-free 选择器的默认模型。

研究转折:长记忆为什么既能大涨,也能崩盘?#

把平均分拆到具体视频与物体后,SAM2Long 的双刃剑特征非常明显:

类型视频 / 物体SAM2.1-largeSAM2Long差值
长记忆大涨0d0030a7 obj551.4987.48+36.0
长记忆大涨1ef6cb7b obj363.0894.44+31.4
长记忆大涨0d0030a7 obj126.5256.59+30.1
长记忆大涨sp9uf9in obj167.7187.64+19.9
长记忆崩盘429d96d4 obj683.0364.76-18.3
长记忆崩盘0u8fy7u2 obj176.0715.82-60.3
长记忆崩盘ukt9w7oi obj169.160.91-68.3

可以把机制理解成:

长记忆收益正确路径的长期稳定错误路径的长期固化\text{长记忆收益} \approx \text{正确路径的长期稳定} - \text{错误路径的长期固化}

在目标稳定、运动连续、干扰物少的视频中,长期记忆能抵抗短期噪声;在小目标、弱提示、密集相似实例的场景中,它也会同样坚定地记住一个错误对象。

差异主要发生在“身份”,而不是“质量”#

进一步对比不同模型的逐帧掩码后,我得到三个重要观察:

  1. 在 64 个物体中,约 50 个物体上 SAM2Long 与 SAM2.1-large 的逐帧互 IoU 超过 0.9,面积比中位数接近 1.00,二者几乎没有可融合的差异;
  2. 真正拉开分数的是约 10-14 个物体,差异不是边界厚薄,而是整段轨迹指向了不同实例;
  3. 不同模型跟错的物体并不完全重合,因此存在按物体选择模型的提升空间。

由此得到两条工程结论:

  • 融合只能“选”,不适合“糊”:不同实例的掩码做像素平均,会得到一个同时覆盖多个对象的错误结果;
  • 多数投票不是免费午餐:三个同源 SAM2 模型可能一起犯错,把少数但正确的 SAM3 投掉。

实验中的多数投票只从 60.59 提升到 61.39,距离 oracle 仍有明显差距。集成的关键不是模型数量,而是错误模式是否足够独立。

多模型选择框架#

整体框架不是逐像素融合,而是对每个 (video, object) 从候选模型中选出一条完整轨迹:

flowchart TD A["同一视频、同一物体的多模型轨迹"] --> B["提取面积、质心、bbox、缺席区间"] B --> C["计算每个模型的 unphysical penalty"] C --> D{"Stage 1:SAM3 是否出现幻觉闪现?"} D -->|高置信| E["切换到最干净的挑战者"] D -->|否| F{"Stage 2:三个 SAM2 是否形成稳定共识?"} F -->|且 SAM3 明显孤立| G["切换到共识簇中的最低 penalty 模型"] F -->|条件不足| H["保留 SAM3"] E --> I["输出所选模型的完整物体轨迹"] G --> I H --> I

Oracle:先测量“选择”这件事值不值得做#

在设计自动选择器前,我先使用平台返回的 per-object 分数构造 oracle:

  • 视频级 oracle:每个视频选成绩最高的模型;
  • 物体级 oracle:每个物体选成绩最高的模型。

视频级 oracle 达到 65.2,物体级 oracle 达到 67.42,显著高于 SAM3 的 60.59。视频级混合中模型使用次数为:

SAM3 ×13
SAM2-tiny ×5
SAM2.1-large ×4
SAM2Long ×2
SAM2Long + TCP ×1

这个分布说明没有一个模型在所有视频上都最好,多模型之间确实存在互补性。Oracle 的意义不是“刷榜方案”,而是回答一个研究问题:如果自动选择器足够好,理论上还有多少空间?

GT-free 选择器:从不合物理的轨迹中找错误#

自动选择器不能使用 GT,因此我把问题反过来:

无法直接证明哪条轨迹是对的,能否先检测哪条轨迹明显不合理?

对每个 (video, object, model),从逐帧质心、面积、bbox 与 active 状态中提取六类特征:

特征捕捉的异常权重
flash从边缘离开后在画面中央幻觉式闪现4
far_reappear在画面内部消失后于远处重现2
teleport单帧位移超过画面对角线的 30%1
blob只持续不超过 2 帧的孤立碎掩码0.5
disengage在画面内部凭空消失并持续缺席8
isolation轨迹活跃但明显偏离其它模型的紧密共识8

综合惩罚定义为:

P=4flash+2far_reappear+teleport+0.5blob+8disengage+8isolation\begin{aligned} P ={}& 4\cdot flash + 2\cdot far\_reappear + teleport \\ &+ 0.5\cdot blob + 8\cdot disengage + 8\cdot isolation \end{aligned}

disengage 的边界条件非常重要:目标从画面边缘正常离开时,不应该因为输出空掩码而受罚;只有目标在画面内部凭空消失才被视作“放弃跟踪”。这个区分能把以下两种情况分开:

  • yjvoxabg:目标真的飞出边缘,输出空掩码是正确行为;
  • 9mv2g80y:目标仍在画面内部,模型却停止跟踪。

Stage 1:检测 SAM3 自己在“幻觉”#

默认信任最强单模型 SAM3,取 penalty 最低的其它模型作为挑战者。只有同时满足以下高置信条件才允许切换:

  1. 所有模型并非都很脏,即最小 penalty 不超过 5;
  2. SAM3 自身出现中央 flash
  3. 挑战者 penalty 不超过 1;
  4. 挑战者相对 SAM3 的 penalty 优势至少为 3。

这是一种“保守默认”策略:宁愿错过一些潜在提升,也不要因为一个看似干净、实际跟错身份的轨迹而频繁切换。

Stage 2:检测 SAM3 安静地跟错对象#

有一类错误不会产生闪烁:SAM3 全程平滑,但盯着错误实例;与此同时,三个 SAM2 模型形成一致轨迹。为此,v8 新增第二道机制:

  1. 用并查集按质心一致度对非 SAM3 模型聚类;
  2. 要求共识簇大小 csize == 3
  3. 共识簇的 disengage 指标 cdis < 0.55
  4. 共识轨迹活跃度 cact > 0.4
  5. SAM3 与共识的面积比 arat >= 0.75
  6. SAM3 与共识的一致度 s3ag < 0.65,确认它确实处于孤立状态。

五道门全部通过后,才切换到共识簇中 penalty 最低的模型。

Stage 1 和 Stage 2 不能简单合并,因为两种错误的几何表现相反:

  • Stage 1 中,SAM3 的轨迹明显闪烁,错误是“吵闹的”;
  • Stage 2 中,SAM3 平滑地跟错,错误是“安静的”。

v8 共触发 4 次切换,4 次全部净增益;报告中选取的 7 个陷阱物体没有误触,最终达到 62.75。

关于泛化能力

v8 的阈值是在包含这些物体的 64-object 数据上调出的,因此 +2.16 带有 in-sample 乐观成分。它是当前实验中效果最好的可复现规则,不应被描述成已经通过独立测试集证明的通用算法。

ML 选择器:一次有价值的负结果#

人工规则可解释,但阈值依赖经验。于是我又尝试用机器学习直接预测每个模型的得分,输入特征包括:

  • 面积、质心、bbox 与缺席区间;
  • 模型之间的互 IoU;
  • disagreement window 的占比与长度;
  • 分歧窗口前后的面积、位置和一致性变化;
  • 共识簇、孤立度和 unphysical penalty。

为了避免同一视频中的多个物体同时出现在训练集和验证集,评估采用 Leave-One-Video-Out CV:

方法留一视频 CV J&F命中最优模型
始终使用 SAM360.59-
Ridge 回归56.2022%
GBM 回归58.6022%
GBM + 保守门控61.30-

裸 ML 甚至低于 SAM3。只有规定“预测优势超过 18 分才允许切换”,把模型压回极保守状态,才能回到 61.30。

特征重要度最高的是分歧窗口占比 w_frac=0.495。但它更像“这个物体有多难”的代理,而不是“哪个模型正确”的信号。模型学会了何时不该信任 SAM3,却没有足够信息学会应该切换到谁。一旦放宽门控,错误切换会迅速吞掉全部增益。

这不是“机器学习无效”,而是一个关于数据与监督的结果:

  • 只有 64 个带标签物体,样本量过小;
  • 标签只有整段聚合 J&F,没有逐帧监督;
  • 正确身份和错误身份可能具有同样平滑、合理的几何轨迹;
  • 同源 SAM 模型的错误高度相关,候选多样性不足。

在当前已尝试的几何/掩码特征与监督条件下,多条路线最终都落在约 61-63 的区间。这个区间只是观察到的结果,不是经过证明的硬上限。

两个关键失败案例#

ukt9w7oi:弱提示、小目标与密集行人#

首帧目标位于街景画面中部偏上,区域很小,周围有大量行人和相似衣着,伴随强透视、遮挡和运动模糊。

SAM2Long 从最初的路径选择阶段就锁定了错误对象,之后长记忆持续巩固错误身份:

SAM2.1-large: 69.16
SAM2Long: 0.91
差值: -68.3

这个案例说明,长记忆无法自动弥补低信息量提示。只要第一步身份选择错了,记得越牢,整段错误越稳定。

可能的工程应对包括:

  • 检测“小首帧掩码 + 高 distractor 密度”,自动回退到 SAM2 联合传播或 SAM3;
  • 使用点、框和 mask 的组合提示;
  • 在早期帧保留多个身份候选,而不是立即提交到单一路径;
  • 引入独立的 re-identification 表征验证候选身份。

0u8fy7u2:鸟群中的 identity switch#

画面中有大量快速运动的同类飞鸟。SAM2Long 在约第 11 帧后平滑地切换到另一只鸟:

SAM2.1-large: 76.07
SAM2Long: 15.82
差值: -60.3

它的质心移动连续、步长合理、面积也没有明显异常,从几何上看完全“合法”。这正是 GT-free 几何选择器最难处理的错误:轨迹没有坏,只是对象身份错了。

我尝试过把候选掩码区域与首帧目标做 HSV 直方图匹配,但鸟的姿态、视角与光照变化太大,同一只鸟在不同帧的外观差异甚至可能大于不同鸟之间的差异,简单颜色特征反而引入误判。

这个案例提示,突破方向可能不再是给 SAM2 输出加更多规则,而是引入 DINO 类语义表征、显式 re-identification,或 Cutie 等身份保持机制不同的 VOS 模型。

自拍实验暴露的概念缺口#

除了标准数据集,我还设计了两段自拍视频:

  1. 叠书后抽走前书:测试模型是否理解先后顺序与遮挡关系;
  2. 硬币叠放:测试模型是否理解空间叠放次序。

模型在简单平移阶段能够稳定传播,但发生遮挡或层叠后容易混淆目标。这类失败和 Benchmark 中的 identity switch 相互补充:

  • Benchmark 失败更像实例身份保持问题;
  • 自拍失败进一步暴露出概念与语义推理的缺失。

SAM2 的 memory propagation 很擅长传播像素证据,却不等价于理解物体永久性、遮挡顺序或“应该继续追踪谁”。

工程环境与复现记录#

实验在三类算力环境上运行:

平台GPU用途
KaggleTesla T4 ×2,各 16GB全量推理与对照实验
本地工作站RTX 4060,8GBbaseline 与自拍数据
腾讯 Cloud StudioA100SAM3 与长时实验补充

主要环境:

Python >= 3.10
PyTorch 2.x + CUDA
sam2
transformers >= 5.12
accelerate
supervision
opencv-python
numpy
scipy
pandas
matplotlib

Windows 环境安装 SAM2 时,为跳过非必要 CUDA 扩展编译,需要设置:

SAM2_BUILD_CUDA=0
SAM2_BUILD_ALLOW_ERRORS=1

仓库的主要结构为:

notebooks/
├── sam2_assignment.ipynb
├── sam2_assignment_comparison.ipynb
└── sam3_assignment.ipynb
scripts/
├── selector/ # v5/v8、ML 选择器与诊断脚本
└── figures/ # 模型对比动画和指标表
结果/ # 各模型逐帧输出和混合提交
score/ # per-object 成绩与汇总

建议按以下顺序复现:

  1. 准备 dataset/JPEGImages 与只含首帧的 dataset/Annotations
  2. 先用 SAM2.1-large 跑通一个视频,逐项验证文件名、尺寸、模式与调色板;
  3. MAX_VIDEOS=None,生成完整 baseline;
  4. 分别运行 SAM2Long、TCP 与 SAM3,保留各模型逐帧输出;
  5. scripts/selector 提取轨迹特征并生成混合提交;
  6. 提交 CodaBench,严格区分自动结果与依赖平台反馈的 oracle 结果。

这次实验真正学到的东西#

1. 平均分会掩盖机制#

SAM2Long 的 58.52 看起来只是比 baseline 低 0.38,但拆到具体物体后,真实现象是 +36-68 同时存在。只看平均值,很容易得出“长记忆没用”这种错误结论。

2. 先验证失败类型,再决定优化层级#

如果错误是边界毛刺,形态学与时序后处理有用;如果错误是实例身份切换,继续调孔洞填充、连通域阈值或投票窗口几乎不会解决问题。优化方法必须和失败层级匹配。

3. 负结果也能确定研究边界#

TCP 无增益、HSV re-identification 失效、裸 ML 低于 baseline,这些结果共同说明:纯几何与浅层外观特征不足以稳定判断身份。它们不是“没做出来”的附注,而是后续为什么需要语义表征、更强监督和异构模型的重要证据。

4. 集成收益来自错误独立性#

三个同源模型不一定比一个强模型更可靠。如果它们共享主干、记忆机制和失效模式,多数投票只会放大相关错误。下一步更值得做的不是继续堆叠 SAM 变体,而是引入身份机制不同的模型。

下一步计划#

如果继续推进这项研究,我会优先尝试:

  1. 逐帧监督:取得逐帧 J&F 或自行构造小规模逐帧标注,学习分歧发生的具体时间点;
  2. 语义外观特征:使用 DINO 等表征替代 HSV 直方图,比较首帧目标与候选轨迹的语义一致性;
  3. 光流与点跟踪:加入光流一致性、点跟踪覆盖率和遮挡恢复质量;
  4. 异构模型集成:加入 Cutie 等非 SAM 系 VOS 模型,提高错误模式独立性;
  5. 早期多假设追踪:在不确定阶段保留多个候选身份,避免长记忆过早固化单一路径;
  6. 独立验证集:把阈值搜索与最终评估完全分离,重新测量 v8 的真实泛化增益。

最终,我更愿意把这个项目理解为一次关于“模型为什么会跟错”的实验,而不只是一次分割模型跑分。掩码是否精细当然重要,但在视频里,首先必须保证这张掩码仍然属于最初指定的那个对象。身份正确,边界优化才有意义;身份错误,再平滑的轨迹也只是稳定地追错。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

从 SAM2 到 GT-free 多模型选择:视频目标分割实验开发笔记
https://cccy0721.top/posts/sam2-vos-model-selection/
作者
Cccy
发布于
2026-07-24
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00