#!D:/IDE/anaconda3/envs/Microscope_Scan python
# -*- coding: UTF-8 -*-
"""
@FileName：FocusingUtil.py
@Description：
@Author：XSH
@Time：2025/11/19 9:24
@Department：研发部
@Copyright：©2025 乐辰生物
"""
import cv2
import numpy as np
import math
import time
import os
import cv2
from AppUtils.Float import Float
from AppUtils.AppUtils import AppUtils
class FocusingUtil:
    @staticmethod
    def isUpWard(ImageAvailable: list[int], _step: int = 20) -> int:
        isUpWard: int = 1
        step      = max(10, _step) if isinstance(_step, int) else 10
        if not isinstance(ImageAvailable, list) or len(ImageAvailable) < step:
            return isUpWard
        maxlength = len(ImageAvailable)
        if maxlength < step:
            return isUpWard
        begin: int = maxlength - step - 1
        diff = [ImageAvailable[i + 1] - ImageAvailable[i] for i in range(begin, maxlength - 1)]
        if sum(diff) <= 0:
            isUpWard = 0
        else:
            trend = sum(diff) / len(diff)
            if Float(trend) < 0.0:
                isUpWard = -1
            elif Float(trend) > 0.0:
                isUpWard = 1
            else:
                isUpWard = 0
        return isUpWard

    @staticmethod
    def get_cell_count_Blurred(img: np.ndarray, thresh: int, MinContourArea) -> int:
        try:
            cell_count = 0
            if img is None or not isinstance(img, np.ndarray) or not isinstance(thresh, int):
                return cell_count
            # 转换为灰度图
            if len(img.shape) >= 3:
                gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            else:
                gray_image = img

            # 使用高斯模糊平滑图像
            sigma: int = int(MinContourArea * 2)
            KSize: int = sigma if sigma % 2 == 1 else sigma + 1

            blurred_image = cv2.GaussianBlur(gray_image, (KSize, KSize), int(MinContourArea))
            # 二值化
            _, binary_image = cv2.threshold(blurred_image, thresh, 255, cv2.THRESH_BINARY_INV)
            contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            # 计数细胞
            cell_count = len(contours)
            return cell_count
        except Exception as e:
            AppUtils.print_Exception(e)
            return 0

    @staticmethod
    def get_cell_count(img: np.ndarray, thresh: int, MinContourArea: int = 30) -> int:
        try:
            cell_count = 0
            if img is None or not isinstance(img, np.ndarray) or not isinstance(thresh, int):
                return cell_count
            # 转换为灰度图
            if len(img.shape) >= 3:
                gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            else:
                gray_image = img
            # 使用高斯模糊平滑图像
            blurred_image = cv2.GaussianBlur(gray_image, (3, 3), 0)
            # 二值化
            _, binary_image = cv2.threshold(blurred_image, thresh, 255, cv2.THRESH_BINARY_INV)
            contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

            # 计数细胞
            cell_count = len(contours)
            if cell_count > 0:
                bbox = [cv2.boundingRect(c)[2: 4] for c in contours]
                bbox = np.max(bbox, axis=1)
                cell_count = int(np.sum(bbox >= MinContourArea))

            return cell_count
        except Exception as e:
            AppUtils.print_Exception(e)
            return 0

    @staticmethod
    def Square_Variance(image: np.ndarray) -> int:
        """ 使用 Brenner 梯度函数计算图像清晰度 """

        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = gray.astype(np.float32)
        gray = np.asarray(gray)
        gradient: int = int(np.asnumpy(np.sum(np.power(gray - gray.mean(), 2))))
        return gradient

    @staticmethod
    def Square_Variance_(image: np.ndarray) -> int:
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = gray.astype(np.float32)
        # 避免多次调用 mean()
        mean_val = gray.mean()
        gradient1 = np.mean(((gray[:-2] - gray[2:]) ** 2))
        gradient2 = np.mean(((gray - mean_val) ** 2))
        return int(gradient1 + gradient2) * 100

    @staticmethod
    def preEval(image):
        """
        只有当最大的灰度值和最小灰度值插值超过阈值才进行评分
        :param image: 图片 numpy
        :return:
        """
        if len(image.shape) == 3 and image.shape[2] == 3:
            image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        else:
            image = image[:, :]
        minGray, maxGray, _, _ = cv2.minMaxLoc(image)
        # hist = cv2.calcHist([image], [0], None, [256], [0, 255])
        # res = hist.reshape(hist.shape[0])
        # res = np.array(list(map(int, res)))
        # count = 0
        # for i in range(int(minGray), int(minGray) + 20):
        #     count += res[i]
        if not maxGray - minGray:
            return False
        return True

    @staticmethod
    def VB(image):
        # if not self.preEval(image):
        #     return 0
        averageGray = cv2.mean(image)[0]
        variance = (image - averageGray) ** 2
        brenner = (np.roll(image, -2, axis=1) - image) ** 2
        res = variance * brenner

        # mean = np.ones(image.shape, dtype=np.uint8) * int(cv2.mean(image)[0])
        # variance = cv2.absdiff(image, mean)
        # new = np.c_[image[:, -2:], image[:, :-2]]
        # brenner = cv2.absdiff(image, new)
        # res = variance * brenner
        return int(res.sum())

    @staticmethod
    def GZV(image):
        if len(image.shape) == 3 and image.shape[2] == 3:
            image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        else:
            image = image[:, :]
        hist = cv2.calcHist([image], [0], None, [256], [0, 255])
        res = hist.reshape(hist.shape[0])
        res = np.array(list(map(int, res)))
        count = cv2.countNonZero(res)
        if 266 - count < 128:
            return False
        return True

    @staticmethod
    def LGV(image):
        # if not self.GZV(image):
        #     return 0
        ret, binary = cv2.threshold(image, 80, 255, cv2.THRESH_BINARY)
        res = (binary == 0).sum()
        return int(res * 100)



    @staticmethod
    def sharpnessEval_1(image):
        """
        :param image: numpy
        :return: score
        """
        if not SlideFocusingPool.preEval(image):
            return 0
        averageGray = cv2.mean(image)[0]
        ret, mask = cv2.threshold(image, averageGray, 255, cv2.THRESH_BINARY)
        # mask = np.where(image >= averageGray, True, False)
        validArea = np.square(image - np.roll(image, -1, axis=1)) + \
                    np.square(image - np.roll(image, -1, axis=0)) + \
                    np.square(image - np.roll(np.roll(image, -1, axis=1), -1, axis=0))
        score = validArea[mask].sum()
        return score

    # SMD梯度函数计算
    @staticmethod
    def SMD(img):
        '''
        :param img:narray 二维灰度图像
        :return: float 图像越清晰越大
        '''
        shape = np.shape(img)
        out = 0
        for x in range(1, shape[0] - 1):
            for y in range(0, shape[1]):
                out += math.fabs(int(img[x, y]) - int(img[x, y - 1]))
                out += math.fabs(int(img[x, y] - int(img[x + 1, y])))
        return out

    @staticmethod
    def SMD2(img):
        '''
        :param img:narray 二维灰度图像
        :return: float 图像越清晰越大
        '''
        shape = np.shape(img)
        out = 0
        for x in range(0, shape[0] - 1):
            for y in range(0, shape[1] - 1):
                out += math.fabs(int(img[x, y]) - int(img[x + 1, y])) * math.fabs(int(img[x, y] - int(img[x, y + 1])))
        return out

    @staticmethod
    def check_blur(img, ref_image=None):
        '''综合评估图像的模糊质量'''
        # 傅里叶变换
        f = np.fft.fft2(img[:, :, 0])
        fshift = np.fft.fftshift(f)
        magnitude_spectrum = 20 * np.log(np.abs(fshift) + 1e-10)  # 加上一个小值以避免log(0)
        # 计算总能量
        total_energy = np.sum(magnitude_spectrum)
        # 计算高频部分的能量占比
        high_freq_energy = np.sum(magnitude_spectrum[magnitude_spectrum > np.percentile(magnitude_spectrum, 90)])
        high_freq_ratio = high_freq_energy / total_energy


        return int(float(high_freq_ratio) * 10000) % 10000

    @staticmethod
    def sq_bre_13(image: np.ndarray, thresh: int):
        # 检查图像是否存在并正确读取
        if image is None:
            return 0
        if len(image.shape) == 3 and image.shape[2] == 3:
            gray = image[:, :, 1]
        else:
            gray = image
        _thresh: int = int(abs(thresh) ) if isinstance(thresh, (float, int)) else 150
        try:
            gray = np.asarray(gray.astype(np.float32))
            edge_strength1 = (gray[:-1] - gray[1:])[:-2]
            edge_strength2 = (gray[:-2] - gray[2:])[:-1]
            edge_strength3 = gray[:-3] - gray[3:]
            cha1 = edge_strength1 - edge_strength2
            cha2 = edge_strength2 - edge_strength3
            edge_strength_1 = np.logical_and(cha1 > 10, cha2 > 10)
            edge_strength_2 = np.logical_and(cha1 < -10, cha2 < -10)
            mask = np.logical_or(edge_strength_1, edge_strength_2)
            mask = np.logical_and(mask, gray[:-3] < _thresh)
            # mask_nan = np.isnan(mask) | np.isinf(mask)
            # mask[mask_nan] = 0.00001
            # print(mask.sum())
            set_data = np.abs(edge_strength2[mask])
            if len(set_data) == 0:
                return  0
            mean = np.mean(set_data)
            gradient1 = np.sum((set_data - mean) ** 2)

            set_data2 = gray[:-3][mask]
            if len(set_data2) == 0:
                return  0
            mean = np.mean(set_data2)
            gradient2 = np.sum((set_data2 - mean) ** 2)
            gradient = (gradient1 + gradient2)
            return int(gradient.item())
        except Exception as e:
            AppUtils.print_Exception(e)
            return 0

    @staticmethod
    def sq_bre_14(img_data: np.ndarray, thresh: int, thresh2: int):
        # 检查图像是否存在并正确读取
        if img_data is None:
            return 0
        if len(img_data.shape) == 3 and img_data.shape[2] == 3:
            img_data_ = img_data[:-3, :, :]
            gray_0 = img_data_[:, :, 0]
            gray_1 = img_data_[:, :, 1]
            gray = np.array(img_data[:, :, 1], dtype=np.int16)
            gray_2 = img_data_[:, :, 2]
            gray_ = gray[:-3, :]

        else:
            img_data_ = img_data[:-3, :]
            gray = np.array(img_data[:, :], dtype=np.int16)
            gray_ = gray[:-3, :]
            gray_0 = None
            gray_1 = None
            gray_2 = None
        _thresh: int = int(abs(thresh)) if isinstance(thresh, (float, int)) else 150
        _thresh2: int = int(abs(thresh2)) if isinstance(thresh2, (int, float)) else 220

        try:
            edge_strength1 = gray_ - gray[1:-2:]
            edge_strength2 = gray_ - gray[2:-1, :]
            edge_strength3 = gray_ - gray[3:, :]

            cha1 = edge_strength1 - edge_strength2
            cha2 = edge_strength2 - edge_strength3
            #
            edge_strength_1 = np.logical_and(cha1 > 10, cha2 > 10)
            edge_strength_2 = np.logical_and(cha1 < -10, cha2 < -10)
            mask = np.logical_or(edge_strength_1, edge_strength_2)
            # mask = np.logical_and(mask, gray[:-3] < _thresh)
            # mask = gray_1 < _thresh
            if gray_0 is not None and gray_1 is not None and gray_2 is not None:
                mask_1 = gray_0 > gray_1
                mask_2 = gray_2 > gray_1
                mask_3 = gray_2 > gray_0
                mask = mask_1 & mask_2 & mask_3 & mask
            set_data = edge_strength2[mask]
            if len(set_data) == 0:
                return 0
            mean = np.mean(set_data)
            gradient1 = np.sum((set_data - mean) ** 2)
            set_data2 = edge_strength3[mask]
            # if len(set_data2) == 0:
            #     return 0
            mean = np.mean(set_data2)
            gradient2 = np.sum((set_data2 - mean) ** 2)
            gradient = (gradient1 + gradient2 / 2)
            return int(gradient.item())
        except Exception as e:
            AppUtils.print_Exception(e)
            return 0

    @staticmethod
    def sq_bre_10(image, if_gpu=True):
        if image is None:
            return 0
        if len(image.shape) == 3 and image.shape[2] == 3:
            gray = image[0, :, :]
        else:
            gray = image
        gray = gray.astype(np.float32)

        if if_gpu:
            gray = np.asarray(gray)
            mean_val = np.mean(gray)
            gradient2 = np.mean((gray - mean_val) ** 2).item()

            edge_strength1 = np.abs(gray[:-2] - gray[2:])
            edge_mean1 = np.mean(edge_strength1)
            edge_count1 = np.sum(edge_strength1 > 30)
            gradient1 = np.mean((edge_strength1 - edge_mean1) ** 2).item()

            edge_strength2 = np.abs(gray[:-1] - gray[1:])
            edge_mean2 = np.mean(edge_strength2)
            edge_count2 = np.sum(edge_strength2 > 20)
            gradient1 += np.mean((edge_strength2 - edge_mean2) ** 2).item()

            edge_count = (edge_count1.item() + edge_count2.item()) / 2
        else:
            mean_val = gray.mean()
            edge_strength = np.abs(gray[:-5] - gray[5:])
            edge_count1 = np.sum(edge_strength > 60)
            edge_mean = edge_strength.mean()
            gradient1 = np.mean((edge_strength - edge_mean) ** 2)
            gradient2 = np.mean((gray - mean_val) ** 2)

            edge_strength2 = np.abs(gray[:-2] - gray[2:])
            edge_count2 = np.sum(edge_strength2 > 60)
            edge_count = (edge_count1.item() / 2 + edge_count2.item()) / 2

        return int(((gradient1 + gradient2) + edge_count) * 10)

    """小图评分"""

    @staticmethod
    def get_imgscore_10X(img_data: np.ndarray):
        if len(img_data.shape) == 3:
            img_data = img_data[:, :, 0]
        data_ = img_data[img_data > 3]

        min_data = np.sort(data_.flatten())[100]
        mean_data = np.mean(data_)
        theata = mean_data - 0.2 * (mean_data - min_data)
        ret, binary = cv2.threshold(img_data, theata, 255, cv2.THRESH_BINARY)
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))

        # 对二值图像进行闭运算
        closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
        closing = np.where(closing == 0, 255, 0)
        # 连通组件标记
        # num_labels: 连通区域的数量，包括背景（通常标签为0）。
        # labels: 与输入图像大小相同的输出图像，其中每个非零像素的值代表其所在连通区域的标签。
        # stats: 一个数组，包含每个连通区域的左上角（x, y）坐标、宽度、高度和面积。第0个元素是背景。
        # centroids: 每个连通区域的质心坐标数组。
        num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(closing.astype(np.uint8), connectivity=4)
        x_y = []
        for id, stat in enumerate(stats):
            x, y, h, w, area = stat
            if x == 0 and y == 0:
                continue
            if area < 20:
                continue
            if 0.8 < h / w < 1.2:
                if area > 200:
                    if area / (w * h) > 0.7:
                        continue
                if area / (w * h) > 0.8:
                    continue
            x_y.append([x, y, h, w, area])

        points = np.array(x_y)[:, :2]
        center = np.mean(points, axis=0)
        distances_squared = np.sum((points - center) ** 2, axis=1)
        variance = np.mean(distances_squared)
        std_dev = np.sqrt(variance)
        return int(std_dev)