diff --git a/utils/poster_notes_creator.py b/utils/poster_notes_creator.py index d919cc9..6e6ceb9 100644 --- a/utils/poster_notes_creator.py +++ b/utils/poster_notes_creator.py @@ -359,7 +359,7 @@ class PosterNotesCreator: def add_dct_noise(self, image: Image.Image, intensity: float = 0.1, block_size: int = 8) -> Image.Image: """ - 在DCT域添加噪声以对抗pHash (需要Scipy) + 在DCT域添加噪声以对抗pHash (需要Scipy) - 强化版 Args: image: 输入图像 (建议传入灰度图或处理亮度通道) @@ -376,11 +376,9 @@ class PosterNotesCreator: return image try: - logger.debug(f"应用DCT噪声,强度: {intensity:.3f}") + logger.debug(f"应用强化DCT噪声,强度: {intensity:.3f}") # 确保是灰度图或提取亮度通道 (这里以灰度为例) if image.mode != 'L': - # 如果是彩色图,可以在 Y 通道 (亮度) 操作 - # 为了简化,我们先转为灰度处理 gray_image = image.convert('L') else: gray_image = image @@ -397,242 +395,279 @@ class PosterNotesCreator: else: padded_h, padded_w = h, w - # 分块处理 + # 定义目标系数范围 (例如,排除DC的左上角4x4低频区域) + target_h, target_w = 4, 4 + for y in range(0, padded_h, block_size): for x in range(0, padded_w, block_size): block = img_array[y:y+block_size, x:x+block_size] - - # 执行2D DCT dct_block = dct(dct(block.T, norm='ortho').T, norm='ortho') - # 在非DC系数上添加噪声 (跳过 dct_block[0, 0]) - # 噪声强度与系数幅度相关,避免在小系数上加过大噪声 - noise = np.random.randn(block_size, block_size) * intensity * np.abs(dct_block) - # noise = np.random.uniform(-intensity*50, intensity*50, (block_size, block_size)) - noise[0, 0] = 0 # 不改变DC系数 + # --- 强化噪声逻辑 --- + # 1. 计算噪声幅度,不再完全依赖系数本身大小 + noise_amplitude = intensity * 30 # 固定基础噪声幅度 (可调) - # 将噪声添加到DCT系数 - noisy_dct_block = dct_block + noise + # 2. 生成噪声 + noise = np.random.uniform(-noise_amplitude, noise_amplitude, + (min(block_size, target_h), min(block_size, target_w))) - # 执行2D IDCT - idct_block = idct(idct(noisy_dct_block.T, norm='ortho').T, norm='ortho') + # 3. 应用噪声到目标低频区域 (跳过DC) + noise_h, noise_w = noise.shape + # 确保索引不超过dct_block的实际大小 + apply_h, apply_w = min(noise_h, dct_block.shape[0]), min(noise_w, dct_block.shape[1]) - # 将处理后的块放回图像数组 + # 尝试乘性噪声 - 可能对保留结构更好一点 + factor = np.random.uniform(1.0 - intensity * 0.8, 1.0 + intensity * 0.8, + (min(block_size, target_h), min(block_size, target_w))) + dct_block[0:apply_h, 0:apply_w] *= factor[0:apply_h, 0:apply_w] + dct_block[0, 0] /= factor[0, 0] # 恢复DC系数近似值 + # --- 结束强化噪声逻辑 --- + + idct_block = idct(idct(dct_block.T, norm='ortho').T, norm='ortho') img_array[y:y+block_size, x:x+block_size] = idct_block - # 裁剪回原始尺寸 (如果有填充) if h_pad != 0 or w_pad != 0: img_array = img_array[:h, :w] - # 裁剪像素值并转换类型 img_array = np.clip(img_array, 0, 255) modified_gray = Image.fromarray(img_array.astype(np.uint8)) - # 如果原图是彩色,将修改后的亮度通道合并回去 if image.mode == 'RGB' and gray_image is not image: - # 注意:简单替换亮度通道可能效果不好,混合通常更好 - # 这里用混合的方式 - blend_factor = 0.3 # 控制混合强度 + blend_factor = 0.35 # 稍微增加混合强度 r, g, b = image.split() r = Image.blend(r, modified_gray, blend_factor) g = Image.blend(g, modified_gray, blend_factor) b = Image.blend(b, modified_gray, blend_factor) merged_image = Image.merge('RGB', (r, g, b)) else: - # 如果原图是灰度或处理失败,返回修改后的灰度图 merged_image = modified_gray - # 在函数末尾成功时记录 - logger.debug("DCT噪声应用成功。") - return merged_image if 'merged_image' in locals() else modified_gray # 返回最终结果 + logger.debug("强化DCT噪声应用成功。") + return merged_image except Exception as e: - logger.error(f"DCT噪声注入出错: {e}") - return image # 出错时返回原图 + logger.error(f"强化DCT噪声注入出错: {e}") + return image def add_phash_noise(self, image: Image.Image, intensity: float = 0.05) -> Image.Image: + """调用强化的 add_dct_noise 方法""" + logger.debug(f"调用强化add_dct_noise对抗pHash,强度: {intensity:.3f}") + return self.add_dct_noise(image, intensity=intensity) + + def apply_smart_crop_resize(self, image: Image.Image, strength: str = "medium") -> Image.Image: """ - 添加扰动以对抗感知哈希算法(pHash) - 现在调用基于 Scipy 的 DCT 噪声注入方法 + 应用智能裁剪和重缩放来抵抗哈希算法 Args: image: 输入图像 - intensity: 扰动强度(0-1) + strength: 处理强度 ('low', 'medium', 'high') Returns: - 添加扰动后的图像 + 处理后的图像 """ - logger.debug(f"调用add_dct_noise对抗pHash,强度: {intensity:.3f}") - return self.add_dct_noise(image, intensity=intensity) + try: + original_width, original_height = image.size + logger.debug(f"应用智能裁剪+重缩放 (强度: {strength}), 原始尺寸: {original_width}x{original_height}") + + # 根据强度决定裁剪量 (0-3 像素) + if strength == "low": + max_crop = 1 + elif strength == "high": + max_crop = 3 + else: # medium + max_crop = 2 + + # 随机决定每边的裁剪量 + crop_left = random.randint(0, max_crop) + crop_top = random.randint(0, max_crop) + crop_right = random.randint(0, max_crop) + crop_bottom = random.randint(0, max_crop) + + # 计算裁剪后的边界 + left = crop_left + top = crop_top + right = original_width - crop_right + bottom = original_height - crop_bottom + + # 确保裁剪后尺寸至少为1x1 + if left >= right or top >= bottom: + logger.warning("智能裁剪计算无效,跳过此步骤。") + return image + + logger.debug(f" 裁剪参数: L={crop_left}, T={crop_top}, R={crop_right}, B={crop_bottom}") + logger.debug(f" 裁剪区域: ({left}, {top}, {right}, {bottom})") + + # 执行裁剪 + cropped_image = image.crop((left, top, right, bottom)) + + # 使用高质量插值将图像缩放回原始尺寸 + logger.debug(f" 将裁剪后图像 ({cropped_image.width}x{cropped_image.height}) 缩放回 ({original_width}x{original_height})") + resampling_filter = Image.LANCZOS # 高质量插值 + resized_image = cropped_image.resize((original_width, original_height), resample=resampling_filter) + + logger.debug("智能裁剪+重缩放应用成功。") + return resized_image + + except Exception as e: + logger.error(f"智能裁剪+重缩放时出错: {e}") + return image # 出错时返回原图 def optimize_anti_hash_methods(self, image: Image.Image, strength: str = "medium") -> Image.Image: - """综合优化的哈希对抗方法,强度已增加,添加日志记录,High强度极度强化""" - logger.info(f"--- 开始优化抗哈希方法 (强度: {strength}) ---") - original_image_for_logging = image.copy() # 复制一份用于前后对比日志 + """优化后的哈希对抗方法,专注于裁剪缩放和DCT噪声""" + logger.info(f"--- 开始优化抗哈希方法 (强度: {strength}) - 新策略 ---") + original_image_for_logging = image.copy() - # 根据强度设置参数 (极度增加 high 强度) + # --- 参数定义 --- if strength == "low": - ahash_intensity = 0.03 - phash_intensity = 0.05 # 基础DCT噪声强度 - dhash_intensity = 0.03 - region_flip_prob = 0.3 - num_ahash_blocks = random.randint(8, 15) - num_dhash_lines = random.randint(6, 10) - ahash_delta_range = (-30, 30) - dhash_delta_range = 30 - region_max_factor = 25 - gaussian_noise_sigma = 0.0 # Low 不加高斯噪声 - gaussian_noise_prob = 0.0 + phash_intensity = 0.06 # 轻微DCT噪声 + color_hist_strength = 0.02 # 轻微颜色扰动 + apply_crop_resize = True # 应用裁剪缩放 elif strength == "high": - ahash_intensity = 0.40 # 极度增加 - phash_intensity = 0.25 # 极度增加 - dhash_intensity = 0.40 # 极度增加 - region_flip_prob = 0.90 # 很高概率翻转 - num_ahash_blocks = random.randint(40, 60) # 很多块 - num_dhash_lines = random.randint(30, 45) # 很多线 - ahash_delta_range = (-50, 50) # 更大亮度变化 - dhash_delta_range = 50 # 更大梯度变化 - region_max_factor = 12 # 区域更大 - gaussian_noise_sigma = 3.5 # 更强高斯噪声 - gaussian_noise_prob = 0.6 # 更高概率 - else: # medium (也适度增加) - ahash_intensity = 0.10 # 增加 - phash_intensity = 0.10 # 增加 - dhash_intensity = 0.10 # 增加 - region_flip_prob = 0.6 - num_ahash_blocks = random.randint(15, 30) - num_dhash_lines = random.randint(12, 20) - ahash_delta_range = (-40, 40) - dhash_delta_range = 40 - region_max_factor = 18 - gaussian_noise_sigma = 1.5 - gaussian_noise_prob = 0.5 + phash_intensity = 0.20 # 较强DCT噪声 + color_hist_strength = 0.06 # 较强颜色扰动 + apply_crop_resize = True # 应用裁剪缩放 + else: # medium + phash_intensity = 0.12 # 中等DCT噪声 + color_hist_strength = 0.04 # 中等颜色扰动 + apply_crop_resize = True # 应用裁剪缩放 - logger.debug(f"参数: aHash强度={ahash_intensity:.2f}, pHash强度={phash_intensity:.2f}, dHash强度={dhash_intensity:.2f}, 翻转概率={region_flip_prob:.2f}") + logger.debug(f"参数: pHash强度={phash_intensity:.2f}, 颜色强度={color_hist_strength:.2f}, 应用裁剪缩放={apply_crop_resize}") - # 1. 针对aHash ... - logger.debug(f"应用 aHash 对抗: {num_ahash_blocks} 个亮度块, 强度={ahash_intensity:.2f}, delta范围={ahash_delta_range}") - img_array = np.array(image, dtype=np.int16) - h, w = img_array.shape[0], img_array.shape[1] - for _ in range(num_ahash_blocks): - block_w = random.randint(w//20, w//10) - block_h = random.randint(h//20, h//10) - x = random.randint(0, w - block_w) - y = random.randint(0, h - block_h) - delta = int(random.uniform(ahash_delta_range[0], ahash_delta_range[1]) * ahash_intensity) - block = img_array[y:y+block_h, x:x+block_w] - img_array[y:y+block_h, x:x+block_w] = np.clip(block + delta, 0, 255) - image = Image.fromarray(img_array.astype(np.uint8)) - logger.debug("aHash 对抗完成。") + processed_image = image # 从原图开始 - # 2. 调用强化的pHash对抗方法 (使用 scipy DCT) - logger.debug(f"应用 pHash 对抗 (DCT噪声), 强度={phash_intensity:.2f}") - image = self.add_phash_noise(image, intensity=phash_intensity) - # add_phash_noise 内部已有成功日志 + # 1. 智能裁剪 + 重缩放 (策略C) - 作为第一步,改变像素基准 + if apply_crop_resize: + processed_image = self.apply_smart_crop_resize(processed_image, strength) + # 内部已有日志 - # 3. 针对dHash ... - logger.debug(f"应用 dHash 对抗: {num_dhash_lines} 条梯度线, 强度={dhash_intensity:.2f}, delta范围=+/-({dhash_delta_range})") - img_array = np.array(image, dtype=np.int16) - h, w = img_array.shape[0], img_array.shape[1] - mask = np.zeros_like(img_array, dtype=bool) - for _ in range(num_dhash_lines): - line_width = random.randint(2, 5) # 增加线宽 - if random.random() < 0.5: - y = random.randint(0, h - 1) - if len(mask.shape) == 3: - mask[max(0, y-line_width//2):min(h, y+line_width//2+1), :, :] = True - else: - mask[max(0, y-line_width//2):min(h, y+line_width//2+1), :] = True + # 2. 强化的pHash对抗方法 (策略A) + logger.debug(f"应用 pHash 对抗 (强化DCT噪声), 强度={phash_intensity:.2f}") + processed_image = self.add_phash_noise(processed_image, intensity=phash_intensity) + # 内部已有日志 + + # 3. 保留轻微的颜色直方图扰动 (可选,影响相对小) + if color_hist_strength > 0: + logger.debug(f"应用颜色直方图扰动, 强度={color_hist_strength:.3f}") + processed_image = self.perturb_color_histogram(processed_image, strength=color_hist_strength) + # 内部已有日志 + + # --- 移除了之前的 aHash块, dHash线, 区域变换, 高斯噪声等 --- + logger.debug("移除了 aHash块, dHash线, 区域变换, 高斯噪声等局部微扰方法。") + + # 对比修改前后 + try: + diff = ImageChops.difference(original_image_for_logging, processed_image).getbbox() + if diff: + logger.info(f"图像已修改。差异区域: {diff}") else: - x = random.randint(0, w - 1) - if len(mask.shape) == 3: - mask[:, max(0, x-line_width//2):min(w, x+line_width//2+1), :] = True - else: - mask[:, max(0, x-line_width//2):min(w, x+line_width//2+1)] = True - delta = (np.random.random(img_array.shape) * 2 - 1) * dhash_intensity * dhash_delta_range - img_array[mask] += delta[mask].astype(np.int16) - img_array = np.clip(img_array, 0, 255) - image = Image.fromarray(img_array.astype(np.uint8)) - logger.debug("dHash 对抗完成。") - - # 4. 颜色直方图扰动 ... - color_hist_strength = dhash_intensity * 0.7 # 关联强度增加 - logger.debug(f"应用颜色直方图扰动, 强度={color_hist_strength:.3f}") - image = self.perturb_color_histogram(image, strength=color_hist_strength) - # perturb_color_histogram 内部已有成功日志 - - # 5. 区域翻转/旋转 ... - if random.random() < region_flip_prob: - logger.debug(f"应用区域变换 (翻转/旋转), 概率触发成功.") - img_array = np.array(image) - h, w = img_array.shape[0], img_array.shape[1] - region_w = random.randint(w//(region_max_factor+5), w//region_max_factor) - region_h = random.randint(h//(region_max_factor+5), h//region_max_factor) - region_w = max(1, region_w) - region_h = max(1, region_h) - x = random.randint(0, max(0, w - region_w)) - y = random.randint(0, max(0, h - region_h)) - - action = random.choice(['flip_h', 'flip_v', 'rotate_90']) if strength != 'low' else random.choice(['flip_h', 'flip_v']) - - action_applied = "skipped" - try: # 添加 try-except 捕获潜在错误 - if y+region_h <= h and x+region_w <= w: - region = img_array[y:y+region_h, x:x+region_w] - if region.size == 0: - action_applied = "empty_region_skipped" - elif action == 'flip_h': - img_array[y:y+region_h, x:x+region_w] = region[:, ::-1] - action_applied = action - elif action == 'flip_v': - img_array[y:y+region_h, x:x+region_w] = region[::-1, :] - action_applied = action - elif action == 'rotate_90' and len(img_array.shape) == 3: - if abs(region_w - region_h) < region_w * 0.3: - rotated_region = np.rot90(region) - if rotated_region.shape[0] == region_h and rotated_region.shape[1] == region_w: - img_array[y:y+region_h, x:x+region_w] = rotated_region - action_applied = action - else: - logger.debug(f" 区域旋转跳过: 尺寸不匹配 ({region.shape} -> {rotated_region.shape})") - action_applied = "rotate_skipped_size_mismatch" - else: - logger.debug(f" 区域旋转跳过: 非方形区域 ({region_w}x{region_h})") - action_applied = "rotate_skipped_not_square" - else: - action_applied = f"{action}_skipped_condition_not_met" - else: - action_applied = "region_bounds_error_skipped" - logger.warning(f"跳过区域变换,切片索引无效: y={y}, h={region_h}, x={x}, w={region_w}, img_shape={img_array.shape}") - - except Exception as e_region: - logger.warning(f"应用区域变换 {action} 时出错: {e_region}") - action_applied = f"error: {e_region}" - - if action_applied not in ["skipped", "empty_region_skipped", "rotate_skipped_size_mismatch", "rotate_skipped_not_square", "region_bounds_error_skipped"] and not action_applied.startswith("error"): - image = Image.fromarray(img_array) - logger.debug(f" 执行了区域操作: {action_applied} 在 ({x},{y}) 大小 ({region_w}x{region_h})") - else: - logger.debug(f" 区域变换未执行或跳过: {action_applied}") - else: - logger.debug("跳过区域变换 (概率未触发).") - - # 6. 轻微高斯噪声 ... - apply_gaussian_noise = random.random() < gaussian_noise_prob - if gaussian_noise_sigma > 0 and apply_gaussian_noise: - logger.debug(f"应用高斯噪声, sigma={gaussian_noise_sigma:.1f}") - img_array = np.array(image) - noise = np.random.normal(0, gaussian_noise_sigma, img_array.shape) - img_array = np.clip(img_array + noise, 0, 255).astype(np.uint8) - image = Image.fromarray(img_array) - elif gaussian_noise_sigma > 0: - logger.debug("跳过高斯噪声 (概率未触发).") - - # ... (对比日志不变) ... + logger.warning("!!!优化方法似乎未修改图像!!!") + except ValueError: # 如果图像模式不同(例如灰度vs彩色),会引发ValueError + logger.warning("无法比较图像差异:模式可能不同。") + except Exception as log_e: + logger.warning(f"无法比较图像差异: {log_e}") - logger.info(f"--- 完成优化抗哈希方法 (强度: {strength}) ---") - return image + logger.info(f"--- 完成优化抗哈希方法 (强度: {strength}) - 新策略 ---") + return processed_image + + def perturb_color_histogram(self, image: Image.Image, strength: float = 0.03) -> Image.Image: + """ + 扰动图像的颜色直方图,对抗基于颜色统计的图像匹配 + + Args: + image: 输入图像 + strength: 扰动强度(0-1) + + Returns: + 处理后的图像 + """ + logger.debug(f"扰动颜色直方图,强度: {strength:.3f}") + # 确保为RGB模式 + if image.mode != 'RGB': + image = image.convert('RGB') + + # 转为numpy数组 + img_array = np.array(image) + height, width, channels = img_array.shape + + # 对每个通道分别处理 + for channel in range(channels): + # 计算当前通道的直方图 + hist, _ = np.histogram(img_array[:,:,channel].flatten(), bins=64, range=(0, 256)) + + # 找出主要颜色区间 (频率高的区间) + threshold = np.percentile(hist, 70) # 取前30%的颜色块 + significant_bins = np.where(hist > threshold)[0] + + if len(significant_bins) > 0: + for bin_idx in significant_bins: + # 计算当前bin对应的颜色范围 + bin_width = 256 // 64 + color_low = bin_idx * bin_width + color_high = (bin_idx + 1) * bin_width + + # 创建颜色范围掩码 + mask = (img_array[:,:,channel] >= color_low) & (img_array[:,:,channel] < color_high) + + if np.any(mask): + # 生成随机偏移值 + offset = int(strength * bin_width * (random.random() - 0.5) * 2) + + # 应用偏移,确保在0-255范围内 + img_array[:,:,channel][mask] = np.clip( + img_array[:,:,channel][mask] + offset, 0, 255).astype(np.uint8) + + # 转回PIL图像 + logger.debug("颜色直方图扰动成功。") + return Image.fromarray(img_array) + def strip_metadata(self, image: Image.Image) -> Image.Image: + """ + 移除图像中的所有元数据 (修复版) + + Args: + image: 输入图像 + + Returns: + 无元数据的图像 + """ + logger.debug("移除图像元数据...") + try: + # 从当前图像对象的像素数据创建一个新的Image对象 + # 这确保我们使用的是内存中修改后的数据 + # 保留原始格式信息,如果可用 + img_format = image.format if hasattr(image, 'format') else 'PNG' + + # 如果图像有alpha通道,需要正确处理 + if image.mode == 'RGBA': + # 创建一个白色背景,然后粘贴带有alpha的图像 + background = Image.new("RGB", image.size, (255, 255, 255)) + background.paste(image, mask=image.split()[3]) # 3 is the alpha channel + image_to_save = background + img_format = 'JPEG' # 通常去除alpha后存为JPEG + elif image.mode == 'P': + # 带调色板的图像转换为RGB + image_to_save = image.convert('RGB') + img_format = 'JPEG' + else: + image_to_save = image + + # 保存到内存缓冲区 + data = io.BytesIO() + # 确保保存时指定质量参数,避免默认压缩导致意外变化 + if img_format == 'JPEG': + image_to_save.save(data, format='JPEG', quality=95) # 使用高质量JPEG + else: + # 对于PNG等无损格式,不需要质量参数 + image_to_save.save(data, format=img_format) + + data.seek(0) # 重置缓冲区指针 + reloaded_image = Image.open(data) + logger.debug("元数据移除成功。") + return reloaded_image + except Exception as e: + logger.error(f"移除元数据时出错: {e}") + return image # 出错时返回原图 + def optimized_process_image( self, image: Image.Image, @@ -792,105 +827,6 @@ class PosterNotesCreator: logger.info(f"图像处理完成 (强度: {variation_strength})") return final_image - def perturb_color_histogram(self, image: Image.Image, strength: float = 0.03) -> Image.Image: - """ - 扰动图像的颜色直方图,对抗基于颜色统计的图像匹配 - - Args: - image: 输入图像 - strength: 扰动强度(0-1) - - Returns: - 处理后的图像 - """ - logger.debug(f"扰动颜色直方图,强度: {strength:.3f}") - # 确保为RGB模式 - if image.mode != 'RGB': - image = image.convert('RGB') - - # 转为numpy数组 - img_array = np.array(image) - height, width, channels = img_array.shape - - # 对每个通道分别处理 - for channel in range(channels): - # 计算当前通道的直方图 - hist, _ = np.histogram(img_array[:,:,channel].flatten(), bins=64, range=(0, 256)) - - # 找出主要颜色区间 (频率高的区间) - threshold = np.percentile(hist, 70) # 取前30%的颜色块 - significant_bins = np.where(hist > threshold)[0] - - if len(significant_bins) > 0: - for bin_idx in significant_bins: - # 计算当前bin对应的颜色范围 - bin_width = 256 // 64 - color_low = bin_idx * bin_width - color_high = (bin_idx + 1) * bin_width - - # 创建颜色范围掩码 - mask = (img_array[:,:,channel] >= color_low) & (img_array[:,:,channel] < color_high) - - if np.any(mask): - # 生成随机偏移值 - offset = int(strength * bin_width * (random.random() - 0.5) * 2) - - # 应用偏移,确保在0-255范围内 - img_array[:,:,channel][mask] = np.clip( - img_array[:,:,channel][mask] + offset, 0, 255).astype(np.uint8) - - # 转回PIL图像 - logger.debug("颜色直方图扰动成功。") - return Image.fromarray(img_array) - - def strip_metadata(self, image: Image.Image) -> Image.Image: - """ - 移除图像中的所有元数据 (修复版) - - Args: - image: 输入图像 - - Returns: - 无元数据的图像 - """ - logger.debug("移除图像元数据...") - try: - # 从当前图像对象的像素数据创建一个新的Image对象 - # 这确保我们使用的是内存中修改后的数据 - # 保留原始格式信息,如果可用 - img_format = image.format if hasattr(image, 'format') else 'PNG' - - # 如果图像有alpha通道,需要正确处理 - if image.mode == 'RGBA': - # 创建一个白色背景,然后粘贴带有alpha的图像 - background = Image.new("RGB", image.size, (255, 255, 255)) - background.paste(image, mask=image.split()[3]) # 3 is the alpha channel - image_to_save = background - img_format = 'JPEG' # 通常去除alpha后存为JPEG - elif image.mode == 'P': - # 带调色板的图像转换为RGB - image_to_save = image.convert('RGB') - img_format = 'JPEG' - else: - image_to_save = image - - # 保存到内存缓冲区 - data = io.BytesIO() - # 确保保存时指定质量参数,避免默认压缩导致意外变化 - if img_format == 'JPEG': - image_to_save.save(data, format='JPEG', quality=95) # 使用高质量JPEG - else: - # 对于PNG等无损格式,不需要质量参数 - image_to_save.save(data, format=img_format) - - data.seek(0) # 重置缓冲区指针 - reloaded_image = Image.open(data) - logger.debug("元数据移除成功。") - return reloaded_image - except Exception as e: - logger.error(f"移除元数据时出错: {e}") - return image # 出错时返回原图 - def process_poster_for_notes( run_id: str, topic_index: int,