123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- 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-06-28/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)) #<class 'shapely.geometry.multipoint.MultiPoint'>
- 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.3:
- matched=True
- elif "hole" in cls_names[idx]:
- if iou > 0.2:
- 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)
- dict_g=dict()
- dict_g["图片名称"]=[]
- for j in range(len(cls_names)):
- for i in ["tp","pred","gt","p","r"]:
- dict_g[str(j)+"_"+i]=[] #装tp、gt、pred,p,r
- for i in range(2,len(dict_e["图片名称"]),):
- good=True
- for j in range(len(cls_names)):
- inds = ["tp", "pred", "gt","p","r"]
- if dict_e[str(j)+"_"+inds[2]][i]>0:
- if dict_e[str(j)+"_"+inds[3]][i]<0.5 or dict_e[str(j)+"_"+inds[4]][i]<0.4:
- good=False
- break
- else:
- if dict_e[str(j)+"_"+inds[1]][i]>0:
- good=False
- break
- if good:
- dict_g["图片名称"].append(dict_e["图片名称"][i])
- for j in range(len(cls_names)):
- inds = ["tp", "pred", "gt", "p", "r"]
- for k in range(len(inds)):
- dict_g[str(j) + "_" + inds[k]].append(dict_e[str(j) + "_" + inds[k]][i])
- dict_g["图片名称"].insert(0, "合计")
- for i in range(len(cls_names)):
- inds = ["tp", "pred", "gt"]
- for j in range(3): # tp,gt,pred
- t = sum(dict_g[str(i) + "_" + inds[j]])
- dict_g[str(i) + "_" + inds[j]].insert(0, t)
- tps = dict_g[str(i) + "_" + inds[0]][0]
- preds = dict_g[str(i) + "_" + inds[1]][0]
- gts = dict_g[str(i) + "_" + inds[2]][0]
- p_t = tps / (preds + 1.0e-10)
- r_t = tps / (gts + 1.0e-10)
- dict_g[str(i) + "_p"].insert(0, p_t)
- dict_g[str(i) + "_r"].insert(0, r_t)
- dst_file= "../eval/eval.txt"
- with open(dst_file, "w",encoding="utf-8") as f:
- for i in range(2, len(dict_g["图片名称"])):
- f.writelines(filenames[dict_g["图片名称"][i]]+"\n")
|