python exception in thread_这个是什么原因,请问怎么处理Exception in thr

博客展示了Python Django项目中'django-main-thread'线程出现异常的情况,追溯错误发现是在运行服务器时,socket模块的getfqdn函数调用gethostbyaddr时出现了'UnicodeDecodeError',即'utf-8'编解码器无法解码特定字节。

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼

这个是什么原因,请问怎么处理

Exception in thread django-main-thread:

Traceback (most recent call last):

File "D:\Python36\lib\threading.py", line 916, in _bootstrap_inner

self.run()

File "D:\Python36\lib\threading.py", line 864, in run

self._target(*self._args, **self._kwargs)

File "C:\Users\ASUS\PycharmProjects\Django\test1\venv\lib\site-packages\django\utils\autoreload.py", line 54, in wrapp

er

fn(*args, **kwargs)

File "C:\Users\ASUS\PycharmProjects\Django\test1\venv\lib\site-packages\django\core\management\commands\runserver.py",

line 139, in inner_run

ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)

File "C:\Users\ASUS\PycharmProjects\Django\test1\venv\lib\site-packages\django\core\servers\basehttp.py", line 203, in

run

httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)

File "C:\Users\ASUS\PycharmProjects\Django\test1\venv\lib\site-packages\django\core\servers\basehttp.py", line 67, in

__init__

super().__init__(*args, **kwargs)

File "D:\Python36\lib\socketserver.py", line 453, in __init__

self.server_bind()

File "D:\Python36\lib\wsgiref\simple_server.py", line 50, in server_bind

HTTPServer.server_bind(self)

File "D:\Python36\lib\http\server.py", line 138, in server_bind

self.server_name = socket.getfqdn(host)

File "D:\Python36\lib\socket.py", line 673, in getfqdn

hostname, aliases, ipaddrs = gethostbyaddr(name)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 2: invalid continuation byte

import asyncio from concurrent.futures import ThreadPoolExecutor import os import time import numpy as np import traceback import cv2 import torch import csv from utils.general import ( check_img_size, non_max_suppression, apply_classifier, scale_coords, xyxy2xywh, strip_optimizer, set_logging) from models.common import DetectMultiBackend from utils.torch_utils import select_device from algo_config.common_config_data import * from algo_utils.code_filter import CodeFilter from algo_utils.code_identifier import CodeIdentifier # from algo_utils.uk_detector import UKDetector import json from math import ceil # from PIL import Image import logging import xml.etree.ElementTree as ET import xml.dom.minidom from utils.plots import Annotator, colors, save_one_box # 同类型code不同级别时,进行合并,key为主code,可考虑更新为可配置 BBOX_MERGE_CLS_INFO = {3: [2]} START_CUT_OFFX = 0 END_CUT_OFFX = 0 logger = logging.getLogger(__name__) def calc_file_size(file_path): if os.path.exists(file_path): file_size = os.path.getsize(file_path) else: file_size = -1 return file_size class Model(): def __init__(self, color_spec, device="", gray_spec=None, common_spec=None, conf_thres=0.45): from ultralytics import YOLO self.color_spec = color_spec self.gray_spec = gray_spec self.common_spec = common_spec self.augment = True self.conf_thres = conf_thres # 0.2 self.iou_thres = 0.5 self.agnostic_nms = False self.classes = None # 是否存储xml和img绘制,只有开启,绘制结果的参数才会生效 self.save_result = False # 是否绘制 self.draw_result_on_img = False imgsz = 1280 self.device_num = "cuda:" + str(device) self.device = select_device(self.device_num) # Initialize set_logging() # model = DetectMultiBackend(weights, device=self.device, dnn=False) model_s = YOLO(self.color_spec['split_model']) model_f = YOLO(self.color_spec['full_model']) self.model_s = model_s self.model_f = model_f self.imgsz = imgsz self.half = False # 获取模型中的标注code名称 self.code_name = list(self.model_s.names.values())+list(self.model_f.names.values()) # 增加unkown code,确保SD1UK始终在最后一个 # profile_preset.json的路径 self.profile_preset_path = self.color_spec['profile_preset_path'] # code过滤 self.code_filter = CodeFilter() # code区分合并 self.code_identifier = CodeIdentifier(self.code_name) # 未知缺陷定位检测 # self.u3_id = self.code_name.index(SD1U3) if SD1U3 in self.code_name else -1 # self.u4_id = self.code_name.index(SD1U4) if SD1U4 in self.code_name else -1 # self.ot_id = self.code_name.index(SD1OT) if SD1OT in self.code_name else -1 # self.uk_detector = UKDetector() def get_code_idx(self, code): return self.code_name.index(code) if code in self.code_name else -1 def update_save_draw_action(self, save_result, draw_result_on_img): """ 更新存储和绘制图像参数 """ self.save_result = save_result self.draw_result_on_img = draw_result_on_img def create_result_dict(self): """ 创建算法响应消息主体 :return: """ result = {} result.setdefault("status", 200) result.setdefault("message", "Success") result.setdefault("result", []) return result.copy() def dump_error_result(self, code, message): """ 异常代码 :param code: :param message: :return: """ self.result["status"] = code if not isinstance(message, str): message = repr(message) self.result["message"] = message def convert_box(self, box): """ 左上、右下转为左上、右上、右下、左下 :param box :return: """ new_box_list = [] for each_box in box: x1, y1, x2, y2 = each_box new_box_list.append([x1, y1, x2, y1, x2, y2, x1, y2]) return new_box_list def convert_code_name(self, cls_list): """Code转换 """ code_name_list = [] for cl in cls_list: code_name_list.append(self.code_name[int(cl)]) return code_name_list def merge_contours(self, contours, img_shape, padding=0): """ 合并多个轮廓的边界框 参数: contours: 轮廓列表 img_shape: 图像形状(H,W) padding: 在合并后的边界框周围添加的额外像素 返回: 合并后的边界框坐标(x, y, w, h) """ if not contours: return 0, 0, img_shape[1], img_shape[0] # 初始化边界框坐标 x_min = img_shape[1] y_min = img_shape[0] x_max = 0 y_max = 0 # 遍历所有轮廓,找到最大边界 for contour in contours: x, y, w, h = cv2.boundingRect(contour) x_min = min(x_min, x) y_min = min(y_min, y) x_max = max(x_max, x + w) y_max = max(y_max, y + h) # 添加padding x_min = max(0, x_min - padding) y_min = max(0, y_min - padding) x_max = min(img_shape[1], x_max + padding) y_max = min(img_shape[0], y_max + padding) return x_min, y_min, x_max - x_min, y_max - y_min def crop_black_borders(self, img, threshold=10, min_area=100000, padding=0): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化处理 _, thresh = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY) # 找到非黑色区域的轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤掉太小的轮廓 filtered_contours = [c for c in contours if cv2.contourArea(c) > min_area] if not filtered_contours: return img # 如果没有找到符合条件的轮廓,返回原图 x, y, w, h = self.merge_contours(filtered_contours, gray.shape, padding) return x, y, w, h def merge_result(self, xyxy_rst, conf_rst, cls_rst): xyxy_results = [] conf_results = [] cls_results = [] # 类别数组 # cls_list = cls_rst.unique() cls_list = np.unique(cls_rst) cls_num = len(cls_list) xyxy_unique = [[] for j in range(cls_num)] conf_unique = [[] for j in range(cls_num)] cls_unique = [[] for j in range(cls_num)] # 按类别分组 for c in range(cls_num): for i in range(len(cls_rst)): if cls_rst[i] == cls_list[c]: xyxy_unique[c].append(xyxy_rst[i]) conf_unique[c].append(conf_rst[i]) cls_unique[c].append(cls_rst[i]) # 按类别合并: 计算iou for i in range(cls_num): box_result, conf_result, cls_result = self.custom_nms(xyxy_unique[i], conf_unique[i], cls_unique[i], 0.15) xyxy_results.append(box_result) conf_results.append(conf_result) cls_results.append(cls_result) # 将数据展平 xyxy_results_ = [element for sublist in xyxy_results for element in sublist] conf_results_ = [element for sublist in conf_results for element in sublist] cls_results_ = [element for sublist in cls_results for element in sublist] return xyxy_results_, conf_results_, cls_results_ def augment_image(self, img): clipLimit = 10 tileGridSize = (8, 8) if len(img.shape) == 2: # 单通道灰度图像 clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=tileGridSize) enhanced_img = clahe.apply(img) enhanced_img = cv2.cvtCOLOR(enhanced_img, cv2.COLOR_GRAY2BGR) elif len(img.shape) == 3: # 三通道彩色图像 # 分离通道 b, g, r = cv2.split(img) # 对每个通道分别应用 CLAHE clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=tileGridSize) enhanced_b = clahe.apply(b) enhanced_g = clahe.apply(g) enhanced_r = clahe.apply(r) # 合并通道 enhanced_img = cv2.merge((enhanced_b, enhanced_g, enhanced_r)) return enhanced_img def bbox_merge_cls(self, xyxy_results, conf_results, cls_results): ''' 同类型code合并框,以merge_info为配置进行合并 ''' def is_overlap(box1, box2): return not (box2[0] > box1[2] or box2[2] < box1[0] or box2[1] > box1[3] or box2[3] < box1[1]) def merge_bboxes(boxes): return [min(box[0] for box in boxes), min(box[1] for box in boxes), max(box[2] for box in boxes), max(box[3] for box in boxes)] to_delete = set() merged_boxes = [] merged_confidences = [] merged_codes = [] for key, vals in BBOX_MERGE_CLS_INFO.items(): # 找到需要过滤合并的code code_sub_indices = [i for i in range(len(cls_results)) if cls_results[i] in vals] code_main_indices = [i for i in range(len(cls_results)) if cls_results[i] == key] for j in code_main_indices: inter_boxes = [xyxy_results[j]] inter_confs = [conf_results[j]] for i in code_sub_indices: if is_overlap(xyxy_results[i], xyxy_results[j]): inter_boxes.append(xyxy_results[i]) inter_confs.append(conf_results[i]) to_delete.add(i) if len(inter_boxes) > 1: merged_box = merge_bboxes(inter_boxes) merged_boxes.append(merged_box) merged_confidences.append(conf_results[j]) merged_codes.append(key) to_delete.add(j) # 准备结果列表 new_boxes = [] new_confs = [] new_codes = [] # 添加未删除的原始框 for i in range(len(xyxy_results)): if i not in to_delete: new_boxes.append(xyxy_results[i]) new_confs.append(conf_results[i]) new_codes.append(cls_results[i]) # 添加合并后的框 new_boxes.extend(merged_boxes) new_confs.extend(merged_confidences) new_codes.extend(merged_codes) return new_boxes, new_confs, new_codes def custom_nms(self, boxes, confs, cls, iou_thresh): boxes = np.array(boxes) x1 = boxes[:, 0] y1 = boxes[:, 1] x2 = boxes[:, 2] y2 = boxes[:, 3] scores = np.array(confs) areas = (y2 - y1 + 1) * (x2 - x1 + 1) keep_boxes = [] index = scores.argsort()[::-1] box_result = [] conf_result = [] cls_result = [] while len(index) > 0: i = index[0] keep_boxes.append(i) x1_overlap = np.maximum(x1[i], x1[index[1:]]) y1_overlap = np.maximum(y1[i], y1[index[1:]]) x2_overlap = np.minimum(x2[i], x2[index[1:]]) y2_overlap = np.minimum(y2[i], y2[index[1:]]) # 计算重叠部分的面积,若没有不重叠部分则面积为 0 w = np.maximum(0, x2_overlap - x1_overlap + 1) h = np.maximum(0, y2_overlap - y1_overlap + 1) overlap_area = w * h ious = overlap_area / (areas[i] + areas[index[1:]] - overlap_area) idx = np.where(ious <= iou_thresh)[0] # 合并大框 big_idx = np.where(ious > iou_thresh)[0] union_index = index[big_idx + 1] if union_index.size == 1: lt_x = np.minimum(boxes[i][0], boxes[:, 0][union_index])[0] lt_y = np.minimum(boxes[i][1], boxes[:, 1][union_index])[0] rb_x = np.maximum(boxes[i][2], boxes[:, 2][union_index])[0] rb_y = np.maximum(boxes[i][3], boxes[:, 3][union_index])[0] elif union_index.size > 1: lt_x = np.min(np.minimum(boxes[i][0], boxes[:, 0][union_index])) lt_y = np.min(np.minimum(boxes[i][1], boxes[:, 1][union_index])) rb_x = np.max(np.maximum(boxes[i][2], boxes[:, 2][union_index])) rb_y = np.max(np.maximum(boxes[i][3], boxes[:, 3][union_index])) else: lt_x = boxes[i][0] lt_y = boxes[i][1] rb_x = boxes[i][2] rb_y = boxes[i][3] box_result.append([lt_x, lt_y, rb_x, rb_y]) conf_result.append(scores[i]) cls_result.append(cls[i]) index = index[idx + 1] return box_result, conf_result, cls_result # 执行推理 def pytorch_infer(self, im): visualize = False augment = False # pred = self.model(im, augment=augment, visualize=visualize) pred = self.model.predict(im, save=False) # NMS conf_thres = 0.15 iou_thres = 0.6 max_det = 100 classes = None agnostic_nms = False # pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) return pred def calc_code_conf_threshold(self, profile): code_id = [] code_conf_thre = [] code_pr_thre = [] if len(profile) != 0: for key, code in profile.items(): if not key in self.code_name: continue code_id.append(self.code_name.index(key)) code_conf_thre.append(code["th"]) code_pr_thre.append(code["pr"]) return code_id, code_conf_thre, code_pr_thre def transform_bbox(self, bbox_resized, origin_size, resized_size, is_need_rotate): W_origin, H_origin = origin_size resized_width, resized_height = resized_size # 缩放因子 if is_need_rotate: scale_x = H_origin / resized_width scale_y = W_origin / resized_height else: scale_y = H_origin / resized_width scale_x = W_origin / resized_height # 提取resized bbox坐标 xmin_r, ymin_r, xmax_r, ymax_r = bbox_resized # 转换到旋转后的坐标系 x_rot_min = xmin_r * scale_x y_rot_min = ymin_r * scale_y x_rot_max = xmax_r * scale_x y_rot_max = ymax_r * scale_y if is_need_rotate: # 限制坐标范围 x_rot_min = max(0, min(x_rot_min, H_origin - 1)) y_rot_min = max(0, min(y_rot_min, W_origin - 1)) x_rot_max = max(0, min(x_rot_max, H_origin - 1)) y_rot_max = max(0, min(y_rot_max, W_origin - 1)) # 逆旋转到原始坐标系 x_origin_left = W_origin - 1 - y_rot_max x_origin_right = W_origin - 1 - y_rot_min ymin_origin = x_rot_min ymax_origin = x_rot_max # 确定min和max xmin_origin = min(x_origin_left, x_origin_right) xmax_origin = max(x_origin_left, x_origin_right) ymin_origin = min(ymin_origin, ymax_origin) ymax_origin = max(ymin_origin, ymax_origin) # 确保不超出原始图像范围 xmin_origin = max(0, min(xmin_origin, W_origin - 1)) xmax_origin = max(0, min(xmax_origin, W_origin - 1)) ymin_origin = max(0, min(ymin_origin, H_origin - 1)) ymax_origin = max(0, min(ymax_origin, H_origin - 1)) else: # 限制坐标范围 x_rot_min = max(0, min(x_rot_min, W_origin - 1)) y_rot_min = max(0, min(y_rot_min, H_origin - 1)) x_rot_max = max(0, min(x_rot_max, W_origin - 1)) y_rot_max = max(0, min(y_rot_max, H_origin - 1)) xmin_origin = x_rot_min xmax_origin = x_rot_max ymin_origin = y_rot_min ymax_origin = y_rot_max return (int(xmin_origin), int(ymin_origin), int(xmax_origin), int(ymax_origin)) def infer_cv_detect(self, origin_image): ''' 检测黑框等CV直接可检出缺陷 ''' return self.code_identifier.identify_black_rect_as_OT(origin_image) def temp_filter_code(self, img_path, pred_cls): """临时过滤,如果有对应code则不输出 """ for k, temp_code_list in TEMP_FILTER_DICT.items(): if os.path.basename(img_path).startswith(k): for temp_code in temp_code_list: temp_idx = self.get_code_idx(temp_code) if temp_idx == pred_cls: return True return False def infer_od(self, origin_image, origin_image_before_rot, source, code_id, code_conf_thre, code_pr_thre, Image_Width, Image_Height, is_need_rotate, target_size=(1280, 1280)): """目标检测推理 传入是resize +(可能旋转)的图像,所以需要传入原始长宽 可能多个处理均需要旋转,所以传入旋转后的图,只在外部旋转一次 """ # Image_Width = origin_image.shape[1] # Image_Height = origin_image.shape[0] xyxy_results = [] conf_results = [] cls_results = [] # 图像 image = origin_image.copy() infer_full = True if infer_full: # Convert image = cv2.resize(image, target_size, interpolation=cv2.INTER_LINEAR) img = image.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB img = np.ascontiguousarray(img) im = torch.from_numpy(img).to(self.device) im = im.half() if self.half else im.float() # uint8 to fp16/32 im /= 255 # 0 - 255 to 0.0 - 1.0 if len(im.shape) == 3: im = im[None] # expand for batch dim # 执行推理 pred = self.pytorch_infer(im) for i, det in enumerate(pred): for *xyxy, conf, cls in zip(det.boxes.xyxy, det.boxes.conf, det.boxes.cls): temp_xyxy = [] # 判断检出的code是否满足置信度阈值,以及是否过滤项 if len(code_id) > 0: clsID_pred = int(cls.cpu().detach()) # 过滤特定产品code pr_threshold = code_pr_thre[code_id.index(clsID_pred)] # 设置为10000的为不需要检出的code,通常是用于抑制过检的OK Code if pr_threshold == 10000: continue if TEMP_FILTER_ENABLE and self.temp_filter_code(source, clsID_pred): continue conf_pred = float(conf.cpu().detach()) conf_threshold = code_conf_thre[code_id.index(clsID_pred)] if conf_pred < conf_threshold: continue else: conf_pred = float(conf.cpu().detach()) clsID_pred = int(cls.cpu().detach()) for _xyxy in xyxy: temp_xyxy = [int(ts.item()) for ts in _xyxy.cpu().detach()] # temp_xyxy.append(int(_xyxy.cpu().detach())) # 图像转换回bbox cur_xyxy = self.transform_bbox(temp_xyxy, (Image_Width, Image_Height), target_size, is_need_rotate) xyxy_results.append(cur_xyxy) conf_results.append(round(conf_pred, 4)) cls_results.append(clsID_pred) # 合并相同code相交的情况 xyxy_results, conf_results, cls_results = self.code_identifier.merge_codes_bbox(xyxy_results, conf_results, cls_results) # 合并不同code重叠的情况 if len(xyxy_results) > 0: xyxy_results, conf_results, cls_results = self.code_identifier.merge_overlap_codes(xyxy_results, conf_results, cls_results, MERGE_DIFF_CODE_BBOX_IOU_THRESHOLD) # 更新P2/P3 code,此处的code需要提取图像,且bbox是经过transform之后的 cls_results, p2_max_bboxes = self.code_identifier.identify_P2P3(origin_image_before_rot, xyxy_results, cls_results) # exists_u3u4 = self.u3_id in cls_results or self.u4_id in cls_results # 此处的坐标已变化为原始图,所以传入的长宽也需要变换之前的 xyxy_results, conf_results, cls_results = self.code_filter.filter_edge_code(xyxy_results, conf_results, cls_results, Image_Width, Image_Height) return xyxy_results, conf_results, cls_results def infer_cut(self, origin_image, code_id, code_conf_thre, code_pr_thre): def post_processing(pred): one_xyxy, one_conf, one_cls = [], [], [] for _, det in enumerate(pred): for *xyxy, conf, cls in zip(det.boxes.xyxy, det.boxes.conf, det.boxes.cls): # 判断检出的code是否满足置信度阈值 if len(code_id) > 0: clsID_pred = int(cls.cpu().detach()) if not clsID_pred in code_id: # 没有在待处理的id中 continue pr_threshold = code_pr_thre[code_id.index(clsID_pred)] conf_pred = float(conf.cpu().detach()) conf_threshold = code_conf_thre[code_id.index(clsID_pred)] if conf_pred < conf_threshold: continue else: conf_pred = float(conf.cpu().detach()) clsID_pred = int(cls.cpu().detach()) # 写入筛选后的结果 temp_xyxy = [] temp_xyxy.append(int(xyxy[0][0].cpu().detach())) temp_xyxy.append(int(xyxy[0][1].cpu().detach())) temp_xyxy.append(int(xyxy[0][2].cpu().detach())) temp_xyxy.append(int(xyxy[0][3].cpu().detach())) # 各类边缘缺陷过滤 # if self.filter_od_edge_code(edge_code_filter, clsID_pred, temp_xyxy): # continue if round(conf_pred, 4) > self.conf_thres: one_xyxy.append(temp_xyxy) one_conf.append(round(conf_pred, 4)) one_cls.append(clsID_pred) return one_xyxy, one_conf, one_cls # 图像增强 image = self.augment_image(origin_image) model_size = self.imgsz overlap = 0.25 overlap_size = int(overlap / 2.0 * model_size) valid_lt_x, valid_lt_y, w, h = self.crop_black_borders(origin_image) valid_rb_x, valid_rb_y = valid_lt_x+w, valid_lt_y+h # 实际切图,第一张保持切图size,其他均回退overlap_size,所以除第一张外, # 实际坐标位移变化为model_size-overlap_size h_index = ceil((h - model_size) / (model_size - overlap_size)) + 1 w_index = ceil((w - model_size) / (model_size - overlap_size)) + 1 xyxy_rst = [] conf_rst = [] cls_rst = [] w_with_end_offx = w - END_CUT_OFFX for i in range(h_index): for j in range(w_index): start_y = max(int(i * model_size - overlap_size * i) + valid_lt_y, 0) start_x = max(int(j * model_size - overlap_size * j) + valid_lt_x, 0) end_x = start_x + model_size end_y = start_y + model_size # 超限位置 if end_x > valid_rb_x: over_size = end_x - w_with_end_offx end_x = w_with_end_offx start_x = start_x - over_size if end_y > valid_rb_y: over_size = end_y - h end_y = h start_y = start_y - over_size # 截图 img_tailor = image[int(start_y):int(end_y), int(start_x):int(end_x)] img = img_tailor.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB img = np.ascontiguousarray(img) im = torch.from_numpy(img).to(self.device) im = im.half() if self.half else im.float() # uint8 to fp16/32 im /= 255 # 0 - 255 to 0.0 - 1.0 if len(im.shape) == 3: im = im[None] # expand for batch dim # 执行推理 pred = self.model_s.predict(im, save=False) one_xyxy, one_conf, one_cls = post_processing(pred) one_xyxy = [[xyxy[0]+start_x, xyxy[1]+start_y, xyxy[2]+start_x, xyxy[3]+start_y] for xyxy in one_xyxy] # if self.draw_result_on_img: # img_name = str(i) + '_' + str(j) + '.jpg' # img_tailor = np.ascontiguousarray(img_tailor) # self.draw_result(img_tailor, one_xyxy, one_conf, one_cls, img_name, self.save_root+'/CUT') xyxy_rst += one_xyxy conf_rst += one_conf cls_rst += one_cls im_full = cv2.resize(image, (self.imgsz, self.imgsz), interpolation=cv2.INTER_LINEAR) im_full = im_full.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB im_full = np.ascontiguousarray(im_full) im = torch.from_numpy(im_full).to(self.device) im = im.half() if self.half else im.float() # uint8 to fp16/32 im /= 255 # 0 - 255 to 0.0 - 1.0 if len(im.shape) == 3: im = im[None] # expand for batch dim # 执行推理 pred = self.model_f.predict(im, save=False) one_xyxy, one_conf, one_cls = post_processing(pred) one_cls = [cls+len(self.model_s.names) for cls in one_cls] one_xyxy = [[xyxy[0]/self.imgsz*w, xyxy[1]/self.imgsz*h, xyxy[2]/self.imgsz*w, xyxy[3]/self.imgsz*h] for xyxy in one_xyxy] xyxy_rst += one_xyxy conf_rst += one_conf cls_rst += one_cls # 缺陷结果合并 xyxy_results, conf_results, cls_results = self.merge_result(xyxy_rst, conf_rst, cls_rst) # 合并相交的STM0/1 xyxy_results, conf_results, cls_results = self.bbox_merge_cls(xyxy_results, conf_results, cls_results) pred_sorted, pred_unique, cls_list = self.sort_by_conf(xyxy_results, conf_results, cls_results) pred_sorted[0] = self.convert_box(pred_sorted[0]) pred_sorted[2] = self.convert_code_name(pred_sorted[2]) return pred_sorted, pred_unique, cls_list, xyxy_results, conf_results, cls_results def split_get_result(self, source, origin_image, profile): """ 单张图片推理,加载图片并且执行前向传播,得到该张图片预测结果 :param source: :return: """ # 解析code阈值 code_id, code_conf_thre, code_pr_thre = self.calc_code_conf_threshold(profile) xyxy_results = [] conf_results = [] cls_results = [] # origin_image = cv2.imdecode(np.fromfile(source, dtype=np.uint8), cv2.IMREAD_COLOR) Image_Width = origin_image.shape[1] Image_Height = origin_image.shape[0] origin_image_before_rot = origin_image.copy() t1 = time.time() # 是否需要旋转处理的图像 is_need_rotate = Image_Width < Image_Height if is_need_rotate: origin_image = cv2.rotate(origin_image, cv2.ROTATE_90_COUNTERCLOCKWISE) with ThreadPoolExecutor(max_workers=2) as executor: future1 = executor.submit(self.infer_od, origin_image, origin_image_before_rot, source, code_id, code_conf_thre, code_pr_thre, Image_Width, Image_Height, is_need_rotate) od_result = future1.result() xyxy_results, conf_results, cls_results = od_result pred_sorted, pred_unique, cls_list = self.sort_by_conf(xyxy_results, conf_results, cls_results) pred_sorted[0] = self.convert_box(pred_sorted[0]) pred_sorted[2] = self.convert_code_name(pred_sorted[2]) # 是否保存 if self.save_result: # 绘制存储 if len(cls_results) == 0: concat_save_path = f"{self.save_root}/OK" else: concat_save_path = f"{self.save_root}/NG" xml_img_folder = os.path.dirname(source) img_name = os.path.basename(source) xml_img_filename = img_name if self.draw_result_on_img: self.draw_result(origin_image_before_rot, xyxy_results, conf_results, cls_results, img_name, concat_save_path) # 存储标注信息 self.dump_xml(xml_img_folder, xml_img_filename, origin_image_before_rot.shape, xyxy_results, conf_results, cls_results, concat_save_path) # 如果没有code if len(conf_results) < 1: # SD1F1为无缺陷 cls_results = ['difficult'] conf_results = ['1.0'] m_H = origin_image_before_rot.shape[0] m_W = origin_image_before_rot.shape[1] xyxy_results = [[0, 0, int(m_W), 0, int(m_W), int(m_H), 0, int(m_H)]] pred_sorted = [xyxy_results, conf_results, cls_results] pred_unique = [] cls_list = [] print("cost:", time.time() - t1) return pred_sorted, pred_unique, cls_list, Image_Width, Image_Height def get_result(self, source, origin_image, profile): """ 单张图片推理,加载图片并且执行前向传播,得到该张图片预测结果 :param source: :return: """ # 解析code阈值 code_id, code_conf_thre, code_pr_thre = self.calc_code_conf_threshold(profile) xyxy_results = [] conf_results = [] cls_results = [] # origin_image = cv2.imdecode(np.fromfile(source, dtype=np.uint8), cv2.IMREAD_COLOR) Image_Width = origin_image.shape[1] Image_Height = origin_image.shape[0] origin_image_before_rot = origin_image.copy() t1 = time.time() # 是否需要旋转处理的图像 # is_need_rotate = Image_Width < Image_Height # if is_need_rotate: # origin_image = cv2.rotate(origin_image, cv2.ROTATE_90_COUNTERCLOCKWISE) # with ThreadPoolExecutor(max_workers=8) as executor: # future1 = executor.submit(self.infer_cut, origin_image, code_id, code_conf_thre, code_pr_thre) # cut_result = future1.result() cut_result = self.infer_cut(origin_image, code_id, code_conf_thre, code_pr_thre) pred_sorted, pred_unique, cls_list, xyxy_results, conf_results, cls_results = cut_result # 如果没有code if len(conf_results) < 1: # SD1F1为无缺陷 cls_results = ['OTHER'] conf_results = [0.9999] xyxy_results = [[0, 0, Image_Width, Image_Height]] pred_sorted = [xyxy_results, conf_results, cls_results] pred_unique = [] cls_list = [] # 是否保存 if self.save_result: # 绘制存储 concat_save_path = f"{self.save_root}/RES" xml_img_folder = os.path.dirname(source) img_name = os.path.basename(source) xml_img_filename = img_name if self.draw_result_on_img: self.draw_result(origin_image_before_rot, xyxy_results, conf_results, cls_results, img_name, concat_save_path) # 存储标注信息 self.dump_xml(xml_img_folder, xml_img_filename, origin_image_before_rot.shape, xyxy_results, conf_results, cls_results, concat_save_path) print("cost:", time.time() - t1) return pred_sorted, pred_unique, cls_list, Image_Width, Image_Height def draw_result(self, image, xyxy_rst, conf_rst, cls_rst, img_name, concat_save_path): save_path = concat_save_path if not os.path.exists(save_path): os.makedirs(save_path) image_save_path = f"{save_path}/{img_name}" line_thickness = 1 annotator = Annotator(image, line_width=line_thickness, example=str(self.code_name)) for i in range(len(cls_rst)): if cls_rst[i] == 'OTHER': c = len(self.code_name) label = f'{'OTHER'} {conf_rst[i]:.2f}' else: c = int(cls_rst[i]) # integer class label = f'{self.code_name[c]} {conf_rst[i]:.2f}' annotator.box_label(xyxy_rst[i], label, color=colors(c, True)) image = annotator.result() cv2.imencode('.jpg', image)[1].tofile(image_save_path) def dump_xml(self, xml_folder, xml_filename, shape, xyxy_results, conf_results, cls_results, concat_save_path): annotation = ET.Element("annotation") # 添加子元素 folder = ET.SubElement(annotation, "folder") folder.text = xml_folder filename = ET.SubElement(annotation, "filename") filename.text = xml_filename segmented = ET.SubElement(annotation, "segmented") segmented.text = str(0) source = ET.SubElement(annotation, "source") database = ET.SubElement(source, "database") database.text = 'Unknown' size = ET.SubElement(annotation, "size") width = ET.SubElement(size, "width") width.text = str(shape[1]) height = ET.SubElement(size, "height") height.text = str(shape[0]) depth = ET.SubElement(size, "depth") depth.text = str(shape[2]) # 填充标注信息 for i in range(len(cls_results)): if cls_results[i] == 'OTHER': label = 'OTHER' else: label = self.code_name[cls_results[i]] m_name = label # m_difficult = label[-1] m_subConf = round(conf_results[i], 3) m_xmin = xyxy_results[i][0] m_ymin = xyxy_results[i][1] m_xmax = xyxy_results[i][2] m_ymax = xyxy_results[i][3] m_object = ET.SubElement(annotation, "object") name = ET.SubElement(m_object, "name") name.text = m_name pose = ET.SubElement(m_object, "pose") pose.text = 'Unspecifed' truncated = ET.SubElement(m_object, "truncated") truncated.text = str(0) difficult = ET.SubElement(m_object, "difficult") difficult.text = str(0) contrast = ET.SubElement(m_object, "contrast") contrast.text = str(0) luminance = ET.SubElement(m_object, "luminance") luminance.text = str(0) subConf = ET.SubElement(m_object, "subConf") subConf.text = str(m_subConf) bndbox = ET.SubElement(m_object, "bndbox") xmin = ET.SubElement(bndbox, "xmin") xmin.text = str(int(m_xmin)) ymin = ET.SubElement(bndbox, "ymin") ymin.text = str(int(m_ymin)) xmax = ET.SubElement(bndbox, "xmax") xmax.text = str(int(m_xmax)) ymax = ET.SubElement(bndbox, "ymax") ymax.text = str(int(m_ymax)) # 将 XML 结构保存为文件 save_path = concat_save_path if not os.path.exists(save_path): os.makedirs(save_path) xml_file = xml_filename.split('.')[0] xml_filepath = f"{save_path}/{xml_file}.xml" tree = ET.ElementTree(annotation) tree.write(xml_filepath, encoding="utf-8", xml_declaration=True) # 使用 xml.dom.minidom 格式化 XML 文件 dom = xml.dom.minidom.parse(xml_filepath) with open(xml_filepath, "w", encoding="utf-8") as f: f.write(dom.toprettyxml(indent=" ")) # 使用四个空格作为缩进 def get_online_anomaly_conf_threshold(self, profile, label): online_conf_thr = 0.1 try: if label in profile and "th" in profile[label]: online_conf_thr = profile[label]["th"] except Exception as e: print(f"Get anomaly conf threshold error:{e}") pass return online_conf_thr def sort_by_priority(self, profile, pred_unique, cls_list): # 判断优先级文档是否存在\内容是否不全 if len(profile) == 0: box_result = [[0, 0, 20, 0, 20, 20, 0, 20]] conf_result = [1.0] cls_result = ['ISSUE'] return box_result, conf_result, cls_result xyxy_unique = pred_unique[0] conf_unique = pred_unique[1] cls_unique = pred_unique[2] # 将code_list转换为code_name_list if not isinstance(cls_list[0], str): code_name_list = self.convert_code_name(cls_list) # 根据优先级,返回优先级最高的code box_result = [] conf_result = [] cls_result = [] for key, value in profile.items(): if key not in code_name_list: continue index = code_name_list.index(key) temp_box = [xyxy_unique[index][0]] if len(temp_box[0]) == 4: box_result = self.convert_box(temp_box) else: box_result = temp_box[0] conf_result.append(conf_unique[index][0]) cls_result.append(key) break return box_result, conf_result, cls_result def sort_by_conf(self, xyxy_results, conf_results, cls_results): ''' 1、先计算code_list 2、再对每一类code按照conf排序 3、返回排序后的结果和code_list ''' # 类别数组 # cls_list = cls_rst.unique() cls_list = np.unique(cls_results) cls_num = len(cls_list) xyxy_unique = [[] for j in range(cls_num)] conf_unique = [[] for j in range(cls_num)] cls_unique = [[] for j in range(cls_num)] # 按类别分组 for c in range(cls_num): temp_xyxy = [] temp_conf = [] temp_cls = [] for i in range(len(cls_results)): if cls_results[i] == cls_list[c]: temp_xyxy.append(xyxy_results[i]) temp_conf.append(conf_results[i]) temp_cls.append(cls_results[i]) sorted_list = sorted(temp_conf, reverse=True) indexes = [temp_conf.index(x) for x in sorted_list] for index in indexes: xyxy_unique[c].append(temp_xyxy[index]) conf_unique[c].append(temp_conf[index]) cls_unique[c].append(float(temp_cls[index])) # 也可以将unique中的元素展平 xyxy_results_ = [element for sublist in xyxy_unique for element in sublist] conf_results_ = [element for sublist in conf_unique for element in sublist] cls_results_ = [element for sublist in cls_unique for element in sublist] return [xyxy_results_, conf_results_, cls_results_], [xyxy_unique, conf_unique, cls_unique], cls_list.tolist() def get_final_result(self, pred_result): """ 计算该张图片的最终结果 (接口测试阶段,默认取第一个结果为最终结果) :param pred_result: :return: """ box_list, conf_list, code_list = pred_result final_result_dic = {} final_result_dic.setdefault("img_cls", [code_list[0]]) final_result_dic.setdefault("img_box", [box_list[0]]) final_result_dic.setdefault("img_score", [conf_list[0]]) return final_result_dic def get_group_final(self, pred_result, gid, savepath): """ 计算该批次任务的最终结果 (接口测试阶段,默认取最后一张图片的第一个结果作为最终结果) :param pred_result: :param gid: :param savepath: :return: """ box_list, conf_list, code_list = pred_result group_final_dic = {} group_final_dic.setdefault("img_cls", [code_list[0]]) if len(box_list[0]) == 1: group_final_dic.setdefault("img_box", box_list[0]) else: group_final_dic.setdefault("img_box", [box_list[0]]) group_final_dic.setdefault("img_score", [conf_list[0]]) group_final_dic.setdefault("gid", gid) group_final_dic.setdefault("defect", len(code_list)) group_final_dic.setdefault("type", "Final") group_final_dic.setdefault("savepath", savepath) return group_final_dic def check_files(self, img_json, retry_times=1): """ 执行推理任务前检查必要的文件是否存在 :param img_json: :return: """ cur_file_path = "" while retry_times >= 0: try: img_info_list = img_json["image"] # 实际任务单次只会有一张,所以可以读图后返回 for each in img_info_list: img_path = each["path"] cur_file_path = img_path raw_img = cv2.imdecode(np.fromfile(img_path, dtype=np.uint8), cv2.IMREAD_COLOR) return raw_img except Exception as e: retry_times -= 1 stack_trace = traceback.format_exc() file_size = calc_file_size(cur_file_path) # 读图失败优先重试,达到重试次数再报错 if retry_times >= 0: logger.info( f"==SD1 Algo==: Image read error, Retry!!:{cur_file_path}, size:{file_size}, {stack_trace}") time.sleep(RETRY_CHECK_SLEEP_TIME) else: self.dump_error_result(610, 'Image read error') logger.error(f"==SD1 Algo==: Image read error:{stack_trace}") logger.info(f"==SD1 Algo==: {cur_file_path}: size:{file_size}") return [] def check_json(self, profile_preset_path): """ 执行推理任务前检查必要的文件是否存在 :param img_json: :return: """ try: with open(profile_preset_path, 'r', encoding='utf-8') as file: profile_preset = json.load(file) except: profile_preset = {} self.dump_error_result(911, 'Lack of profile_preset.jsom') return profile_preset def parse_code_priority(self, img_json, profile_preset): ''' 如果csv完整,就使用csv信息 如果csv信息不完整,就使用json信息补充 如果csv为空,就直接使用json配置 ''' profile = {} try: profile_path = img_json["info"]["profile_path"] with open(profile_path, 'r', encoding="utf-8") as f: reader = csv.reader(f) csv_code = [] for row in reader: if row[0] != 'DEFECT_SIZE': priority = row[1] confidence = row[2] defcet_name = row[3] # 有code名,缺失优先级、置信度,且在预置的json中,则使用预置的值 if priority == '': # csv优先级为空,使用默认值,如果默认值没有,则设置为666 if len(profile_preset) > 0 and defcet_name in profile_preset.keys(): priority = profile_preset[defcet_name]["PRIORITY"] else: priority = 666 if confidence == '': if len(profile_preset) > 0 and defcet_name in profile_preset.keys(): confidence = profile_preset[defcet_name]["CONFIDENCE"] else: confidence = 0.2 profile[defcet_name] = {} profile[defcet_name].setdefault('th', float(confidence)) profile[defcet_name].setdefault('pr', int(priority)) # csv信息不完整,使用json信息填充 if len(profile) < len(profile_preset): for key, value in profile_preset.items(): if key not in profile.keys(): profile[key] = {} profile[key].setdefault('th', float(value['CONFIDENCE'])) profile[key].setdefault('pr', int(value['PRIORITY'])) elif len(profile_preset) < 0: # 如果没有json文件 if len(profile) < len(self.code_name): for c_name in self.code_name: if c_name not in profile.keys(): profile[c_name] = {} profile[c_name].setdefault('th', 0.15) profile[c_name].setdefault('pr', 666) except Exception as e: if len(profile_preset) > 0: for key, value in profile_preset.items(): profile[key] = {} profile[key].setdefault('th', float(value['CONFIDENCE'])) profile[key].setdefault('pr', int(value['PRIORITY'])) else: for c_name in self.code_name: if c_name not in profile.keys(): profile[c_name] = {} profile[c_name].setdefault('th', 0.15) profile[c_name].setdefault('pr', 1) # 按照pr等级排序 profile = dict(sorted(profile.items(), key=lambda x: x[1]['pr'])) return profile def infer(self, img_json, testing=False): """ infrence main program :param img_json: :return: reulst(有固定格式,按照接口规范返回) """ # 创建推理响应主体 self.result = self.create_result_dict() pattern_results_list = [] img_info_list = img_json['image'] self.save_root = img_json["info"]["saveROOT_PATH"] # 检查图片及配置文件是否存在 origin_image = self.check_files(img_json, RETRY_TIMES) # 检查profile_preset.json文件是否存在 profile_preset = self.check_json(self.profile_preset_path) if self.result['status'] == 200: # 读取用户配置文件 # 获取code优先级 profile = self.parse_code_priority(img_json, profile_preset) p_box_list = [] p_conf_list = [] p_cls_list = [] p_code_list = [] for each in img_info_list: img_path = each["path"] # 预测结果 # if not testing: # pred_result, pred_unique, cls_list, Image_Width, Image_Height = self.get_result(img_path, origin_image, profile) # else: # pred_result, pred_unique, cls_list, Image_Width, Image_Height = self.split_get_result(img_path, origin_image, profile) pred_result, pred_unique, cls_list, Image_Width, Image_Height = self.get_result(img_path, origin_image, profile) # 结果优先级排序 if len(cls_list) > 0: priority_result = self.sort_by_priority(profile, pred_unique, cls_list) else: priority_result = pred_result final_result = self.get_final_result(priority_result) # 单张图片的推理结果 pattern_results = {} pattern_results.setdefault("img_cls", pred_result[2]) pattern_results.setdefault("img_box", pred_result[0]) pattern_results.setdefault("img_score", pred_result[1]) if "uid" in each: pattern_results.setdefault("uid", each["uid"]) if "gid" in each: pattern_results.setdefault("gid", each["gid"]) pattern_results.setdefault("defect", len(pred_result[2])) if "type" in each: pattern_results.setdefault("type", each["type"]) pattern_results.setdefault("savepath", img_json["info"]["saveROOT_PATH"]) pattern_results.setdefault("final", final_result) # 业务特殊需求返回 ATTR = {"IMAGE_PIXEL_WIDTH": Image_Width, "IMAGE_PIXEL_HEIGHT": Image_Height} pattern_results.setdefault("ATTR", ATTR) pattern_results_list.append(pattern_results) # 用于group寻优 if len(cls_list) > 0 and priority_result[2][0] != 'ISSUE': p_box_list.append([priority_result[0]]) p_conf_list.append(priority_result[1]) p_cls_list.append(priority_result[2]) p_code_list.append(self.code_name.index(priority_result[2][0])) else: p_box_list.append([priority_result[0]]) p_conf_list.append(priority_result[1]) p_cls_list.append(priority_result[2]) p_code_list.append(-1) # 多张图最优结果中再次取最优 if len(np.unique(p_code_list)) == 1 and np.unique(p_code_list) == -1: priority_result = [p_box_list[0], p_conf_list[0], p_cls_list[0]] p_code_list = [] else: length = len(p_code_list) del_index = 0 while del_index < length: if p_code_list[del_index] == -1: del p_box_list[del_index] del p_conf_list[del_index] del p_cls_list[del_index] del p_code_list[del_index] length -= 1 else: del_index += 1 if len(p_code_list) > 0: p_pred_unique = [p_box_list, p_conf_list, p_cls_list] priority_result = self.sort_by_priority(profile, p_pred_unique, p_code_list) if 'gid' in img_json['image'][0]: group_result = self.get_group_final(priority_result, img_json['image'][0]['gid'], img_json['info']['saveROOT_PATH']) pattern_results_list.append(group_result) self.result["result"] = pattern_results_list return self.result 这段代码详细解释一下
最新发布
09-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值