refactor(chat): 简化 GIF 抽帧逻辑为均匀采样
旧的基于均方误差(MSE)的 GIF 抽帧逻辑过于复杂,其参数难以调整且结果不可预测。 现在,该逻辑被重构为一个简单的均匀采样算法,固定抽取 4 帧来代表整个动画。这不仅简化了代码、移除了不必要的参数,还确保了对于任何 GIF 都能生成一致且有代表性的预览图,同时提升了处理性能。 BREAKING CHANGE: `transform_gif` 函数签名已更改,移除了 `similarity_threshold` 和 `max_frames` 参数。
This commit is contained in:
@@ -288,14 +288,12 @@ class ImageManager:
|
|||||||
return "[图片(处理失败)]"
|
return "[图片(处理失败)]"
|
||||||
|
|
||||||
@staticmethod
|
@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
|
# sourcery skip: use-contextlib-suppress
|
||||||
"""将GIF转换为水平拼接的静态图像, 跳过相似的帧
|
"""将GIF转换为水平拼接的静态图像, 均匀抽取4帧。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
gif_base64: GIF的base64编码字符串
|
gif_base64: GIF的base64编码字符串
|
||||||
similarity_threshold: 判定帧相似的阈值 (MSE),越小表示要求差异越大才算不同帧,默认1000.0
|
|
||||||
max_frames: 最大抽取的帧数,默认15
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Optional[str]: 拼接后的JPG图像的base64编码字符串, 或者在失败时返回None
|
Optional[str]: 拼接后的JPG图像的base64编码字符串, 或者在失败时返回None
|
||||||
@@ -323,34 +321,17 @@ class ImageManager:
|
|||||||
logger.warning("GIF中没有找到任何帧")
|
logger.warning("GIF中没有找到任何帧")
|
||||||
return None # 空的GIF直接返回None
|
return None # 空的GIF直接返回None
|
||||||
|
|
||||||
# --- 新的帧选择逻辑 ---
|
# --- 新的帧选择逻辑:均匀抽取4帧 ---
|
||||||
selected_frames = []
|
num_frames = len(all_frames)
|
||||||
last_selected_frame_np = None
|
if num_frames <= 4:
|
||||||
|
# 如果总帧数小于等于4,则全部选中
|
||||||
for i, current_frame in enumerate(all_frames):
|
selected_frames = all_frames
|
||||||
current_frame_np = np.array(current_frame)
|
else:
|
||||||
|
# 使用linspace计算4个均匀分布的索引
|
||||||
# 第一帧总是要选的
|
indices = np.linspace(0, num_frames - 1, 4, dtype=int)
|
||||||
if i == 0:
|
selected_frames = [all_frames[i] for i in indices]
|
||||||
selected_frames.append(current_frame)
|
|
||||||
last_selected_frame_np = current_frame_np
|
logger.debug(f"GIF Frame Analysis: Total frames={num_frames}, Selected indices={indices if num_frames > 4 else list(range(num_frames))}")
|
||||||
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
|
|
||||||
# 如果差异不大就跳过这一帧啦
|
|
||||||
|
|
||||||
# --- 帧选择逻辑结束 ---
|
# --- 帧选择逻辑结束 ---
|
||||||
|
|
||||||
# 如果选择后连一帧都没有(比如GIF只有一帧且后续处理失败?)或者原始GIF就没帧,也返回None
|
# 如果选择后连一帧都没有(比如GIF只有一帧且后续处理失败?)或者原始GIF就没帧,也返回None
|
||||||
|
|||||||
Reference in New Issue
Block a user