refactor(chat): 简化 GIF 抽帧逻辑为均匀采样

旧的基于均方误差(MSE)的 GIF 抽帧逻辑过于复杂,其参数难以调整且结果不可预测。

现在,该逻辑被重构为一个简单的均匀采样算法,固定抽取 4 帧来代表整个动画。这不仅简化了代码、移除了不必要的参数,还确保了对于任何 GIF 都能生成一致且有代表性的预览图,同时提升了处理性能。

BREAKING CHANGE: `transform_gif` 函数签名已更改,移除了 `similarity_threshold` 和 `max_frames` 参数。
This commit is contained in:
tt-P607
2025-10-22 01:54:13 +08:00
parent 48d99defc7
commit 6a81fd40b5

View File

@@ -288,14 +288,12 @@ class ImageManager:
return "[图片(处理失败)]"
@staticmethod
def transform_gif(gif_base64: str, similarity_threshold: float = 1000.0, max_frames: int = 15) -> str | None:
def transform_gif(gif_base64: str) -> str | None:
# sourcery skip: use-contextlib-suppress
"""将GIF转换为水平拼接的静态图像, 跳过相似的帧
"""将GIF转换为水平拼接的静态图像, 均匀抽取4帧。
Args:
gif_base64: GIF的base64编码字符串
similarity_threshold: 判定帧相似的阈值 (MSE)越小表示要求差异越大才算不同帧默认1000.0
max_frames: 最大抽取的帧数默认15
Returns:
Optional[str]: 拼接后的JPG图像的base64编码字符串, 或者在失败时返回None
@@ -323,34 +321,17 @@ class ImageManager:
logger.warning("GIF中没有找到任何帧")
return None # 空的GIF直接返回None
# --- 新的帧选择逻辑 ---
selected_frames = []
last_selected_frame_np = None
for i, current_frame in enumerate(all_frames):
current_frame_np = np.array(current_frame)
# 第一帧总是要选的
if i == 0:
selected_frames.append(current_frame)
last_selected_frame_np = current_frame_np
continue
# 计算和上一张选中帧的差异(均方误差 MSE
if last_selected_frame_np is not None:
mse = np.mean((current_frame_np - last_selected_frame_np) ** 2)
# logger.debug(f"帧 {i} 与上一选中帧的 MSE: {mse}") # 可以取消注释来看差异值
# 如果差异够大,就选它!
if mse > similarity_threshold:
selected_frames.append(current_frame)
last_selected_frame_np = current_frame_np
# 检查是不是选够了
if len(selected_frames) >= max_frames:
# logger.debug(f"已选够 {max_frames} 帧,停止选择。")
break
# 如果差异不大就跳过这一帧啦
# --- 新的帧选择逻辑均匀抽取4帧 ---
num_frames = len(all_frames)
if num_frames <= 4:
# 如果总帧数小于等于4则全部选中
selected_frames = all_frames
else:
# 使用linspace计算4个均匀分布的索引
indices = np.linspace(0, num_frames - 1, 4, dtype=int)
selected_frames = [all_frames[i] for i in indices]
logger.debug(f"GIF Frame Analysis: Total frames={num_frames}, Selected indices={indices if num_frames > 4 else list(range(num_frames))}")
# --- 帧选择逻辑结束 ---
# 如果选择后连一帧都没有比如GIF只有一帧且后续处理失败或者原始GIF就没帧也返回None