Ejemplo n.º 1
0
 def process_list_delete(self, tif_path, patch_params, **kwargs):
     """
     delete方式检测图片
     :param tif_path: 图片路径
     :param save_path: 保存路径,不使用
     :param patch_params:  参数组,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
     :return:
     """
     pass
     self.vertices = []
     slide = cv2.imread(tif_path)
     vertices_first = BBoxes()
     vertices = BBoxes()
     for patch_param in patch_params:
         vertices_temp = self._detect_img(slide, *patch_param)
         # todo 超过2组参数场景后续需要确认delete算法
         if not vertices_first:
             vertices_first = vertices_temp
         vertices = vertices_first - vertices_temp
     else:
         return vertices
Ejemplo n.º 2
0
    def __init__(self, uid, *args, **kwargs):
        """
        初始化模型的检测实体
        :param args: 预留
        :param thread_max: 分片监测的最大线程数,默认不支持并发,单线程启动
        :param kwargs: 预留  thresh_model, 分片出框的置信度
        """
        self.uuid = uid
        self.model = self.model_dict.get(self.uuid)
        if not self.model or not isinstance(self.model, Model):
            raise Exception("uuid {} 对应的模型已经不存在".format(self.uuid))

        # 并发处理,结果回填需要加锁
        self.vertices = BBoxes()
        self._lock = RLock()
Ejemplo n.º 3
0
    def detect_for_image(
            self, sub_slide, vertice: BBox, width, height,
            **kwargs):  # , patch_width, patch_height, padding=True):
        """
        子图检测
        :param sub_image: 图片路径
        :param vertice:  参数组,如果传递空,则不分片,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
        :return: detect_type, type_name, class_confidence
        """
        # TIPS:modified for mrcnn
        # self.return_bboxes = BBoxes()
        vertice_result = BBoxes()
        # 【0】如果子模型有过滤
        if not self.model.judge_by_bbox_geo_feature(vertice, width, height):
            return vertice_result

        # 【1】优先选择子模型 todo  暂时仅支持分类子模型,传入图片
        detect_type, type_name, class_confidence = self.model.detect_sub_model(
            sub_slide)

        # deepcopy 防止引用被修改
        vertice_temp = predict_pipe._merge_bbox_with_classify(
            deepcopy(vertice), detect_type, type_name, class_confidence)
        # 父子模型结果merge失败
        if not vertice_temp:
            return vertice_result

        # 子模型类型,防止合并
        vertice_temp.class_type = "sub_model.{}".format(
            self.model.model_cfg.model_file)

        # 【2】子模型的中间结果是否需要输出 ;  没有子模型,返回结果
        if self.model.model_cfg.out or type_name not in self.model.model_cfg.sub_model_dict:
            # 子模型结果和vertice合并失败时,vertice_temp为空
            vertice_result.append(vertice_temp)

        # 还有子模型,递归调用
        if type_name in self.model.model_cfg.sub_model_dict:
            vertice_result_temp = self.model.model_cfg.sub_model_dict[
                type_name].detect(image=sub_slide,
                                  vertice=deepcopy(vertice),
                                  WIDTH=width,
                                  HEIGHT=height)
            # 拼接递归结果
            vertice_result.extend(vertice_result_temp)

        # 如果有后处理
        if callable(self.model.post_handle_for_result):
            vertice_result = self.model.post_handle_for_result(vertice_result)

        return vertice_result
Ejemplo n.º 4
0
    def detect_sub_img(self,
                       box,
                       img,
                       thresh,
                       WIDTH,
                       HEIGHT,
                       edge=False,
                       merge_flag=False):
        """
        监测子图片img缺陷,并转换成大图坐标,构造成类方法,所有对象共用model和监测方法
        :param box:  切图的起始坐标和宽度
        :param img: 图片本身
        :param thresh: 置信度阈值门限,子图的置信度
        :param WIDTH: 整图宽度
        :param HEIGHT: 整图高度
        :param edge: 图片边缘出的框是否去除
        :param merge_flag: 子图(可能没有分片就是全图)的框是否合并
        :return: 标注bbox,参照box传入的起始x和y坐标,转换bbox成大图坐标
        """
        # resize大小 20190722于涛确认不需要resize
        # sub_img = cv2.resize(img, (self.model.config.IMAGE_MIN_DIM, self.model.config.IMAGE_MAX_DIM))
        # 子图片检查到的缺陷
        start = time.time()
        results = self.model.detect(img)
        vertices = BBoxes()
        # 缺陷数为0
        if len(results) == 0:
            return vertices

        for defect in results:
            try:
                # 如果置信度不到门限,不处理
                if defect['confidence'] < thresh:
                    continue

                class_name = defect.get("name")
                class_id = defect.get("class_id")
                if not class_name:
                    class_name = self.names[class_id]

                # 如果需要去除边缘框
                if edge and (defect['x1'] <= 0 or defect['y1'] <= 0 or
                             defect['x2'] >= WIDTH or defect['y2'] >= HEIGHT):
                    logging.warning("缺陷{}的坐标{}靠近子图片边缘{}".format(
                        class_name, defect, box))
                    continue

                vertice = BBox([
                    defect['x1'] + box[0], defect['y1'] + box[1],
                    defect['x2'] + box[0], defect['y2'] + box[1], class_name,
                    defect['confidence']
                ])
                # 几何参数判断
                if not self.judge_by_bbox_geo_feature(
                        vertice=vertice, WIDTH=WIDTH, HEIGHT=HEIGHT):
                    # 不满足几何参数配置,返回
                    logging.warning(
                        "[{}]缺陷{}的几何参数S:{}, w:{}, h:{}, hTow:{}不满足配置门限".format(
                            self.model_cfg.model_type, vertice, vertice.S,
                            vertice.w, vertice.h, vertice.hTow))
                    continue

                # append能够自动转换成自定义的BBox列表
                vertices.append(vertice)
            except Exception as e:
                # 发生异常,这一个标注框不处理,不影响其他标注框
                logging.error("解析标注框发生异常:{}".format(e), exc_info=1)

        # 一次检测结果merge
        if merge_flag:
            vertices = vertices.merge_each_other()

        # if cls.debug:
        #     logging.warning("分片{}的检测耗时{:.3f}结果:{}".format(box, time.time() - start, vertices))
        #     # 将图片保存到文件
        #     #  [1400, 1400, 2448, 2050]
        #     cv2.imwrite("_".join([str(item) for item in box]) + ".jpg", img)
        return vertices
Ejemplo n.º 5
0
    def process_list_merge(
            self, slide, patch_params,
            **kwargs):  # , patch_width, patch_height, padding=True):
        """
        merge方式检测图片
        :param slide: 图片
        :param patch_params:  参数组,如果传递空,则不分片,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
        :return:
        """
        # TIPS:modified for mrcnn
        # if self.debug:
        #     logging.warning("开始检测图片{}".format(tif_path))
        sp = slide.shape
        # 先判断图像清晰度,清晰度不够,不需要检测
        if not self._judge_image_vague(slide):
            return []
        WIDTH = sp[1]  # 图片宽度
        HEIGHT = sp[0]  # 图片高度

        vertices = BBoxes()
        if not patch_params:
            patch_params = [
                None,
            ]

        for patch_param in patch_params:
            # 不需要切片
            if not patch_param:
                vertices_temp = self._detect_img(slide)
            # 需要切片
            else:
                vertices_temp = self._detect_img(slide, *patch_param)
            # todo 超过2组参数场景后续需要确认merge算法
            vertices = vertices | vertices_temp
        else:
            pass

        self.return_bboxes = BBoxes()

        # 如果自定义了过滤方法  例如杆号 保留最大方差的框,根据方差计算筛选杆号的框,待后续考虑
        if callable(self.model.verify_after_main_model):
            vertices = self.model.verify_after_main_model(
                self.model.model_cfg, slide, vertices)

        for vertice in vertices:
            # 类型判断
            if not isinstance(vertice, BBox):
                logging.error("bbox {} 必须为BBox类型".format(vertice))
                continue

            # 子模型置信度有效且小于模型置信度,才需要筛选  3个门限处理在配置文件中完成
            # 小于门限的不处理
            if vertice.confidence < self.model.model_cfg.thresh_ai:
                continue

            # x2, y2归一化
            vertice.x2 = min(vertice.x2, WIDTH)
            vertice.y2 = min(vertice.y2, HEIGHT)

            # ai_name 初值
            vertice.ai_name = vertice.class_name

            # 不需要运行子模型,检测结果直接返回 todo  待整合
            if not self.model.model_cfg.sub_model_dict:
                self.return_bboxes.append(vertice)
                continue

            # 判断是否需要子模型检测
            if callable(self.model.need_sub_model_detect):
                is_need, vertice = self.model.need_sub_model_detect(vertice)
                if not is_need:
                    self.return_bboxes.append(vertice)
                    continue

            # 如果有子模型,同时需要输出中间结果
            if self.model.model_cfg.out:
                self.return_bboxes.append(vertice)

            # 消除虚警,子类型检测
            try:
                self._classify_sub_img(slide,
                                       vertice=vertice,
                                       WIDTH=WIDTH,
                                       HEIGHT=HEIGHT)
            except Exception as e:
                logging.error("图片{}的{}区域子模型预测发生异常:{}".format(sp, vertice, e),
                              exc_info=True)

        # 如果有后处理
        if callable(self.model.post_handle_for_result):
            self.return_bboxes = self.model.post_handle_for_result(
                self.model.model_cfg, self.return_bboxes)

        # 格式转化你返回
        results = []
        for vertice in self.return_bboxes:
            if not isinstance(vertice, BBox):
                logging.error("[{}]{}错误的bbox类型{}".format(
                    self.model.model_cfg.model_type, vertice, type(vertice)))
                continue

            results.append(vertice.dict())

        return results
Ejemplo n.º 6
0
    def _detect_img(self,
                    slide,
                    start_x=0,
                    start_y=0,
                    patch_width=0,
                    patch_height=0,
                    padding=True):
        """
        按照一组分片参数检测图片,分片,每片图片检测
        :param slide: 图片 或者 图片路径
        :param start_x: 起始x坐标
        :param start_y: 起始y坐标
        :param patch_width: 分片宽度 0时表示不切片
        :param patch_height: 分片高度
        :param padding:
        :return:
        """
        # 清空缓存
        self.vertices = BBoxes()

        sp = slide.shape
        WIDTH = sp[1]
        HEIGHT = sp[0]

        # 运行参数构造
        run_params = dict(thresh=self.model.model_cfg.thresh_model
                          or self.model.model_cfg.thresh_ai,
                          WIDTH=WIDTH,
                          HEIGHT=HEIGHT,
                          edge=self.model.edge,
                          merge_flag=self.model.model_cfg.merge_one_img)

        # 单线程
        excutor = None
        pool = []
        if self.model.thread_max > 1:
            excutor = ThreadPoolExecutor(max_workers=self.model.thread_max)
            # if self.debug:
            #     logging.warning("分片采用线程池{}的方式监测".format(self.thread_max))

        for box, img in yield_sub_img(img_path=slide,
                                      start_x=start_x,
                                      start_y=start_y,
                                      sub_width=patch_width,
                                      sub_height=patch_height,
                                      WIDTH=WIDTH,
                                      HEIGHT=HEIGHT,
                                      padding=padding):
            try:
                # 单线程
                if not excutor:
                    # 子图片检查到的缺陷
                    bboxes = self.model.detect_sub_img(box=box,
                                                       img=img,
                                                       **run_params)
                    # # 合并到大图
                    # merge方式合并
                    self.vertices = self.vertices | bboxes
                # 多线程采用线程池的方式
                else:
                    t = excutor.submit(self.model.detect_sub_img,
                                       box=box,
                                       img=img,
                                       **run_params)
                    pool.append(t)
            except Exception as e:
                logging.error("[{}]子图{}检查发生异常:{}".format(
                    self.model.model_cfg.model_type, box, e),
                              exc_info=1)
        else:
            pass

        for task in as_completed(pool):
            vertices = task.result()
            # merge方式合并
            self.vertices = self.vertices | vertices

        return self.vertices
Ejemplo n.º 7
0
class predict_pipe(object):
    # model提取为类公共变量
    # model = None
    # model_cfg = None
    # names = []
    # debug = False
    # 模型字典
    model_dict = {}

    @classmethod
    def load(cls,
             model,
             model_cfg: ModelConfigLoad,
             verify_after_main_model=None,
             need_sub_model_detect=None,
             handle_for_result=None,
             **kwargs):
        """
        初始化模型的检测实体,一个进程里面最好只load一次,不要修改
        :param verify_after_main_model:  在主模型和子模型之间需要处理的函数
        :param model:  模型本身
        :param names: 对象列表
        :param model_cfg: 模型配置传递进来
        :param verify_after_main_model: 主模型校验 verify_after_main_model(obj: predict_pipe, img, bboxes)
        :param need_sub_model_detect: 传入的方法,判断是否需要子模型检测
        :param handle_for_result: 结果的后处理方法 handle_for_result(obj: predict_pipe, img, bboxes)
        :return:
        """
        uid = str(uuid.uuid1())
        cls.model_dict[uid] = Model(
            model,
            model_cfg,
            verify_after_main_model=verify_after_main_model,
            need_sub_model_detect=need_sub_model_detect,
            post_handle_for_result=handle_for_result,
        )
        return uid

    """
    生产环境监测图片,一个进程初始化一个实例
    检测主要是执行 process_list_merge 进行图片分析,具体参数含义参见函数定义
    """

    def __init__(self, uid, *args, **kwargs):
        """
        初始化模型的检测实体
        :param args: 预留
        :param thread_max: 分片监测的最大线程数,默认不支持并发,单线程启动
        :param kwargs: 预留  thresh_model, 分片出框的置信度
        """
        self.uuid = uid
        self.model = self.model_dict.get(self.uuid)
        if not self.model or not isinstance(self.model, Model):
            raise Exception("uuid {} 对应的模型已经不存在".format(self.uuid))

        # 并发处理,结果回填需要加锁
        self.vertices = BBoxes()
        self._lock = RLock()

    def _detect_img(self,
                    slide,
                    start_x=0,
                    start_y=0,
                    patch_width=0,
                    patch_height=0,
                    padding=True):
        """
        按照一组分片参数检测图片,分片,每片图片检测
        :param slide: 图片 或者 图片路径
        :param start_x: 起始x坐标
        :param start_y: 起始y坐标
        :param patch_width: 分片宽度 0时表示不切片
        :param patch_height: 分片高度
        :param padding:
        :return:
        """
        # 清空缓存
        self.vertices = BBoxes()

        sp = slide.shape
        WIDTH = sp[1]
        HEIGHT = sp[0]

        # 运行参数构造
        run_params = dict(thresh=self.model.model_cfg.thresh_model
                          or self.model.model_cfg.thresh_ai,
                          WIDTH=WIDTH,
                          HEIGHT=HEIGHT,
                          edge=self.model.edge,
                          merge_flag=self.model.model_cfg.merge_one_img)

        # 单线程
        excutor = None
        pool = []
        if self.model.thread_max > 1:
            excutor = ThreadPoolExecutor(max_workers=self.model.thread_max)
            # if self.debug:
            #     logging.warning("分片采用线程池{}的方式监测".format(self.thread_max))

        for box, img in yield_sub_img(img_path=slide,
                                      start_x=start_x,
                                      start_y=start_y,
                                      sub_width=patch_width,
                                      sub_height=patch_height,
                                      WIDTH=WIDTH,
                                      HEIGHT=HEIGHT,
                                      padding=padding):
            try:
                # 单线程
                if not excutor:
                    # 子图片检查到的缺陷
                    bboxes = self.model.detect_sub_img(box=box,
                                                       img=img,
                                                       **run_params)
                    # # 合并到大图
                    # merge方式合并
                    self.vertices = self.vertices | bboxes
                # 多线程采用线程池的方式
                else:
                    t = excutor.submit(self.model.detect_sub_img,
                                       box=box,
                                       img=img,
                                       **run_params)
                    pool.append(t)
            except Exception as e:
                logging.error("[{}]子图{}检查发生异常:{}".format(
                    self.model.model_cfg.model_type, box, e),
                              exc_info=1)
        else:
            pass

        for task in as_completed(pool):
            vertices = task.result()
            # merge方式合并
            self.vertices = self.vertices | vertices

        return self.vertices

    def square_padding(self, slide, width, height):
        """
        子图正方形扩充
        :param slide: 子图
        :param width:子图的宽
        :param height:子图的高
        :return:
        """
        max_length = max(width, height)
        img = np.zeros((max_length, max_length, 3), np.uint8)
        # 在左右或上下两边补零
        if height > width:
            img[0:height,
                round((height - width) / 2):round((height - width) / 2) +
                width] = slide
        else:
            img[round((width - height) / 2):height +
                round((width - height) / 2), 0:width] = slide
        return img

    def _classify_sub_img(self, slide, vertice: BBox, WIDTH, HEIGHT):
        """
        虚警分类消除,杆号检测子模型,仅仅主模型中调用
        :param slide:
        :param vertice:  x1, y1, x2, y2, class_name, confidence
        :return:
        """
        if self.model.model_cfg.expand_type > 0:
            sub_slide = self.square_padding(
                slide[vertice.y1:vertice.y2, vertice.x1:vertice.x2], vertice.w,
                vertice.h)
        # 如果子图需要扩充,可能配置为0
        elif self.model.model_cfg.expand_type == 0 and not self.model.model_cfg.padding_rate:
            # 扩充并处理  参数列表按照上下左右顺序
            sub_bbox = vertice.expand_by_padding(
                *self.model.model_cfg.padding_pixel, w=WIDTH, h=HEIGHT)
            sub_slide = slide[sub_bbox.y1:sub_bbox.y2, sub_bbox.x1:sub_bbox.x2]
        else:
            # 倍数扩充只有1倍
            sub_bbox = vertice.expand_by_rate(
                self.model.model_cfg.padding_rate, w=WIDTH, h=HEIGHT)
            sub_slide = slide[sub_bbox.y1:sub_bbox.y2, sub_bbox.x1:sub_bbox.x2]

        # 优先选择递归子模型检测
        if vertice.class_name and vertice.class_name in self.model.model_cfg.sub_model_dict.keys(
        ):
            # 在子模型里面merge父子模型结果
            vertice_result = self.model.model_cfg.sub_model_dict[
                vertice[4]].detect(image=sub_slide,
                                   vertice=vertice,
                                   WIDTH=WIDTH,
                                   HEIGHT=HEIGHT)
            self.return_bboxes.extend(vertice_result)
            return

        logging.error("{}没有对应的子模型".format(vertice.class_name))
        # 没有子模型
        return

    @staticmethod
    def _merge_bbox_with_classify(vertice: BBox, detect_type, class_name,
                                  class_confidence):
        """合并框和子分类"""
        # 无效合并
        if detect_type is None or class_name is None or class_confidence is None:
            return None

        # 杆号识别
        if detect_type == DetectType.ganhao:
            # 杆号为空,则消除该框,不考虑该框
            if not class_name:
                return ""
            #
            vertice.class_confidence = class_confidence
            vertice.number = class_name
        # 分类子模型
        elif detect_type == DetectType.classify:
            # 分类模型更新
            if not not class_name:
                vertice.class_name = class_name
                vertice.class_confidence = class_confidence
        # 其他场景暂时不处理
        else:
            pass

        return vertice

    def _judge_image_vague(self, image, vague_min=12.5):
        """
        判断图像是否模糊
        :param image:
        :param vague_min:
        :return:
        """
        # 未配置,默认不配置
        if not self.model.model_cfg.vague_min:
            return True

        try:
            image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            # image_gray=image
            image_vague = cv2.Laplacian(image_gray, cv2.CV_64F).var()
        except Exception as e:
            logging.error("判断图像是否模糊异常:{}".format(e))
            return True

        # logging.warning("图像模糊计算值为:{}".format(image_vague))
        if image_vague < self.model.model_cfg.vague_min:
            return False
        else:
            return True

    def detect_for_image(
            self, sub_slide, vertice: BBox, width, height,
            **kwargs):  # , patch_width, patch_height, padding=True):
        """
        子图检测
        :param sub_image: 图片路径
        :param vertice:  参数组,如果传递空,则不分片,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
        :return: detect_type, type_name, class_confidence
        """
        # TIPS:modified for mrcnn
        # self.return_bboxes = BBoxes()
        vertice_result = BBoxes()
        # 【0】如果子模型有过滤
        if not self.model.judge_by_bbox_geo_feature(vertice, width, height):
            return vertice_result

        # 【1】优先选择子模型 todo  暂时仅支持分类子模型,传入图片
        detect_type, type_name, class_confidence = self.model.detect_sub_model(
            sub_slide)

        # deepcopy 防止引用被修改
        vertice_temp = predict_pipe._merge_bbox_with_classify(
            deepcopy(vertice), detect_type, type_name, class_confidence)
        # 父子模型结果merge失败
        if not vertice_temp:
            return vertice_result

        # 子模型类型,防止合并
        vertice_temp.class_type = "sub_model.{}".format(
            self.model.model_cfg.model_file)

        # 【2】子模型的中间结果是否需要输出 ;  没有子模型,返回结果
        if self.model.model_cfg.out or type_name not in self.model.model_cfg.sub_model_dict:
            # 子模型结果和vertice合并失败时,vertice_temp为空
            vertice_result.append(vertice_temp)

        # 还有子模型,递归调用
        if type_name in self.model.model_cfg.sub_model_dict:
            vertice_result_temp = self.model.model_cfg.sub_model_dict[
                type_name].detect(image=sub_slide,
                                  vertice=deepcopy(vertice),
                                  WIDTH=width,
                                  HEIGHT=height)
            # 拼接递归结果
            vertice_result.extend(vertice_result_temp)

        # 如果有后处理
        if callable(self.model.post_handle_for_result):
            vertice_result = self.model.post_handle_for_result(vertice_result)

        return vertice_result

    def process_list_merge(
            self, slide, patch_params,
            **kwargs):  # , patch_width, patch_height, padding=True):
        """
        merge方式检测图片
        :param slide: 图片
        :param patch_params:  参数组,如果传递空,则不分片,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
        :return:
        """
        # TIPS:modified for mrcnn
        # if self.debug:
        #     logging.warning("开始检测图片{}".format(tif_path))
        sp = slide.shape
        # 先判断图像清晰度,清晰度不够,不需要检测
        if not self._judge_image_vague(slide):
            return []
        WIDTH = sp[1]  # 图片宽度
        HEIGHT = sp[0]  # 图片高度

        vertices = BBoxes()
        if not patch_params:
            patch_params = [
                None,
            ]

        for patch_param in patch_params:
            # 不需要切片
            if not patch_param:
                vertices_temp = self._detect_img(slide)
            # 需要切片
            else:
                vertices_temp = self._detect_img(slide, *patch_param)
            # todo 超过2组参数场景后续需要确认merge算法
            vertices = vertices | vertices_temp
        else:
            pass

        self.return_bboxes = BBoxes()

        # 如果自定义了过滤方法  例如杆号 保留最大方差的框,根据方差计算筛选杆号的框,待后续考虑
        if callable(self.model.verify_after_main_model):
            vertices = self.model.verify_after_main_model(
                self.model.model_cfg, slide, vertices)

        for vertice in vertices:
            # 类型判断
            if not isinstance(vertice, BBox):
                logging.error("bbox {} 必须为BBox类型".format(vertice))
                continue

            # 子模型置信度有效且小于模型置信度,才需要筛选  3个门限处理在配置文件中完成
            # 小于门限的不处理
            if vertice.confidence < self.model.model_cfg.thresh_ai:
                continue

            # x2, y2归一化
            vertice.x2 = min(vertice.x2, WIDTH)
            vertice.y2 = min(vertice.y2, HEIGHT)

            # ai_name 初值
            vertice.ai_name = vertice.class_name

            # 不需要运行子模型,检测结果直接返回 todo  待整合
            if not self.model.model_cfg.sub_model_dict:
                self.return_bboxes.append(vertice)
                continue

            # 判断是否需要子模型检测
            if callable(self.model.need_sub_model_detect):
                is_need, vertice = self.model.need_sub_model_detect(vertice)
                if not is_need:
                    self.return_bboxes.append(vertice)
                    continue

            # 如果有子模型,同时需要输出中间结果
            if self.model.model_cfg.out:
                self.return_bboxes.append(vertice)

            # 消除虚警,子类型检测
            try:
                self._classify_sub_img(slide,
                                       vertice=vertice,
                                       WIDTH=WIDTH,
                                       HEIGHT=HEIGHT)
            except Exception as e:
                logging.error("图片{}的{}区域子模型预测发生异常:{}".format(sp, vertice, e),
                              exc_info=True)

        # 如果有后处理
        if callable(self.model.post_handle_for_result):
            self.return_bboxes = self.model.post_handle_for_result(
                self.model.model_cfg, self.return_bboxes)

        # 格式转化你返回
        results = []
        for vertice in self.return_bboxes:
            if not isinstance(vertice, BBox):
                logging.error("[{}]{}错误的bbox类型{}".format(
                    self.model.model_cfg.model_type, vertice, type(vertice)))
                continue

            results.append(vertice.dict())

        return results

    # todo delete方式暂时不支持
    def process_list_delete(self, tif_path, patch_params, **kwargs):
        """
        delete方式检测图片
        :param tif_path: 图片路径
        :param save_path: 保存路径,不使用
        :param patch_params:  参数组,一组切片参数[ (start_x, start_y, patch_width, patch_height, padding)]
        :return:
        """
        pass
        self.vertices = []
        slide = cv2.imread(tif_path)
        vertices_first = BBoxes()
        vertices = BBoxes()
        for patch_param in patch_params:
            vertices_temp = self._detect_img(slide, *patch_param)
            # todo 超过2组参数场景后续需要确认delete算法
            if not vertices_first:
                vertices_first = vertices_temp
            vertices = vertices_first - vertices_temp
        else:
            return vertices