import os
import random
import logging
import json
from PIL import Image, ImageChops
import traceback
from typing import List, Tuple, Dict, Any, Optional
import concurrent.futures
import numpy as np
from PIL import ImageEnhance, ImageFilter
from .output_handler import OutputHandler
import io
import math

# 尝试导入 scipy，如果失败则标记
try:
    from scipy.fftpack import dct, idct
    SCIPY_AVAILABLE = True
except ImportError:
    SCIPY_AVAILABLE = False
    dct = None
    idct = None

logger = logging.getLogger(__name__)

class PosterNotesCreator:
    """
    处理原始海报作为主图，并随机选择额外的图片作为笔记图片。
    确保选择的笔记图片与海报中使用的图片不重复。
    """
    
    def __init__(self, output_handler: OutputHandler):
        """
        初始化 PosterNotesCreator
        
        Args:
            output_handler: 可选的 OutputHandler 实例，用于处理输出
        """
        self.output_handler = output_handler
        logging.info("PosterNotesCreator 初始化完成")
    
    def create_notes_images(
        self,
        run_id: str,
        topic_index: int,
        variant_index: int,
        poster_image_path: str,
        poster_metadata_path: str,
        source_image_dir: str,
        num_additional_images: int,
        output_filename_template: str = "note_{index}.jpg"
    ) -> List[str]:
        """
        创建笔记图像
        
        Args:
            run_id: 运行ID
            topic_index: 主题索引
            variant_index: 变体索引
            poster_image_path: 海报图像路径
            poster_metadata_path: 海报元数据路径
            source_image_dir: 源图像目录
            num_additional_images: 要使用的额外图像数量
            output_filename_template: 输出文件名模板
            
        Returns:
            List[str]: 保存的笔记图像路径列表
        """
        # 检查输入路径是否存在
        if not os.path.exists(poster_image_path):
            logger.error(f"海报图像不存在: {poster_image_path}")
            return []
        
        if not os.path.exists(poster_metadata_path):
            logger.error(f"海报元数据不存在: {poster_metadata_path}")
            return []
        
        if not os.path.exists(source_image_dir) or not os.path.isdir(source_image_dir):
            logger.error(f"源图像目录不存在: {source_image_dir}")
            return []
        
        # 从元数据文件中读取已使用的图像信息
        try:
            with open(poster_metadata_path, 'r', encoding='utf-8') as f:
                poster_metadata = json.load(f)
        except Exception as e:
            logger.error(f"无法读取海报元数据: {e}")
            return []
        
        # 获取已经在海报中使用的图像
        used_images = []
        if 'collage_images' in poster_metadata:
            used_images = poster_metadata['collage_images']
            logger.info(f"海报中已使用 {len(used_images)} 张图像: {', '.join(used_images)}")
        
        # 列出源目录中的所有图像文件
        image_extensions = ('.jpg', '.jpeg', '.png', '.bmp')
        available_images = [
            f for f in os.listdir(source_image_dir)
            if os.path.isfile(os.path.join(source_image_dir, f)) and
            f.lower().endswith(image_extensions)
        ]
        
        if not available_images:
            logger.error(f"源目录中没有找到图像: {source_image_dir}")
            return []
        
        logger.info(f"源目录中找到 {len(available_images)} 张图像")
        
        # 过滤掉已经在海报中使用的图像
        available_images = [img for img in available_images if img not in used_images]
        
        if not available_images:
            logger.warning("所有图像都已在海报中使用，无法创建额外笔记")
            return []
        
        logger.info(f"过滤后可用图像数量: {len(available_images)}")
        
        # 如果可用图像少于请求数量，进行警告但继续处理
        if len(available_images) < num_additional_images:
            logger.warning(
                f"可用图像数量 ({len(available_images)}) 少于请求的笔记数量 ({num_additional_images})，"
                f"将使用所有可用图像"
            )
            selected_images = available_images
        else:
            # 随机选择额外图像
            selected_images = random.sample(available_images, num_additional_images)
        
        logger.info(f"已选择 {len(selected_images)} 张图像作为笔记")
        
        # 保存选择的笔记图像
        saved_paths = []
        for i, image_filename in enumerate(selected_images):
            try:
                # 加载图像
                image_path = os.path.join(source_image_dir, image_filename)
                image = Image.open(image_path)
                
                # 生成输出文件名
                output_filename = output_filename_template.format(index=i+1)
                
                # 创建元数据
                note_metadata = {
                    "original_image": image_filename,
                    "note_index": i + 1,
                    "source_dir": source_image_dir,
                    "associated_poster": os.path.basename(poster_image_path)
                }
                
                # 使用输出处理器保存图像
                saved_path = self.output_handler.handle_generated_image(
                    run_id,
                    topic_index,
                    variant_index,
                    'note',  # 图像类型为note
                    image,
                    output_filename,
                    note_metadata
                )
                
                saved_paths.append(saved_path)
                logger.info(f"已保存笔记图像 {i+1}/{len(selected_images)}: {saved_path}")
                
            except Exception as e:
                logger.error(f"处理图像时出错 '{image_filename}': {e}")
        
        return saved_paths

    def create_additional_images(
        self,
        run_id: str,
        topic_index: int,
        variant_index: int,
        poster_metadata_path: str,
        source_image_dir: str,
        num_additional_images: int = 3,
        output_filename_template: str = "additional_{index}.jpg",
        variation_strength: str = "medium",
        extra_effects: bool = True
    ) -> List[str]:
        """选择未被海报使用的图像作为额外配图，并处理为3:4比例"""
        logger.info(f"开始为主题 {topic_index} 变体 {variant_index} 选择额外配图")
        
        # 获取候选图像
        candidate_images = self.get_candidate_images(
            poster_metadata_path,
            source_image_dir,
            num_additional_images
        )
        
        if not candidate_images:
            logger.warning("没有找到合适的候选图像")
            return []
            
        # 生成唯一的随机种子
        seed_str = f"{run_id}_{topic_index}_{variant_index}"
        seed = sum(ord(c) for c in seed_str)
        logger.info(f"使用随机种子: {seed}，基于: {seed_str}")
        
        # 使用多进程并行处理图像
        saved_paths = []
        with concurrent.futures.ProcessPoolExecutor(max_workers=min(4, len(candidate_images))) as executor:
            # 创建任务
            future_to_image = {}
            for i, image_filename in enumerate(candidate_images):
                image_path = os.path.join(source_image_dir, image_filename)
                # 为每个图像创建单独的种子
                image_seed = seed + i
                
                future = executor.submit(
                    self.process_single_image,
                    run_id,
                    topic_index,
                    variant_index,
                    image_path,
                    image_filename,
                    i,
                    source_image_dir,
                    output_filename_template.format(index=i+1),
                    image_seed,
                    variation_strength,
                    extra_effects
                )
                future_to_image[future] = (i, image_filename)
            
            # 收集结果
            for future in concurrent.futures.as_completed(future_to_image):
                i, image_filename = future_to_image[future]
                try:
                    saved_path = future.result()
                    if saved_path:
                        saved_paths.append(saved_path)
                        logger.info(f"已保存额外配图 {i+1}/{len(candidate_images)}: {saved_path}")
                except Exception as e:
                    logger.error(f"处理图像时出错 '{image_filename}': {e}")
                    logger.error(traceback.format_exc())
        
        return saved_paths
    
    def get_candidate_images(self, poster_metadata_path, source_image_dir, num_images):
        """获取候选图像列表，排除已用于海报的图像"""
        # 检查输入路径是否存在
        if not os.path.exists(poster_metadata_path):
            logger.error(f"海报元数据不存在: {poster_metadata_path}")
            return []
        
        if not os.path.exists(source_image_dir) or not os.path.isdir(source_image_dir):
            logger.error(f"源图像目录不存在: {source_image_dir}")
            return []
        
        # 从元数据文件中读取已使用的图像信息
        try:
            with open(poster_metadata_path, 'r', encoding='utf-8') as f:
                poster_metadata = json.load(f)
        except Exception as e:
            logger.error(f"无法读取海报元数据: {e}")
            return []
        
        # 获取已经在海报中使用的图像
        used_images = []
        if 'collage_images' in poster_metadata:
            used_images = poster_metadata['collage_images']
            logger.info(f"海报中已使用 {len(used_images)} 张图像: {', '.join(used_images)}")
        
        # 列出源目录中的所有图像文件
        image_extensions = ('.jpg', '.jpeg', '.png', '.bmp')
        available_images = [
            f for f in os.listdir(source_image_dir)
            if os.path.isfile(os.path.join(source_image_dir, f)) and
            f.lower().endswith(image_extensions)
        ]
        
        if not available_images:
            logger.error(f"源目录中没有找到图像: {source_image_dir}")
            return []
        
        logger.info(f"源目录中找到 {len(available_images)} 张图像")
        
        # 过滤掉已经在海报中使用的图像
        available_images = [img for img in available_images if img not in used_images]
        
        if not available_images:
            logger.warning("所有图像都已在海报中使用，无法创建额外配图")
            return []
        
        logger.info(f"过滤后可用图像数量: {len(available_images)}")
        
        # 如果可用图像少于请求数量，进行警告但继续处理
        if len(available_images) < num_images:
            logger.warning(
                f"可用图像数量 ({len(available_images)}) 少于请求的配图数量 ({num_images})，"
                f"将使用所有可用图像"
            )
            selected_images = available_images
        else:
            # 随机选择额外图像
            random.seed(sum(map(ord, ''.join(available_images))))  # 确保结果一致性
            selected_images = random.sample(available_images, num_images)
            random.seed()  # 重置随机种子
        
        return selected_images
    
    def process_single_image(
        self,
        run_id,
        topic_index,
        variant_index,
        image_path,
        image_filename,
        index,
        source_dir,
        output_filename,
        seed,
        variation_strength,
        extra_effects
    ):
        """处理单张图像 - 此方法可在独立进程中运行"""
        try:
                # 加载图像
            image = Image.open(image_path)
                
                # 处理图像为3:4比例，并添加微小变化
            processed_image = self.optimized_process_image(
                    image, 
                    (3, 4), 
                    add_variation=True,
                seed=seed,
                    variation_strength=variation_strength,
                    extra_effects=extra_effects
                )
                
                # 创建元数据
            additional_metadata = {
                    "original_image": image_filename,
                "additional_index": index + 1,
                "source_dir": source_dir,
                    "is_additional_image": True,
                    "processed": True,
                    "aspect_ratio": "3:4",
                    "variation_applied": True,
                    "variation_strength": variation_strength,
                    "extra_effects": extra_effects
                }
                
                # 使用输出处理器保存图像
            return self.output_handler.handle_generated_image(
                    run_id,
                    topic_index,
                    variant_index,
                    'additional',  # 图像类型为additional
                    processed_image,
                    output_filename,
                    additional_metadata
                )
        except Exception as e:
            logger.error(f"处理图像时出错 '{image_filename}': {e}")
            logger.error(traceback.format_exc())
            return None
    
    def add_dct_noise(self, image: Image.Image, intensity: float = 0.1, block_size: int = 8) -> Image.Image:
        """
        在DCT域添加噪声以对抗pHash (需要Scipy) - 强化版
        
        Args:
            image: 输入图像 (建议传入灰度图或处理亮度通道)
            intensity: 噪声强度 (0-1)
            block_size: DCT块大小 (通常为8)
            
        Returns:
            添加噪声后的图像
        """
        if not SCIPY_AVAILABLE:
            logger.warning("Scipy 未安装，无法执行DCT噪声注入。请运行 'pip install scipy'")
            # 可以选择返回原图，或执行一个简化的备用方案
            # 这里我们返回原图
            return image
        
        try:
            logger.debug(f"应用强化DCT噪声，强度: {intensity:.3f}")
            # 确保是灰度图或提取亮度通道 (这里以灰度为例)
            if image.mode != 'L':
                gray_image = image.convert('L')
            else:
                gray_image = image
            
            img_array = np.array(gray_image, dtype=float)
            h, w = img_array.shape
            
            # 确保尺寸是块大小的倍数
            h_pad = (block_size - h % block_size) % block_size
            w_pad = (block_size - w % block_size) % block_size
            if h_pad != 0 or w_pad != 0:
                img_array = np.pad(img_array, ((0, h_pad), (0, w_pad)), mode='reflect')
                padded_h, padded_w = img_array.shape
            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]
                    dct_block = dct(dct(block.T, norm='ortho').T, norm='ortho')
                    
                    # --- 强化噪声逻辑 ---
                    # 1. 计算噪声幅度，不再完全依赖系数本身大小
                    noise_amplitude = intensity * 30 # 固定基础噪声幅度 (可调)
                    
                    # 2. 生成噪声
                    noise = np.random.uniform(-noise_amplitude, noise_amplitude,
                                            (min(block_size, target_h), min(block_size, target_w)))
                    
                    # 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.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
                
        except Exception as e:
            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:
        """
        应用智能裁剪和重缩放来抵抗哈希算法 (增强版)
        
        Args:
            image: 输入图像
            strength: 处理强度 ('low', 'medium', 'high')
            
        Returns:
            处理后的图像
        """
        try:
            original_width, original_height = image.size
            logger.debug(f"应用智能裁剪+重缩放 (强度: {strength}), 原始尺寸: {original_width}x{original_height}")

            # 根据强度决定裁剪量 (增强)
            if strength == "low":
                max_crop = 3  # 原为 1
            elif strength == "high":
                max_crop = 10 # 原为 3
            else: # medium
                max_crop = 6  # 原为 2
            
            logger.debug(f"增强型智能裁剪: max_crop = {max_crop} 像素")

            # 随机决定每边的裁剪量
            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("智能裁剪计算无效（裁剪过多），尝试使用较小裁剪量。")
                # 尝试减小裁剪量再次计算
                safe_max_crop = min(original_width // 4, original_height // 4, max_crop) # 保证不裁掉整个图
                crop_left = random.randint(0, safe_max_crop)
                crop_top = random.randint(0, safe_max_crop)
                crop_right = random.randint(0, safe_max_crop)
                crop_bottom = random.randint(0, safe_max_crop)
                left = crop_left
                top = crop_top
                right = original_width - crop_right
                bottom = original_height - crop_bottom
                if left >= right or top >= bottom: # 再次失败则跳过
                     logger.error("智能裁剪再次失败，跳过此步骤。")
                     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 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:
            # 确保图像处于适合保存的模式（例如RGB）
            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
            elif image.mode == 'P':
                 # 带调色板的图像转换为RGB
                 image_to_save = image.convert('RGB')
            elif image.mode == 'L':
                 # 灰度图通常可以保存为JPEG或PNG
                 image_to_save = image
            elif image.mode == 'RGB':
                 image_to_save = image # 已经是RGB，直接使用
            else:
                 logger.warning(f"未知的图像模式 {image.mode}，尝试转换为RGB进行元数据剥离。")
                 image_to_save = image.convert('RGB')
                
            # 保存到内存缓冲区，强制使用JPEG格式以剥离元数据
            data = io.BytesIO()
            # --- FIX: 强制使用JPEG格式保存到缓冲区 ---
            save_format = 'JPEG'
            logger.debug(f"强制使用 {save_format} 格式保存以剥离元数据")
            image_to_save.save(data, format=save_format, quality=95) # 使用高质量JPEG
            # --- END FIX ---
                
            data.seek(0) # 重置缓冲区指针
            reloaded_image = Image.open(data)
            logger.debug("元数据移除成功。")
            return reloaded_image

        except Exception as e:
            logger.error(f"移除元数据时出错: {e}")
            logger.error(traceback.format_exc()) # 打印详细错误
            return image # 出错时返回原图

    def apply_overlay_noise(self, image: Image.Image, alpha: int = 10, noise_type: str = 'uniform') -> Image.Image:
        """
        在图像上叠加一个低透明度的噪声图层

        Args:
            image: 输入图像
            alpha: 叠加噪声图层的 Alpha 值 (0-255)
            noise_type: 'gaussian' 或 'uniform'

        Returns:
            叠加噪声后的图像
        """
        try:
            logger.debug(f"应用低透明度噪声叠加: alpha={alpha}, type={noise_type}")
            # 确保图像是 RGBA 模式以处理透明度
            if image.mode != 'RGBA':
                base_image = image.convert('RGBA') 
            else:
                base_image = image.copy() # 操作副本

            width, height = base_image.size
            
            # 创建噪声图层 (灰度噪声即可)
            if noise_type == 'gaussian':
                # 生成范围在 0-255 的高斯噪声，均值128
                noise_array = np.random.normal(loc=128, scale=40, size=(height, width)).clip(0, 255).astype(np.uint8)
            else: # uniform
                noise_array = np.random.randint(0, 256, size=(height, width), dtype=np.uint8)
            
            noise_image = Image.fromarray(noise_array, mode='L')
            
            # 将噪声灰度图转换为 RGBA，并设置 alpha 通道
            noise_rgba = noise_image.convert('RGBA')
            # 创建一个全为指定 alpha 值的通道
            alpha_channel = Image.new('L', noise_image.size, alpha) 
            noise_rgba.putalpha(alpha_channel)

            # 使用 alpha_composite 进行混合叠加
            # alpha_composite 要求两个输入都是 RGBA
            combined_image = Image.alpha_composite(base_image, noise_rgba)
            
            # 通常我们希望最终结果是 RGB，所以转换回去
            # 如果原图就是 RGBA 且需要保留透明度，则省略此步
            final_image = combined_image.convert('RGB') 

            logger.debug("低透明度噪声叠加应用成功。")
            return final_image

        except Exception as e:
            logger.error(f"应用叠加噪声时出错: {e}")
            logger.error(traceback.format_exc()) # 打印详细错误
            return image # 出错时返回原图

    def apply_ahash_specific_disruption(self, image: Image.Image, strength: str = "medium") -> Image.Image:
        """
        专门针对aHash的干扰方法：插入亮度带 (增强版)
        Args:
            image: 输入图像
            strength: 处理强度 ('low', 'medium', 'high')
        Returns:
            处理后的图像
        """
        try:
            # 设定强度相关参数 (增强)
            if strength == "low":
                intensity = 0.08  # 原为 0.02
                bands = 2
            elif strength == "high":
                intensity = 0.18  # 原为 0.04
                bands = 4
            else:  # medium
                intensity = 0.12  # 原为 0.03
                bands = 3
                
            logger.debug(f"应用aHash特定干扰 (亮度带) (增强版), 强度:{strength}, 条带数:{bands}, 强度因子:{intensity:.3f}")
            
            # ... (其余逻辑不变) ...
            result = image.copy()
            width, height = result.size
            pixels = result.load()
            
            is_horizontal = random.choice([True, False])
            band_positions = []
            if is_horizontal:
                for _ in range(bands):
                    base_pos = random.randint(0, height - 1)
                    band_positions.append(base_pos)
            else:
                for _ in range(bands):
                    base_pos = random.randint(0, width - 1)
                    band_positions.append(base_pos)
            
            for y_idx in range(height): # Renamed y to y_idx to avoid conflict
                for x_idx in range(width): # Renamed x to x_idx to avoid conflict
                    is_on_band = False
                    if is_horizontal:
                        for pos in band_positions:
                            if abs(y_idx - pos) <= 1: 
                                is_on_band = True
                                break
                    else:
                        for pos in band_positions:
                            if abs(x_idx - pos) <= 1:
                                is_on_band = True
                                break
                    
                    if is_on_band:
                        pixel = pixels[x_idx, y_idx]
                        if isinstance(pixel, int): 
                            r_val = g_val = b_val = pixel # Renamed r,g,b to r_val, g_val, b_val
                            is_rgb = False
                        else: 
                            if len(pixel) >= 3:
                                r_val, g_val, b_val = pixel[0], pixel[1], pixel[2]
                                is_rgb = True
                            else:
                                continue 
                        
                        factor = 1.0 + intensity * (1 if random.random() > 0.5 else -1)
                        r_val, g_val, b_val = int(r_val * factor), int(g_val * factor), int(b_val * factor)
                        r_val, g_val, b_val = max(0, min(255, r_val)), max(0, min(255, g_val)), max(0, min(255, b_val))
                        
                        if is_rgb:
                            if len(pixel) == 4: 
                                pixels[x_idx, y_idx] = (r_val, g_val, b_val, pixel[3])
                            else: 
                                pixels[x_idx, y_idx] = (r_val, g_val, b_val)
                        else: 
                            pixels[x_idx, y_idx] = r_val
            
            logger.debug(f"aHash特定干扰完成: {'水平' if is_horizontal else '垂直'}亮度带")
            return result
            
        except Exception as e:
            logger.error(f"应用aHash特定干扰时出错: {e}")
            logger.error(traceback.format_exc())
            return image

    def apply_dhash_specific_disruption(self, image: Image.Image, strength: str = "medium") -> Image.Image:
        """
        专门针对dHash的干扰方法：梯度反向模式 (增强版)
        Args:
            image: 输入图像
            strength: 处理强度 ('low', 'medium', 'high')
        Returns:
            处理后的图像
        """
        try:
            # 设定强度相关参数 (增强)
            if strength == "low":
                gradient_strength = 0.08  # 原为 0.02
                regions = 2
            elif strength == "high":
                gradient_strength = 0.18  # 原为 0.04
                regions = 4
            else:  # medium
                gradient_strength = 0.12  # 原为 0.03
                regions = 3
                
            logger.debug(f"应用dHash特定干扰 (梯度反向) (增强版), 强度:{strength}, 区域数:{regions}, 梯度强度:{gradient_strength:.3f}")
            
            # ... (其余逻辑不变, 确保使用增强的 gradient_strength) ...
            result = image.copy()
            width, height = result.size
            
            for _ in range(regions):
                region_w = random.randint(width//12, width//8) # Renamed region_width to region_w
                region_h = random.randint(height//12, height//8) # Renamed region_height to region_h
                region_x_coord = random.randint(0, width - region_w) # Renamed region_x to region_x_coord
                region_y_coord = random.randint(0, height - region_h) # Renamed region_y to region_y_coord
                
                region = result.crop((region_x_coord, region_y_coord, region_x_coord + region_w, region_y_coord + region_h))
                region_array = np.array(region)
                is_rgb = len(region_array.shape) == 3
                
                if is_rgb:
                    gray_region = np.mean(region_array, axis=2).astype(np.uint8)
                else:
                    gray_region = region_array
                
                h_gradients = np.zeros_like(gray_region, dtype=np.int16)
                v_gradients = np.zeros_like(gray_region, dtype=np.int16)
                
                for y_idx in range(region_h): # Renamed y to y_idx
                    for x_idx in range(region_w-1): # Renamed x to x_idx
                        h_gradients[y_idx, x_idx] = int(gray_region[y_idx, x_idx+1]) - int(gray_region[y_idx, x_idx])
                
                for y_idx in range(region_h-1): # Renamed y to y_idx
                    for x_idx in range(region_w): # Renamed x to x_idx
                        v_gradients[y_idx, x_idx] = int(gray_region[y_idx+1, x_idx]) - int(gray_region[y_idx, x_idx])
                
                modified_region = region_array.astype(np.float32)
                for y_idx in range(region_h): # Renamed y to y_idx
                    for x_idx in range(region_w): # Renamed x to x_idx
                        if x_idx < region_w-1 and abs(h_gradients[y_idx, x_idx]) > 5:
                            h_change = -h_gradients[y_idx, x_idx] * gradient_strength
                            if is_rgb:
                                for c_channel in range(3): # Renamed c to c_channel
                                    modified_region[y_idx, x_idx+1, c_channel] = np.clip(
                                        modified_region[y_idx, x_idx+1, c_channel] + h_change/2, 0, 255)
                                    modified_region[y_idx, x_idx, c_channel] = np.clip(
                                        modified_region[y_idx, x_idx, c_channel] - h_change/2, 0, 255)
                            else:
                                modified_region[y_idx, x_idx+1] = np.clip(
                                    modified_region[y_idx, x_idx+1] + h_change/2, 0, 255)
                                modified_region[y_idx, x_idx] = np.clip(
                                    modified_region[y_idx, x_idx] - h_change/2, 0, 255)
                        
                        if y_idx < region_h-1 and abs(v_gradients[y_idx, x_idx]) > 5:
                            v_change = -v_gradients[y_idx, x_idx] * gradient_strength
                            if is_rgb:
                                for c_channel in range(3): # Renamed c to c_channel
                                    modified_region[y_idx+1, x_idx, c_channel] = np.clip(
                                        modified_region[y_idx+1, x_idx, c_channel] + v_change/2, 0, 255)
                                    modified_region[y_idx, x_idx, c_channel] = np.clip(
                                        modified_region[y_idx, x_idx, c_channel] - v_change/2, 0, 255)
                            else:
                                modified_region[y_idx+1, x_idx] = np.clip(
                                    modified_region[y_idx+1, x_idx] + v_change/2, 0, 255)
                                modified_region[y_idx, x_idx] = np.clip(
                                    modified_region[y_idx, x_idx] - v_change/2, 0, 255)
                
                modified_region = modified_region.astype(np.uint8)
                modified_region_image = Image.fromarray(modified_region)
                result.paste(modified_region_image, (region_x_coord, region_y_coord))
            
            logger.debug(f"dHash特定干扰完成: 在{regions}个区域应用梯度反向")
            return result
            
        except Exception as e:
            logger.error(f"应用dHash特定干扰时出错: {e}")
            logger.error(traceback.format_exc())
            return image

    def apply_phash_specific_disruption(self, image: Image.Image, strength: str = "medium") -> Image.Image:
        """
        专门针对pHash的干扰方法：定向DCT系数修改 (增强版)
        Args:
            image: 输入图像
            strength: 处理强度 ('low', 'medium', 'high')
        Returns:
            处理后的图像
        """
        if not SCIPY_AVAILABLE:
            logger.warning("Scipy 未安装，无法执行pHash专用干扰。请运行 'pip install scipy'")
            return image
        
        try:
            # 设定强度相关参数 (增强)
            if strength == "low":
                intensity = 0.20  # 原为 0.10
                key_positions_count = 4
            elif strength == "high":
                intensity = 0.40  # 原为 0.20
                key_positions_count = 8
            else:  # medium
                intensity = 0.30  # 原为 0.15
                key_positions_count = 6
                
            logger.debug(f"应用pHash特定干扰 (定向DCT干扰) (增强版), 强度:{strength}, 密度:{key_positions_count}, 强度因子:{intensity:.2f}")
            
            # ... (其余逻辑不变, 确保使用增强的 intensity) ...
            gray_image = image.convert('L')
            img_array_np = np.array(gray_image) # Renamed img_array to img_array_np
            h_img, w_img = img_array_np.shape # Renamed h,w to h_img,w_img
            
            resized_array = np.array(gray_image.resize((32, 32), Image.LANCZOS))
            dct_array = dct(dct(resized_array.T, norm='ortho').T, norm='ortho')
            
            key_positions = []
            for i_pos in range(1, 8): # Renamed i to i_pos
                for j_pos in range(1, 8): # Renamed j to j_pos
                    key_positions.append((i_pos, j_pos))
            
            selected_positions = random.sample(key_positions, k=min(len(key_positions), key_positions_count))
            
            block_h_size, block_w_size = h_img // 32, w_img // 32 # Renamed block_height, block_width
            
            for dct_y_coord, dct_x_coord in selected_positions: # Renamed dct_y, dct_x
                orig_y_coord = dct_y_coord * block_h_size # Renamed orig_y to orig_y_coord
                orig_x_coord = dct_x_coord * block_w_size # Renamed orig_x to orig_x_coord
                
                pattern_s = min(block_h_size, block_w_size) # Renamed pattern_size to pattern_s
                for y_off in range(pattern_s): # Renamed y_offset to y_off
                    for x_off in range(pattern_s): # Renamed x_offset to x_off
                        y_val = orig_y_coord + y_off # Renamed y to y_val
                        x_val = orig_x_coord + x_off # Renamed x to x_val
                        if 0 <= y_val < h_img and 0 <= x_val < w_img:
                            offset_val = intensity * 20 * math.sin(2 * math.pi * (y_off / pattern_s)) * \
                                     math.cos(2 * math.pi * (x_off / pattern_s)) # Renamed offset to offset_val
                            img_array_np[y_val, x_val] = np.clip(img_array_np[y_val, x_val] + offset_val, 0, 255)
            
            result_img = Image.fromarray(img_array_np.astype(np.uint8)) # Renamed result to result_img
            
            if image.mode != 'L':
                r_channel, g_channel, b_channel = image.split()[:3] # Renamed r,g,b to r_channel, g_channel, b_channel
                
                diff_img = ImageChops.difference(gray_image, result_img) # Renamed diff to diff_img
                diff_array_np = np.array(diff_img) # Renamed diff_array to diff_array_np
                
                r_array_np = np.array(r_channel) # Renamed r_array to r_array_np
                g_array_np = np.array(g_channel) # Renamed g_array to g_array_np
                b_array_np = np.array(b_channel) # Renamed b_array to b_array_np
                
                transfer_factor = 0.8
                r_array_np = np.clip(r_array_np + diff_array_np * transfer_factor, 0, 255).astype(np.uint8)
                g_array_np = np.clip(g_array_np + diff_array_np * transfer_factor, 0, 255).astype(np.uint8)
                b_array_np = np.clip(b_array_np + diff_array_np * transfer_factor, 0, 255).astype(np.uint8)
                
                r_new_img = Image.fromarray(r_array_np) # Renamed r_new to r_new_img
                g_new_img = Image.fromarray(g_array_np) # Renamed g_new to g_new_img
                b_new_img = Image.fromarray(b_array_np) # Renamed b_new to b_new_img
                
                if image.mode == 'RGBA':
                    alpha_channel = image.split()[3] # Renamed a to alpha_channel
                    result_img = Image.merge('RGBA', (r_new_img, g_new_img, b_new_img, alpha_channel))
                else:
                    result_img = Image.merge('RGB', (r_new_img, g_new_img, b_new_img))
            
            logger.debug(f"pHash特定干扰完成: 修改了{len(selected_positions)}个DCT关键位置")
            return result_img
            
        except Exception as e:
            logger.error(f"应用pHash特定干扰时出错: {e}")
            logger.error(traceback.format_exc())
            return image

    def apply_block_based_perturbations(self, image: Image.Image, block_size: int = 16, strength: str = "medium") -> Image.Image:
        """
        对图像各个块应用不同的、独立的干扰策略 (增强版)
        Args:
            image: 输入图像
            block_size: 块大小
            strength: 处理强度 ('low', 'medium', 'high')
        Returns:
            处理后的图像
        """
        try:
            # 设定强度相关参数 (增强)
            if strength == "low":
                factor_range = 0.08  # 原为 0.03
                skip_prob = 0.5     
            elif strength == "high":
                factor_range = 0.18  # 原为 0.06
                skip_prob = 0.2
            else:  # medium
                factor_range = 0.12  # 原为 0.045
                skip_prob = 0.35
                
            logger.debug(f"应用块级混合干扰 (增强版), 块大小:{block_size}, 强度:{strength}, 因子范围:{factor_range:.3f}")
            
            # ... (其余逻辑不变, 确保使用增强的 factor_range) ...
            result = image.copy() # Renamed result_img to result
            width, height = image.size
            img_array = np.array(result)
            is_rgb = len(img_array.shape) == 3
            
            strategies = ['brightness', 'contrast', 'hue_shift', 'gradient_flip', 'micro_pattern', 'skip']
            processed_blocks = 0
            skipped_blocks = 0
            
            for y_coord in range(0, height, block_size): # Renamed y to y_coord
                for x_coord in range(0, width, block_size): # Renamed x to x_coord
                    block_w = min(block_size, width - x_coord)
                    block_h = min(block_size, height - y_coord)
                    
                    if block_w < 4 or block_h < 4:
                        continue
                    
                    current_strategy = 'skip' if random.random() < skip_prob else random.choice([s for s in strategies if s != 'skip']) # Renamed strategy to current_strategy
                    
                    if is_rgb:
                        current_block = img_array[y_coord:y_coord+block_h, x_coord:x_coord+block_w, :] # Renamed block to current_block
                    else:
                        current_block = img_array[y_coord:y_coord+block_h, x_coord:x_coord+block_w]
                    
                    if current_strategy == 'skip':
                        skipped_blocks +=1
                    elif current_strategy == 'brightness':
                        factor = 1.0 + random.uniform(-factor_range, factor_range)
                        current_block = (current_block.astype(float) * factor).clip(0, 255).astype(np.uint8)
                        processed_blocks += 1
                    elif current_strategy == 'contrast':
                        factor = 1.0 + random.uniform(-factor_range, factor_range)
                        if is_rgb:
                            mean_val = np.mean(current_block, axis=(0, 1), keepdims=True)
                            current_block = (((current_block.astype(float) - mean_val) * factor) + mean_val).clip(0, 255).astype(np.uint8)
                        else:
                            mean_val = np.mean(current_block)
                            current_block = (((current_block.astype(float) - mean_val) * factor) + mean_val).clip(0, 255).astype(np.uint8)
                        processed_blocks += 1
                    elif current_strategy == 'hue_shift' and is_rgb:
                        r_factor = 1.0 - random.uniform(0, factor_range/2)
                        g_factor = 1.0 - random.uniform(0, factor_range/2)
                        b_factor = 1.0 - random.uniform(0, factor_range/2)
                        r_ch, g_ch, b_ch = current_block[:,:,0], current_block[:,:,1], current_block[:,:,2] # Renamed r,g,b to r_ch,g_ch,b_ch
                        current_block[:,:,0] = (r_ch * r_factor + g_ch * (1-r_factor)).clip(0, 255).astype(np.uint8)
                        current_block[:,:,1] = (g_ch * g_factor + b_ch * (1-g_factor)).clip(0, 255).astype(np.uint8)
                        current_block[:,:,2] = (b_ch * b_factor + r_ch * (1-b_factor)).clip(0, 255).astype(np.uint8)
                        processed_blocks += 1
                    elif current_strategy == 'gradient_flip':
                        if block_w > 2 and block_h > 2:
                            mid_w, mid_h = block_w // 2, block_h // 2
                            pattern_s = min(mid_w, mid_h) # Renamed pattern_size to pattern_s
                            for by_idx in range(1, pattern_s-1): # Renamed by to by_idx
                                for bx_idx in range(1, pattern_s-1): # Renamed bx to bx_idx
                                    if is_rgb:
                                        curr_val = np.mean(current_block[by_idx, bx_idx, :]) # Renamed curr to curr_val
                                        right_val = np.mean(current_block[by_idx, bx_idx+1, :]) # Renamed right to right_val
                                        below_val = np.mean(current_block[by_idx+1, bx_idx, :]) # Renamed below to below_val
                                        if abs(curr_val - right_val) > 5:
                                            diff_val = (curr_val - right_val) * factor_range # Renamed diff to diff_val
                                            current_block[by_idx, bx_idx, :] = np.clip(current_block[by_idx, bx_idx, :] - diff_val/2, 0, 255).astype(np.uint8)
                                            current_block[by_idx, bx_idx+1, :] = np.clip(current_block[by_idx, bx_idx+1, :] + diff_val/2, 0, 255).astype(np.uint8)
                                        if abs(curr_val - below_val) > 5:
                                            diff_val = (curr_val - below_val) * factor_range
                                            current_block[by_idx, bx_idx, :] = np.clip(current_block[by_idx, bx_idx, :] - diff_val/2, 0, 255).astype(np.uint8)
                                            current_block[by_idx+1, bx_idx, :] = np.clip(current_block[by_idx+1, bx_idx, :] + diff_val/2, 0, 255).astype(np.uint8)
                                    else:
                                        curr_val = float(current_block[by_idx, bx_idx])
                                        right_val = float(current_block[by_idx, bx_idx+1])
                                        below_val = float(current_block[by_idx+1, bx_idx])
                                        if abs(curr_val - right_val) > 5:
                                            diff_val = (curr_val - right_val) * factor_range
                                            current_block[by_idx, bx_idx] = np.clip(current_block[by_idx, bx_idx] - diff_val/2, 0, 255).astype(np.uint8)
                                            current_block[by_idx, bx_idx+1] = np.clip(current_block[by_idx, bx_idx+1] + diff_val/2, 0, 255).astype(np.uint8)
                                        if abs(curr_val - below_val) > 5:
                                            diff_val = (curr_val - below_val) * factor_range
                                            current_block[by_idx, bx_idx] = np.clip(current_block[by_idx, bx_idx] - diff_val/2, 0, 255).astype(np.uint8)
                                            current_block[by_idx+1, bx_idx] = np.clip(current_block[by_idx+1, bx_idx] + diff_val/2, 0, 255).astype(np.uint8)
                            processed_blocks += 1
                    elif current_strategy == 'micro_pattern':
                        pattern_type = random.choice(['dot', 'line', 'cross'])
                        center_y_coord, center_x_coord = block_h // 2, block_w // 2 # Renamed center_y, center_x
                        pattern_coords = []
                        if pattern_type == 'dot':
                            pattern_coords = [(center_y_coord, center_x_coord)]
                        elif pattern_type == 'line':
                            if random.choice([True, False]):
                                pattern_coords = [(center_y_coord, cx_val) for cx_val in range(center_x_coord-1, center_x_coord+2)] # Renamed cx to cx_val
                            else:
                                pattern_coords = [(cy_val, center_x_coord) for cy_val in range(center_y_coord-1, center_y_coord+2)] # Renamed cy to cy_val
                        else: 
                            pattern_coords.extend([(center_y_coord, cx_val) for cx_val in range(center_x_coord-1, center_x_coord+2)])
                            pattern_coords.extend([(cy_val, center_x_coord) for cy_val in range(center_y_coord-1, center_y_coord+2) if (cy_val, center_x_coord) not in pattern_coords])
                        
                        pattern_strength = random.uniform(factor_range*50, factor_range*100)
                        for py_coord, px_coord in pattern_coords: # Renamed py,px to py_coord,px_coord
                            if 0 <= py_coord < block_h and 0 <= px_coord < block_w:
                                if is_rgb:
                                    target_channel = random.randint(0, 2) # Renamed channel to target_channel
                                    if random.choice([True, False]): 
                                        current_block[py_coord, px_coord, target_channel] = np.clip(current_block[py_coord, px_coord, target_channel] + pattern_strength, 0, 255).astype(np.uint8)
                                    else:
                                        current_block[py_coord, px_coord, target_channel] = np.clip(current_block[py_coord, px_coord, target_channel] - pattern_strength, 0, 255).astype(np.uint8)
                                else:
                                    if random.choice([True, False]): 
                                        current_block[py_coord, px_coord] = np.clip(current_block[py_coord, px_coord] + pattern_strength, 0, 255).astype(np.uint8)
                                    else:
                                        current_block[py_coord, px_coord] = np.clip(current_block[py_coord, px_coord] - pattern_strength, 0, 255).astype(np.uint8)
                        processed_blocks += 1
                    
                    if is_rgb:
                        img_array[y_coord:y_coord+block_h, x_coord:x_coord+block_w, :] = current_block
                    else:
                        img_array[y_coord:y_coord+block_h, x_coord:x_coord+block_w] = current_block
            
            result = Image.fromarray(img_array) # Result was already defined
            
            logger.debug(f"块级混合干扰完成: 处理了{processed_blocks}个块, 跳过了{skipped_blocks}个块")
            return result
            
        except Exception as e:
            logger.error(f"应用块级混合干扰时出错: {e}")
            logger.error(traceback.format_exc())
            return image

    def apply_strategic_hash_disruption(self, image: Image.Image, strength: str = "medium") -> Image.Image:
        """
        战略性哈希干扰：对各种哈希算法进行有针对性的干扰
        
        整合了多种针对性干扰策略，包括块级混合干扰和针对特定哈希算法的干扰。
        
        Args:
            image: 输入图像
            strength: 处理强度 ('low', 'medium', 'high')
            
        Returns:
            处理后的图像
        """
        try:
            logger.info(f"开始战略性哈希干扰 (强度: {strength})")
            original_image_for_logging = image.copy()
            
            # 设定策略应用概率
            if strength == "low":
                ahash_prob = 0.7
                dhash_prob = 0.7
                phash_prob = 0.9
                block_prob = 0.6
                block_size = 24
            elif strength == "high":
                ahash_prob = 0.9
                dhash_prob = 0.9
                phash_prob = 0.95
                block_prob = 0.8
                block_size = 16
            else:  # medium
                ahash_prob = 0.8
                dhash_prob = 0.8
                phash_prob = 0.9
                block_prob = 0.7
                block_size = 20
            
            logger.debug(f"策略概率: aHash={ahash_prob:.1f}, dHash={dhash_prob:.1f}, pHash={phash_prob:.1f}, 块级={block_prob:.1f}")
            
            # 保存原图
            result = image.copy()
            applied_strategies = []
            
            # 1. 应用块级混合干扰
            if random.random() < block_prob:
                result = self.apply_block_based_perturbations(result, block_size=block_size, strength=strength)
                applied_strategies.append(f"BlockBased({block_size})")
            
            # 2. 应用针对特定哈希算法的干扰
            # 2.1 aHash特定干扰
            if random.random() < ahash_prob:
                result = self.apply_ahash_specific_disruption(result, strength)
                applied_strategies.append("aHash")
            
            # 2.2 dHash特定干扰
            if random.random() < dhash_prob:
                result = self.apply_dhash_specific_disruption(result, strength)
                applied_strategies.append("dHash")
            
            # 2.3 pHash特定干扰（最重要的一个）
            if random.random() < phash_prob:
                result = self.apply_phash_specific_disruption(result, strength)
                applied_strategies.append("pHash")
            
            logger.info(f"已应用战略干扰: {', '.join(applied_strategies)}")
            
            # 对比修改前后
            try:
                diff = ImageChops.difference(original_image_for_logging.convert('RGB'), result.convert('RGB')).getbbox()
                if diff:
                    logger.info(f"图像已修改。差异区域: {diff}")
                else:
                    logger.warning("！！！战略干扰似乎未修改图像！！！")
            except Exception as log_e:
                logger.warning(f"无法比较图像差异: {log_e}")
            
            logger.info(f"战略性哈希干扰完成 (强度: {strength})")
            return result
            
        except Exception as e:
            logger.error(f"应用战略性哈希干扰时出错: {e}")
            logger.error(traceback.format_exc())
            return image  # 出错时返回原图

    def optimize_anti_hash_methods(self, image: Image.Image, strength: str = "medium") -> Image.Image:
        """优化后的哈希对抗方法，使用新的分层增强策略"""
        logger.info(f"--- 开始优化抗哈希方法 (强度: {strength}) - 分层增强策略 ---")
        processed_image = image.copy()

        # 定义各阶段强度参数
        global_max_crop: int
        global_overlay_alpha: int
        global_color_hist_strength: float
        
        if strength == "low":
            global_max_crop = 3
            global_overlay_alpha = random.randint(8, 12)
            global_color_hist_strength = 0.03
        elif strength == "high":
            global_max_crop = 10 
            global_overlay_alpha = random.randint(18, 25) 
            global_color_hist_strength = 0.08
        else: # medium
            global_max_crop = 6 
            global_overlay_alpha = random.randint(12, 18) 
            global_color_hist_strength = 0.05

        logger.debug(f"分层策略 - 全局扰动参数: strength_for_crop='{strength}' (内部max_crop将按新标准), overlay_alpha={global_overlay_alpha}, color_hist_strength={global_color_hist_strength:.3f}")

        # --- 层 1: 基础全局扰动 ---
        logger.info("应用基础全局扰动...")
        
        # 1.1 智能裁剪 + 重缩放 (现在 apply_smart_crop_resize 内部已增强)
        processed_image = self.apply_smart_crop_resize(processed_image, strength) 

        # 1.2 低透明度噪声叠加
        processed_image = self.apply_overlay_noise(processed_image, alpha=global_overlay_alpha, noise_type='uniform')

        # 1.3 颜色直方图扰动
        if global_color_hist_strength > 0: # 确保强度大于0才应用
            processed_image = self.perturb_color_histogram(processed_image, strength=global_color_hist_strength)

        # --- 层 2: 战略性哈希干扰 (在基础扰动之上) ---
        # apply_strategic_hash_disruption 内部调用的各 specific 和 block_based 方法已增强
        logger.info("应用战略性哈希干扰 (各子方法已增强)...")
        processed_image = self.apply_strategic_hash_disruption(processed_image, strength) 

        # --- 清除元数据 ---
        processed_image = self.strip_metadata(processed_image)
        
        logger.info(f"--- 完成优化抗哈希方法 (强度: {strength}) - 分层增强策略 ---")
        return processed_image

    def optimized_process_image(
        self, 
        image: Image.Image, 
        target_ratio: Tuple[int, int], 
        add_variation: bool = True, 
        seed: int = None,
        variation_strength: str = "medium",
        extra_effects: bool = True
    ) -> Image.Image:
        """优化后的图像处理方法，使用更高效的算法，添加反查重技术"""
        # 设置随机种子
        if seed is not None:
            random.seed(seed)
            np.random.seed(seed)
        
        # 根据微调强度设置参数 (保留变化因子等)
        if variation_strength == "low":
            brightness_factor = random.uniform(0.97, 1.03)
            contrast_factor = random.uniform(0.97, 1.03)
            saturation_factor = random.uniform(0.97, 1.03)
            max_rotation = 0.5
            border_size = random.randint(0, 1)
            # use_extra = random.random() < 0.3 and extra_effects #<-- 旧逻辑
        elif variation_strength == "high":
            brightness_factor = random.uniform(0.92, 1.08)
            contrast_factor = random.uniform(0.92, 1.08)
            saturation_factor = random.uniform(0.92, 1.08)
            max_rotation = 2.0
            border_size = random.randint(0, 3)
            # use_extra = extra_effects #<-- 旧逻辑 (本身就是直接赋值)
        else:  # medium
            brightness_factor = random.uniform(0.95, 1.05)
            contrast_factor = random.uniform(0.95, 1.05)
            saturation_factor = random.uniform(0.95, 1.05)
            max_rotation = 1.0
            border_size = random.randint(0, 2)
            # use_extra = random.random() < 0.7 and extra_effects #<-- 旧逻辑

        # --- FIX: 直接使用传入的 extra_effects 控制是否启用抗哈希和额外效果 ---
        use_extra = extra_effects
        # --- END FIX ---
        
        # 调整图像为目标比例
        width, height = image.size
        current_ratio = width / height
        target_ratio_value = target_ratio[0] / target_ratio[1]
        
        # 调整大小
        if current_ratio > target_ratio_value:  # 图片较宽
            new_height = 1200
            new_width = int(new_height * current_ratio)
        else:  # 图片较高
            new_width = 900
            new_height = int(new_width / current_ratio)
        
        # 高效调整尺寸
        resized_image = image.resize((new_width, new_height), Image.LANCZOS)
        
        # 裁剪为目标比例
        resized_width, resized_height = resized_image.size
        if resized_width / resized_height > target_ratio_value:
            crop_width = int(resized_height * target_ratio_value)
            max_offset = max(1, min(10, (resized_width - crop_width) // 10))
            offset = random.randint(-max_offset, max_offset) if add_variation else 0
            crop_x1 = max(0, min((resized_width - crop_width) // 2 + offset, resized_width - crop_width))
            crop_x2 = crop_x1 + crop_width
            result = resized_image.crop((crop_x1, 0, crop_x2, resized_height))
        else:
            crop_height = int(resized_width / target_ratio_value)
            max_offset = max(1, min(10, (resized_height - crop_height) // 10))
            offset = random.randint(-max_offset, max_offset) if add_variation else 0
            crop_y1 = max(0, min((resized_height - crop_height) // 2 + offset, resized_height - crop_height))
            crop_y2 = crop_y1 + crop_height
            result = resized_image.crop((0, crop_y1, resized_width, crop_y2))
        
        # 如果不需要变化或是低强度且禁用额外效果
        if not add_variation:
            logger.info("add_variation=False，跳过所有变化和抗哈希处理。")
            # 重置随机种子
            if seed is not None:
                random.seed()
                np.random.seed()
                
            # 清除元数据后返回
            return self.strip_metadata(result)
        
        logger.info(f"应用基础变化和抗哈希处理 (强度: {variation_strength}, 额外效果: {use_extra})")
        processed_image = result.convert('RGB')
        
        # 1. 亮度调整
        if abs(brightness_factor - 1.0) > 0.01:
            enhancer = ImageEnhance.Brightness(processed_image)
            processed_image = enhancer.enhance(brightness_factor)
        
        # 2. 对比度调整
        if abs(contrast_factor - 1.0) > 0.01:
            enhancer = ImageEnhance.Contrast(processed_image)
            processed_image = enhancer.enhance(contrast_factor)
        
        # 3. 饱和度调整
        if abs(saturation_factor - 1.0) > 0.01:
            enhancer = ImageEnhance.Color(processed_image)
            processed_image = enhancer.enhance(saturation_factor)
        
        # 4. 旋转 (只在中高强度时应用)
        if variation_strength != "low" and abs(max_rotation) > 0.1:
            rotation_angle = random.uniform(-max_rotation, max_rotation)
            if abs(rotation_angle) > 0.1:  # 只有当角度足够大时才旋转
                processed_image = processed_image.rotate(rotation_angle, resample=Image.BICUBIC, expand=False)
        
        # 5. 应用抗哈希技术
        if use_extra:
            logger.debug("调用 optimize_anti_hash_methods...")
            processed_image = self.optimize_anti_hash_methods(processed_image, variation_strength)
        else:
            logger.info("use_extra=False，跳过 optimize_anti_hash_methods。")
        
        # 应用模糊/锐化/边框等额外效果 (如果 use_extra 为 True)
        if use_extra:
            logger.debug("应用额外效果 (模糊/锐化/边框)...")
            # 根据强度决定是否应用特定效果
            apply_sharpen = random.random() < 0.4
            apply_blur = not apply_sharpen and random.random() < 0.3
            
            # 锐化
            if apply_sharpen:
                enhancer = ImageEnhance.Sharpness(processed_image)
                sharpness = 1.2 if variation_strength == "high" else 1.1
                processed_image = enhancer.enhance(sharpness)
            
            # 模糊
            elif apply_blur:
                radius = 0.7 if variation_strength == "high" else 0.4
                processed_image = processed_image.filter(ImageFilter.GaussianBlur(radius=radius))
            
            # 边框处理 (在图像不太小的情况下)
            if border_size > 0 and min(processed_image.size) > 300:
                border_color = (
                    random.randint(0, 5),
                    random.randint(0, 5),
                    random.randint(0, 5)
                )
                w, h = processed_image.size
                bordered = Image.new('RGB', (w + border_size*2, h + border_size*2), border_color)
                bordered.paste(processed_image, (border_size, border_size))
            
            logger.debug("额外效果应用完成。")
        else:
            logger.info("use_extra=False，跳过额外效果。")
        
        # **关键：确保在所有修改之后调用修复后的 strip_metadata**
        logger.debug("最后调用 strip_metadata 清除元数据。")
        final_image = self.strip_metadata(processed_image)
        
        # 重置随机种子
        if seed is not None:
            random.seed()
            np.random.seed()
            logger.debug("随机种子已重置。")
        
        logger.info(f"图像处理完成 (强度: {variation_strength})")
        return final_image

def process_poster_for_notes(
    run_id: str,
    topic_index: int,
    variant_index: int,
    poster_image_path: str,
    poster_metadata_path: str,
    source_image_dir: str,
    num_additional_images: int,
    output_handler: OutputHandler,
    output_filename_template: str = "note_{index}.jpg"
) -> List[str]:
    """
    处理海报并创建笔记图像
    
    Args:
        run_id: 运行ID
        topic_index: 主题索引
        variant_index: 变体索引
        poster_image_path: 海报图像路径
        poster_metadata_path: 海报元数据路径
        source_image_dir: 源图像目录
        num_additional_images: 要使用的额外图像数量
        output_handler: 输出处理器
        output_filename_template: 输出文件名模板
        
    Returns:
        List[str]: 保存的笔记图像路径列表
    """
    logger.info(f"开始为海报创建笔记图像: {poster_image_path}")
    
    # 验证输入
    if not os.path.exists(poster_image_path):
        logger.error(f"海报图像不存在: {poster_image_path}")
        return []
    
    # 创建处理器实例并处理
    creator = PosterNotesCreator(output_handler)
    return creator.create_notes_images(
        run_id,
        topic_index,
        variant_index,
        poster_image_path,
        poster_metadata_path,
        source_image_dir,
        num_additional_images,
        output_filename_template
    )

def select_additional_images(
    run_id: str,
    topic_index: int,
    variant_index: int,
    poster_metadata_path: str,
    source_image_dir: str,
    num_additional_images: int,
    output_handler: OutputHandler,
    output_filename_template: str = "additional_{index}.jpg",
    variation_strength: str = "medium",
    extra_effects: bool = True
) -> List[str]:
    """
    选择未被海报使用的图像作为额外配图，并处理为3:4比例
    """
    logger.info(f"开始为主题 {topic_index} 变体 {variant_index} 选择额外配图")
    
    # 验证输入
    if not os.path.exists(poster_metadata_path):
        logger.error(f"海报元数据不存在: {poster_metadata_path}")
        return []
    
    # 创建处理器实例
    creator = PosterNotesCreator(output_handler)
    
    # 使用优化后的方法处理图像
    return creator.create_additional_images(
        run_id,
        topic_index,
        variant_index,
        poster_metadata_path,
        source_image_dir,
        num_additional_images,
        output_filename_template,
        variation_strength,
        extra_effects
    ) 