import glob import json import os import shutil import random import cv2 import numpy as np import base64 import math import io import PIL.Image import os.path as osp import pandas as pd import shapely from shapely.geometry import box, Polygon,MultiPolygon,collection from shapely.geometry import Point import shapely from shapely import ops root_path = "../runs/ma/2023-07-01/detect/exp" mask_iou=True cls_names=["crack","hole","debonding","rarefaction"] gtss=dict() #所有图片的gt,字典的键是图片名,值是掩码类别和坐标 predss=dict() #所有图片的预测结果,字典是图片名,值是掩码类别和坐标 filenames=dict() #键值对是图片名称和它对应的完整存储路径 with open(os.path.join(root_path,"detect_for_val.txt"),"r", encoding="utf-8") as f: lines=f.readlines() for line in lines: file = line.split(" ",2)[-1].rsplit(":",1)[0] jfile = file.rsplit(".",1)[0]+".json" bsname = os.path.basename(file).rsplit(".",1)[0] #图片基本名称 #gts[parent+"+"+bsname]=[] gts_cls = [] for _ in range(len(cls_names)): gts_cls.append([]) print("file.strip(): ", file.strip()) if os.path.exists(jfile.strip()): with open(jfile.strip(), "rb") as f_json: #读取gt文件的json文件 json_dict = json.load(f_json) for shape in json_dict["shapes"]: #读取标注的每一个掩码 ps = [] # 如果是圆,就将圆转化为多边形 if len(shape["points"]) == 2: #圆有的是采用两点标注法,计算圆心和圆的半径,然后转化为Polygon # print("circle,dst_img_path:",dst_img_path) r = math.sqrt(math.pow(shape["points"][0][0] - shape["points"][1][0], 2) + math.pow( shape["points"][0][1] - shape["points"][1][1], 2)) circle = Point(shape["points"][0]).buffer(r) poly1 = Polygon(circle) else: poly1 = Polygon(shape["points"]) # print("bsname: ", bsname,parent) # if bsname=="478.521mm" and parent=="BB1B2-20170928": # print("shape: ", shape) if "crack" in shape["label"] : #只挑选自己指定的类别,设备类不做指标计算 idx = cls_names.index("crack") gts_cls[idx].append(poly1) #gts_cls按类别保存每一类的所有标注框 elif "hole" in shape["label"] : #只挑选自己指定的类别,设备类不做指标计算 idx = cls_names.index("hole") gts_cls[idx].append(poly1) #gts_cls按类别保存每一类的所有标注框 elif "debonding" in shape["label"]: # 只挑选自己指定的类别,设备类不做指标计算 idx = cls_names.index("debonding") gts_cls[idx].append(poly1) # gts_cls按类别保存每一类的所有标注框 elif "rarefaction" in shape["label"]: # 只挑选自己指定的类别,设备类不做指标计算 idx = cls_names.index("rarefaction") gts_cls[idx].append(poly1) # gts_cls按类别保存每一类的所有标注框 gtss[bsname]=gts_cls filenames[bsname]=file print("gtss: ",len(gtss),gtss) for file in glob.glob(root_path+"/labels/*"): #遍历每一个检测结果txt文件 bsname=os.path.basename(file).rsplit(".",1)[0] preds_cls = [] for _ in range(len(cls_names)): preds_cls.append([]) with open(file,"r", encoding="utf-8") as f: for line in f.readlines(): line=line.strip().split() if len(line)==0: continue cls_id=int(line[0]) if cls_id>len(cls_names): print("cls_id 超出类别索引") else: ps = line[1:] polygon_ps = [] for i in range(0, len(ps), 2): x, y = int(float(ps[i])), int(float(ps[i + 1])) polygon_ps.append([x, y ]) poly = Polygon(polygon_ps) preds_cls[cls_id].append(poly) predss[bsname]=preds_cls print("predss: ",len(predss),predss) nums=dict() tp_gt_preds=[] prs=[] for _ in range(len(cls_names)): tp_gt_pred_cls=[] pr_cls=[] for _ in range(3): tp_gt_pred_cls.append(0) #装每一类的tp、gt、pred pr_cls.append([]) pr_cls.append([]) #装每一类的p、r tp_gt_preds.append(tp_gt_pred_cls) prs.append(pr_cls) #print("tp_gt_preds: ", tp_gt_preds) for k,gts_cls in gtss.items(): #遍历每一个gt文件的真值框 #print("k:",k,gts_cls) tp_gt_pred_cls=[] #print(type(predss)) if k not in predss.keys(): for idx in range(len(cls_names)): # 按类别遍历每一个类别的真值框 tp_gt_pred = [0, 0, 0, 0, 0] gts = gts_cls[idx] gt_num = len(gts) tp_gt_pred[2] = gt_num tp_gt_pred_cls.append(tp_gt_pred) nums[k] = tp_gt_pred_cls continue preds_cls=predss[k] #print(k,gts_cls) #print(k,preds_cls) for idx in range(len(cls_names)): #按类别遍历每一个类别的真值框 tp_gt_pred = [0, 0, 0,0,0] gts,preds = gts_cls[idx],preds_cls[idx] gt_num,pred_num=len(gts),len(preds) tp_gt_pred[2]=gt_num tp_gt_pred[1]=pred_num tp_num=0 pred_match=[] for _ in range(len(preds)): pred_match.append(0) for gt in gts: #遍历每一个真值框 gt_ps = (list(gt.exterior.coords)) xs=[] ys=[] for p in gt_ps: xs.append(p[0]) ys.append(p[1]) gt_x1,gt_x2,gt_y1,gt_y2=min(xs),max(xs),min(ys),max(ys) #根据真值多边形的点坐标,计算外接矩形的左上角和右下角坐标 gt_a=(gt_x2-gt_x1)*(gt_y2-gt_y1) # print(type(union)) # ps = np.array(ps, dtype=np.float).astype(np.int) ps = ps.reshape(-1, 2) for i in range(len(preds)): #遍历对应图片的对应类的所有检测框 pred=preds[i] if pred_match[i]==1: #如果预测框和真值框匹配上了,就不再遍历该预测框 continue pred_ps = (list(pred.exterior.coords)) #预测多边形的点集 xs = [] ys = [] for p in pred_ps: xs.append(p[0]) ys.append(p[1]) pred_x1, pred_x2, pred_y1, pred_y2 = min(xs), max(xs), min(ys), max(ys) #预测多边形的最小外接矩形 pred_a = (pred_x2 - pred_x1) * (pred_y2 - pred_y1) x1=max([gt_x1,pred_x1]) x2 = min([gt_x2, pred_x2]) y1 = max([gt_y1, pred_y1]) y2 = min([gt_y2, pred_y2]) #预测框与真值框的交集矩形 if x2>x1 and y2>y1: inter_a=(x2-x1)*(y2-y1) iou=inter_a/(gt_a+pred_a-inter_a) # matched=False if "crack" in cls_names[idx]: #不同类别有不同的tp iou阈值 if iou>0.2: matched=True elif "hole" in cls_names[idx]: if iou > 0.3: matched = True elif "debonding" in cls_names[idx]: if iou > 0.4: matched = True elif "rarefaction" in cls_names[idx]: if iou>0.5: matched=True if matched: #如果预测框和真值框匹配上了,对应的标记位就置为1 pred_match[i]=1 tp_num +=1 break #只要有一个pred与gt匹配上了,tp就加1,然后退出,不再遍历后面的pred tp_gt_pred[0] = tp_num #保存当前图片的tp,并计算该图片的p和r tp_gt_pred[3] = tp_num * 1.0 / (pred_num + 1.0e-10) tp_gt_pred[4] = tp_num * 1.0 / (gt_num + 1.0e-10) tp_gt_pred_cls.append(tp_gt_pred) #print("tp_gt_preds: ", tp_gt_preds,i) tp_gt_preds[idx][0] += tp_num #将当前类的所有图片的tp、pred和gt累加起来 tp_gt_preds[idx][2] += gt_num tp_gt_preds[idx][1] += pred_num nums[k]=tp_gt_pred_cls #k是图片名,值是该图每一类的tp、pred、gt、p和r #print("k , nums[k]: ", k, nums[k]) dict_e=dict() dict_e["图片名称"]=[] for j in range(len(cls_names)): for i in ["tp","pred","gt","p","r"]: dict_e[str(j)+"_"+i]=[] #装tp、gt、pred,p,r #将所有图片的检测结果保存到字典里,准备存入excel表里,表列名就是图片名和每一类的tp、gt、pred,p,r for name,tp_gt_pred_cls in nums.items(): dict_e["图片名称"].append(name) for i in range(len(tp_gt_pred_cls)): inds=["tp","pred","gt","p","r"] for j in range(5): #tp,gt,pred,p,r dict_e[str(i)+"_"+inds[j]].append(tp_gt_pred_cls[i][j]) #print("dict_e: ", dict_e) dict_e["图片名称"].insert(0,"合计") #将所有图片的tp、gt、pred累加起来,计算总的tp、gt、pred,并在此基础上计算所有图片每一类的tp、gt、pred,p,r。 for i in range(len(cls_names)): inds = ["tp", "pred", "gt"] for j in range(3): # tp,gt,pred t =sum(dict_e[str(i) + "_" + inds[j]]) dict_e[str(i) + "_" + inds[j]].insert(0,t) tps=dict_e[str(i) + "_" + inds[0]][0] preds = dict_e[str(i) + "_" + inds[1]][0] gts = dict_e[str(i) + "_" + inds[2]][0] p_t=tps/(preds+1.0e-10) r_t=tps/(gts+1.0e-10) dict_e[str(i) + "_p" ].insert(0,p_t) dict_e[str(i) + "_r"].insert(0,r_t) #将字典写入excel表中 df = pd.DataFrame(dict_e) # 创建DataFrame files = glob.glob(os.path.dirname(__file__) + "/指标eval"+"*.xlsx") if len(files)==0: #print("ssss") max_i=1 else: max_i=0 for file in files: i = os.path.basename(file).rsplit("eval",1)[1].rsplit(".",1)[0] #print(i) max_i = max(int(i),max_i) max_i +=1 df.to_excel("指标eval" + str(max_i) + ".xlsx") # 存表,去除原始索引列(0,1,2...)