import cv2
import numpy as np
import torch
from PIL import Image
import torchvision
import functools
import inspect

# 套用裝飾器到現有函數
def unsharp_mask(image, kernel_size=(5, 5), sigma=1.0, amount=1.0, threshold=0):
    """使用OpenCV實現的Unsharp Mask銳化處理
    參數:
        image: PIL.Image對象(RGB格式)
        kernel_size: 高斯模糊的核大小，必須是奇數
        sigma: 高斯模糊的標準差
        amount: 銳化程度，值越大效果越強
        threshold: 邊緣檢測閾值，僅在邊緣處進行銳化
    返回:
        銳化後的PIL.Image對象
    """
    # 轉換PIL圖像為numpy數組
    numpy_img = np.array(image, dtype=np.uint8)
    
    # 對原圖進行高斯模糊
    blurred = cv2.GaussianBlur(numpy_img, kernel_size, sigma)
    
    # 計算銳化後的圖像
    sharpened = cv2.addWeighted(numpy_img, 1 + amount, blurred, -amount, 0)
    
    # 如果設置了threshold，只在邊緣處應用銳化
    if threshold > 0:
        low_contrast_mask = np.absolute(numpy_img - blurred) < threshold
        np.copyto(sharpened, numpy_img, where=low_contrast_mask)
    
    # 確保像素值在有效範圍內
    sharpened = np.clip(sharpened, 0, 255).astype(np.uint8)
    
    # 轉回PIL圖像
    return Image.fromarray(sharpened)

def histogram_equalization(image):
    """GPU加速的一般直方圖等化
    參數:
        image: PIL.Image對象(RGB格式)
    返回:
        直方圖等化後的PIL.Image對象
    """
    # 轉換為numpy數組並轉為PyTorch張量
    numpy_img = np.array(image)
    tensor_img = torch.from_numpy(numpy_img).float().to('cuda')
    
    # 分離通道並進行直方圖等化
    result = torch.zeros_like(tensor_img)
    for i in range(3):  # 對RGB三個通道分別處理
        channel = tensor_img[..., i]
        
        # 計算直方圖
        hist = torch.histc(channel, bins=256, min=0, max=255)
        
        # 計算累積分布函數(CDF)
        cdf = torch.cumsum(hist, dim=0)
        cdf_normalized = ((cdf - cdf.min()) * 255) / (cdf.max() - cdf.min())
        
        # 應用直方圖等化
        result[..., i] = cdf_normalized[channel.long()]
    
    # 轉回CPU和numpy數組
    result = torch.clamp(result, 0, 255).byte()
    result_np = result.cpu().numpy()
    return Image.fromarray(result_np)

def Contrast_Limited_Adaptive_Histogram_Equalization(image, clip_limit=3.0, tile_size=(8, 8)):
    """使用OpenCV實現的對比度限制自適應直方圖均衡化(CLAHE)
    
    參數:
        image: PIL.Image對象(RGB格式)
        clip_limit: 剪切限制，用於限制對比度增強的程度，較大的值會產生更強的對比度
        tile_size: 圖像分塊大小的元組(height, width)，較小的值會產生更局部的增強效果
    
    返回:
        CLAHE處理後的PIL.Image對象
    """
    # 將PIL圖像轉換為OpenCV格式（BGR）
    numpy_img = np.array(image)
    bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
    
    # 轉換到LAB色彩空間
    lab_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2LAB)
    
    # 創建CLAHE對象
    clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_size)
    
    # 分離LAB通道
    l, a, b = cv2.split(lab_img)
    
    # 對L通道應用CLAHE
    l_clahe = clahe.apply(l)
    
    # 合併處理後的L通道與原始的a和b通道
    lab_output = cv2.merge([l_clahe, a, b])
    
    # 將LAB轉回BGR，然後轉換為RGB
    bgr_output = cv2.cvtColor(lab_output, cv2.COLOR_LAB2BGR)
    rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
    
    # 轉換為PIL圖像並返回
    return Image.fromarray(rgb_output)

def adaptive_histogram_equalization_without_limit(image, tile_size=(8, 8)):
    """使用OpenCV實現的自適應直方圖均衡化(AHE)
    
    參數:
        image: PIL.Image對象(RGB格式)
        tile_size: 圖像分塊大小的元組(height, width)，較小的值會產生更局部的增強效果
    
    返回:
        AHE處理後的PIL.Image對象
    """
    # 將PIL圖像轉換為OpenCV格式（BGR）
    numpy_img = np.array(image)
    bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
    
    # 轉換到LAB色彩空間
    lab_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2LAB)
    
    # 分離LAB通道
    l, a, b = cv2.split(lab_img)
    
    # 創建AHE對象（不設置clip limit）
    clahe = cv2.createCLAHE(clipLimit=None, tileGridSize=tile_size)
    
    # 對L通道應用AHE
    l_ahe = clahe.apply(l)
    
    # 合併處理後的L通道與原始的a和b通道
    lab_output = cv2.merge([l_ahe, a, b])
    
    # 將LAB轉回BGR，然後轉換為RGB
    bgr_output = cv2.cvtColor(lab_output, cv2.COLOR_LAB2BGR)
    rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
    
    # 轉換為PIL圖像並返回
    return Image.fromarray(rgb_output)

def laplacian_sharpen(image):
    """
    GPU加速的拉普拉斯銳化處理函數
    參數:
        image: PIL.Image對象(RGB格式)
    返回:
        銳化後的PIL.Image對象
    """
    # 轉換為numpy數組並轉為PyTorch張量
    numpy_img = np.array(image)
    tensor_img = torch.from_numpy(numpy_img).float().to('cuda')
    
    # 創建拉普拉斯算子
    laplacian_kernel = torch.tensor([
        [0, 1, 0],
        [1, -4, 1],
        [0, 1, 0]
    ], dtype=torch.float32, device='cuda').unsqueeze(0).unsqueeze(0)
    
    # 對每個通道進行處理
    result = torch.zeros_like(tensor_img)
    for i in range(3):  # RGB三個通道
        channel = tensor_img[..., i]
        # 添加批次和通道維度
        channel = channel.unsqueeze(0).unsqueeze(0)
        # 應用拉普拉斯算子
        laplacian = torch.nn.functional.conv2d(channel, laplacian_kernel, padding=1)
        # 移除批次和通道維度
        laplacian = laplacian.squeeze()
        # 銳化處理：原圖 - 拉普拉斯
        result[..., i] = channel.squeeze() - laplacian
    
    # 確保像素值在合理範圍內
    result = torch.clamp(result, 0, 255).byte()
    
    # 轉回CPU和numpy數組
    result_np = result.cpu().numpy()
    return Image.fromarray(result_np)

def adjust_hsv(image, v_adjustment=0):
    """調整圖像的HSV色彩空間中的H和V通道
    
    參數:
        image: PIL.Image對象(RGB格式)
        v_adjustment: V通道的調整值，範圍建議在[-255, 255]之間
    
    返回:
        HSV調整後的PIL.Image對象
    """
    # 將PIL圖像轉換為OpenCV格式（BGR）
    numpy_img = np.array(image)
    bgr_img = cv2.cvtColor(numpy_img, cv2.COLOR_RGB2BGR)
    
    # 轉換到HSV色彩空間
    hsv_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2HSV)
    
    # 調整V通道
    hsv_img[..., 2] = np.clip(hsv_img[..., 2] + v_adjustment, 0, 255)
    
    # 將HSV轉回BGR，然後轉換為RGB
    bgr_output = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
    rgb_output = cv2.cvtColor(bgr_output, cv2.COLOR_BGR2RGB)
    
    # 轉換為PIL圖像並返回
    return Image.fromarray(rgb_output)

def gamma_correction(image, gamma=1.0):
    """對圖像進行伽馬校正
    
    參數:
        image: PIL.Image對象(RGB格式)
        gamma: 伽馬值，gamma > 1 時圖像變暗，gamma < 1 時圖像變亮，gamma = 1 時保持不變
    
    返回:
        伽馬校正後的PIL.Image對象
    """
    # 將PIL圖像轉換為numpy數組
    numpy_img = np.array(image)
    
    # 將像素值歸一化到[0, 1]範圍
    normalized = numpy_img.astype(float) / 255.0
    
    # 應用伽馬校正
    corrected = np.power(normalized, gamma)
    
    # 將值縮放回[0, 255]範圍
    output = np.clip(corrected * 255.0, 0, 255).astype(np.uint8)
    
    # 轉換回PIL圖像並返回
    return Image.fromarray(output)

def Hight_Light(image, Threshold):
    image = np.array(image)

    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # 使用閾值檢測高光點（白色液滴）
    _, thresh = cv2.threshold(gray, Threshold, 255, cv2.THRESH_BINARY)
    # 使用形態學操作（膨脹）來擴大遮罩區域
    kernel = np.ones((5, 5), np.uint8)
    dilated = cv2.dilate(thresh, kernel, iterations=1)
    # 使用 inpaint 修復高光點
    image_inpaint = cv2.inpaint(image, dilated, 3, cv2.INPAINT_TELEA)

    return Image.fromarray(image_inpaint)

def median_filter(image: Image.Image, kernel_size: int = 3):
    """
    中值濾波（Median Filter）實現
    
    參數:
        image: PIL.Image對象(RGB格式)
        kernel_size: 濾波核大小，必須是奇數
    
    返回:
        濾波後的PIL.Image對象
    """
    # 確保kernel_size是奇數
    if kernel_size % 2 == 0:
        kernel_size += 1
    
    # 轉換PIL圖像為numpy數組
    numpy_img = np.array(image, dtype=np.uint8)
    
    # 對每個通道應用中值濾波
    result = np.zeros_like(numpy_img)
    for i in range(3):  # 對RGB三個通道分別處理
        result[:, :, i] = cv2.medianBlur(numpy_img[:, :, i], kernel_size)
    
    # 確保像素值在有效範圍內
    result = np.clip(result, 0, 255).astype(np.uint8)
    
    # 轉回PIL圖像
    return Image.fromarray(result)

def mean_filter(image: Image.Image, kernel_size: int = 3):
    """
    均質濾波（Mean Filter）實現
    
    參數:
        image: PIL.Image對象(RGB格式)
        kernel_size: 濾波核大小，必須是奇數
    
    返回:
        濾波後的PIL.Image對象
    """
    # 確保kernel_size是奇數
    if kernel_size % 2 == 0:
        kernel_size += 1
    
    # 轉換PIL圖像為numpy數組
    numpy_img = np.array(image, dtype=np.uint8)
    
    # 創建均質濾波核（所有元素都是1/(kernel_size*kernel_size)）
    kernel = np.ones((kernel_size, kernel_size), np.float32) / (kernel_size * kernel_size)
    
    # 對每個通道應用均質濾波
    result = np.zeros_like(numpy_img)
    for i in range(3):  # 對RGB三個通道分別處理
        result[:, :, i] = cv2.filter2D(numpy_img[:, :, i], -1, kernel)
    
    # 確保像素值在有效範圍內
    result = np.clip(result, 0, 255).astype(np.uint8)
    
    # 轉回PIL圖像
    return Image.fromarray(result)