Esempio 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
Esempio n. 2
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
Esempio n. 3
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()
Esempio 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
Esempio 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
Esempio 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