# -*- 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": 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 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: 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 = [], [], [], [] errorFile_one2n = [] for evtname, event in evtDicts.items(): evt_names, evt_barcodes, evt_similars, evt_types = [], [], [], [] 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) 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 and event.barcode in evt_barcodes: 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(tpsimi)+len(fnsimi)+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(tpsimi)+len(fnsimi)}") 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('TP') 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('FP') 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('TN') 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('FN') plt.show() return fpevents def main(): '''1. 生成事件字典并保存至 eventDataPath, 只需运行一次 ''' init_eventdict(eventSourcePath, stype="source") '''2. 读取事件字典 ''' evtDicts = read_eventdict(eventDataPath) '''3. 1:n 比对事件评估 ''' fpevents = one2n_pr(evtDicts, pattern=3) 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\测试视频数据以及日志\算法全流程测试\202412\result\ShoppingDict_pkfile" resultPath = r"\\192.168.1.28\share\测试视频数据以及日志\算法全流程测试\202412\result\contrast" eventDataPath = os.path.join(resultPath, "evtobjs") similPath = os.path.join(resultPath, "simidata") if not os.path.exists(eventDataPath): os.makedirs(eventDataPath) if not os.path.exists(similPath): os.makedirs(similPath) main()