421 lines
13 KiB
Python
421 lines
13 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Created on Tue Apr 8 10:07:17 2025
|
||
|
||
@author: wqg
|
||
"""
|
||
|
||
import csv
|
||
import os
|
||
import platform
|
||
import sys
|
||
import pickle
|
||
import cv2
|
||
import numpy as np
|
||
from pathlib import Path
|
||
import matplotlib.pyplot as plt
|
||
import matplotlib.pyplot as plt
|
||
from typing import List, Tuple
|
||
from scipy.spatial.distance import cdist
|
||
from scipy.spatial import ConvexHull
|
||
from shapely.geometry import Point, Polygon
|
||
|
||
##################################################### for method: run_yrt()
|
||
FILE = Path(__file__).resolve()
|
||
ROOT = FILE.parents[1]
|
||
if str(ROOT) not in sys.path:
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from track_reid import yolov10_resnet_tracker
|
||
from event_time_specify import devide_motion_state
|
||
|
||
|
||
def cross(o: Tuple[float, float], a: Tuple[float, float], b: Tuple[float, float]) -> float:
|
||
""" 计算向量 OA × OB 的叉积 """
|
||
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
|
||
|
||
def compute_convex_hull(points: List[Tuple[float, float]]) -> List[Tuple[float, float]]:
|
||
""" 使用 Andrew's Monotone Chain 算法求二维点集的凸包 """
|
||
points = sorted(set(points)) # 排序并去重
|
||
if len(points) <= 1:
|
||
return points
|
||
|
||
lower = []
|
||
for p in points:
|
||
while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
|
||
lower.pop()
|
||
lower.append(p)
|
||
|
||
upper = []
|
||
for p in reversed(points):
|
||
while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
|
||
upper.pop()
|
||
upper.append(p)
|
||
|
||
# 去掉重复的连接点
|
||
return lower[:-1] + upper[:-1]
|
||
|
||
def is_point_in_convex_hull(point: Tuple[float, float], hull: List[Tuple[float, float]]) -> bool:
|
||
""" 判断一个点是否在凸包(含边界)内 """
|
||
n = len(hull)
|
||
if n < 3:
|
||
# 对于点或线段,直接判断是否共线或在线段上
|
||
if n == 1:
|
||
return point == hull[0]
|
||
if n == 2:
|
||
a, b = hull
|
||
return abs(cross(a, b, point)) < 1e-10 and min(a[0], b[0]) <= point[0] <= max(a[0], b[0]) and min(a[1], b[1]) <= point[1] <= max(a[1], b[1])
|
||
return False
|
||
|
||
for i in range(n):
|
||
a = hull[i]
|
||
b = hull[(i + 1) % n]
|
||
if cross(a, b, point) < -1e-10: # 必须全部在左边或边上
|
||
return False
|
||
return True
|
||
|
||
|
||
|
||
def plot_convex_hull(points: List[Tuple[float, float]], hull: List[Tuple[float, float]], test_points: List[Tuple[float, float]] = None):
|
||
x_all, y_all = zip(*points)
|
||
fig, ax = plt.subplots()
|
||
|
||
ax.set_xlim(0, 1024)
|
||
ax.set_ylim(1280, 0)
|
||
|
||
ax.plot(x_all, y_all, 'o', label='Points')
|
||
|
||
# 凸包闭环线
|
||
hull_loop = hull + [hull[0]]
|
||
hx, hy = zip(*hull_loop)
|
||
ax.plot(hx, hy, 'r-', linewidth=2, label='Convex Hull')
|
||
|
||
# 如果有测试点
|
||
if test_points:
|
||
for pt in test_points:
|
||
color = 'green' if is_point_in_convex_hull(pt, hull) else 'black'
|
||
ax.plot(pt[0], pt[1], 's', color=color, markersize=8)
|
||
ax.text(pt[0] + 0.05, pt[1], f'{pt}', fontsize=9)
|
||
|
||
ax.legend()
|
||
ax.grid(True)
|
||
plt.title("Convex Hull Visualization")
|
||
plt.show()
|
||
|
||
|
||
def convex_scipy():
|
||
points = np.array([
|
||
[0, 0],
|
||
[2, 0],
|
||
[1, 1],
|
||
[2, 2],
|
||
[0, 2],
|
||
[1, 0.5]])
|
||
hull = ConvexHull(points)
|
||
|
||
# 凸包顶点的索引
|
||
print("凸包顶点索引:{}".format(hull.vertices))
|
||
print("凸包顶点坐标:")
|
||
for i in hull.vertices:
|
||
print(points[i])
|
||
|
||
|
||
# 将凸包坐标构造成 Polygon
|
||
hull_points = points[hull.vertices]
|
||
polygon = Polygon(hull_points)
|
||
|
||
# 判断一个点是否在凸包内
|
||
p = Point(1, 1) # 示例点
|
||
print("是否在凸包内:", polygon.contains(p)) # True or False
|
||
|
||
|
||
def test_convex():
|
||
# 测试数据
|
||
sample_points = [(0, 0), (1, 1), (2, 2), (2, 0), (0, 2), (1, 0.5)]
|
||
convex_hull = compute_convex_hull(sample_points)
|
||
|
||
# 测试点在凸包内
|
||
test_point_inside = (1, 1)
|
||
test_point_outside = (3, 3)
|
||
test_point_on_edge = (1, 0)
|
||
|
||
inside = is_point_in_convex_hull(test_point_inside, convex_hull)
|
||
outside = is_point_in_convex_hull(test_point_outside, convex_hull)
|
||
on_edge = is_point_in_convex_hull(test_point_on_edge, convex_hull)
|
||
|
||
convex_hull, inside, outside, on_edge
|
||
|
||
# 展示图像
|
||
plot_convex_hull(sample_points, convex_hull, [test_point_inside, test_point_outside, test_point_on_edge])
|
||
|
||
def array2frame(tboxes):
|
||
"tboxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]"
|
||
idx = np.where(tboxes[:, 6] != 0)[0]
|
||
bboxes = tboxes[idx, :]
|
||
frameID = np.sort(np.unique(bboxes[:, 7].astype(int)))
|
||
fboxes = []
|
||
for fid in frameID:
|
||
idx = np.where(bboxes[:, 7] == fid)[0]
|
||
box = bboxes[idx, :]
|
||
fboxes.append(box)
|
||
return fboxes
|
||
|
||
def convex_based(tboxes, width, TH=40):
|
||
fboxes = array2frame(tboxes)
|
||
fnum = len(fboxes)
|
||
|
||
fids = np.array([i+1 for i in range(fnum)])[:, np.newaxis]
|
||
state = np.zeros((fnum, 1), dtype=np.int64)
|
||
frameState = np.concatenate((fids, state), axis = 1).astype(np.int64)
|
||
if fnum < width:
|
||
return frameState
|
||
|
||
for idx1 in range(width, fnum+1):
|
||
idx0 = idx1 - width
|
||
idx = idx1 - width//2 - 1
|
||
|
||
iboxes = fboxes[:idx]
|
||
cboxes = fboxes[idx][:, 0:4]
|
||
|
||
cur_xy = np.zeros((len(cboxes), 2))
|
||
cur_xy[:, 0] = (fboxes[idx][:, 0]+fboxes[idx][:, 2])/2
|
||
cur_xy[:, 1] = (fboxes[idx][:, 1]+fboxes[idx][:, 3])/2
|
||
for i in range(width//2):
|
||
x1, y1, x2, y2 = iboxes[i][:, 0], iboxes[i][:, 1], iboxes[i][:, 2], iboxes[i][:, 3]
|
||
|
||
boxes = np.array([(x1, y1), (x1, y2), (x2, y1), (x2, y2)]).transpose(0, 2, 1).reshape(-1, 2)
|
||
box1 = [(x, y) for x, y in boxes]
|
||
convex_hull = compute_convex_hull(box1)
|
||
|
||
for pt in cur_xy:
|
||
inside = is_point_in_convex_hull(pt, convex_hull)
|
||
|
||
if not inside:
|
||
break
|
||
if not inside:
|
||
break
|
||
|
||
# Based on the distance between the four corners of the current frame boxes
|
||
# and adjacent frame boxes
|
||
iboxes = fboxes[idx0:idx] + fboxes[idx+1:idx1]
|
||
cboxes = fboxes[idx][:, 0:4]
|
||
cx1, cy1, cx2, cy2 = cboxes[:, 0], cboxes[:, 1], cboxes[:, 2], cboxes[:, 3]
|
||
cxy = np.array([(cx1, cy1), (cx1, cy2), (cx2, cy1), (cx2, cy2)]).transpose(0, 2, 1).reshape(-1, 2)
|
||
|
||
iiboxes = np.concatenate(iboxes, axis=0)
|
||
ix1, iy1, ix2, iy2 = iiboxes[:, 0], iiboxes[:, 1], iiboxes[:, 2], iiboxes[:, 3]
|
||
ixy = np.array([(ix1, iy1), (ix1, iy2), (ix2, iy1), (ix2, iy2)]).transpose(0, 2, 1).reshape(-1, 2)
|
||
|
||
Dist = cdist(cxy, ixy).round(2)
|
||
max_dist = np.max(np.min(Dist, axis=1))
|
||
if max_dist > TH and not inside:
|
||
frameState[idx, 1] = 1
|
||
# plot_convex_hull(boxes, convex_hull, [pt])
|
||
frameState[idx, 1] = 1
|
||
|
||
return frameState
|
||
|
||
|
||
def single_point(tboxes, width, TH=60):
|
||
"""width: window width, >=2"""
|
||
|
||
|
||
fboxes = array2frame(tboxes)
|
||
fnum = len(fboxes)
|
||
|
||
fids = np.array([i+1 for i in range(fnum)])[:, np.newaxis]
|
||
state = np.zeros((fnum, 1), dtype=np.int64)
|
||
frameState = np.concatenate((fids, state), axis = 1).astype(np.int64)
|
||
|
||
|
||
if fnum < width:
|
||
return frameState
|
||
|
||
for idx1 in range(width, fnum+1):
|
||
idx0 = idx1 - width
|
||
idx = idx1 - width//2 - 1
|
||
|
||
iboxe1 = fboxes[idx0:idx]
|
||
iboxe2 = fboxes[idx+1:idx1]
|
||
iboxes = fboxes[idx0:idx] + fboxes[idx+1:idx1]
|
||
|
||
cboxes = fboxes[idx][:, 0:4]
|
||
cur_xy = np.zeros((len(cboxes), 2))
|
||
cur_xy[:, 0] = (fboxes[idx][:, 0]+fboxes[idx][:, 2])/2
|
||
cur_xy[:, 1] = (fboxes[idx][:, 1]+fboxes[idx][:, 3])/2
|
||
Dist = np.empty((len(cboxes), 0))
|
||
for i in range(width-1):
|
||
boxes = iboxes[i][:, 0:4]
|
||
|
||
box_xy = np.zeros((len(boxes), 2))
|
||
box_xy[:, 0] = (boxes[:, 0]+boxes[:, 2])/2
|
||
box_xy[:, 1] = (boxes[:, 1]+boxes[:, 3])/2
|
||
dist2 = cdist(cur_xy, box_xy).round(2)
|
||
|
||
Dist = np.concatenate((Dist, dist2), axis=1)
|
||
|
||
max_dist = np.max(np.min(Dist, axis=1))
|
||
|
||
if max_dist > TH:
|
||
frameState[idx, 1] = 1
|
||
|
||
return frameState
|
||
|
||
|
||
|
||
def intrude():
|
||
pkpath = Path("/home/wqg/dataset/small-goods/pkfiles")
|
||
savepath = Path("/home/wqg/dataset/small-goods/illustration_convex")
|
||
|
||
if not savepath.exists():
|
||
savepath.mkdir(parents=True, exist_ok=True)
|
||
|
||
err_trail, err_single, err_all = [], [], []
|
||
num = 0
|
||
for pth in pkpath.iterdir():
|
||
# item = r"69042386_20250407-145737_front_returnGood_b82d28427666_15_17700000001.pickle"
|
||
# pth = pkpath/item
|
||
|
||
with open(str(pth), 'rb') as f:
|
||
yrt = pickle.load(f)
|
||
|
||
evtname = pth.stem
|
||
|
||
bboxes = []
|
||
trackerboxes = np.empty((0, 10), dtype=np.float64)
|
||
for frameDict in yrt:
|
||
boxes = frameDict["bboxes"]
|
||
tboxes = frameDict["tboxes"]
|
||
tboxes = np.concatenate((tboxes, tboxes[:,7][:, None]), axis=1)
|
||
|
||
bboxes.append(boxes)
|
||
|
||
trackerboxes = np.concatenate((trackerboxes, np.array(tboxes)), axis=0)
|
||
|
||
'''single-points based for intrusion detection'''
|
||
# wd =5
|
||
# fstate1 = single_point(trackerboxes, wd)
|
||
|
||
'''convex-based '''
|
||
width = 5
|
||
fstate = convex_based(trackerboxes, width, TH=60)
|
||
|
||
# fstate = np.zeros(fstate1.shape)
|
||
# fstate[:, 0] = fstate1[:, 0]
|
||
# fstate[:, 1] = fstate1[:, 1] * fstate2[:, 1]
|
||
|
||
'''trajectory based for intrusion detection
|
||
period: 0 1 2 3
|
||
fid timestamp(fid) 基于滑动窗的tid扩展 滑动窗覆盖的运动区间
|
||
'''
|
||
win_width = 12
|
||
period, handState = devide_motion_state(trackerboxes, win_width)
|
||
|
||
num += 1
|
||
if np.all(period[:,2:4]==0):
|
||
err_trail.append(evtname)
|
||
if np.all(fstate[:,1]==0):
|
||
err_single.append(evtname)
|
||
if np.all(period[:,2:4]==0) and np.all(fstate[:,1]==0):
|
||
err_all.append(evtname)
|
||
|
||
fig, (ax1, ax2) = plt.subplots(2, 1)
|
||
ax1.plot(period[:, 1], period[:, 2], 'bo-', linewidth=1, markersize=4)
|
||
ax1.plot(period[:, 1], period[:, 3], 'rx-', linewidth=1, markersize=8)
|
||
|
||
ax2.plot(fstate[:, 0], fstate[:, 1], 'rx-', linewidth=1, markersize=8)
|
||
plt.savefig(os.path.join(str(savepath), f"{evtname}.png"))
|
||
|
||
|
||
plt.close()
|
||
# if num==1:
|
||
# break
|
||
|
||
rate_trail = 1 - len(err_trail)/num
|
||
rate_single = 1 - len(err_single)/num
|
||
rate_all = 1 - len(err_all)/num
|
||
|
||
print(f"rate_trail: {rate_trail}")
|
||
print(f"rate_single: {rate_single}")
|
||
print(f"rate_all: {rate_all}")
|
||
|
||
txtpath = savepath.parents[0] / "error.txt"
|
||
with open(str(txtpath), "w") as f:
|
||
f.write(f"rate_trail: {rate_trail}" + "\n")
|
||
f.write(f"rate_single: {rate_single}" + "\n")
|
||
f.write(f"rate_all: {rate_all}" + "\n")
|
||
|
||
f.write("\n" + "err_trail" + "\n")
|
||
for line in err_trail:
|
||
f.write(line + "\n")
|
||
|
||
f.write("\n" + "err_single" + "\n")
|
||
for line in err_single:
|
||
f.write(line + "\n")
|
||
|
||
f.write("\n" + "err_all" + "\n")
|
||
for line in err_all:
|
||
f.write(line + "\n")
|
||
print("Done!")
|
||
|
||
|
||
|
||
|
||
def run_yrt():
|
||
datapath = Path("/home/wqg/dataset/small-goods/videos/")
|
||
savepath = Path("/home/wqg/dataset/small-goods/result/")
|
||
pkpath = Path("/home/wqg/dataset/small-goods/pkfiles/")
|
||
|
||
if not savepath.exists():
|
||
savepath.mkdir(parents=True, exist_ok=True)
|
||
if not pkpath.exists():
|
||
pkpath.mkdir(parents=True, exist_ok=True)
|
||
|
||
|
||
optdict = {}
|
||
optdict["weights"] = ROOT / 'ckpts/best_v10s_width0375_1205.pt'
|
||
optdict["is_save_img"] = False
|
||
optdict["is_save_video"] = True
|
||
|
||
k = 0
|
||
for pth in datapath.iterdir():
|
||
item = "69042386_20250407-145819_back_returnGood_b82d28427666_15_17700000001.mp4"
|
||
pth = pth.parents[0] /item
|
||
|
||
optdict["source"] = pth
|
||
optdict["save_dir"] = savepath
|
||
|
||
# try:
|
||
yrtOut = yolov10_resnet_tracker(**optdict)
|
||
|
||
pkpath_ = pkpath / f"{Path(pth).stem}.pickle"
|
||
with open(str(pkpath_), 'wb') as f:
|
||
pickle.dump(yrtOut, f)
|
||
|
||
k += 1
|
||
if k==1:
|
||
break
|
||
# except Exception as e:
|
||
# print("abc")
|
||
|
||
|
||
|
||
if __name__ == '__main__':
|
||
# run_yrt()
|
||
|
||
intrude()
|
||
|
||
# test_convex()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|