# -*- coding: utf-8 -*- """ Created on Wed Dec 18 11:49:01 2024 @author: ym """ import os import pickle import numpy as np from pathlib import Path import matplotlib.pyplot as plt from scipy.spatial.distance import cdist from utils.event import ShoppingEvent def init_eventdict(sourcePath, stype="data"): '''stype: str, 'source': 由 videos 或 images 生成的 pickle 文件 'data': 从 data 文件中读取的现场运行数据 ''' k, errEvents = 0, [] for bname in os.listdir(sourcePath): # bname = r"20241126-135911-bdf91cf9-3e9a-426d-94e8-ddf92238e175_6923555210479" source_path = os.path.join(sourcePath, bname) if stype=="data" or stype=="realtime": pickpath = os.path.join(eventDataPath, f"{bname}.pickle") if not os.path.isdir(source_path) or os.path.isfile(pickpath): continue if stype=="source": pickpath = os.path.join(eventDataPath, bname) if not os.path.isfile(source_path) or os.path.isfile(pickpath): continue evt = os.path.splitext(os.path.split(pickpath)[-1])[0].split('_') cont = len(evt)>=2 and evt[-1].isdigit() and len(evt[-1])>=10 if not cont: continue try: event = ShoppingEvent(source_path, stype) with open(pickpath, 'wb') as f: pickle.dump(event, f) print(bname) except Exception as e: errEvents.append(source_path) print(e) # k += 1 # if k==1: # break # errfile = os.path.join(resultPath, 'error_events.txt') # with open(errfile, 'a', encoding='utf-8') as f: # for line in errEvents: # f.write(line + '\n') def read_eventdict(eventDataPath): evtDict = {} for filename in os.listdir(eventDataPath): evtname, ext = os.path.splitext(filename) if ext != ".pickle": continue evtpath = os.path.join(eventDataPath, filename) with open(evtpath, 'rb') as f: evtdata = pickle.load(f) evtDict[evtname] = evtdata return evtDict def simi_calc(event, o2nevt, typee=None): if typee == "11": boxes1 = event.front_boxes boxes2 = o2nevt.front_boxes feat1 = event.front_feats feat2 = o2nevt.front_feats if typee == "10": boxes1 = event.front_boxes boxes2 = o2nevt.back_boxes feat1 = event.front_feats feat2 = o2nevt.back_feats if typee == "00": boxes1 = event.back_boxes boxes2 = o2nevt.back_boxes feat1 = event.back_feats feat2 = o2nevt.back_feats if typee == "01": boxes1 = event.back_boxes boxes2 = o2nevt.front_boxes feat1 = event.back_feats feat2 = o2nevt.front_feats '''自定义事件特征选择''' if typee==3 and len(event.feats_compose) and len(o2nevt.feats_compose): feat1 = [event.feats_compose] feat2 = [o2nevt.feats_compose] if len(feat1) and len(feat2): matrix = 1 - cdist(feat1[0], feat2[0], 'cosine') simi = np.mean(matrix) else: simi = None return simi def one2n_pr(evtDicts, pattern=1): ''' pattern: 1: process.data 中记录的相似度 2: 根据 process.data 中标记的 type 选择特征计算相似度 3: 以其它方式选择特征计算相似度 ''' tpevents, fnevents, fpevents, tnevents = [], [], [], [] tpsimi, fnsimi, tnsimi, fpsimi = [], [], [], [] one2nFile, errorFile_one2n = [], [] for evtname, event in evtDicts.items(): evt_names, evt_barcodes, evt_similars, evt_types = [], [], [], [] if len(event.barcode)==0: continue for ndict in event.one2n: nname = ndict["event"] barcode = ndict["barcode"] similar = ndict["similar"] typee = ndict["type"].strip() evt_names.append(nname) evt_barcodes.append(barcode) evt_types.append(typee) if pattern==1: evt_similars.append(similar) if pattern==2 or pattern==3: o2n_evt = [evt for name, evt in evtDicts.items() if name.find(nname[:15])==0] if len(o2n_evt)==1: o2nevt = o2n_evt[0] else: continue if pattern==2: simival = simi_calc(event, o2nevt, typee) if pattern==3: simival = simi_calc(event, o2nevt, typee=pattern) if simival==None: continue evt_similars.append(simival) ## process.data的oneTon的各项中,均不包括当前事件的barcode if event.barcode not in evt_barcodes: errorFile_one2n.append(evtname) continue else: one2nFile.append(evtname) if len(evt_names)==len(evt_barcodes) and len(evt_barcodes)==len(evt_similars) \ and len(evt_similars)==len(evt_types) and len(evt_names)>0: # maxsim = evt_similars[evt_similars.index(max(evt_similars))] maxsim = max(evt_similars) for i in range(len(evt_names)): bcd, simi = evt_barcodes[i], evt_similars[i] if bcd==event.barcode and simi==maxsim: tpsimi.append(simi) tpevents.append(evtname) elif bcd==event.barcode and simi!=maxsim: fnsimi.append(simi) fnevents.append(evtname) elif bcd!=event.barcode and simi!=maxsim: tnsimi.append(simi) tnevents.append(evtname) elif bcd!=event.barcode and simi==maxsim: fpsimi.append(simi) fpevents.append(evtname) else: errorFile_one2n.append(evtname) ''' 1:n 数据存储,需根据相似度排序''' PPrecise, PRecall = [], [] NPrecise, NRecall = [], [] Thresh = np.linspace(-0.2, 1, 100) for th in Thresh: '''============================= 1:n 计算''' TP = sum(np.array(tpsimi) >= th) FP = sum(np.array(fpsimi) >= th) FN = sum(np.array(fnsimi) < th) TN = sum(np.array(tnsimi) < th) PPrecise.append(TP/(TP+FP+1e-6)) PRecall.append(TP/(len(one2nFile)+1e-6)) NPrecise.append(TN/(TN+FN+1e-6)) NRecall.append(TN/(len(tnsimi)+len(fpsimi)+1e-6)) '''4. ============================= 1:n 曲线,''' fig, ax = plt.subplots() ax.plot(Thresh, PPrecise, 'r', label='Precise_Pos: TP/TPFP') ax.plot(Thresh, PRecall, 'b', label='Recall_Pos: TP/TPFN') ax.plot(Thresh, NPrecise, 'g', label='Precise_Neg: TN/TNFP') ax.plot(Thresh, NRecall, 'c', label='Recall_Neg: TN/TNFN') ax.set_xlim([0, 1]) ax.set_ylim([0, 1]) ax.grid(True) ax.set_title('1:n Precise & Recall') ax.set_xlabel(f"Event Num: {len(one2nFile)}") ax.legend() plt.show() ## ============================= 1:n 直方图''' fig, axes = plt.subplots(2, 2) axes[0, 0].hist(tpsimi, bins=60, range=(-0.2, 1), edgecolor='black') axes[0, 0].set_xlim([-0.2, 1]) axes[0, 0].set_title(f'TP: {len(tpsimi)}') axes[0, 1].hist(fpsimi, bins=60, range=(-0.2, 1), edgecolor='black') axes[0, 1].set_xlim([-0.2, 1]) axes[0, 1].set_title(f'FP: {len(fpsimi)}') axes[1, 0].hist(tnsimi, bins=60, range=(-0.2, 1), edgecolor='black') axes[1, 0].set_xlim([-0.2, 1]) axes[1, 0].set_title(f'TN: {len(tnsimi)}') axes[1, 1].hist(fnsimi, bins=60, range=(-0.2, 1), edgecolor='black') axes[1, 1].set_xlim([-0.2, 1]) axes[1, 1].set_title(f'FN: {len(fnsimi)}') plt.show() return fpevents def main(): '''1. 生成事件字典并保存至 eventDataPath, 只需运行一次 ''' init_eventdict(eventSourcePath, stype="source") # 'source', 'data', 'realtime' # for pfile in os.listdir(eventDataPath): # evt = os.path.splitext(pfile)[0].split('_') # cont = len(evt)>=2 and evt[-1].isdigit() and len(evt[-1])>=10 # if not cont: # continue '''2. 读取事件字典 ''' evtDicts = read_eventdict(eventDataPath) '''3. 1:n 比对事件评估 ''' fpevents = one2n_pr(evtDicts, pattern=2) fpErrFile = str(Path(resultPath).joinpath("one2n_fp_Error.txt")) with open(fpErrFile, "w") as file: for item in fpevents: file.write(item + "\n") if __name__ == '__main__': eventSourcePath = r"\\192.168.1.28\share\测试视频数据以及日志\全实时测试\result_V12\ShoppingDict_pkfile" resultPath = r"\\192.168.1.28\share\测试视频数据以及日志\全实时测试\testing" eventDataPath = os.path.join(resultPath, "evtobjs_data") if not os.path.exists(eventDataPath): os.makedirs(eventDataPath) # similPath = os.path.join(resultPath, "simidata") # if not os.path.exists(similPath): # os.makedirs(similPath) main()