301 lines
9.9 KiB
Python
301 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Created on Wed Dec 18 11:49:01 2024
|
||
|
||
@author: ym
|
||
"""
|
||
import os
|
||
import pickle
|
||
import copy
|
||
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, eventDataPath, stype="data"):
|
||
'''stype: str,
|
||
'source': 由 videos 或 images 生成的 pickle 文件
|
||
'data': 从 data 文件中读取的现场运行数据
|
||
"realtime": 全实时数据,从 data 文件中读取的现场运行数据
|
||
|
||
sourcePath:事件文件夹,事件类型包含2种:
|
||
(1) pipeline生成的 pickle 文件
|
||
(2) 直接采集的事件文件夹
|
||
'''
|
||
k, errEvents = 0, []
|
||
for evtname in os.listdir(sourcePath):
|
||
bname, ext = os.path.splitext(evtname)
|
||
source_path = os.path.join(sourcePath, evtname)
|
||
|
||
if stype=="source" and ext not in ['.pkl', '.pickle']: continue
|
||
if stype=="data" and os.path.isfile(source_path): continue
|
||
if stype=="realtime" and os.path.isfile(source_path): continue
|
||
|
||
evt = bname.split('_')
|
||
condt = len(evt)>=2 and evt[-1].isdigit() and len(evt[-1])>=10
|
||
if not condt: continue
|
||
|
||
pickpath = os.path.join(eventDataPath, f"{bname}.pickle")
|
||
if os.path.isfile(pickpath): continue
|
||
|
||
# event = ShoppingEvent(source_path, stype)
|
||
try:
|
||
event = ShoppingEvent(source_path, stype)
|
||
with open(pickpath, 'wb') as f:
|
||
pickle.dump(event, f)
|
||
print(evtname)
|
||
except Exception as e:
|
||
errEvents.append(source_path)
|
||
print(f"Error: {evtname}, {e}")
|
||
# k += 1
|
||
# if k==1:
|
||
# break
|
||
|
||
errfile = Path(eventDataPath).parent / 'error_events.txt'
|
||
with open(str(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, pattern, typee=None):
|
||
if pattern==1 or pattern==2:
|
||
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 pattern==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: 利用 process.data 中的轨迹特征,以其它方式计算相似度
|
||
'''
|
||
tpevents, fnevents, fpevents, tnevents = [], [], [], []
|
||
tpsimi, fnsimi, tnsimi, fpsimi = [], [], [], []
|
||
one2nFile, errorFile_one2n = [], []
|
||
errorFile_one2n_ = []
|
||
evts_output = []
|
||
for evtname, event in evtDicts.items():
|
||
evt_names, evt_barcodes, evt_similars, evt_types = [], [], [], []
|
||
|
||
if len(event.one2n)==0 or len(event.barcode)==0:
|
||
continue
|
||
|
||
evts_output.append(evtname)
|
||
|
||
for ndict in event.one2n:
|
||
nname = ndict["event"]
|
||
barcode = ndict["barcode"]
|
||
similar = ndict["similar"]
|
||
typee = ndict["type"].strip()
|
||
|
||
if len(barcode)==0:
|
||
continue
|
||
if typee.find(",") >=0:
|
||
typee = typee.split(",")[-1]
|
||
|
||
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:
|
||
continue
|
||
|
||
simival = simi_calc(event, o2n_evt[0], pattern, typee)
|
||
if simival==None:
|
||
continue
|
||
evt_similars.append(simival)
|
||
|
||
evt_names.append(nname)
|
||
evt_barcodes.append(barcode)
|
||
evt_types.append(typee)
|
||
|
||
# if evtname == "20250226-170321-327_6903244678377":
|
||
# print("evtname")
|
||
|
||
## 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)==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/(TP+FN+1e-6))
|
||
NPrecise.append(TN/(TN+FN+1e-6))
|
||
NRecall.append(TN/(TN+FP+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.set_xticks(np.arange(0, 1, 0.1))
|
||
ax.set_yticks(np.arange(0, 1, 0.1))
|
||
ax.grid(True, linestyle='--')
|
||
|
||
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, eventDataPath, stype="realtime") # '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=1)
|
||
|
||
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\测试视频数据以及日志\全实时测试\V12\2025-3-4_2"
|
||
resultPath = r"\\192.168.1.28\share\测试视频数据以及日志\全实时测试\testing"
|
||
|
||
eventDataPath = os.path.join(resultPath, "evtobjs_wang")
|
||
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()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|