回传数据解析,兼容v5和v10
This commit is contained in:
0
tracking/dotrack/__init__.py
Normal file
0
tracking/dotrack/__init__.py
Normal file
BIN
tracking/dotrack/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks_back.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks_back.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks_back.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks_back.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks_front.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks_front.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/dotracks_front.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/dotracks_front.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/track_back.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/track_back.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/track_back.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/track_back.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/track_front.cpython-312.pyc
Normal file
BIN
tracking/dotrack/__pycache__/track_front.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/dotrack/__pycache__/track_front.cpython-39.pyc
Normal file
BIN
tracking/dotrack/__pycache__/track_front.cpython-39.pyc
Normal file
Binary file not shown.
721
tracking/dotrack/dotracks.py
Normal file
721
tracking/dotrack/dotracks.py
Normal file
@ -0,0 +1,721 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Mar 4 18:16:01 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
from pathlib import Path
|
||||
from scipy.spatial.distance import cdist
|
||||
from tracking.utils.mergetrack import track_equal_track, readDict
|
||||
curpath = Path(__file__).resolve().parents[0]
|
||||
|
||||
curpath = Path(curpath)
|
||||
parpath = curpath.parent
|
||||
|
||||
class MoveState:
|
||||
"""商品运动状态标志"""
|
||||
Static = 0
|
||||
DownWard = 1
|
||||
UpWard = 2
|
||||
FreeMove = 3
|
||||
Unknown = -1
|
||||
|
||||
def bbox_ioa(box1, box2, iou=False, eps=1e-7):
|
||||
"""
|
||||
Calculate the intersection over box2 area given box1 and box2. Boxes are in x1y1x2y2 format.
|
||||
|
||||
Args:
|
||||
box1 (np.array): A numpy array of shape (n, 4) representing n bounding boxes.
|
||||
box2 (np.array): A numpy array of shape (m, 4) representing m bounding boxes.
|
||||
iou (bool): Calculate the standard iou if True else return inter_area/box2_area.
|
||||
eps (float, optional): A small value to avoid division by zero. Defaults to 1e-7.
|
||||
|
||||
Returns:
|
||||
(np.array): A numpy array of shape (n, m) representing the intersection over box2 area.
|
||||
"""
|
||||
|
||||
# Get the coordinates of bounding boxes
|
||||
b1_x1, b1_y1, b1_x2, b1_y2 = box1.T
|
||||
b2_x1, b2_y1, b2_x2, b2_y2 = box2.T
|
||||
|
||||
# Intersection area
|
||||
inter_area = (np.minimum(b1_x2[:, None], b2_x2) - np.maximum(b1_x1[:, None], b2_x1)).clip(0) * \
|
||||
(np.minimum(b1_y2[:, None], b2_y2) - np.maximum(b1_y1[:, None], b2_y1)).clip(0)
|
||||
|
||||
# box2 area
|
||||
area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
|
||||
if iou:
|
||||
box1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
|
||||
area = area + box1_area[:, None] - inter_area
|
||||
|
||||
# Intersection over box2 area
|
||||
return inter_area / (area + eps)
|
||||
|
||||
class ShoppingCart:
|
||||
|
||||
def __init__(self, bboxes):
|
||||
self.bboxes = bboxes
|
||||
self.loadrate = self.load_rate()
|
||||
|
||||
def load_rate(self):
|
||||
bboxes = self.bboxes
|
||||
|
||||
fid = min(bboxes[:, 7])
|
||||
idx = bboxes[:, 7] == fid
|
||||
boxes = bboxes[idx]
|
||||
|
||||
temp = np.zeros(self.incart.shape, np.uint8)
|
||||
for i in range(boxes.shape[0]):
|
||||
x1, y1, x2, y2, tid = boxes[i, 0:5]
|
||||
cv2.rectangle(temp, (int(x1), int(y1)), (int(x2), int(y2)), 255, cv2.FILLED)
|
||||
|
||||
'''1. and 滤除购物车边框外的干扰'''
|
||||
loadstate = cv2.bitwise_and(self.incart, temp)
|
||||
|
||||
'''2. xor 得到购物车内内被填充的区域'''
|
||||
# loadstate = cv2.bitwise_xor(self.incart, temp1)
|
||||
|
||||
num_loadstate = cv2.countNonZero(loadstate)
|
||||
num_incart = cv2.countNonZero(self.incart)
|
||||
loadrate = num_loadstate / (num_incart+0.01)
|
||||
|
||||
# edgeline = cv2.imread("./shopcart/cart_tempt/edgeline.png", cv2.IMREAD_GRAYSCALE)
|
||||
# cv2.imwrite(f"./test/temp.png", cv2.add(temp, edgeline))
|
||||
# cv2.imwrite(f"./test/incart.png", cv2.add(self.incart, edgeline))
|
||||
# cv2.imwrite(f"./test/loadstate.png", cv2.add(loadstate, edgeline))
|
||||
|
||||
return loadrate
|
||||
|
||||
@property
|
||||
def incart(self):
|
||||
img = cv2.imread(str(parpath/'shopcart/cart_tempt/incart.png'), cv2.IMREAD_GRAYSCALE)
|
||||
ret, binary = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)
|
||||
|
||||
return binary
|
||||
|
||||
@property
|
||||
def outcart(self):
|
||||
img = cv2.imread(str(parpath/'shopcart/cart_tempt/outcart.png'), cv2.IMREAD_GRAYSCALE)
|
||||
ret, binary = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)
|
||||
|
||||
return binary
|
||||
|
||||
@property
|
||||
def cartedge(self):
|
||||
img = cv2.imread(str(parpath/'shopcart/cart_tempt/cartedge.png'), cv2.IMREAD_GRAYSCALE)
|
||||
ret, binary = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)
|
||||
|
||||
return binary
|
||||
|
||||
class Track:
|
||||
'''抽象基类,不能实例化对象'''
|
||||
def __init__(self, boxes, features=None, imgshape=(1024, 1280)):
|
||||
'''
|
||||
boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
0 1 2 3 4 5 6 7 8
|
||||
'''
|
||||
# assert len(set(boxes[:, 4].astype(int))) == 1, "For a Track, track_id more than 1"
|
||||
# assert len(set(boxes[:, 6].astype(int))) == 1, "For a Track, class number more than 1"
|
||||
|
||||
self.boxes = boxes
|
||||
self.features = features
|
||||
self.slt_boxes = self.select_boxes()
|
||||
|
||||
self.tid = int(boxes[0, 4])
|
||||
self.cls = int(boxes[0, 6])
|
||||
self.frnum = boxes.shape[0]
|
||||
|
||||
self.isCornpoint = False
|
||||
self.imgshape = imgshape
|
||||
# self.isBorder = False
|
||||
# self.state = MoveState.Unknown
|
||||
|
||||
'''轨迹开始帧、结束帧 ID'''
|
||||
# self.start_fid = int(np.min(boxes[:, 7]))
|
||||
# self.end_fid = int(np.max(boxes[:, 7]))
|
||||
|
||||
''''''
|
||||
self.Hands = []
|
||||
|
||||
self.HandsIou = []
|
||||
|
||||
self.Goods = []
|
||||
self.GoodsIou = []
|
||||
|
||||
|
||||
'''5个关键点(中心点、左上点、右上点、左下点、右下点 )坐标'''
|
||||
self.compute_cornpoints()
|
||||
|
||||
'''5个关键点轨迹特征,可以在子类中实现,降低顺序处理时的计算量
|
||||
(中心点、左上点、右上点、左下点、右下点 )轨迹特征'''
|
||||
self.compute_cornpts_feats()
|
||||
|
||||
'''应计算各个角点面积、平均面积'''
|
||||
mw, mh = np.mean(boxes[:, 2]-boxes[:, 0]), np.mean((boxes[:, 3]-boxes[:, 1]))
|
||||
self.mwh = np.mean((mw, mh))
|
||||
self.Area = mw * mh
|
||||
|
||||
'''
|
||||
最后一帧与第一帧间的位移:
|
||||
vshift: 正值为向下,负值为向上
|
||||
hshift: 负值为向购物车边框两边移动,正值为物品向中心移动
|
||||
'''
|
||||
self.vshift = self.cornpoints[-1, 1] - self.cornpoints[0, 1] # 纵向位移
|
||||
self.hshift = abs(self.cornpoints[0, 0] - self.imgshape[0]/2) - \
|
||||
abs(self.cornpoints[-1, 0] - self.imgshape[0]/2)
|
||||
|
||||
'''手部状态分析'''
|
||||
self.HAND_STATIC_THRESH = 100
|
||||
if self.cls == 0:
|
||||
self.extract_hand_features()
|
||||
|
||||
def select_boxes(self):
|
||||
|
||||
slt_boxes = []
|
||||
idx = np.argsort(self.boxes[:, 7])
|
||||
boxes = self.boxes[idx]
|
||||
features = self.features[idx]
|
||||
|
||||
for i in range(len(boxes)):
|
||||
simi = None
|
||||
box, tid, fid, bid = boxes[i, :4], int(boxes[i, 4]), int(boxes[i, 7]), int(boxes[i, 8])
|
||||
|
||||
if i == 0:
|
||||
slt_boxes.append(boxes[i, :])
|
||||
continue
|
||||
|
||||
if len(boxes)!=len(features):
|
||||
print("check!")
|
||||
continue
|
||||
|
||||
box0, tid0, fid0, bid0 = boxes[i-1, :4], int(boxes[i-1, 4]), int(boxes[i-1, 7]), int(boxes[i-1, 8])
|
||||
|
||||
# 当前 box 和轨迹上一个 box 的iou
|
||||
iou = bbox_ioa(box[None, :], box0[None, :])
|
||||
|
||||
# 当前 box 和轨迹上一个 box 的 feat similarity
|
||||
feat0 = features[i, :][None, :]
|
||||
feat1 = features[i-1, :][None, :]
|
||||
simi = 1 - np.maximum(0.0, cdist(feat0, feat1, "cosine"))[0][0]
|
||||
|
||||
if iou > 0.85 and simi>0.85:
|
||||
continue
|
||||
|
||||
slt_boxes.append(boxes[i, :])
|
||||
|
||||
|
||||
return np.array(slt_boxes)
|
||||
|
||||
|
||||
def compute_cornpoints(self):
|
||||
'''
|
||||
cornpoints 共10项,分别是个点的坐标值(x, y)
|
||||
(center, top_left, top_right, bottom_left, bottom_right)
|
||||
'''
|
||||
boxes = self.boxes
|
||||
cornpoints = np.zeros((self.frnum, 10))
|
||||
cornpoints[:,0] = (boxes[:, 0] + boxes[:, 2]) / 2
|
||||
cornpoints[:,1] = (boxes[:, 1] + boxes[:, 3]) / 2
|
||||
cornpoints[:,2], cornpoints[:,3] = boxes[:, 0], boxes[:, 1]
|
||||
cornpoints[:,4], cornpoints[:,5] = boxes[:, 2], boxes[:, 1]
|
||||
cornpoints[:,6], cornpoints[:,7] = boxes[:, 0], boxes[:, 3]
|
||||
cornpoints[:,8], cornpoints[:,9] = boxes[:, 2], boxes[:, 3]
|
||||
|
||||
self.cornpoints = cornpoints
|
||||
def compute_cornpts_feats(self):
|
||||
'''
|
||||
'''
|
||||
# print(f"TrackID: {self.tid}")
|
||||
trajectory = []
|
||||
trajlens = []
|
||||
trajdist = []
|
||||
trajrects = []
|
||||
trajrects_wh = []
|
||||
for k in range(5):
|
||||
# diff_xy2 = np.power(np.diff(self.cornpoints[:, 2*k:2*(k+1)], axis = 0), 2)
|
||||
# trajlen = np.sum(np.sqrt(np.sum(diff_xy2, axis = 1)))
|
||||
|
||||
X = self.cornpoints[:, 2*k:2*(k+1)]
|
||||
|
||||
traj = np.linalg.norm(np.diff(X, axis=0), axis=1)
|
||||
trajectory.append(traj)
|
||||
|
||||
trajlen = np.sum(traj)
|
||||
trajlens.append(trajlen)
|
||||
|
||||
ptdist = np.max(cdist(X, X))
|
||||
trajdist.append(ptdist)
|
||||
|
||||
'''最小外接矩形:
|
||||
rect[0]: 中心(x, y)
|
||||
rect[1]: (w, h)
|
||||
rect[0]: 旋转角度 (-90°, 0]
|
||||
'''
|
||||
rect = cv2.minAreaRect(X.astype(np.int64))
|
||||
rect_wh = max(rect[1])
|
||||
|
||||
|
||||
trajrects_wh.append(rect_wh)
|
||||
trajrects.append(rect)
|
||||
|
||||
self.trajectory = trajectory
|
||||
self.trajlens = trajlens
|
||||
self.trajdist = trajdist
|
||||
self.trajrects = trajrects
|
||||
self.trajrects_wh = trajrects_wh
|
||||
|
||||
|
||||
|
||||
def trajfeature(self):
|
||||
'''
|
||||
分两种情况计算轨迹特征(检测框边界不在图像边界范围内,在图像边界范围内):
|
||||
-最小长度轨迹:trajmin
|
||||
-最小轨迹长度:trajlen_min
|
||||
-最小轨迹欧氏距离:trajdist_max
|
||||
'''
|
||||
|
||||
# idx1 = self.trajlens.index(max(self.trajlens))
|
||||
idx1 = self.trajrects_wh.index(max(self.trajrects_wh))
|
||||
|
||||
trajmax = self.trajectory[idx1]
|
||||
trajlen_max = self.trajlens[idx1]
|
||||
trajdist_max = self.trajdist[idx1]
|
||||
if not self.isCornpoint:
|
||||
# idx2 = self.trajlens.index(min(self.trajlens))
|
||||
idx2 = self.trajrects_wh.index(min(self.trajrects_wh))
|
||||
|
||||
trajmin = self.trajectory[idx2]
|
||||
trajlen_min = self.trajlens[idx2]
|
||||
trajdist_min = self.trajdist[idx2]
|
||||
else:
|
||||
trajmin = self.trajectory[0]
|
||||
trajlen_min = self.trajlens[0]
|
||||
trajdist_min = self.trajdist[0]
|
||||
|
||||
|
||||
'''最小轨迹长度/最大轨迹长度,越小,代表运动幅度越小'''
|
||||
trajlen_rate = trajlen_min/(trajlen_max+0.0001)
|
||||
|
||||
'''最小轨迹欧氏距离/目标框尺度均值'''
|
||||
trajdist_rate = trajdist_min/(self.mwh+0.0001)
|
||||
|
||||
|
||||
|
||||
self.trajmin = trajmin
|
||||
self.trajmax = trajmax
|
||||
self.TrajFeat = [trajlen_min, trajlen_max,
|
||||
trajdist_min, trajdist_max,
|
||||
trajlen_rate, trajdist_rate]
|
||||
|
||||
def pt_state_fids(self, det_y, STATIC_THRESH = 8):
|
||||
'''
|
||||
前摄时,y一般选择为 box 的 y1 坐标,且需限定商品在购物车内。
|
||||
inputs:
|
||||
y:1D array,
|
||||
parameters:
|
||||
STATIC_THRESH:轨迹处于静止状态的阈值。
|
||||
outputs:
|
||||
输出为差分值小于 STATIC_THRESH 的y中元素的(start, end)索引
|
||||
ranges = [(x1, y1),
|
||||
(x1, y1),
|
||||
...]
|
||||
'''
|
||||
# print(f"The ID is: {self.tid}")
|
||||
|
||||
# det_y = np.diff(y, axis=0)
|
||||
ranges, rangex = [], []
|
||||
|
||||
static_indices = np.where(np.abs(det_y) < STATIC_THRESH)[0]
|
||||
|
||||
if len(static_indices) == 0:
|
||||
rangex.append((0, len(det_y)))
|
||||
return ranges, rangex
|
||||
|
||||
start_index = static_indices[0]
|
||||
|
||||
for i in range(1, len(static_indices)):
|
||||
if static_indices[i] != static_indices[i-1] + 1:
|
||||
ranges.append((start_index, static_indices[i-1] + 1))
|
||||
start_index = static_indices[i]
|
||||
ranges.append((start_index, static_indices[-1] + 1))
|
||||
|
||||
if len(ranges) == 0:
|
||||
rangex.append((0, len(det_y)))
|
||||
return ranges, rangex
|
||||
|
||||
idx1, idx2 = ranges[0][0], ranges[-1][1]
|
||||
|
||||
if idx1 != 0:
|
||||
rangex.append((0, idx1))
|
||||
|
||||
# 轨迹的最后阶段是运动状态
|
||||
for k in range(1, len(ranges)):
|
||||
index1 = ranges[k-1][1]
|
||||
index2 = ranges[k][0]
|
||||
rangex.append((index1, index2))
|
||||
|
||||
if idx2 != len(det_y):
|
||||
rangex.append((idx2, len(det_y)))
|
||||
|
||||
return ranges, rangex
|
||||
|
||||
def PositionState(self, camerType="back"):
|
||||
'''
|
||||
camerType: back, 后置摄像头
|
||||
front, 前置摄像头
|
||||
'''
|
||||
if camerType=="back":
|
||||
incart = cv2.imread(str(parpath/'shopcart/cart_tempt/incart.png'), cv2.IMREAD_GRAYSCALE)
|
||||
outcart = cv2.imread(str(parpath/'shopcart/cart_tempt/outcart.png'), cv2.IMREAD_GRAYSCALE)
|
||||
else:
|
||||
incart = cv2.imread(str(parpath/'shopcart/cart_tempt/incart_ftmp.png'), cv2.IMREAD_GRAYSCALE)
|
||||
outcart = cv2.imread(str(parpath/'shopcart/cart_tempt/outcart_ftmp.png'), cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
# incart = cv2.imread('./cart_tempt/incart_ftmp.png', cv2.IMREAD_GRAYSCALE)
|
||||
# outcart = cv2.imread('./cart_tempt/outcart_ftmp.png', cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
|
||||
xc, yc = self.cornpoints[:,0].clip(0,self.imgshape[0]-1).astype(np.int64), self.cornpoints[:,1].clip(0,self.imgshape[1]-1).astype(np.int64)
|
||||
x1, y1 = self.cornpoints[:,6].clip(0,self.imgshape[0]-1).astype(np.int64), self.cornpoints[:,7].clip(0,self.imgshape[1]-1).astype(np.int64)
|
||||
x2, y2 = self.cornpoints[:,8].clip(0,self.imgshape[0]-1).astype(np.int64), self.cornpoints[:,9].clip(0,self.imgshape[1]-1).astype(np.int64)
|
||||
|
||||
# print(self.tid)
|
||||
Cent_inCartnum = np.count_nonzero(incart[(yc, xc)])
|
||||
LB_inCartnum = np.count_nonzero(incart[(y1, x1)])
|
||||
RB_inCartnum = np.count_nonzero(incart[(y2, x2)])
|
||||
|
||||
Cent_outCartnum = np.count_nonzero(outcart[(yc, xc)])
|
||||
LB_outCartnum = np.count_nonzero(outcart[(y1, x1)])
|
||||
RB_outCartnum = np.count_nonzero(outcart[(y2, x2)])
|
||||
|
||||
'''Track完全在车内:左下角点、右下角点与 outcart 的交集为 0'''
|
||||
self.isWholeInCart = False
|
||||
if LB_outCartnum + RB_outCartnum == 0:
|
||||
self.isWholeInCart = True
|
||||
|
||||
'''Track完全在车外:左下角点、中心点与 incart 的交集为 0
|
||||
右下角点、中心点与 incart 的交集为 0
|
||||
'''
|
||||
self.isWholeOutCart = False
|
||||
if Cent_inCartnum + LB_inCartnum == 0 or Cent_inCartnum + RB_inCartnum == 0:
|
||||
self.isWholeOutCart = True
|
||||
|
||||
|
||||
self.Cent_isIncart = False
|
||||
self.LB_isIncart = False
|
||||
self.RB_isIncart = False
|
||||
if Cent_inCartnum: self.Cent_isIncart = True
|
||||
if LB_inCartnum: self.LB_isIncart = True
|
||||
if RB_inCartnum: self.RB_isIncart = True
|
||||
|
||||
self.posState = self.Cent_isIncart+self.LB_isIncart+self.RB_isIncart
|
||||
|
||||
|
||||
def is_freemove(self):
|
||||
# if self.tid==4:
|
||||
# print(f"track ID: {self.tid}")
|
||||
# boxes = self.boxes
|
||||
# features = self.features
|
||||
# similars = 1 - np.maximum(0.0, cdist(self.features, self.features, metric = 'cosine'))
|
||||
|
||||
box1 = self.boxes[0, :4]
|
||||
box2 = self.boxes[-1, :4]
|
||||
|
||||
''' 第1帧、最后一帧subimg的相似度 '''
|
||||
feat1 = self.features[0, :][None, :]
|
||||
feat2 = self.features[-1, :][None, :]
|
||||
similar = 1 - np.maximum(0.0, cdist(feat1, feat2, metric = 'cosine'))
|
||||
condta = similar > 0.8
|
||||
|
||||
''' 第1帧、最后一帧 boxes 四个角点间的距离 '''
|
||||
ptd = box2 - box1
|
||||
ptd1 = np.linalg.norm((ptd[0], ptd[1]))
|
||||
ptd2 = np.linalg.norm((ptd[2], ptd[1]))
|
||||
ptd3 = np.linalg.norm((ptd[0], ptd[3]))
|
||||
ptd4 = np.linalg.norm((ptd[2], ptd[3]))
|
||||
condtb = ptd1<50 and ptd2<50 and ptd3<50 and ptd4<50
|
||||
|
||||
condt = condta and condtb
|
||||
return condt
|
||||
|
||||
|
||||
def extract_hand_features(self):
|
||||
assert self.cls == 0, "The class of traj must be HAND!"
|
||||
|
||||
self.isHandStatic = False
|
||||
|
||||
x0 = (self.boxes[:, 0] + self.boxes[:, 2]) / 2
|
||||
y0 = (self.boxes[:, 1] + self.boxes[:, 3]) / 2
|
||||
|
||||
handXY = np.stack((x0, y0), axis=-1)
|
||||
# handMaxY0 = np.max(y0)
|
||||
|
||||
handCenter = np.array([(max(x0)+min(x0))/2, (max(y0)+min(y0))/2])
|
||||
|
||||
handMaxDist = np.max(np.linalg.norm(handXY - handCenter))
|
||||
|
||||
if handMaxDist < self.HAND_STATIC_THRESH:
|
||||
self.isHandStatic = True
|
||||
|
||||
return
|
||||
|
||||
|
||||
class doTracks:
|
||||
def __init__(self, bboxes, trackefeats):
|
||||
'''fundamental property
|
||||
trackefeats: dict, key 格式 "fid_bid"
|
||||
'''
|
||||
self.bboxes = bboxes
|
||||
# self.TracksDict = TracksDict
|
||||
self.frameID = np.unique(bboxes[:, 7].astype(int))
|
||||
self.trackID = np.unique(bboxes[:, 4].astype(int))
|
||||
|
||||
self.lboxes = self.array2list()
|
||||
self.lfeats = self.getfeats(trackefeats)
|
||||
|
||||
'''对 self.tracks 中的元素进行分类,将 track 归入相应列表中'''
|
||||
self.Hands = []
|
||||
self.Kids = []
|
||||
self.Static = []
|
||||
self.Residual = []
|
||||
self.Confirmed = []
|
||||
self.DownWard = [] # subset of self.Residual
|
||||
self.UpWard = [] # subset of self.Residual
|
||||
self.FreeMove = [] # subset of self.Residual
|
||||
|
||||
|
||||
|
||||
|
||||
def array2list(self):
|
||||
'''
|
||||
将 bboxes 变换为 track 列表
|
||||
bboxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
Return:
|
||||
lboxes:列表,列表中元素具有同一 track_id,x1y1x2y2 格式
|
||||
[x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
'''
|
||||
track_ids = self.bboxes[:, 4].astype(int)
|
||||
lboxes = []
|
||||
for t_id in self.trackID:
|
||||
# print(f"The ID is: {t_id}")
|
||||
idx = np.where(track_ids == t_id)[0]
|
||||
box = self.bboxes[idx, :]
|
||||
|
||||
assert len(set(box[:, 7])) == len(box), "Please check!!!"
|
||||
|
||||
lboxes.append(box)
|
||||
|
||||
return lboxes
|
||||
|
||||
def getfeats(self, trackefeats):
|
||||
lboxes = self.lboxes
|
||||
lfeats = []
|
||||
for boxes in lboxes:
|
||||
feats = []
|
||||
for i in range(boxes.shape[0]):
|
||||
fid, bid = int(boxes[i, 7]), int(boxes[i, 8])
|
||||
key = f"{int(fid)}_{int(bid)}"
|
||||
if key in trackefeats:
|
||||
feats.append(trackefeats[key])
|
||||
feats = np.asarray(feats, dtype=np.float32)
|
||||
lfeats.append(feats)
|
||||
|
||||
return lfeats
|
||||
|
||||
|
||||
|
||||
def similarity(self):
|
||||
nt = len(self.tracks)
|
||||
similar_dict = {}
|
||||
if nt >= 2:
|
||||
for i in range(nt):
|
||||
for j in range(i, nt):
|
||||
tracka = self.tracks[i]
|
||||
trackb = self.tracks[j]
|
||||
similar = self.feat_similarity(tracka, trackb)
|
||||
similar_dict.update({(tracka.tid, trackb.tid): similar})
|
||||
return similar_dict
|
||||
|
||||
|
||||
def feat_similarity(self, tracka, trackb, metric='cosine'):
|
||||
boxes_a, boxes_b = tracka.boxes, trackb.boxes
|
||||
na, nb = tracka.boxes.shape[0], trackb.boxes.shape[0]
|
||||
feata, featb = [], []
|
||||
for i in range(na):
|
||||
fid, bid = tracka.boxes[i, 7:9]
|
||||
feata.append(self.features_dict[fid][bid])
|
||||
for i in range(nb):
|
||||
fid, bid = trackb.boxes[i, 7:9]
|
||||
featb.append(self.features_dict[fid][bid])
|
||||
|
||||
feata = np.asarray(feata, dtype=np.float32)
|
||||
featb = np.asarray(featb, dtype=np.float32)
|
||||
similarity_matrix = 1-np.maximum(0.0, cdist(feata, featb, metric))
|
||||
|
||||
feata_m = np.mean(feata, axis =0)[None,:]
|
||||
featb_m = np.mean(featb, axis =0)[None,:]
|
||||
simi_ab = 1 - cdist(feata_m, featb_m, metric)
|
||||
print(f'tid {int(boxes_a[0, 4])} vs {int(boxes_b[0, 4])}: {simi_ab[0][0]}')
|
||||
|
||||
# return np.max(similarity_matrix)
|
||||
return simi_ab
|
||||
|
||||
def merge_tracks_loop(self, alist):
|
||||
na, nb = len(alist), 0
|
||||
while na!=nb:
|
||||
na = len(alist)
|
||||
alist = self.merge_tracks(alist) #func is from subclass
|
||||
nb = len(alist)
|
||||
return alist
|
||||
|
||||
def base_merge_tracks(self, Residual):
|
||||
"""
|
||||
对不同id,但可能是同一商品的目标进行归并
|
||||
"""
|
||||
mergedTracks = []
|
||||
alist = [t for t in Residual]
|
||||
while alist:
|
||||
atrack = alist[0]
|
||||
cur_list = []
|
||||
cur_list.append(atrack)
|
||||
alist.pop(0)
|
||||
|
||||
blist = [b for b in alist]
|
||||
alist = []
|
||||
for btrack in blist:
|
||||
# afids = []
|
||||
# for track in cur_list:
|
||||
# afids.extend(list(track.boxes[:, 7].astype(np.int_)))
|
||||
# bfids = btrack.boxes[:, 7].astype(np.int_)
|
||||
# interfid = set(afids).intersection(set(bfids))
|
||||
# if len(interfid):
|
||||
# print("wait!!!")
|
||||
# if track_equal_track(atrack, btrack) and len(interfid)==0:
|
||||
if track_equal_track(atrack, btrack):
|
||||
cur_list.append(btrack)
|
||||
else:
|
||||
alist.append(btrack)
|
||||
|
||||
mergedTracks.append(cur_list)
|
||||
|
||||
return mergedTracks
|
||||
|
||||
@staticmethod
|
||||
def join_tracks(tlista, tlistb):
|
||||
"""Combine two lists of stracks into a single one."""
|
||||
exists = {}
|
||||
res = []
|
||||
for t in tlista:
|
||||
exists[t.tid] = 1
|
||||
res.append(t)
|
||||
for t in tlistb:
|
||||
tid = t.tid
|
||||
if not exists.get(tid, 0):
|
||||
exists[tid] = 1
|
||||
res.append(t)
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def sub_tracks(tlista, tlistb):
|
||||
track_ids_b = {t.tid for t in tlistb}
|
||||
return [t for t in tlista if t.tid not in track_ids_b]
|
||||
|
||||
|
||||
|
||||
def array2frame(self, bboxes):
|
||||
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 isintrude(self):
|
||||
'''
|
||||
boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
0 1 2 3 4 5 6 7 8
|
||||
'''
|
||||
OverlapNum = 3
|
||||
bboxes = self.bboxes.astype(np.int64)
|
||||
fboxes = self.array2frame(bboxes)
|
||||
|
||||
incart = cv2.bitwise_not(self.incart)
|
||||
sum_incart = np.zeros(incart.shape, dtype=np.int64)
|
||||
for fid, boxes in enumerate(fboxes):
|
||||
for i in range(len(boxes)):
|
||||
x1, y1, x2, y2 = boxes[i, 0:4]
|
||||
sum_incart[y1:y2, x1:x2] += 1
|
||||
|
||||
sumincart = np.zeros(sum_incart.shape, dtype=np.uint8)
|
||||
idx255 = np.where(sum_incart >= OverlapNum)
|
||||
sumincart[idx255] = 255
|
||||
|
||||
idxnzr = np.where(sum_incart!=0)
|
||||
base = np.zeros(sum_incart.shape, dtype=np.uint8)
|
||||
base[idxnzr] = 255
|
||||
|
||||
contours_sum, _ = cv2.findContours(sumincart, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
contours_base, _ = cv2.findContours(base, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
have_existed, invasion = [], []
|
||||
for k, ct_temp in enumerate(contours_base):
|
||||
tmp1 = np.zeros(sum_incart.shape, dtype=np.uint8)
|
||||
cv2.drawContours(tmp1, [ct_temp], -1, 255, cv2.FILLED)
|
||||
|
||||
# 确定轮廓的包含关系
|
||||
for ct_sum in contours_sum:
|
||||
tmp2 = np.zeros(sum_incart.shape, dtype=np.uint8)
|
||||
cv2.drawContours(tmp2, [ct_sum], -1, 255, cv2.FILLED)
|
||||
tmp = cv2.bitwise_and(tmp1, tmp2)
|
||||
if np.count_nonzero(tmp) == np.count_nonzero(tmp2):
|
||||
have_existed.append(k)
|
||||
|
||||
inIdx = [i for i in range(len(contours_base)) if i not in have_existed]
|
||||
invasion = np.zeros(sum_incart.shape, dtype=np.uint8)
|
||||
|
||||
for i in inIdx:
|
||||
cv2.drawContours(invasion, [contours_base[i]], -1, 255, cv2.FILLED)
|
||||
cv2.imwrite("./result/intrude/invasion.png", invasion)
|
||||
|
||||
|
||||
Intrude = True if len(inIdx)>=1 else False
|
||||
print(f"is intruded: {Intrude}")
|
||||
|
||||
return Intrude
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
277
tracking/dotrack/dotracks_back.py
Normal file
277
tracking/dotrack/dotracks_back.py
Normal file
@ -0,0 +1,277 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Mar 4 18:36:31 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
import copy
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
FILE = Path(__file__).resolve()
|
||||
ROOT = FILE.parents[2] # YOLOv5 root directory
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.append(str(ROOT))
|
||||
|
||||
from tracking.utils.mergetrack import track_equal_track
|
||||
|
||||
|
||||
from scipy.spatial.distance import cdist
|
||||
|
||||
curpath = Path(__file__).resolve().parents[0]
|
||||
curpath = Path(curpath)
|
||||
parpath = curpath.parent
|
||||
|
||||
|
||||
from .dotracks import doTracks, ShoppingCart
|
||||
from .track_back import backTrack
|
||||
|
||||
|
||||
class doBackTracks(doTracks):
|
||||
def __init__(self, bboxes, trackefeats):
|
||||
|
||||
super().__init__(bboxes, trackefeats)
|
||||
|
||||
self.tracks = [backTrack(b, f) for b, f in zip(self.lboxes, self.lfeats)]
|
||||
|
||||
# self.similar_dict = self.similarity()
|
||||
# self.shopcart = ShoppingCart(bboxes)
|
||||
|
||||
self.incart = self.getincart()
|
||||
|
||||
|
||||
def getincart(self):
|
||||
img1 = cv2.imread(str(parpath/'shopcart/cart_tempt/incart.png'), cv2.IMREAD_GRAYSCALE)
|
||||
img2 = cv2.imread(str(parpath/'shopcart/cart_tempt/cartedge.png'), cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
|
||||
ret, binary1 = cv2.threshold(img1, 250, 255, cv2.THRESH_BINARY)
|
||||
ret, binary2 = cv2.threshold(img2, 250, 255, cv2.THRESH_BINARY)
|
||||
|
||||
binary = cv2.bitwise_or(binary1, binary2)
|
||||
|
||||
|
||||
return binary
|
||||
|
||||
|
||||
|
||||
def classify(self):
|
||||
'''功能:对 tracks 中元素分类 '''
|
||||
|
||||
tracks = self.tracks
|
||||
# 提取手的frame_id,并和动目标的frame_id 进行关联
|
||||
hand_tracks = [t for t in tracks if t.cls==0]
|
||||
self.Hands.extend(hand_tracks)
|
||||
|
||||
tracks = self.sub_tracks(tracks, hand_tracks)
|
||||
|
||||
# 提取小孩的track,并计算状态:left, right, incart
|
||||
kid_tracks = [t for t in tracks if t.cls==9]
|
||||
kid_states = [self.kid_state(t) for t in kid_tracks]
|
||||
self.Kids = [x for x in zip(kid_tracks, kid_states)]
|
||||
|
||||
tracks = self.sub_tracks(tracks, kid_tracks)
|
||||
|
||||
out_trcak = [t for t in tracks if t.isWholeOutCart]
|
||||
tracks = self.sub_tracks(tracks, out_trcak)
|
||||
|
||||
static_tracks = [t for t in tracks if t.frnum>1 and t.is_static()]
|
||||
self.Static.extend(static_tracks)
|
||||
|
||||
'''剔除静止目标后的 tracks'''
|
||||
tracks = self.sub_tracks(tracks, static_tracks)
|
||||
|
||||
|
||||
tracks_free = [t for t in tracks if t.frnum>1 and t.is_freemove()]
|
||||
self.FreeMove.extend(tracks_free)
|
||||
tracks = self.sub_tracks(tracks, tracks_free)
|
||||
|
||||
|
||||
|
||||
# '''购物框边界外具有运动状态的干扰目标'''
|
||||
# out_trcak = [t for t in tracks if t.is_OutTrack()]
|
||||
# tracks = self.sub_tracks(tracks, out_trcak)
|
||||
|
||||
|
||||
|
||||
'''轨迹循环归并'''
|
||||
# merged_tracks = self.merge_tracks(tracks)
|
||||
merged_tracks = self.merge_tracks_loop(tracks)
|
||||
|
||||
[self.associate_with_hand(htrack, gtrack) for htrack in hand_tracks for gtrack in tracks]
|
||||
|
||||
|
||||
tracks = [t for t in merged_tracks if t.frnum > 1]
|
||||
|
||||
self.merged_tracks = merged_tracks
|
||||
|
||||
static_tracks = [t for t in tracks if t.frnum>1 and t.is_static()]
|
||||
self.Static.extend(static_tracks)
|
||||
|
||||
tracks = self.sub_tracks(tracks, static_tracks)
|
||||
|
||||
# for gtrack in tracks:
|
||||
# for htrack in hand_tracks:
|
||||
# hand_ious = self.associate_with_hand(htrack, gtrack)
|
||||
# if len(hand_ious):
|
||||
# gtrack.Hands.append(htrack)
|
||||
# gtrack.HandsIou.append(hand_ious)
|
||||
# htrack.Goods.append((gtrack, hand_ious))
|
||||
|
||||
# for htrack in hand_tracks:
|
||||
# self.merge_based_hands(htrack)
|
||||
|
||||
self.Residual = tracks
|
||||
self.Confirmed = self.confirm_track()
|
||||
|
||||
def confirm_track(self):
|
||||
Confirmed = None
|
||||
mindist = 0
|
||||
for track in self.Residual:
|
||||
md = min(track.trajrects_wh)
|
||||
if md > mindist:
|
||||
mindist = copy.deepcopy(md)
|
||||
Confirmed = copy.deepcopy(track)
|
||||
|
||||
if Confirmed is not None:
|
||||
return [Confirmed]
|
||||
|
||||
return []
|
||||
|
||||
|
||||
# def merge_based_hands(self, htrack):
|
||||
# gtracks = htrack.Goods
|
||||
|
||||
# if len(gtracks) >= 2:
|
||||
# atrack, afious = gtracks[0]
|
||||
# btrack, bfious = gtracks[1]
|
||||
|
||||
|
||||
|
||||
|
||||
def associate_with_hand(self, htrack, gtrack):
|
||||
'''
|
||||
迁移至基类:
|
||||
手部 Track、商品 Track 建立关联的依据:
|
||||
a. 运动帧的帧索引有交集
|
||||
b. 帧索引交集部分iou均大于0
|
||||
'''
|
||||
|
||||
assert htrack.cls==0 and gtrack.cls!=0 and gtrack.cls!=9, 'Track cls is Error!'
|
||||
|
||||
hand_ious = []
|
||||
|
||||
hboxes = np.empty(shape=(0, 9), dtype = np.float64)
|
||||
gboxes = np.empty(shape=(0, 9), dtype = np.float64)
|
||||
|
||||
|
||||
# start, end 为索引值,需要 start:(end+1)
|
||||
for start, end in htrack.moving_index:
|
||||
hboxes = np.concatenate((hboxes, htrack.boxes[start:end+1, :]), axis=0)
|
||||
for start, end in gtrack.moving_index:
|
||||
gboxes = np.concatenate((gboxes, gtrack.boxes[start:end+1, :]), axis=0)
|
||||
|
||||
hfids, gfids = hboxes[:, 7], gboxes[:, 7]
|
||||
fids = sorted(set(hfids).intersection(set(gfids)))
|
||||
|
||||
|
||||
if len(fids)==0:
|
||||
return None
|
||||
|
||||
# print(f"Goods ID: {gtrack.tid}, Hand ID: {htrack.tid}")
|
||||
|
||||
for f in fids:
|
||||
h = np.where(hboxes[:,7] == f)[0][0]
|
||||
g = np.where(gboxes[:,7] == f)[0][0]
|
||||
|
||||
x11, y11, x12, y12 = hboxes[h, 0:4]
|
||||
x21, y21, x22, y22 = gboxes[g, 0:4]
|
||||
|
||||
x1, y1 = max((x11, x21)), max((y11, y21))
|
||||
x2, y2 = min((x12, x22)), min((y12, y22))
|
||||
|
||||
union = (x2 - x1).clip(0) * (y2 - y1).clip(0)
|
||||
area1 = (x12 - x11) * (y12 - y11)
|
||||
area2 = (x22 - x21) * (y22 - y21)
|
||||
|
||||
iou = union / (area1 + area2 - union + 1e-6)
|
||||
|
||||
if iou >= 0.01:
|
||||
gtrack.Hands.append((htrack.tid, f, iou))
|
||||
|
||||
|
||||
return gtrack.Hands
|
||||
|
||||
def merge_tracks(self, Residual):
|
||||
"""
|
||||
对不同id,但可能是同一商品的目标进行归并
|
||||
和 dotrack_front.py中函数相同,可以合并,可以合并至基类
|
||||
"""
|
||||
mergedTracks = self.base_merge_tracks(Residual)
|
||||
|
||||
oldtracks, newtracks = [], []
|
||||
for tracklist in mergedTracks:
|
||||
if len(tracklist) > 1:
|
||||
boxes = np.empty((0, 9), dtype=np.float32)
|
||||
feats = np.empty((0, 256), dtype=np.float32)
|
||||
for i, track in enumerate(tracklist):
|
||||
if i==0: ntid, ncls=track.boxes[0, 4], track.boxes[0, 6]
|
||||
iboxes = track.boxes.copy()
|
||||
ifeats = track.features.copy()
|
||||
|
||||
# iboxes[:, 4], iboxes[:, 6] = ntid, ncls
|
||||
|
||||
boxes = np.concatenate((boxes, iboxes), axis=0)
|
||||
feats = np.concatenate((feats, ifeats), axis=0)
|
||||
|
||||
oldtracks.append(track)
|
||||
|
||||
fid_indices = np.argsort(boxes[:, 7])
|
||||
|
||||
boxes_fid = boxes[fid_indices]
|
||||
feats_fid = feats[fid_indices]
|
||||
|
||||
|
||||
|
||||
newtracks.append(backTrack(boxes_fid, feats_fid))
|
||||
elif len(tracklist) == 1:
|
||||
oldtracks.append(tracklist[0])
|
||||
newtracks.append(tracklist[0])
|
||||
|
||||
|
||||
redu = self.sub_tracks(Residual, oldtracks)
|
||||
merged = self.join_tracks(redu, newtracks)
|
||||
|
||||
return merged
|
||||
|
||||
def kid_state(self, track):
|
||||
|
||||
left_dist = track.cornpoints[:, 2]
|
||||
right_dist = 1024 - track.cornpoints[:, 4]
|
||||
|
||||
if np.sum(left_dist<30)/track.frnum>0.8 and np.sum(right_dist>512)/track.frnum>0.7:
|
||||
kidstate = "left"
|
||||
elif np.sum(left_dist>512)/track.frnum>0.7 and np.sum(right_dist<30)/track.frnum>0.8:
|
||||
kidstate = "right"
|
||||
else:
|
||||
kidstate = "incart"
|
||||
|
||||
return kidstate
|
||||
|
||||
|
||||
def isuptrack(self, track):
|
||||
Flag = False
|
||||
|
||||
return Flag
|
||||
|
||||
def isdowntrack(self, track):
|
||||
Flag = False
|
||||
|
||||
return Flag
|
||||
|
||||
def isfreetrack(self, track):
|
||||
Flag = False
|
||||
|
||||
return Flag
|
193
tracking/dotrack/dotracks_front.py
Normal file
193
tracking/dotrack/dotracks_front.py
Normal file
@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Mar 4 18:38:20 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import cv2
|
||||
import copy
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
curpath = Path(__file__).resolve().parents[0]
|
||||
curpath = Path(curpath)
|
||||
parpath = curpath.parent
|
||||
# from tracking.utils.mergetrack import track_equal_track
|
||||
from .dotracks import doTracks
|
||||
from .track_front import frontTrack
|
||||
|
||||
class doFrontTracks(doTracks):
|
||||
def __init__(self, bboxes, frameDictList):
|
||||
super().__init__(bboxes, frameDictList)
|
||||
|
||||
# self.tracks = [frontTrack(b) for b in self.lboxes]
|
||||
self.tracks = [frontTrack(b, f) for b, f in zip(self.lboxes, self.lfeats)]
|
||||
|
||||
self.incart = self.getincart()
|
||||
|
||||
def getincart(self):
|
||||
img = cv2.imread(str(parpath/'shopcart/cart_tempt/incart_ftmp.png'), cv2.IMREAD_GRAYSCALE)
|
||||
ret, binary = cv2.threshold(img, 250, 255, cv2.THRESH_BINARY)
|
||||
|
||||
return binary
|
||||
|
||||
|
||||
def classify(self):
|
||||
'''功能:对 tracks 中元素分类 '''
|
||||
|
||||
tracks = self.tracks
|
||||
|
||||
'''提取手的 tracks'''
|
||||
hand_tracks = [t for t in tracks if t.cls==0]
|
||||
|
||||
|
||||
self.Hands.extend(hand_tracks)
|
||||
tracks = self.sub_tracks(tracks, hand_tracks)
|
||||
|
||||
|
||||
|
||||
'''提取小孩的 tracks'''
|
||||
kid_tracks = [t for t in tracks if t.cls==9]
|
||||
tracks = self.sub_tracks(tracks, kid_tracks)
|
||||
|
||||
out_trcak = [t for t in tracks if t.isWholeOutCart]
|
||||
tracks = self.sub_tracks(tracks, out_trcak)
|
||||
|
||||
'''静态 tracks'''
|
||||
static_tracks = [t for t in tracks if t.frnum>1 and t.is_static()]
|
||||
|
||||
|
||||
'''剔除静止目标后的 tracks'''
|
||||
tracks = self.sub_tracks(tracks, static_tracks)
|
||||
|
||||
tracks_free = [t for t in tracks if t.frnum>1 and t.is_freemove()]
|
||||
self.FreeMove.extend(tracks_free)
|
||||
tracks = self.sub_tracks(tracks, tracks_free)
|
||||
|
||||
# [self.associate_with_hand(htrack, gtrack) for htrack in hand_tracks for gtrack in tracks]
|
||||
'''轨迹循环归并'''
|
||||
merged_tracks = self.merge_tracks_loop(tracks)
|
||||
|
||||
[self.associate_with_hand(htrack, gtrack) for htrack in hand_tracks for gtrack in merged_tracks]
|
||||
|
||||
tracks = [t for t in merged_tracks if t.frnum > 1]
|
||||
|
||||
# for gtrack in tracks:
|
||||
# # print(f"Goods ID:{gtrack.tid}")
|
||||
# for htrack in hand_tracks:
|
||||
# hand_ious = self.associate_with_hand(htrack, gtrack)
|
||||
# if len(hand_ious):
|
||||
# gtrack.Hands.append(htrack)
|
||||
# gtrack.HandsIou.append(hand_ious)
|
||||
|
||||
'''静止 tracks 判断与剔除静止 tracks'''
|
||||
static_tracks = [t for t in tracks if t.frnum>1 and t.is_static()]
|
||||
tracks = self.sub_tracks(tracks, static_tracks)
|
||||
|
||||
freemoved_tracks = [t for t in tracks if t.is_free_move()]
|
||||
tracks = self.sub_tracks(tracks, freemoved_tracks)
|
||||
|
||||
self.Residual = tracks
|
||||
self.Confirmed = self.confirm_track()
|
||||
|
||||
def confirm_track(self):
|
||||
Confirmed = None
|
||||
mindist = 0
|
||||
for track in self.Residual:
|
||||
md = min(track.trajrects_wh)
|
||||
if md > mindist:
|
||||
mindist = copy.deepcopy(md)
|
||||
Confirmed = copy.deepcopy(track)
|
||||
|
||||
if Confirmed is not None:
|
||||
return [Confirmed]
|
||||
|
||||
return []
|
||||
|
||||
def associate_with_hand(self, htrack, gtrack):
|
||||
'''
|
||||
迁移至基类:
|
||||
手部 Track、商品 Track 建立关联的依据:
|
||||
a. 运动帧的帧索引有交集
|
||||
b. 帧索引交集部分iou均大于0
|
||||
'''
|
||||
assert htrack.cls==0 and gtrack.cls!=0 and gtrack.cls!=9, 'Track cls is Error!'
|
||||
|
||||
hboxes = np.empty(shape=(0, 9), dtype = np.float64)
|
||||
gboxes = np.empty(shape=(0, 9), dtype = np.float64)
|
||||
|
||||
# start, end 为索引值,需要 start:(end+1)
|
||||
for start, end in htrack.dynamic_y2:
|
||||
hboxes = np.concatenate((hboxes, htrack.boxes[start:end+1, :]), axis=0)
|
||||
for start, end in gtrack.dynamic_y1:
|
||||
gboxes = np.concatenate((gboxes, gtrack.boxes[start:end+1, :]), axis=0)
|
||||
|
||||
hfids, gfids = hboxes[:, 7], gboxes[:, 7]
|
||||
fids = sorted(set(hfids).intersection(set(gfids)))
|
||||
|
||||
if len(fids)==0:
|
||||
return None
|
||||
|
||||
# print(f"Goods ID: {gtrack.tid}, Hand ID: {htrack.tid}")
|
||||
for f in fids:
|
||||
h = np.where(hfids==f)[0][0]
|
||||
g = np.where(gfids==f)[0][0]
|
||||
|
||||
x11, y11, x12, y12 = hboxes[h, 0:4]
|
||||
x21, y21, x22, y22 = gboxes[g, 0:4]
|
||||
|
||||
x1, y1 = max((x11, x21)), max((y11, y21))
|
||||
x2, y2 = min((x12, x22)), min((y12, y22))
|
||||
|
||||
union = (x2 - x1).clip(0) * (y2 - y1).clip(0)
|
||||
area1 = (x12 - x11) * (y12 - y11)
|
||||
area2 = (x22 - x21) * (y22 - y21)
|
||||
|
||||
iou = union / (area1 + area2 - union + 1e-6)
|
||||
|
||||
if iou >= 0.01:
|
||||
gtrack.Hands.append((htrack.tid, f, iou))
|
||||
|
||||
return gtrack.Hands
|
||||
|
||||
|
||||
|
||||
def merge_tracks(self, Residual):
|
||||
"""
|
||||
对不同id,但可能是同一商品的目标进行归并
|
||||
和 dotrack_back.py中函数相同,可以合并至基类
|
||||
"""
|
||||
mergedTracks = self.base_merge_tracks(Residual)
|
||||
|
||||
oldtracks, newtracks = [], []
|
||||
for tracklist in mergedTracks:
|
||||
if len(tracklist) > 1:
|
||||
boxes = np.empty((0, 9), dtype=np.float32)
|
||||
feats = np.empty((0, 256), dtype=np.float32)
|
||||
for i, track in enumerate(tracklist):
|
||||
if i==0: ntid, ncls=track.boxes[0, 4], track.boxes[0, 6]
|
||||
iboxes = track.boxes.copy()
|
||||
ifeats = track.features.copy()
|
||||
|
||||
# iboxes[:, 4], iboxes[:, 6] = ntid, ncls
|
||||
|
||||
boxes = np.concatenate((boxes, iboxes), axis=0)
|
||||
feats = np.concatenate((feats, ifeats), axis=0)
|
||||
|
||||
oldtracks.append(track)
|
||||
|
||||
fid_indices = np.argsort(boxes[:, 7])
|
||||
boxes_fid = boxes[fid_indices]
|
||||
feats_fid = feats[fid_indices]
|
||||
|
||||
newtracks.append(frontTrack(boxes_fid, feats_fid))
|
||||
elif len(tracklist) == 1:
|
||||
oldtracks.append(tracklist[0])
|
||||
newtracks.append(tracklist[0])
|
||||
|
||||
|
||||
redu = self.sub_tracks(Residual, oldtracks)
|
||||
merged = self.join_tracks(redu, newtracks)
|
||||
|
||||
return merged
|
||||
|
241
tracking/dotrack/track_back.py
Normal file
241
tracking/dotrack/track_back.py
Normal file
@ -0,0 +1,241 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Mar 4 18:28:47 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import cv2
|
||||
import numpy as np
|
||||
from scipy.spatial.distance import cdist
|
||||
from sklearn.decomposition import PCA
|
||||
from .dotracks import MoveState, Track
|
||||
|
||||
from pathlib import Path
|
||||
curpath = Path(__file__).resolve().parents[0]
|
||||
curpath = Path(curpath)
|
||||
parpath = curpath.parent
|
||||
|
||||
class backTrack(Track):
|
||||
# boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
# 0, 1, 2, 3, 4, 5, 6, 7, 8
|
||||
def __init__(self, boxes, features, imgshape=(1024, 1280)):
|
||||
|
||||
super().__init__(boxes, features, imgshape)
|
||||
|
||||
'''该函数依赖项: self.cornpoints
|
||||
MarginState: list, seven elements, 表示轨迹中boxes出现在图像的
|
||||
[左上,右上,左中,右中,左下,右下底部]
|
||||
'''
|
||||
self.isCornpoint, self.MarginState = self.isimgborder()
|
||||
|
||||
'''该函数依赖项: self.isCornpoint,不能在父类中初始化'''
|
||||
self.trajfeature()
|
||||
|
||||
|
||||
'''静止点帧索引'''
|
||||
# self.static_index = self.compute_static_fids()
|
||||
|
||||
'''运动点帧索引(运动帧两端的静止帧索引)'''
|
||||
# self.moving_index = self.compute_dynamic_fids()
|
||||
|
||||
self.static_index, self.moving_index = self.compute_static_dynamic_fids()
|
||||
|
||||
'''该函数依赖项: self.cornpoints,定义 4 个商品位置变量:
|
||||
self.Cent_isIncart, self.LB_isIncart, self.RB_isIncart
|
||||
self.posState = self.Cent_isIncart+self.LB_isIncart+self.RB_isIncart'''
|
||||
self.PositionState(camerType="back")
|
||||
|
||||
'''self.feature_ious = (incart_iou, outcart_iou, cartboarder_iou, maxbox_iou, minbox_iou)
|
||||
self.incartrates = incartrates'''
|
||||
self.compute_ious_feat()
|
||||
|
||||
|
||||
|
||||
|
||||
def isimgborder(self, BoundPixel=10, BoundThresh=0.3):
|
||||
|
||||
x1, y1 = self.cornpoints[:,2], self.cornpoints[:,3],
|
||||
x2, y2 = self.cornpoints[:,8], self.cornpoints[:,9]
|
||||
|
||||
condt1 = sum(abs(x1)<BoundPixel) / self.frnum > BoundThresh
|
||||
condt2 = sum(abs(y1)<BoundPixel) / self.frnum > BoundThresh
|
||||
condt3 = sum(abs(x2-self.imgshape[0])<BoundPixel) / self.frnum > BoundThresh
|
||||
condt4 = sum(abs(y2-self.imgshape[1])<BoundPixel) / self.frnum > BoundThresh
|
||||
|
||||
condt = condt1 or condt2 or condt3 or condt4
|
||||
isCornpoint = False
|
||||
if condt:
|
||||
isCornpoint = True
|
||||
|
||||
condtA = condt1 and condt2
|
||||
condtB = condt3 and condt2
|
||||
condtC = condt1 and not condt2 and not condt4
|
||||
condtD = condt3 and not condt2 and not condt4
|
||||
condtE = condt1 and condt4
|
||||
condtF = condt3 and condt4
|
||||
condtG = condt4 and not condt1 and not condt3
|
||||
MarginState = [condtA, condtB, condtC, condtD, condtE, condtF, condtG]
|
||||
|
||||
return isCornpoint, MarginState
|
||||
|
||||
def PCA(self):
|
||||
self.pca = PCA()
|
||||
|
||||
X = self.cornpoints[:, 0:2]
|
||||
self.pca.fit(X)
|
||||
|
||||
|
||||
def compute_ious_feat(self):
|
||||
'''输出:
|
||||
self.feature_ious = (incart_iou, outcart_iou, cartboarder_iou, maxbox_iou, minbox_iou)
|
||||
self.incartrates = incartrates,
|
||||
其中:
|
||||
boxes流:track中所有boxes形成的轨迹图,可分为三部分:incart, outcart, cartboarder
|
||||
incart_iou, outcart_iou, cartboarder_iou:各部分和 boxes流的 iou。
|
||||
incart_iou = 0,track在购物车外,
|
||||
outcart_iou = 0,track在购物车内,也可能是通过左下角、右下角置入购物车,
|
||||
maxbox_iou, minbox_iou:track中最大、最小 box 和boxes流的iou,二者差值越小,越接近 1,表明track的运动型越小。
|
||||
incartrates: 各box和incart的iou时序,由小变大,反应的是置入过程,由大变小,反应的是取出过程
|
||||
'''
|
||||
incart = cv2.imread(str(parpath/"shopcart/cart_tempt/incart.png"), cv2.IMREAD_GRAYSCALE)
|
||||
outcart = cv2.imread(str(parpath/"shopcart/cart_tempt/outcart.png"), cv2.IMREAD_GRAYSCALE)
|
||||
cartboarder = cv2.imread(str(parpath/"shopcart/cart_tempt/cartboarder.png"), cv2.IMREAD_GRAYSCALE)
|
||||
|
||||
incartrates = []
|
||||
temp = np.zeros(incart.shape, np.uint8)
|
||||
maxarea, minarea = 0, self.imgshape[0]*self.imgshape[1]
|
||||
for i in range(self.frnum):
|
||||
# x, y, w, h = self.boxes[i, 0:4]
|
||||
|
||||
x = (self.boxes[i, 2] + self.boxes[i, 0]) / 2
|
||||
w = (self.boxes[i, 2] - self.boxes[i, 0]) / 2
|
||||
y = (self.boxes[i, 3] + self.boxes[i, 1]) / 2
|
||||
h = (self.boxes[i, 3] - self.boxes[i, 1]) / 2
|
||||
|
||||
|
||||
if w*h > maxarea: maxarea = w*h
|
||||
if w*h < minarea: minarea = w*h
|
||||
cv2.rectangle(temp, (int(x-w/2), int(y-h/2)), (int(x+w/2), int(y+h/2)), 255, cv2.FILLED)
|
||||
|
||||
temp1 = np.zeros(incart.shape, np.uint8)
|
||||
cv2.rectangle(temp1, (int(x-w/2), int(y-h/2)), (int(x+w/2), int(y+h/2)), 255, cv2.FILLED)
|
||||
temp2 = cv2.bitwise_and(incart, temp1)
|
||||
inrate = cv2.countNonZero(temp1)/(w*h)
|
||||
incartrates.append(inrate)
|
||||
|
||||
isincart = cv2.bitwise_and(incart, temp)
|
||||
isoutcart = cv2.bitwise_and(outcart, temp)
|
||||
iscartboarder = cv2.bitwise_and(cartboarder, temp)
|
||||
|
||||
num_temp = cv2.countNonZero(temp)
|
||||
num_incart = cv2.countNonZero(isincart)
|
||||
num_outcart = cv2.countNonZero(isoutcart)
|
||||
num_cartboarder = cv2.countNonZero(iscartboarder)
|
||||
|
||||
incart_iou = num_incart/(num_temp+1e-6)
|
||||
outcart_iou = num_outcart/(num_temp+1e-6)
|
||||
cartboarder_iou = num_cartboarder/(num_temp+1e-6)
|
||||
maxbox_iou = maxarea/(num_temp+1e-6)
|
||||
minbox_iou = minarea/(num_temp+1e-6)
|
||||
|
||||
self.feature_ious = (incart_iou, outcart_iou, cartboarder_iou, maxbox_iou, minbox_iou)
|
||||
self.incartrates = incartrates
|
||||
|
||||
|
||||
def compute_static_dynamic_fids(self):
|
||||
|
||||
if self.MarginState[0] or self.MarginState[2]:
|
||||
idx1 = 4
|
||||
elif self.MarginState[1] or self.MarginState[3]:
|
||||
idx1 = 3
|
||||
elif self.MarginState[4]:
|
||||
idx1 = 2
|
||||
elif self.MarginState[5]:
|
||||
idx1 = 1
|
||||
elif self.MarginState[6]:
|
||||
if self.trajlens[1] < self.trajlens[2]:
|
||||
idx1 = 1
|
||||
else:
|
||||
idx1 = 2
|
||||
else:
|
||||
idx1 = self.trajlens.index(min(self.trajlens))
|
||||
|
||||
# idx1 = self.trajlens.index(min(self.trajlens))
|
||||
trajmin = self.trajectory[idx1]
|
||||
|
||||
static, dynamic = self.pt_state_fids(trajmin)
|
||||
|
||||
static = np.array(static)
|
||||
dynamic = np.array(dynamic)
|
||||
|
||||
if static.size:
|
||||
indx = np.argsort(static[:, 0])
|
||||
static = static[indx]
|
||||
if dynamic.size:
|
||||
indx = np.argsort(dynamic[:, 0])
|
||||
dynamic = dynamic[indx]
|
||||
|
||||
return static, dynamic
|
||||
|
||||
|
||||
def is_static(self):
|
||||
'''静态情况 1: 目标关键点最小相对运动轨迹 < 0.2, 指标值偏大
|
||||
TrajFeat = [trajlen_min, trajlen_max,
|
||||
trajdist_min, trajdist_max,
|
||||
trajlen_rate, trajdist_rate]
|
||||
'''
|
||||
# print(f"TrackID: {self.tid}")
|
||||
|
||||
boxes = self.boxes
|
||||
|
||||
'''静态情况 1: '''
|
||||
condt1 = self.TrajFeat[5] < 0.2 or self.TrajFeat[3] < 120
|
||||
|
||||
'''静态情况 2: 目标初始状态为静止,适当放宽关键点最小相对运动轨迹 < 0.5'''
|
||||
condt2 = self.static_index.size > 0 \
|
||||
and self.static_index[0, 0] <= 2 \
|
||||
and self.static_index[0, 1] >= 5 \
|
||||
and self.TrajFeat[5] < 0.5 \
|
||||
and self.TrajFeat[1] < 240 \
|
||||
and self.isWholeInCart
|
||||
# and self.posState >= 2
|
||||
# and self.TrajFeat[0] < 240 \
|
||||
|
||||
'''静态情况 3: 目标初始状态和最终状态均为静止'''
|
||||
condt3 = self.static_index.shape[0] >= 2 \
|
||||
and self.static_index[0, 0] <= 2 \
|
||||
and self.static_index[0, 1] >= 5 \
|
||||
and self.static_index[-1, 1] >= self.frnum-3 \
|
||||
and self.TrajFeat[1] < 240 \
|
||||
and self.isWholeInCart
|
||||
# and self.posState >= 2
|
||||
# and self.TrajFeat[0] < 240 \
|
||||
|
||||
condt4 = self.static_index.shape[0] >= 2 \
|
||||
and self.static_index[0, 0] <= 2 \
|
||||
and self.static_index[0, 1] >= 6 \
|
||||
and self.static_index[-1, 0] <= self.frnum-5 \
|
||||
and self.static_index[-1, 1] >= self.frnum-2
|
||||
|
||||
condt = condt1 or condt2 or condt3 or condt4
|
||||
|
||||
return condt
|
||||
|
||||
|
||||
|
||||
|
||||
def is_OutTrack(self):
|
||||
if self.posState <= 1:
|
||||
isout = True
|
||||
else:
|
||||
isout = False
|
||||
return isout
|
||||
|
||||
def compute_distance(self):
|
||||
pass
|
||||
|
||||
def move_start_fid(self):
|
||||
pass
|
||||
|
||||
def move_end_fid(self):
|
||||
pass
|
194
tracking/dotrack/track_front.py
Normal file
194
tracking/dotrack/track_front.py
Normal file
@ -0,0 +1,194 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Mar 4 18:33:01 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
# from sklearn.cluster import KMeans
|
||||
from .dotracks import MoveState, Track
|
||||
|
||||
from pathlib import Path
|
||||
curpath = Path(__file__).resolve().parents[0]
|
||||
curpath = Path(curpath)
|
||||
parpath = curpath.parent
|
||||
|
||||
|
||||
class frontTrack(Track):
|
||||
# boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
# 0, 1, 2, 3, 4, 5, 6, 7, 8
|
||||
def __init__(self, boxes, features, imgshape=(1024, 1280)):
|
||||
|
||||
super().__init__(boxes, features, imgshape)
|
||||
|
||||
self.CART_HIGH_THRESH1 = imgshape[1]/2.98
|
||||
|
||||
'''y1、y2静止状态区间,值是 boxes 中对 axis=0 的索引,不是帧索引'''
|
||||
det_y1 = np.diff(boxes[:, 1], axis=0)
|
||||
det_y2 = np.diff(boxes[:, 3], axis=0)
|
||||
self.static_y1, self.dynamic_y1 = self.pt_state_fids(det_y1)
|
||||
self.static_y2, self.dynamic_y2 = self.pt_state_fids(det_y2)
|
||||
|
||||
self.isCornpoint = self.is_left_or_right_cornpoint()
|
||||
self.isBotmpoint = self.is_bottom_cornpoint()
|
||||
|
||||
'''该函数依赖项: self.isCornpoint,不能在父类中初始化'''
|
||||
self.trajfeature()
|
||||
|
||||
self.PositionState(camerType="front")
|
||||
|
||||
'''手部状态分析'''
|
||||
self.HAND_STATIC_THRESH = 100
|
||||
self.CART_POSIT_0 = 430
|
||||
self.CART_POSIT_1 = 620
|
||||
|
||||
def is_left_or_right_cornpoint(self):
|
||||
''' 基于 all(boxes),
|
||||
boxes左下角点和图像左下角点重叠 或
|
||||
boxes右下角点和图像左下角点重叠
|
||||
'''
|
||||
x1, y1 = self.boxes[:, 0], self.boxes[:, 1]
|
||||
x2, y2 = self.boxes[:, 2], self.boxes[:, 3]
|
||||
|
||||
# Left-Bottom cornpoint
|
||||
condt1 = all(x1 < 5) and all(y2 > self.imgshape[1]-5)
|
||||
|
||||
# Right-Bottom cornpoint
|
||||
condt2 = all(x2 > self.imgshape[0]-5) and all(y2 > self.imgshape[1]-5)
|
||||
|
||||
condt = condt1 or condt2
|
||||
|
||||
return condt
|
||||
|
||||
def is_edge_cornpoint(self):
|
||||
'''基于 all(boxes),boxes是否和图像左右边缘重叠'''
|
||||
x1, x2 = self.boxes[:, 0], self.boxes[:, 2]
|
||||
condt = all(x1 < 3) or all(x2 > self.imgshape[0]-3)
|
||||
|
||||
return condt
|
||||
|
||||
def is_bottom_cornpoint(self):
|
||||
'''基于 all(boxes),boxes是否和图像下边缘重叠'''
|
||||
condt = all(self.boxes[:, 3] > self.imgshape[1]-20)
|
||||
|
||||
return condt
|
||||
|
||||
|
||||
def is_static(self):
|
||||
assert self.frnum > 1, "boxes number must greater than 1"
|
||||
# print(f"The ID is: {self.tid}")
|
||||
|
||||
# 手部和小孩目标不考虑
|
||||
if self.cls == 0 or self.cls == 9:
|
||||
return False
|
||||
|
||||
# boxes 全部 y2=1280
|
||||
if self.isBotmpoint:
|
||||
return True
|
||||
|
||||
boxes = self.boxes
|
||||
y0 = (boxes[:, 1]+boxes[:, 3])/2
|
||||
|
||||
## 纵轴矢量和
|
||||
sum_y0 = y0[-1] - y0[0]
|
||||
sum_y1 = boxes[-1, 1]-boxes[0, 1]
|
||||
sum_y2 = boxes[-1, 3]-boxes[0, 3]
|
||||
|
||||
# 一些需要考虑的特殊情况
|
||||
isbottom = max(boxes[:, 3]) > 1280-3
|
||||
istop = min(boxes[:, 1]) < 3
|
||||
isincart = min(y0) > self.CART_HIGH_THRESH1
|
||||
uncert = abs(sum_y1)<100 and abs(sum_y2)<100
|
||||
|
||||
'''初始条件:商品中心点始终在购物车内、'''
|
||||
condt0 = max((boxes[:, 1]+boxes[:, 3])/2) > self.CART_HIGH_THRESH1
|
||||
|
||||
'''条件1:轨迹运动纵向和(y1 或 y2)描述商品轨迹长度,存在情况:
|
||||
(1). 检测框可能与图像上下边缘重合,
|
||||
(2). 上边或下边存在跳动
|
||||
'''
|
||||
if isbottom and istop:
|
||||
condt1 = abs(sum_y0) < 300
|
||||
elif isbottom: # y2在底部,用y1表征运动
|
||||
condt1 = sum_y1 > -120 and abs(sum_y0)<80 # 有底部点,方向向上阈值小于100
|
||||
elif istop: # y1在顶部,用y2表征运动
|
||||
condt1 = abs(sum_y2) < 100
|
||||
else:
|
||||
condt1 = (abs(sum_y1) < 30 or abs(sum_y2)<30)
|
||||
|
||||
'''条件2:轨迹的开始和结束阶段均处于静止状态, 利用静止状态区间判断,用 y1
|
||||
a. 商品在购物车内,
|
||||
b. 检测框的起始阶段和结束阶段均为静止状态
|
||||
c. 静止帧长度 > 3'''
|
||||
|
||||
condt2 = False
|
||||
if len(self.static_y1)>=2:
|
||||
condt_s0 = self.static_y1[0][0]==0 and self.static_y1[0][1] - self.static_y1[0][0] >= 3
|
||||
condt_s1 = self.static_y1[-1][1]==self.frnum-1 and self.static_y1[-1][1] - self.static_y1[-1][0] >= 3
|
||||
condt2 = condt_s0 and condt_s1 and isincart
|
||||
|
||||
|
||||
condt = condt0 and (condt1 or condt2)
|
||||
|
||||
return condt
|
||||
|
||||
|
||||
def is_upward(self):
|
||||
'''判断商品是否取出,'''
|
||||
print(f"The ID is: {self.tid}")
|
||||
|
||||
def is_free_move(self):
|
||||
if self.frnum == 1:
|
||||
return True
|
||||
# print(f"The ID is: {self.tid}")
|
||||
|
||||
|
||||
y0 = (self.boxes[:, 1] + self.boxes[:, 3]) / 2
|
||||
det_y0 = np.diff(y0, axis=0)
|
||||
sum_y0 = y0[-1] - y0[0]
|
||||
|
||||
'''情况1:中心点向下 '''
|
||||
## 初始条件:商品第一次检测到在购物车内
|
||||
condt0 = y0[0] > self.CART_HIGH_THRESH1
|
||||
|
||||
condt_a = False
|
||||
## 条件1:商品初始为静止状态,静止条件应严格一些
|
||||
condt11, condt12 = False, False
|
||||
if len(self.static_y1)>0:
|
||||
condt11 = self.static_y1[0][0]==0 and self.static_y1[0][1] - self.static_y1[0][0] >= 5
|
||||
if len(self.static_y2)>0:
|
||||
condt12 = self.static_y2[0][0]==0 and self.static_y2[0][1] - self.static_y2[0][0] >= 5
|
||||
|
||||
# 条件2:商品中心发生向下移动
|
||||
condt2 = y0[-1] > y0[0]
|
||||
|
||||
# 综合判断a
|
||||
condt_a = condt0 and (condt11 or condt12) and condt2
|
||||
|
||||
'''情况2:中心点向上 '''
|
||||
## 商品中心点向上移动,但没有关联的Hand轨迹,也不是左右边界点
|
||||
condt_b = condt0 and len(self.Hands)==0 and y0[-1] < y0[0] and (not self.is_edge_cornpoint()) and min(y0)>self.CART_HIGH_THRESH1
|
||||
|
||||
|
||||
'''情况3: 商品在购物车内,但运动方向无序'''
|
||||
## 中心点在购物车内,纵向轨迹和小于轨迹差中绝对值最大的两个值的和,说明运动没有主方向
|
||||
condt_c = False
|
||||
if self.frnum > 3:
|
||||
condt_c = all(y0>self.CART_HIGH_THRESH1) and \
|
||||
(abs(sum_y0) < sum(np.sort(np.abs(det_y0))[::-1][:2])-1)
|
||||
|
||||
condt = (condt_a or condt_b or condt_c) and self.cls!=0
|
||||
|
||||
return condt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
91
tracking/dotrack/track_select.py
Normal file
91
tracking/dotrack/track_select.py
Normal file
@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Jul 29 10:28:21 2024
|
||||
未来需将这一部分和轨迹分析代码集成
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
from scipy.spatial.distance import cdist
|
||||
|
||||
class TProp:
|
||||
def __init__(self, boxes):
|
||||
|
||||
self.boxes = boxes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TProp:
|
||||
'''抽象基类,不能实例化对象'''
|
||||
def __init__(self, boxes):
|
||||
'''
|
||||
boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
0 1 2 3 4 5 6 7 8
|
||||
'''
|
||||
# assert len(set(boxes[:, 4].astype(int))) == 1, "For a Track, track_id more than 1"
|
||||
# assert len(set(boxes[:, 6].astype(int))) == 1, "For a Track, class number more than 1"
|
||||
|
||||
self.boxes = boxes
|
||||
|
||||
'''5个关键点(中心点、左上点、右上点、左下点、右下点 )坐标'''
|
||||
self.compute_cornpoints()
|
||||
|
||||
'''5个关键点轨迹特征,可以在子类中实现,降低顺序处理时的计算量
|
||||
(中心点、左上点、右上点、左下点、右下点 )轨迹特征'''
|
||||
self.compute_cornpts_feats()
|
||||
|
||||
self.distmax = max(self.trajdist)
|
||||
|
||||
|
||||
def compute_cornpoints(self):
|
||||
'''
|
||||
cornpoints 共10项,分别是个点的坐标值(x, y)
|
||||
(center, top_left, top_right, bottom_left, bottom_right)
|
||||
'''
|
||||
boxes = self.boxes
|
||||
cornpoints = np.zeros((self.frnum, 10))
|
||||
cornpoints[:,0] = (boxes[:, 0] + boxes[:, 2]) / 2
|
||||
cornpoints[:,1] = (boxes[:, 1] + boxes[:, 3]) / 2
|
||||
cornpoints[:,2], cornpoints[:,3] = boxes[:, 0], boxes[:, 1]
|
||||
cornpoints[:,4], cornpoints[:,5] = boxes[:, 2], boxes[:, 1]
|
||||
cornpoints[:,6], cornpoints[:,7] = boxes[:, 0], boxes[:, 3]
|
||||
cornpoints[:,8], cornpoints[:,9] = boxes[:, 2], boxes[:, 3]
|
||||
|
||||
self.cornpoints = cornpoints
|
||||
def compute_cornpts_feats(self):
|
||||
'''
|
||||
'''
|
||||
trajectory = []
|
||||
trajlens = []
|
||||
trajdist = []
|
||||
trajrects = []
|
||||
for k in range(5):
|
||||
# diff_xy2 = np.power(np.diff(self.cornpoints[:, 2*k:2*(k+1)], axis = 0), 2)
|
||||
# trajlen = np.sum(np.sqrt(np.sum(diff_xy2, axis = 1)))
|
||||
|
||||
X = self.cornpoints[:, 2*k:2*(k+1)]
|
||||
|
||||
traj = np.linalg.norm(np.diff(X, axis=0), axis=1)
|
||||
trajectory.append(traj)
|
||||
|
||||
trajlen = np.sum(traj)
|
||||
trajlens.append(trajlen)
|
||||
|
||||
ptdist = np.max(cdist(X, X))
|
||||
trajdist.append(ptdist)
|
||||
|
||||
'''最小外接矩形:
|
||||
rect[0]: 中心(x, y)
|
||||
rect[1]: (w, h)
|
||||
rect[0]: 旋转角度 (-90°, 0]
|
||||
'''
|
||||
rect = cv2.minAreaRect(X.astype(np.int64))
|
||||
trajrects.append(rect)
|
||||
|
||||
self.trajectory = trajectory
|
||||
self.trajlens = trajlens
|
||||
self.trajdist = trajdist
|
||||
self.trajrects = trajrects
|
Reference in New Issue
Block a user