import cv2
import numpy as np
from PIL import Image, ImageEnhance, ImageFilter, ImageOps
import settings

# 锐化图片
def sharpen_image(img, factor=1.0):
    # 创建一个ImageEnhance对象
    enhancer = ImageEnhance.Sharpness(img)
    # 应用增强，值为0.0给出模糊图像，1.0给出原始图像，大于1.0给出锐化效果
    # 调整这个值来增加或减少锐化的程度
    sharp_img = enhancer.enhance(factor)
    return sharp_img


def to_resize(_im, width=None, high=None) -> Image:
    _im_x, _im_y = _im.size
    if width and high:
        if _im_x >= _im_y:
            high = None
        else:
            width = None
    if width:
        re_x = int(width)
        re_y = int(_im_y * re_x / _im_x)
    else:
        re_y = int(high)
        re_x = int(_im_x * re_y / _im_y)
    _im = _im.resize((re_x, re_y),resample=settings.RESIZE_IMAGE_MODE)
    return _im


def pil_to_cv2(pil_image):
    # 将 PIL 图像转换为 RGB 或 RGBA 格式
    if pil_image.mode != 'RGBA':
        pil_image = pil_image.convert('RGBA')
    # 将 PIL 图像转换为 numpy 数组
    cv2_image = np.array(pil_image)
    # 由于 PIL 的颜色顺序是 RGB，而 OpenCV 的颜色顺序是 BGR，因此需要交换颜色通道
    cv2_image = cv2.cvtColor(cv2_image, cv2.COLOR_RGBA2BGRA)
    return cv2_image


def cv2_to_pil(cv_img):
    return Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))


def get_mini_crop_img(img):
    old_x, old_y = img.size
    x1, y1, x2, y2 = img.getbbox()

    goods_w, goods_h = x2 - x1, y2 - y1
    _w, _h = int(goods_w / 10), int(goods_h / 10)  # 上下左右扩展位置
    new_x1, new_y1, new_x2, new_y2 = x1 - _w, y1 - _h, x2 + _w, y2 + _h  # 防止超限
    new_x1 = 0 if new_x1 < 0 else new_x1
    new_y1 = 0 if new_y1 < 0 else new_y1
    new_x2 = old_x if new_x2 > old_x else new_x2
    new_y2 = old_y if new_y2 > old_y else new_y2
    img = img.crop((new_x1, new_y1, new_x2, new_y2))  # 切图
    box = (new_x1, new_y1, new_x2, new_y2)
    return img, box


def expand_mask(mask, expansion_radius=5, blur_radius=0):
    # 对蒙版进行膨胀处理
    mask = mask.filter(ImageFilter.MaxFilter(expansion_radius * 2 + 1))
    # 应用高斯模糊滤镜
    if blur_radius > 0:
        mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))

    return mask


def expand_or_shrink_mask(pil_image, expansion_radius=5, iterations=1, blur_radius=0):
    """
    对输入的PIL黑白图像（掩膜）进行膨胀或腐蚀操作，以扩大或缩小前景区域。

    :param pil_image: 输入的PIL黑白图像对象
    :param expansion_radius: 结构元素大小，默认是一个3x3的小正方形；负值表示收缩
    :param iterations: 操作迭代次数，默认为1次
    :param blur_radius: 高斯模糊的半径，默认不应用模糊
    :return: 修改后的PIL黑白图像对象
    """

    # 将PIL图像转换为numpy数组，并确保其为8位无符号整数类型
    img_np = np.array(pil_image).astype(np.uint8)

    # 如果不是二值图像，则应用阈值处理
    if len(np.unique(img_np)) > 2:  # 检查是否为二值图像
        _, img_np = cv2.threshold(img_np, 127, 255, cv2.THRESH_BINARY)

    # 定义结构元素（例如正方形）
    abs_expansion_radius = abs(expansion_radius)
    kernel = np.ones((abs_expansion_radius, abs_expansion_radius), np.uint8)

    # 根据expansion_radius的符号选择膨胀或腐蚀操作
    if expansion_radius >= 0:
        modified_img_np = cv2.dilate(img_np, kernel, iterations=iterations)
    else:
        modified_img_np = cv2.erode(img_np, kernel, iterations=iterations)

    # 如果提供了blur_radius，则应用高斯模糊
    if blur_radius > 0:
        modified_img_np = cv2.GaussianBlur(
            modified_img_np, (blur_radius * 2 + 1, blur_radius * 2 + 1), 0
        )

    # 将numpy数组转换回PIL图像
    modified_pil_image = Image.fromarray(modified_img_np)

    return modified_pil_image


def find_lowest_non_transparent_points(cv2_png):
    # cv2_png 为cv2格式的带有alpha通道的图片

    alpha_channel = cv2_png[:, :, 3]
    """使用Numpy快速查找每列的最低非透明点"""
    h, w = alpha_channel.shape
    # 创建一个掩码，其中非透明像素为True
    mask = alpha_channel > 0
    # 使用np.argmax找到每列的第一个非透明像素的位置
    # 因为是从底部向上找，所以需要先翻转图像
    flipped_mask = np.flip(mask, axis=0)
    min_y_values = h - np.argmax(flipped_mask, axis=0) - 1
    # 将全透明列的值设置为-1
    min_y_values[~mask.any(axis=0)] = -1
    return min_y_values


def draw_shifted_line(
    image,
    min_y_values,
    shift_amount=15,
    one_line_pos=(0, 100),
    line_color=(0, 0, 0),
    line_thickness=20,
    app=None,
    crop_image_box=None,
):
    """
    image:jpg cv2格式的原始图
    min_y_values 透明图中，不透明区域的最低那条线
    shift_amount：向下偏移值
    line_color：线颜色
    line_thickness：线宽
    """
    # 将最低Y值向下迁移20个像素，但确保不超过图片的高度
    # 创建空白图片
    image = np.ones((image.shape[0], image.shape[1], 3), dtype=np.uint8) * 255

    # 对线条取转成图片
    shifted_min_y_values = np.clip(min_y_values + shift_amount, 0, image.shape[0] - 1)
    # 使用Numpy索引批量绘制直线
    min_y_threshold = 50  # Y轴像素小于50的不处理
    valid_x = (shifted_min_y_values >= min_y_threshold) & (shifted_min_y_values != -1)

    # print("valid_x", len(valid_x))
    # 对曲线取平均值
    # # 对曲线取平均值
    # min_y = np.max(min_y_values)
    # min_y_values_2 = min_y_values + min_y
    # min_y_values_2 = min_y_values_2 / 2
    # min_y_values_2 = min_y_values_2.astype(int)
    # shifted_min_y_values = np.clip(min_y_values_2 + shift_amount, 0, image.shape[0] - 1)

    if settings.SHADOW_PROCESSING == 0:
        if crop_image_box:
            # 800像素宽;鞋子前后20%进行移除
            shoe_width = crop_image_box[2] - crop_image_box[0]
            _half_show_width = int(shoe_width * 0.15)
            valid_x[: crop_image_box[0] + _half_show_width] = False
            valid_x[crop_image_box[2] - _half_show_width :] = False

    x_coords = np.arange(image.shape[1])[valid_x]
    y_start = shifted_min_y_values[valid_x]
    y_end = y_start + line_thickness

    # todo 使用Numpy广播机制创建线条区域的索引
    # todo 鞋子曲线线条
    if settings.SHADOW_PROCESSING == 0:
        for x, start, end in zip(x_coords, y_start, y_end):
            image[start:end, x, :3] = line_color  # 只修改RGB通道

    # 计算整个图像的最低非透明点
    lowest_y = (
        np.max(min_y_values[min_y_values != -1]) if np.any(min_y_values != -1) else -1
    )
    # 绘制原最低非透明点处的线
    cv2.line(
        image,
        (one_line_pos[0], lowest_y + settings.LOWER_Y),
        (one_line_pos[1], lowest_y + 5),
        line_color,
        thickness=line_thickness,
    )
    # 调整 _y = lowest_y + 18
    _y = lowest_y + 200
    if _y > image.shape[0]:  # 超过图片尺寸
        _y = image.shape[0] - settings.CHECK_LOWER_Y
    return image, _y


def clean_colors(img):
    # 转成灰度图
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return img


def calculated_shadow_brightness(img: Image):
    # 打开图片并转换为灰度模式
    image = img.convert('L')
    # 将图片数据转为numpy数组
    image_data = np.array(image)
    # 创建布尔掩码以识别非白色区域
    non_white_mask = image_data < 252

    # 使用掩码提取非白色像素的亮度值
    non_white_values = image_data[non_white_mask]

    # print(len(non_white_values),len(image_data))
    # 如果存在非白色像素，则计算平均亮度；否则返回0
    if len(non_white_values) > 0:
        average_brightness = np.mean(non_white_values)
    else:
        average_brightness = 0  # 没有非白色像素时的情况

    return average_brightness


def levels_adjust(img, Shadow, Midtones, Highlight, OutShadow, OutHighlight, Dim):
    # 色阶处理
    # img 为cv2格式

    # dim = 3的时候调节RGB三个分量， 0调节B，1调节G，2调节R
    if Dim == 3:
        mask_shadow = img < Shadow
        img[mask_shadow] = Shadow
        mask_Highlight = img > Highlight
        img[mask_Highlight] = Highlight
    else:
        mask_shadow = img[..., Dim] < Shadow
        img[mask_shadow] = Shadow
        mask_Highlight = img[..., Dim] > Highlight
        img[mask_Highlight] = Highlight

    if Dim == 3:
        Diff = Highlight - Shadow
        rgbDiff = img - Shadow
        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
        data = np.array(outClRgb * 255, dtype='uint8')
        img = data
    else:
        Diff = Highlight - Shadow
        rgbDiff = img[..., Dim] - Shadow
        clRgb = np.power(rgbDiff / Diff, 1 / Midtones)
        outClRgb = clRgb * (OutHighlight - OutShadow) / 255 + OutShadow
        data = np.array(outClRgb * 255, dtype='uint8')
        img[..., Dim] = data
    return img


def calculate_average_brightness_opencv(img_gray, rows_to_check):
    # 二值化的图片 CV对象
    # 计算图片亮度
    height, width = img_gray.shape

    brightness_list = []
    for row in rows_to_check:
        if 0 <= row < height:
            # 直接计算该行的平均亮度
            row_data = img_gray[row, :]
            average_brightness = np.mean(row_data)
            brightness_list.append(average_brightness)
        else:
            print(f"警告：行号{row}超出图片范围，已跳过。")

    return brightness_list
