#!D:/IDE/anaconda3/envs python
# -*- coding: UTF-8 -*-
"""
 @Author: Administrator
 @FileName：SlideFocusingPool.py\n
 @DateTime: 2025/6/30 17:42\n
 @Author  ：xsh
"""
import threading
import cv2
import numpy as np
import math
import time
import os
import cv2
from PyQt5 import Qt
from datetime import datetime, date
from AppInterfaces.ThreadPoolInterface                      import ThreadPoolInterface, PoolUtil    #
from AppBackEnd.BackEndEventBus.DataOnlyDrawPool            import DataOnlyDrawPool
from AppBackEnd.BackEndEventBus.DataWebSocketPool           import DataWebSocketPool
from AppInterfaces.EventInterface                           import EventInterface
from AppInterfaces.utils                                    import *
from AppCore.DBModels                                       import *
from AppCore.Levels                                         import *
from AppCore.Requests                                       import *
from AppCore.Events                                         import *
from AppCore.Readers                                        import WebReader, AppWebSocket
from AppCore.Impls                                          import WebSocketServer, WebSocketClient
from AppUtils.utils                                         import *
from AppSettings.utils                                      import *
from AppCore.AppEvents.ImageEventInterface                  import ImageEventInterface
from AppCore.AppManagers.ProcessManager                     import ImageSaveProcess, SaveSlide
from AppUtils.AppDBUtils import AppDBUtils
from AppInterfaces.AppEventInterface                        import AppEventInterface
class SlideFocusingPool(Qt.QThread, AppEventInterface, SingletonInterface):
    AppendItem          : Qt.pyqtSignal = Qt.pyqtSignal(object)
    _IOLocker           : Qt.QReadWriteLock = Qt.QReadWriteLock()
    FocusingComplete    : Qt.pyqtSignal = Qt.pyqtSignal()
    Locker              : Qt.QMutex = Qt.QMutex()
    FocusModels         : list[SlideModelInterface]
    FocusScores         : list[int]
    ImageAvailable      : list[int]
    isCell              : bool
    isGrab              : bool
    isComplete          : int
    isShowFocusing         : bool
    IsSecond            : bool
    OnlyDrawPool        : DataOnlyDrawPool          # 图像显示线程
    WebSocketPool       : DataWebSocketPool         # 网络发送线程
    @classmethod
    def GetInstance(cls, *args, **kwargs) -> 'SlideFocusingPool':
        return super(SlideFocusingPool, cls).GetInstance(*args, **kwargs)

    def __init__(self, parent: Qt.QObject = None):
        super(SlideFocusingPool, self).__init__(parent=parent)
        self.Initialization('DataOnlyDrawPool', self, parent)
        self.Mode = EventLevel.QualityMode
        self.isShowFocusing = True
        self.IsSecond       = False
        self.Start()

    def SendEvent(self, event, **args):
        self.AppendItem.emit(event)
        AppTimeLoop.processGlobalEvents(200)

    def StartWork(self, **args):
        self.initPools()
        self.Start()

    def StopWork(self, **args):
        self.delePools()


    def initConnect(self):
        super(SlideFocusingPool, self).initConnect()
        TYPE: Qt.Qt.ConnectionType = Qt.Qt.ConnectionType(Qt.Qt.QueuedConnection | Qt.Qt.UniqueConnection)
        self.AppendItem.connect(slot=self.slot_AppendItem, type=TYPE)

    def StartFocusing(self, isShowFocusing: bool = True):
        self.initPools          ()
        self.initFocusModels    ()
        self.isShowFocusing     = isShowFocusing
        self.Start              ()

    def StopFocusing(self, isShowFocusing: bool = True):
        self.initFocusModels()
        self.isShowFocusing = isShowFocusing

    def deleConnect(self):
        super(SlideFocusingPool, self).deleConnect()
    #
    def slot_AppendItem(self, item):
        self.appendItem(item)

    def appendItem(self, item: SlideModelInterface, *args, **kwargs):
        if not self.isRunning():
            self.is_running = True
            self.start(Qt.QThread.HighPriority)
        if isinstance(item, SlideModelInterface):
            with self.condition:
                self.DataBuffer.put(item)
                self.condition.notify_all()

    def Start(self):
        if self.isRunning():
            return
        self.initFocusModels()
        self.initPools()
        self.is_running = True
        self.start(Qt.QThread.HighPriority)




    @property
    def hasFocusModels(self) -> bool:
        return hasattr(self, 'FocusModels') and isinstance(self.FocusModels, list)

    def initFocusModels(self):
        self.FocusModels        = []
        self.FocusScores        = []
        self.ImageAvailable     = []
        self.isGrab             = False
        self.isCell             = False
        self.IsSecond           = False
        self.isComplete         = 0

    def run(self) -> None:
        if not self.hasFocusModels:
            self.initFocusModels()
        if not self.isAvailable:
            self.initPools()
        self.process()


    def WaitDone(self, item: SlideModelInterface, *args, **kwargs):
        CellCount               : int
        MinCellCount            : int
        CellThreshold           : int
        ImageScore              : int
        FocusingThreshold       : int
        MinFocusCount           : int
        FocusCompleteCount      : int
        _FocusCompleteCount     : int
        MaxImageCount           : int
        FocusingColorThreshold  : int
        isMicronucleus          : bool  #
        try:
            if isinstance(item, SlideModelInterface):
                if not item.hasImageSource:
                    return
                if item.hasFocusing:
                    CellThreshold           = item.Focusing.CellThreshold                 #
                    MinCellCount            = item.Focusing.MinCellCount                  #
                    FocusingThreshold       = item.Focusing.FocusingThreshold             #
                    FocusCompleteCount      = item.Focusing.FocusingFinishCount           #
                    MaxImageCount           = item.Focusing.MaxImageCount                 #
                    MinContourArea          = item.Focusing.FocusingKSize                 #
                    FocusingColorThreshold  = item.Focusing.FocusingColorThreshold        #
                else:
                    CellThreshold, MinCellCount, FocusingThreshold, FocusingColorThreshold, FocusCompleteCount, MaxImageCount, MinContourArea = FocusingConfigModel.getFocusingConfig(item.ObjectiveLevel, item.Level)
                isMicronucleus = ObjectiveLevel.isMicronucleus(item.ObjectiveLevel)
                MinCompleteNumber   : int = max(20, int(MaxImageCount * 0.25))
                CompleteNumber      : int = max(20, int(MaxImageCount * 0.25))
                if ScanStatusLevel.isLowFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.9))
                elif ScanStatusLevel.isHigFirstFocusing(item.Level):
                    CompleteNumber = max(20, int(MaxImageCount * 0.45))
                else:
                    CompleteNumber = max(20, int(MaxImageCount * 0.25))


                """20251124更新"""
                ImageScore = 0
                src_image   = item.toImage()
                CellCount   = self.get_cell_count(src_image, CellThreshold, MinContourArea)
                image = src_image[:,::4,:]
                if isMicronucleus:
                    # image = ImageUtils.image_resize(src_image, 1024, 768)
                    image = src_image[:, ::4, :]
                else:
                #     image = ImageUtils.image_resize(src_image, 1024, 1024)
                    image = src_image[:, ::2, :]

                self.isGrab = CellCount >= MinCellCount
                if not self.isCell:
                    self.isCell = self.isGrab
                if self.isCell and not self.isGrab:
                    if not self.IsSecond:
                        if len(self.ImageAvailable) >= MinCompleteNumber:
                                self.IsSecond = True
                if self.isGrab:
                    ImageScore = self.sq_bre_14(image if isMicronucleus else image[:, :, 1], FocusingThreshold, FocusingColorThreshold)
                    # ImageScore = self.sq_bre_10(image)
                item.ImageScore = ImageScore

                self.FocusModels.append(item)
                self.ImageAvailable.append(ImageScore)
                """判断当前评分是上升 还是下降"""
                isUpWard = self.isUpWard()
                if self.isComplete >= FocusCompleteCount:
                    """如果当前还在上升"""
                    if len(self.ImageAvailable) >= MinCompleteNumber:
                        self.FocusingComplete.emit()
                    else:
                        self.isComplete += 1
                else:
                    if isUpWard < 0:
                        self.isComplete += 1
                    else:
                        if self.isCell:
                            if self.IsSecond:
                                self.isComplete += 1
                            else:
                                if  ScanStatusLevel.isLowFirstFocusing(item.Level):
                                    if len(self.ImageAvailable) >= CompleteNumber:
                                        self.isComplete += 1
                                elif ScanStatusLevel.isHigFirstFocusing(item.Level):
                                    if len(self.ImageAvailable) >= CompleteNumber:
                                        self.isComplete += 1
                                else:
                                    if len(self.ImageAvailable) >= CompleteNumber:
                                        self.isComplete += 1
                        else:
                            if   ScanStatusLevel.isLowFirstFocusing(item.Level):
                                if len(self.ImageAvailable) >= CompleteNumber:
                                    self.isComplete += 1
                            elif ScanStatusLevel.isHigFirstFocusing(item.Level):
                                if len(self.ImageAvailable) >= CompleteNumber:
                                    self.isComplete += 1
                            else:
                                if len(self.ImageAvailable) >= CompleteNumber:
                                    self.isComplete += 1

                self.ItemSlot(item, src_image, image, {})
                AppTimeLoop.processGlobalEvents(1000)
                if len(self.FocusModels) > MaxImageCount:
                    self.isComplete = FocusCompleteCount + 1
        except Exception as e:
            AppUtils.print_Exception(e)


    def ItemSlot(self, item: SlideModelInterface, src_image: np.ndarray, image: np.ndarray, buffer: dict):
        try:
            if item.hasPreview and item.preview.is_save:
                # if ObjectiveLevel.isHigObjective(item.ObjectiveLevel):
                filename, fmt = AppDBUtils.save_filename(item, ObjectiveLevel.isMicronucleus(item.ObjectiveLevel))
                item.SavePath = filename
                self.SaveImage(filename, src_image.copy())

            if self.isShowFocusing:
                if HeadEndShowMode.get('DEBUG_SHOW', False):
                    self.onlyDraw.AppendItem.emit(image.copy(), {})
                if item.hasPreview and item.preview.isMicronucleus():
                    self.webSocket.AppendItem.emit(Response.MVideoStream(buffer=image.copy()))
                else:
                    self.webSocket.AppendItem.emit(Response.VideoStream(buffer=image.copy()))
            AppTimeLoop.processGlobalEvents(1000)
        except Exception as e:
            AppUtils.print_Exception(e)


    def getMaxScoreItem(self) -> SlideFocusingModel:
        if self.hasFocusModels and self.focus_length > 0 and len(self.ImageAvailable) > 0:
            MaxScoreIndex: int = int(np.argmax(self.ImageAvailable))
            item = self.FocusModels[MaxScoreIndex].copy()
            return item
        return None

    def getMaxFocusingItem(self) -> (int, SlideFocusingModel):
        if self.hasFocusModels and self.focus_length > 0 and len(self.ImageAvailable) > 0:
            MaxScoreIndex: int = int(np.argmax(self.ImageAvailable))
            item = self.FocusModels[MaxScoreIndex].copy()
            return MaxScoreIndex, item
        return None, None

    @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))
            # print(f'细胞数量:{cell_count}')
            #
            return cell_count
        except Exception as e:
            AppUtils.print_Exception(e)
            return 0
    def isFocusingUpWard(self) -> bool:
        isFocusingHig: bool = False
        maxlength = len(self.ImageAvailable)
        step = 10
        if maxlength < step:
            return isFocusingHig
        begin: int = 0
        diff = [self.ImageAvailable[i + 1] - self.ImageAvailable[i] for i in range(begin, 10)]
        trend = sum(diff) / len(diff)
        isFocusingHig = trend < 0
        return isFocusingHig

    def isFocusingHig(self) -> bool:
        isFocusingHig: bool = False
        maxlength = len(self.ImageAvailable)
        step = 10
        if maxlength < step:
            return isFocusingHig
        begin: int = 0
        diff = [self.ImageAvailable[i + 1] - self.ImageAvailable[i] for i in range(begin, 10)]
        trend = sum(diff) / len(diff)
        isFocusingHig = trend < 0
        return isFocusingHig

    def isUpWard(self) -> int:
        isUpWard: int = 1
        maxlength = len(self.ImageAvailable)
        step      = 10
        if maxlength < step:
            return isUpWard
        begin: int = maxlength - step - 1
        diff = [self.ImageAvailable[i + 1] - self.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

    def initPools(self):
        self.initOnlyDrawPool       ()
        self.initWebSocketPool      ()

    def delePools(self):
        self.OnlyDrawPool       = None
        self.WebSocketPool      = None


    @property
    def isAvailable(self) -> bool:
        return self.isRunning() and self.hasWebSocketPool

    @property
    def hasOnlyDrawPool    (self) -> bool:
        return hasattr(self, 'OnlyDrawPool'   ) and isinstance(self.OnlyDrawPool   , DataOnlyDrawPool   )

    @property
    def hasWebSocketPool   (self) -> bool:
        return hasattr(self, 'WebSocketPool'  ) and isinstance(self.WebSocketPool  , DataWebSocketPool  )



    @property
    def onlyDraw     (self) -> DataOnlyDrawPool     :
        return None if not self.hasOnlyDrawPool    else self.OnlyDrawPool

    @property
    def webSocket    (self) -> DataWebSocketPool    :
        return None if not self.hasWebSocketPool   else self.WebSocketPool


    def initOnlyDrawPool    (self):
        if not self.hasOnlyDrawPool:
            self.OnlyDrawPool = DataOnlyDrawPool   .GetInstance()

    def initWebSocketPool   (self):
        if not self.hasWebSocketPool:
            self.WebSocketPool = DataWebSocketPool  .GetInstance()

    @property
    def focus_length(self) -> int:
        return 0 if not self.hasFocusModels else len(self.FocusModels)

    @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, thresh2: int):
        # 检查图像是否存在并正确读取
        if image is None:
            return 0
        if len(image.shape) == 3 and image.shape[2] == 3:
            gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)  # image[:, :, 1]
            gray_ = image[:, :, -1]

        else:
            gray = image
            gray_ = 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:
            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)
            if gray_ is not None:
                mask_ = np.logical_and(image[:-3, :, -1] > image[:-3, :, 1], image[:-3, :, 0] > image[:-3, :, 1])
                mask = np.logical_and(mask, mask_)
            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[:, :, 1]
        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 > 10)
            # gradient1 = np.mean((edge_strength1 - edge_mean1) ** 2).item()

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

            edge_count = (edge_count1.item() + edge_count2.item() / 5) / 2
        else:
            mean_val = gray.mean()
            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 > 10)
            # gradient1 = np.mean((edge_strength1 - edge_mean1) ** 2).item()

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

            edge_count = (edge_count1 + edge_count2 / 5) / 2
        gradient1 = 0
        return int(((gradient1 + gradient2)* 10 + edge_count))



    @property
    def hasShowFocusing(self) -> bool:
        return hasattr(self, 'isShowFocusing') and isinstance(self.isShowFocusing, (bool, int))
    @property
    def IsShowFocusing(self) -> bool:
        return False if not self.hasShowImage else self.isShowFocusing

    @IsShowFocusing.setter
    def IsShowFocusing(self, enable: (bool, int)):
        self.isShowFocusing = enable
    """小图评分"""
    @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)