增加了裁剪缩放和改变噪声的强度
This commit is contained in:
parent
5dabb8f807
commit
71b9a5ec3a
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user