回传数据解析,兼容v5和v10
This commit is contained in:
0
tracking/__init__.py
Normal file
0
tracking/__init__.py
Normal file
BIN
tracking/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tracking/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/__pycache__/contrast_analysis.cpython-39.pyc
Normal file
BIN
tracking/__pycache__/contrast_analysis.cpython-39.pyc
Normal file
Binary file not shown.
1
tracking/data/说明文档.txt
Normal file
1
tracking/data/说明文档.txt
Normal file
@ -0,0 +1 @@
|
||||
文件夹 trackdicts_20240608 和 trackdicts_1 下的数据为和手部关联前的跟踪结果数据
|
337
tracking/deprecated/contrast_one2one.py
Normal file
337
tracking/deprecated/contrast_one2one.py
Normal file
@ -0,0 +1,337 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Aug 30 17:53:03 2024
|
||||
have Deprecated!
|
||||
|
||||
1. 确认在相同CamerType下,track.data 中 CamerID 项数量 = 图像数 = 帧ID数 = 最大帧ID
|
||||
|
||||
2. 读取0/1_tracking_output.data 中数据,boxes、feats,len(boxes)=len(feats)
|
||||
帧ID约束
|
||||
|
||||
3. 优先选择前摄
|
||||
|
||||
4. 保存图像数据
|
||||
|
||||
5. 一次购物事件类型
|
||||
shopEvent: {barcode:
|
||||
type: getout, input
|
||||
front_traj:[{imgpath: str,
|
||||
box: arrar(1, 9),
|
||||
feat: array(1, 256)
|
||||
}]
|
||||
back_traj: [{imgpath: str,
|
||||
box: arrar(1, 9),
|
||||
feat: array(1, 256)
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@author: ym
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import pickle
|
||||
sys.path.append(r"D:\DetectTracking")
|
||||
from tracking.utils.read_data import extract_data, read_tracking_output, read_deletedBarcode_file
|
||||
|
||||
IMG_FORMAT = ['.bmp', '.jpg', '.jpeg', '.png']
|
||||
|
||||
def creat_shopping_event(basepath):
|
||||
eventList = []
|
||||
|
||||
'''一、构造放入商品事件列表'''
|
||||
k = 0
|
||||
for filename in os.listdir(basepath):
|
||||
# filename = "20240723-155413_6904406215720"
|
||||
|
||||
'''filename下为一次购物事件'''
|
||||
filepath = os.path.join(basepath, filename)
|
||||
|
||||
'''================ 0. 检查 filename 及 filepath 正确性和有效性 ================'''
|
||||
nmlist = filename.split('_')
|
||||
if filename.find('2024')<0 or len(nmlist)!=2 or len(nmlist[0])!=15 or len(nmlist[1])<11:
|
||||
continue
|
||||
if not os.path.isdir(filepath): continue
|
||||
print(f"Event name: {filename}")
|
||||
|
||||
'''================ 1. 构造事件描述字典,暂定 9 items ==============='''
|
||||
event = {}
|
||||
event['barcode'] = nmlist[1]
|
||||
event['type'] = 'input'
|
||||
event['filepath'] = filepath
|
||||
event['back_imgpaths'] = []
|
||||
event['front_imgpaths'] = []
|
||||
event['back_boxes'] = np.empty((0, 9), dtype=np.float64)
|
||||
event['front_boxes'] = np.empty((0, 9), dtype=np.float64)
|
||||
event['back_feats'] = np.empty((0, 256), dtype=np.float64)
|
||||
event['front_feats'] = np.empty((0, 256), dtype=np.float64)
|
||||
# event['feats_compose'] = np.empty((0, 256), dtype=np.float64)
|
||||
# event['feats_select'] = np.empty((0, 256), dtype=np.float64)
|
||||
|
||||
|
||||
'''================= 1. 读取 data 文件 ============================='''
|
||||
for dataname in os.listdir(filepath):
|
||||
# filename = '1_track.data'
|
||||
datapath = os.path.join(filepath, dataname)
|
||||
if not os.path.isfile(datapath): continue
|
||||
|
||||
CamerType = dataname.split('_')[0]
|
||||
''' 3.1 读取 0/1_track.data 中数据,暂不考虑'''
|
||||
# if dataname.find("_track.data")>0:
|
||||
# bboxes, ffeats, trackerboxes, tracker_feat_dict, trackingboxes, tracking_feat_dict = extract_data(datapath)
|
||||
|
||||
''' 3.2 读取 0/1_tracking_output.data 中数据'''
|
||||
if dataname.find("_tracking_output.data")>0:
|
||||
tracking_output_boxes, tracking_output_feats = read_tracking_output(datapath)
|
||||
if len(tracking_output_boxes) != len(tracking_output_feats): continue
|
||||
if CamerType == '0':
|
||||
event['back_boxes'] = tracking_output_boxes
|
||||
event['back_feats'] = tracking_output_feats
|
||||
elif CamerType == '1':
|
||||
event['front_boxes'] = tracking_output_boxes
|
||||
event['front_feats'] = tracking_output_feats
|
||||
|
||||
# '''1.1 事件的特征表征方式选择'''
|
||||
# bk_feats = event['back_feats']
|
||||
# ft_feats = event['front_feats']
|
||||
|
||||
# feats_compose = np.empty((0, 256), dtype=np.float64)
|
||||
# if len(ft_feats):
|
||||
# feats_compose = np.concatenate((feats_compose, ft_feats), axis=0)
|
||||
# if len(bk_feats):
|
||||
# feats_compose = np.concatenate((feats_compose, bk_feats), axis=0)
|
||||
# event['feats_compose'] = feats_compose
|
||||
|
||||
# '''3. 构造前摄特征'''
|
||||
# if len(ft_feats):
|
||||
# event['feats_select'] = ft_feats
|
||||
|
||||
|
||||
|
||||
'''================ 2. 读取图像文件地址,并按照帧ID排序 ============='''
|
||||
frontImgs, frontFid = [], []
|
||||
backImgs, backFid = [], []
|
||||
for imgname in os.listdir(filepath):
|
||||
name, ext = os.path.splitext(imgname)
|
||||
if ext not in IMG_FORMAT or name.find('frameId')<0: continue
|
||||
|
||||
CamerType = name.split('_')[0]
|
||||
frameId = int(name.split('_')[3])
|
||||
imgpath = os.path.join(filepath, imgname)
|
||||
if CamerType == '0':
|
||||
backImgs.append(imgpath)
|
||||
backFid.append(frameId)
|
||||
if CamerType == '1':
|
||||
frontImgs.append(imgpath)
|
||||
frontFid.append(frameId)
|
||||
|
||||
frontIdx = np.argsort(np.array(frontFid))
|
||||
backIdx = np.argsort(np.array(backFid))
|
||||
|
||||
'''2.1 生成依据帧 ID 排序的前后摄图像地址列表'''
|
||||
frontImgs = [frontImgs[i] for i in frontIdx]
|
||||
backImgs = [backImgs[i] for i in backIdx]
|
||||
|
||||
'''2.2 将前、后摄图像路径添加至事件字典'''
|
||||
bfid = event['back_boxes'][:, 7].astype(np.int64)
|
||||
ffid = event['front_boxes'][:, 7].astype(np.int64)
|
||||
if len(bfid) and max(bfid) <= len(backImgs):
|
||||
event['back_imgpaths'] = [backImgs[i-1] for i in bfid]
|
||||
if len(ffid) and max(ffid) <= len(frontImgs):
|
||||
event['front_imgpaths'] = [frontImgs[i-1] for i in ffid]
|
||||
|
||||
|
||||
'''================ 3. 判断当前事件有效性,并添加至事件列表 =========='''
|
||||
condt1 = len(event['back_imgpaths'])==0 or len(event['front_imgpaths'])==0
|
||||
condt2 = len(event['front_feats'])==0 and len(event['back_feats'])==0
|
||||
|
||||
if condt1 or condt2:
|
||||
print(f" Error, condt1: {condt1}, condt2: {condt2}")
|
||||
continue
|
||||
|
||||
eventList.append(event)
|
||||
|
||||
# k += 1
|
||||
# if k==1:
|
||||
# continue
|
||||
|
||||
'''一、构造放入商品事件列表,暂不处理'''
|
||||
# delepath = os.path.join(basepath, 'deletedBarcode.txt')
|
||||
# bcdList = read_deletedBarcode_file(delepath)
|
||||
# for slist in bcdList:
|
||||
# getoutFold = slist['SeqDir'].strip()
|
||||
# getoutPath = os.path.join(basepath, getoutFold)
|
||||
|
||||
# '''取出事件文件夹不存在,跳出循环'''
|
||||
# if not os.path.exists(getoutPath) and not os.path.isdir(getoutPath):
|
||||
# continue
|
||||
|
||||
# ''' 生成取出事件字典 '''
|
||||
# event = {}
|
||||
# event['barcode'] = slist['Deleted'].strip()
|
||||
# event['type'] = 'getout'
|
||||
# event['basepath'] = getoutPath
|
||||
|
||||
|
||||
return eventList
|
||||
|
||||
def get_std_barcodeDict(bcdpath):
|
||||
stdBlist = []
|
||||
for filename in os.listdir(bcdpath):
|
||||
filepath = os.path.join(bcdpath, filename)
|
||||
if not os.path.isdir(filepath) or not filename.isdigit(): continue
|
||||
|
||||
stdBlist.append(filename)
|
||||
|
||||
|
||||
bcdpaths = [(barcode, os.path.join(bcdpath, barcode)) for barcode in stdBlist]
|
||||
|
||||
stdBarcodeDict = {}
|
||||
for barcode, bpath in bcdpaths:
|
||||
stdBarcodeDict[barcode] = []
|
||||
for root, dirs, files in os.walk(bpath):
|
||||
|
||||
imgpaths = []
|
||||
if "base" in dirs:
|
||||
broot = os.path.join(root, "base")
|
||||
for imgname in os.listdir(broot):
|
||||
imgpath = os.path.join(broot, imgname)
|
||||
_, ext = os.path.splitext(imgpath)
|
||||
if ext not in IMG_FORMAT: continue
|
||||
imgpaths.append(imgpath)
|
||||
|
||||
stdBarcodeDict[barcode].extend(imgpaths)
|
||||
break
|
||||
|
||||
else:
|
||||
for imgname in files:
|
||||
imgpath = os.path.join(root, imgname)
|
||||
_, ext = os.path.splitext(imgpath)
|
||||
if ext not in IMG_FORMAT: continue
|
||||
imgpaths.append(imgpath)
|
||||
stdBarcodeDict[barcode].extend(imgpaths)
|
||||
|
||||
jsonpath = os.path.join(r'\\192.168.1.28\share\测试_202406\contrast\barcodes', f"{barcode}.pickle")
|
||||
with open(jsonpath, 'wb') as f:
|
||||
pickle.dump(stdBarcodeDict, f)
|
||||
|
||||
print(f"Barcode: {barcode}")
|
||||
|
||||
|
||||
|
||||
return stdBarcodeDict
|
||||
|
||||
|
||||
def one2one_test(filepath):
|
||||
|
||||
savepath = r'\\192.168.1.28\share\测试_202406\contrast'
|
||||
|
||||
'''获得 Barcode 列表'''
|
||||
bcdpath = r'\\192.168.1.28\share\已标注数据备份\对比数据\barcode\barcode_1771'
|
||||
stdBarcodeDict = get_std_barcodeDict(bcdpath)
|
||||
|
||||
|
||||
eventList = creat_shopping_event(filepath)
|
||||
print("=========== eventList have generated! ===========")
|
||||
barcodeDict = {}
|
||||
for event in eventList:
|
||||
'''9 items: barcode, type, filepath, back_imgpaths, front_imgpaths,
|
||||
back_boxes, front_boxes, back_feats, front_feats
|
||||
'''
|
||||
|
||||
barcode = event['barcode']
|
||||
if barcode not in stdBarcodeDict.keys():
|
||||
continue
|
||||
|
||||
|
||||
if len(event['feats_select']):
|
||||
event_feats = event['feats_select']
|
||||
elif len(event['back_feats']):
|
||||
event_feats = event['back_feats']
|
||||
else:
|
||||
continue
|
||||
|
||||
std_bcdpath = os.path.join(bcdpath, barcode)
|
||||
|
||||
|
||||
|
||||
for root, dirs, files in os.walk(std_bcdpath):
|
||||
if "base" in files:
|
||||
std_bcdpath = os.path.join(root, "base")
|
||||
break
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
'''保存一次购物事件的轨迹子图'''
|
||||
basename = os.path.basename(event['filepath'])
|
||||
spath = os.path.join(savepath, basename)
|
||||
if not os.path.exists(spath):
|
||||
os.makedirs(spath)
|
||||
cameras = ('front', 'back')
|
||||
for camera in cameras:
|
||||
if camera == 'front':
|
||||
boxes = event['front_boxes']
|
||||
imgpaths = event['front_imgpaths']
|
||||
else:
|
||||
boxes = event['back_boxes']
|
||||
imgpaths = event['back_imgpaths']
|
||||
|
||||
for i, box in enumerate(boxes):
|
||||
x1, y1, x2, y2, tid, score, cls, fid, bid = box
|
||||
|
||||
imgpath = imgpaths[i]
|
||||
image = cv2.imread(imgpath)
|
||||
subimg = image[int(y1/2):int(y2/2), int(x1/2):int(x2/2), :]
|
||||
|
||||
camerType, timeTamp, _, frameID = os.path.basename(imgpath).split('.')[0].split('_')
|
||||
subimgName = f"{camerType}_{tid}_fid({fid}, {frameID}).png"
|
||||
subimgPath = os.path.join(spath, subimgName)
|
||||
|
||||
cv2.imwrite(subimgPath, subimg)
|
||||
print(f"Image saved: {basename}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
fplist = [r'\\192.168.1.28\share\测试_202406\0723\0723_1',
|
||||
r'\\192.168.1.28\share\测试_202406\0723\0723_2',
|
||||
# r'\\192.168.1.28\share\测试_202406\0723\0723_3',
|
||||
r'\\192.168.1.28\share\测试_202406\0722\0722_01',
|
||||
r'\\192.168.1.28\share\测试_202406\0722\0722_02'
|
||||
]
|
||||
|
||||
|
||||
|
||||
for filepath in fplist:
|
||||
one2one_test(filepath)
|
||||
|
||||
# for filepath in fplist:
|
||||
# try:
|
||||
# one2one_test(filepath)
|
||||
|
||||
# except Exception as e:
|
||||
# print(f'{filepath}, Error: {e}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
main()
|
806
tracking/deprecated/eventsmatch.py
Normal file
806
tracking/deprecated/eventsmatch.py
Normal file
@ -0,0 +1,806 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Apr 16 11:51:07 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import cv2
|
||||
import os
|
||||
import numpy as np
|
||||
# import time
|
||||
import pickle
|
||||
import json
|
||||
# import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
import shutil
|
||||
import random
|
||||
import math
|
||||
import sys
|
||||
from scipy.spatial.distance import cdist
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from pathlib import Path
|
||||
from utils.gen import Profile
|
||||
|
||||
sys.path.append(r"D:\DetectTracking\tracking")
|
||||
|
||||
from dotrack.dotracks_back import doBackTracks
|
||||
from dotrack.dotracks_front import doFrontTracks
|
||||
from utils.drawtracks import plot_frameID_y2, draw_all_trajectories
|
||||
|
||||
# from utils.drawtracks import draw5points, drawTrack, drawtracefeat, drawFeatures
|
||||
# from datetime import datetime
|
||||
|
||||
from utils.mergetrack import readDict
|
||||
|
||||
import csv
|
||||
|
||||
|
||||
def read_csv_file():
|
||||
|
||||
file_path = r'D:\DeepLearning\yolov5_track\tracking\matching\featdata\Similarity.csv'
|
||||
with open(file_path, mode='r', newline='') as file:
|
||||
data = list(csv.reader(file))
|
||||
|
||||
matrix = []
|
||||
for i in range(1, len(data)):
|
||||
matrix.append(data[i][1:])
|
||||
|
||||
matrix = np.array(matrix, dtype = np.float32)
|
||||
|
||||
simil = 1 + (matrix-1)/2
|
||||
|
||||
print("done!!!")
|
||||
|
||||
|
||||
|
||||
|
||||
def get_img_filename(imgpath = r'./matching/images/' ):
|
||||
imgexts = ['.png', '.jpg', '.jpeg']
|
||||
ImgFileList = []
|
||||
for root, dirs, files in os.walk(imgpath):
|
||||
ImgList = []
|
||||
for file in files:
|
||||
_, ext = os.path.splitext(file)
|
||||
if ext in imgexts:
|
||||
ImgFileList.append(os.path.join(root, file))
|
||||
|
||||
return ImgFileList
|
||||
|
||||
|
||||
def calculate_similarity_track(similarmode = 'other'):
|
||||
'''
|
||||
similarmode:
|
||||
'mean'
|
||||
'other'
|
||||
|
||||
'''
|
||||
|
||||
ContrastDict = np.load('./matching/featdata/imgs_feats_data_refined.pkl', allow_pickle=True)
|
||||
print(f"The Num of imgsample: {len(ContrastDict)}")
|
||||
|
||||
''''================= 构造空字典 MatchObjDict ================='''
|
||||
def splitkey(key):
|
||||
videoname = key.split('_')
|
||||
BarCode = videoname[0]
|
||||
SampleTime = videoname[1].split('-')[1]
|
||||
CameraType = videoname[2]
|
||||
ActionType = videoname[3]
|
||||
TrackID = '_'.join(key.split('_')[7:])
|
||||
|
||||
return BarCode, SampleTime, CameraType, ActionType, TrackID
|
||||
|
||||
MatchObjList = []
|
||||
CameraList = []
|
||||
for key in ContrastDict.keys():
|
||||
BarCode, SampleTime, CameraType, ActionType, FeatureID = splitkey(key)
|
||||
|
||||
MatchObjList.append('_'.join([BarCode, SampleTime, ActionType]))
|
||||
CameraList.append(CameraType)
|
||||
|
||||
# MatchObjSet = set(MatchObjList)
|
||||
# CameraSet = set(CameraList)
|
||||
|
||||
|
||||
objects = list(set(MatchObjList))
|
||||
cameras = list(set(CameraList))
|
||||
assert len(cameras) == 2, "The num of cameras is error!"
|
||||
|
||||
MatchObjDict = {}
|
||||
for obj in objects:
|
||||
CameraDict = {}
|
||||
for camera in cameras:
|
||||
CameraDict[camera] = {}
|
||||
|
||||
MatchObjDict[obj] = CameraDict
|
||||
|
||||
|
||||
|
||||
for key, value in ContrastDict.items():
|
||||
BarCode, SampleTime, CameraType, ActionType, FeatureID = splitkey(key)
|
||||
|
||||
MatchObj = '_'.join([BarCode, SampleTime, ActionType])
|
||||
|
||||
vdict = {}
|
||||
if FeatureID not in MatchObjDict[MatchObj][CameraType]:
|
||||
vdict[FeatureID] = value['feature']
|
||||
MatchObjDict[MatchObj][CameraType].update(vdict)
|
||||
|
||||
print(f"The Num of MatchObjDict: {len(MatchObjDict)}")
|
||||
|
||||
# MatchKeys = [key for key in MatchObjDict.keys()]
|
||||
num = len(objects)
|
||||
GtMatrix = np.zeros((num, num), dtype=np.float32)
|
||||
Similarity = np.zeros((num, num), dtype=np.float32)
|
||||
InterMatrix = np.zeros((num, num), dtype=np.float32) # 类间
|
||||
IntraMatrix = np.zeros((num, num), dtype=np.float32) # 类内
|
||||
|
||||
'''生成GT矩阵: GtMatrix, IntraMatrix, InterMatrix'''
|
||||
for i, obi in enumerate(objects):
|
||||
barcode_i = obi.split('_')[0]
|
||||
for j, obj in enumerate(objects):
|
||||
barcode_j = obj.split('_')[0]
|
||||
if barcode_i == barcode_j:
|
||||
GtMatrix[i, j] = 1
|
||||
if i!=j: IntraMatrix[i, j] = 1
|
||||
else:
|
||||
GtMatrix[i, j] = 0
|
||||
InterMatrix[i, j] = 1
|
||||
|
||||
|
||||
'''生成相似度矩阵: Similarity '''
|
||||
|
||||
ObjFeatList = []
|
||||
for i, obi in enumerate(objects):
|
||||
obidict = MatchObjDict[obi]
|
||||
camlist = []
|
||||
for camera in obidict.keys():
|
||||
featlist = []
|
||||
for fid in obidict[camera].keys():
|
||||
featlist.append(MatchObjDict[obi][camera][fid])
|
||||
|
||||
camlist.append(featlist)
|
||||
|
||||
ObjFeatList.append(camlist)
|
||||
|
||||
Similarity_1 = Similarity.copy()
|
||||
for i in range(len(objects)):
|
||||
obi = ObjFeatList[i]
|
||||
for j in range(len(objects)):
|
||||
obj = ObjFeatList[j]
|
||||
simival = []
|
||||
for ii in range(len(obi)):
|
||||
if len(obi[ii])==0: continue
|
||||
feat_ii = np.asarray(obi[ii])
|
||||
|
||||
for jj in range(len(obj)):
|
||||
if len(obj[jj])==0: continue
|
||||
feat_jj = np.asarray(obj[jj])
|
||||
|
||||
if similarmode == 'mean':
|
||||
featii = np.mean(feat_ii, axis=0)
|
||||
featjj = np.mean(feat_jj, axis=0)
|
||||
try:
|
||||
matrix = 1- np.maximum(0.0, cdist(featii[None, :], featjj[None, :], 'cosine'))
|
||||
except Exception as e:
|
||||
print(f'error is {e.__class__.__name__}')
|
||||
|
||||
else:
|
||||
matrix = 1- np.maximum(0.0, cdist(feat_ii, feat_jj, 'cosine'))
|
||||
simival.append(np.max(matrix))
|
||||
if len(simival)==0: continue
|
||||
|
||||
Similarity[i, j] = max(simival)
|
||||
|
||||
# feat_i = np.empty((0, 256), dtype = np.float32)
|
||||
# feat_j = np.empty((0, 256), dtype = np.float32)
|
||||
# for ii in range(len(obi)):
|
||||
# feat_ii = np.asarray(obi[ii])
|
||||
# feat_i = np.concatenate((feat_i, feat_ii), axis=0)
|
||||
# for jj in range(len(obj)):
|
||||
# feat_jj = np.asarray(obi[jj])
|
||||
# feat_j = np.concatenate((feat_j, feat_jj), axis=0)
|
||||
|
||||
# if similarmode == 'mean':
|
||||
# feati = np.mean(feat_i, axis=0)
|
||||
# featj = np.mean(feat_j, axis=0)
|
||||
# matrix = 1- np.maximum(0.0, cdist(feati[None, :], featj[None, :], 'cosine'))
|
||||
# else:
|
||||
# matrix = 1- np.maximum(0.0, cdist(feat_i, feat_j, 'cosine'))
|
||||
|
||||
# Similarity_1[i, j] = np.max(matrix)
|
||||
|
||||
|
||||
SimiDict = {'keys': objects, 'GtMatrix': GtMatrix, 'Similarity': Similarity,
|
||||
'IntraMatrix':IntraMatrix, 'InterMatrix':InterMatrix}
|
||||
with open(r"./matching/featdata/MatchDict_track.pkl", "wb") as f:
|
||||
pickle.dump(SimiDict, f)
|
||||
|
||||
# SimiDict_1 = {'keys': objects, 'GtMatrix': GtMatrix, 'Similarity':Similarity_1,
|
||||
# 'IntraMatrix':IntraMatrix, 'InterMatrix':InterMatrix}
|
||||
# with open(r"./matching/featdata/MatchDict_track_1.pkl", "wb") as f:
|
||||
# pickle.dump(SimiDict_1, f)
|
||||
|
||||
df_GtMatrix = pd.DataFrame(data=GtMatrix, columns = objects, index = objects)
|
||||
df_GtMatrix.to_csv('./matching/featdata/GtMatrix_track.csv',index=True)
|
||||
|
||||
df_similarity = pd.DataFrame(data=Similarity, columns = objects, index = objects)
|
||||
df_similarity.to_csv('./matching/featdata/Similarity_track.csv',index=True)
|
||||
|
||||
# df_similarity_1 = pd.DataFrame(data=Similarity_1, columns = objects, index = objects)
|
||||
# df_similarity_1.to_csv('./matching/featdata/Similarity_track_1.csv',index=True)
|
||||
|
||||
print("Done!!!!")
|
||||
|
||||
# SimilarMode = ['mean', 'max']
|
||||
def calculate_similarity(similarmode = 'mean'):
|
||||
ContrastDict = np.load('./matching/featdata/imgs_feats_data_noplane.pkl', allow_pickle=True)
|
||||
print(f"The Num of imgsample: {len(ContrastDict)}")
|
||||
|
||||
FrontBackMerged = True
|
||||
TracKeys = {}
|
||||
for key, value in ContrastDict.items():
|
||||
feature = value['feature']
|
||||
videoname = key.split('_')[:7]
|
||||
BarCode = videoname[0]
|
||||
SampleTime = videoname[1].split('-')[1]
|
||||
CameraType = videoname[2]
|
||||
ActionType = videoname[3]
|
||||
TrackID = key.split('_')[7]
|
||||
|
||||
if FrontBackMerged:
|
||||
TracKey = '_'.join([BarCode, SampleTime, ActionType])
|
||||
else:
|
||||
TracKey = '_'.join([BarCode, SampleTime, CameraType, ActionType])
|
||||
|
||||
if TracKey in TracKeys:
|
||||
TracKeys[TracKey].append(feature)
|
||||
else:
|
||||
TracKeys[TracKey] = []
|
||||
TracKeys[TracKey].append(feature)
|
||||
|
||||
'''===== 生成GT矩阵: Similarity、GtMatrix、IntraMatrix、InterMatrix ====='''
|
||||
num = len(TracKeys)
|
||||
keys = [key for key in TracKeys.keys()]
|
||||
|
||||
GtMatrix = np.zeros((num, num), dtype=np.float32)
|
||||
Similarity = np.zeros((num, num), dtype=np.float32)
|
||||
|
||||
InterMatrix = np.zeros((num, num), dtype=np.float32) # 类间
|
||||
IntraMatrix = np.zeros((num, num), dtype=np.float32) # 类内
|
||||
|
||||
for i, key_i in enumerate(keys):
|
||||
barcode_i = key_i.split('_')[0]
|
||||
feat_i = np.asarray(TracKeys[key_i], dtype=np.float32)
|
||||
for j, key_j in enumerate(keys):
|
||||
barcode_j = key_j.split('_')[0]
|
||||
feat_j = np.asarray(TracKeys[key_j], dtype=np.float32)
|
||||
|
||||
if similarmode == 'mean':
|
||||
feati = np.mean(feat_i, axis=0)
|
||||
featj = np.mean(feat_j, axis=0)
|
||||
matrix = 1- np.maximum(0.0, cdist(feati[None, :], featj[None, :], 'cosine'))
|
||||
else:
|
||||
matrix = 1- np.maximum(0.0, cdist(feat_i, feat_j, 'cosine'))
|
||||
Similarity[i, j] = np.max(matrix)
|
||||
|
||||
if barcode_i == barcode_j:
|
||||
GtMatrix[i, j] = 1
|
||||
if i!=j: IntraMatrix[i, j] = 1
|
||||
else:
|
||||
GtMatrix[i, j] = 0
|
||||
InterMatrix[i, j] = 1
|
||||
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# '''生成相似度矩阵: Similarity '''
|
||||
# for i, key_i in enumerate(keys):
|
||||
# feat_i = np.asarray(TracKeys[key_i], dtype=np.float32)
|
||||
# for j, key_j in enumerate(keys):
|
||||
# feat_j = np.asarray(TracKeys[key_j], dtype=np.float32)
|
||||
#
|
||||
# if similarmode == 'mean':
|
||||
# feati = np.mean(feat_i, axis=0)
|
||||
# featj = np.mean(feat_j, axis=0)
|
||||
# matrix = 1- np.maximum(0.0, cdist(feati[None, :], featj[None, :], 'cosine'))
|
||||
# else:
|
||||
# matrix = 1- np.maximum(0.0, cdist(feat_i, feat_j, 'cosine'))
|
||||
# Similarity[i, j] = np.max(matrix)
|
||||
# =============================================================================
|
||||
|
||||
MatchDict = {'keys': keys, 'GtMatrix':GtMatrix, 'Similarity':Similarity,
|
||||
'IntraMatrix':IntraMatrix, 'InterMatrix':InterMatrix}
|
||||
with open(r"./matching/featdata/MatchDict_noplane.pkl", "wb") as f:
|
||||
pickle.dump(MatchDict, f)
|
||||
|
||||
df_GtMatrix = pd.DataFrame(data=GtMatrix, columns = keys, index = keys)
|
||||
df_GtMatrix.to_csv('./matching/featdata/GtMatrix_noplane.csv',index=True)
|
||||
|
||||
df_similarity = pd.DataFrame(data=Similarity, columns = keys, index = keys)
|
||||
df_similarity.to_csv('./matching/featdata/Similarity_noplane.csv',index=True)
|
||||
|
||||
|
||||
def sortN_matching(filename = r'./matching/featdata/MatchDict.pkl'):
|
||||
SimilarDict = np.load(filename, allow_pickle=True)
|
||||
|
||||
'''********** keys的顺序与Similarity中行列值索引一一对应 **********'''
|
||||
keys = SimilarDict['keys']
|
||||
Similarity = SimilarDict['Similarity']
|
||||
|
||||
'''1. 将时间根据 Barcode 归并,并确保每个 Barcode 下至少两个事件'''
|
||||
BarcodeDict1 = {}
|
||||
for i, key in enumerate(keys):
|
||||
barcode = key.split('_')[0]
|
||||
|
||||
if barcode not in BarcodeDict1.keys():
|
||||
BarcodeDict1[barcode] = []
|
||||
BarcodeDict1[barcode].append(i)
|
||||
|
||||
|
||||
BarcodeDict = {}
|
||||
BarcodeList = []
|
||||
for barcode, value in BarcodeDict1.items():
|
||||
if len(value) < 2: continue
|
||||
BarcodeDict[barcode] = value
|
||||
BarcodeList.append(barcode)
|
||||
|
||||
BarcodeList = list(set(BarcodeList))
|
||||
|
||||
'''实验参数设定
|
||||
N: 任意选取的 Barcode 数
|
||||
R:重复实验次数,每次从同一 Barcode 下随机选取2个事件,分别归入加购、退购集合
|
||||
Thresh:相似度阈值
|
||||
|
||||
'''
|
||||
N = 10
|
||||
if N > len(BarcodeList):
|
||||
N = math.ceil(len(BarcodeList)/2)
|
||||
R = 20
|
||||
Thresh = np.linspace(0.1, 1, 100)
|
||||
# Thresh = np.linspace(0.601, 0.7, 100)
|
||||
|
||||
Recall, Precision = [], []
|
||||
for th in Thresh:
|
||||
recall = np.zeros((1, R), dtype=np.float32)
|
||||
precision = np.zeros((1, R), dtype=np.float32)
|
||||
|
||||
for rep in range(R):
|
||||
BarcodeSelect = random.sample(BarcodeList, N)
|
||||
|
||||
AddDict = {}
|
||||
TakeoutDict = {}
|
||||
for barcode in BarcodeSelect:
|
||||
barlist = BarcodeDict[barcode]
|
||||
if len(barlist) < 2:continue
|
||||
|
||||
selected = random.sample(barlist, 2)
|
||||
|
||||
AddDict[barcode] = selected[0]
|
||||
TakeoutDict[barcode] = selected[1]
|
||||
|
||||
OrderMatrix = np.zeros((N, N), dtype=np.float32)
|
||||
GTMatrix = np.zeros((N, N), dtype=np.float32)
|
||||
|
||||
MatchMatrix_1 = np.zeros((N, N), dtype=np.float32)
|
||||
MatchMatrix_2 = np.zeros((N, N), dtype=np.float32)
|
||||
|
||||
i = 0
|
||||
for keyi in BarcodeSelect:
|
||||
ii = TakeoutDict[keyi]
|
||||
j = 0
|
||||
for keyj in BarcodeSelect:
|
||||
jj = AddDict[keyj]
|
||||
|
||||
OrderMatrix[i, j] = Similarity[int(ii), int(jj)]
|
||||
|
||||
if keyi == keyj:
|
||||
GTMatrix[i, j] = 1
|
||||
|
||||
j += 1
|
||||
i += 1
|
||||
|
||||
max_indices = np.argmax(OrderMatrix, axis = 1)
|
||||
for i in range(N):
|
||||
MatchMatrix_1[i, max_indices[i]] = 1
|
||||
|
||||
similar = OrderMatrix[i, max_indices[i]]
|
||||
if similar > th:
|
||||
MatchMatrix_2[i, max_indices[i]] = 1
|
||||
|
||||
|
||||
GT_indices = np.where(GTMatrix == 1)
|
||||
|
||||
FNTP = MatchMatrix_2[GT_indices]
|
||||
pred_indices = np.where(MatchMatrix_2 == 1)
|
||||
|
||||
|
||||
|
||||
TP = np.sum(FNTP==1)
|
||||
FN = np.sum(FNTP==0)
|
||||
FPTP = GTMatrix[pred_indices]
|
||||
|
||||
FP = np.sum(FPTP == 0)
|
||||
# assert TP == np.sum(FPTP == 0), "Please Check Errors!!!"
|
||||
|
||||
recall[0, rep] = TP/(TP+FN)
|
||||
precision[0, rep] = TP/(TP+FP+1e-3) # 阈值太大时可能TP、FP都为0,
|
||||
|
||||
Recall.append(recall)
|
||||
Precision.append(precision)
|
||||
|
||||
|
||||
|
||||
Recall = np.asarray(Recall).reshape([len(Thresh),-1])
|
||||
Precision = np.asarray(Precision).reshape([len(Thresh),-1])
|
||||
|
||||
reclmean = np.sum(Recall, axis=1) / (np.count_nonzero(Recall, axis=1) + 1e-3)
|
||||
precmean = np.sum(Precision, axis=1) / (np.count_nonzero(Precision, axis=1) + 1e-3)
|
||||
|
||||
print("Done!!!!!")
|
||||
|
||||
# th1, recl = [c[0] for c in Recall], [c[1] for c in Recall]
|
||||
# th2, prep = [c[0] for c in Precision], [c[1] for c in Precision]
|
||||
|
||||
recl = [r for r in reclmean]
|
||||
prep = [p for p in precmean]
|
||||
|
||||
|
||||
'''================= Precision & Recall ================='''
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(Thresh, recl, 'g', label='Recall = TP/(TP+FN)')
|
||||
ax.plot(Thresh, prep, 'r', label='PrecisePos = TP/(TP+FP)')
|
||||
# ax.set_xlim([0, 1])
|
||||
# ax.set_ylim([0, 1])
|
||||
ax.grid(True)
|
||||
ax.set_title('Precision & Recall')
|
||||
ax.legend()
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
def match_evaluate(filename = r'./matching/featdata/MatchDict.pkl'):
|
||||
SimiDict = np.load(filename, allow_pickle=True)
|
||||
|
||||
keys = SimiDict['keys']
|
||||
GtMatrix = SimiDict['GtMatrix']
|
||||
Similarity = SimiDict['Similarity']
|
||||
|
||||
IntraMatrix = SimiDict['IntraMatrix']
|
||||
InterMatrix = SimiDict['InterMatrix']
|
||||
|
||||
|
||||
BarcodeList = []
|
||||
for key in keys:
|
||||
BarcodeList.append(key.split('_')[0])
|
||||
BarcodeList = list(set(BarcodeList))
|
||||
|
||||
|
||||
IntraRows, IntraCols = np.nonzero(IntraMatrix)
|
||||
InterRows, InterCols = np.nonzero(InterMatrix)
|
||||
IntraN, InterN = len(IntraRows), len(InterRows)
|
||||
assert IntraN <= InterN, "类内大于类间数,样本不平衡"
|
||||
|
||||
InterNSelect = IntraN
|
||||
Thresh = np.linspace(0.1, 1, 100)
|
||||
# Thresh = np.linspace(0.2, 0.4, 11)
|
||||
Correct = []
|
||||
PrecisePos = []
|
||||
PreciseNeg = []
|
||||
Recall = []
|
||||
CorrectMatries = []
|
||||
for th in Thresh:
|
||||
MatchMatrix = Similarity > th
|
||||
CorrectMatrix = MatchMatrix == GtMatrix
|
||||
|
||||
CorrectMatries.append(CorrectMatrix)
|
||||
|
||||
nn = np.random.permutation(np.arange(InterN))[:InterNSelect]
|
||||
InterRowsSelect, InterColsSelect = InterRows[nn], InterCols[nn]
|
||||
|
||||
IntraCorrMatrix = CorrectMatrix[IntraRows, IntraCols]
|
||||
InterCorrMatrix = CorrectMatrix[InterRowsSelect, InterColsSelect]
|
||||
|
||||
TP = np.sum(IntraCorrMatrix)
|
||||
TN = np.sum(InterCorrMatrix)
|
||||
FN = IntraN - TP
|
||||
FP = InterNSelect - TN
|
||||
|
||||
if TP+FP > 0:
|
||||
PrecisePos.append((th, TP/(TP+FP)))
|
||||
if TN+FN > 0:
|
||||
PreciseNeg.append((th, TN/(TN+FN)))
|
||||
if TP+FN > 0:
|
||||
Recall.append((th, TP/(TP+FN)))
|
||||
|
||||
if TP+TN+FP+FN > 0:
|
||||
Correct.append((th, (TP+TN)/(TP+TN+FP+FN)))
|
||||
# print(f'Th: {th}')
|
||||
# print(f'TP:{TP}, FP:{FP}, TN:{TN}, FN:{FN}')
|
||||
|
||||
|
||||
CorrectMatries = np.asarray(CorrectMatries)
|
||||
|
||||
'''====================== 分析错误原因 =========================='''
|
||||
'''
|
||||
keys两种构成方式,其中的元素来自于:MatchingDict
|
||||
BarCode, SampleTime, ActionType #不考虑摄像头类型(前后摄)
|
||||
BarCode, SampleTime, CameraType, ActionType# 考虑摄像头类型(前后摄)
|
||||
为了便于显示,在图像文件名中,将 ActionType 进行了缩写,匹配时取 [:3]
|
||||
"addGood" --------> "add"
|
||||
"returnGood" --------> "return"
|
||||
'''
|
||||
##============= 获取图像存储位置,可以通过 keys 检索到对应的图像文件
|
||||
imgpath = r'./matching/images/'
|
||||
ImgFileList = get_img_filename(imgpath)
|
||||
|
||||
rowx, colx = np.where(CorrectMatries[66,:,:] == False)
|
||||
rows, cols = [], []
|
||||
for i in range(len(rowx)):
|
||||
ri, ci = rowx[i], colx[i]
|
||||
if ci > ri:
|
||||
rows.append(ri)
|
||||
cols.append(ci)
|
||||
|
||||
|
||||
KeysError = [(keys[rows[i]], keys[cols[i]]) for i in range(len(rows))]
|
||||
SimiScore = [Similarity[rows[i], cols[i]] for i in range(len(rows))]
|
||||
|
||||
|
||||
for i, keykey in enumerate(KeysError):
|
||||
key1, key2 = keykey
|
||||
sscore = SimiScore[i]
|
||||
|
||||
kt1, kt2 = key1.split('_'), key2.split('_')
|
||||
|
||||
if len(kt1)==3 and len(kt2)==3:
|
||||
file1 = [f for f in ImgFileList if kt1[0] in f and kt1[1] in f and kt1[2][:3] in f]
|
||||
file2 = [f for f in ImgFileList if kt2[0] in f and kt2[1] in f and kt2[2][:3] in f]
|
||||
|
||||
elif len(kt1)==4 and len(kt1)==4:
|
||||
file1 = [f for f in ImgFileList if kt1[0] in f and kt1[1] in f and kt1[2] in f and kt1[3][:3] in f]
|
||||
file2 = [f for f in ImgFileList if kt2[0] in f and kt2[1] in f and kt2[2] in f and kt2[3][:3] in f]
|
||||
else:
|
||||
pass
|
||||
|
||||
if len(file1)==0 or len(file2)==0:
|
||||
continue
|
||||
|
||||
if kt1[0] == kt2[0]:
|
||||
gt = "same"
|
||||
else:
|
||||
gt = "diff"
|
||||
|
||||
path = Path(f'./matching/results/{i}_{gt}_{sscore:.2f}')
|
||||
if path.exists() and path.is_dir():
|
||||
shutil.rmtree(path)
|
||||
path1, path2 = path.joinpath(key1), path.joinpath(key2)
|
||||
|
||||
path1.mkdir(parents=True, exist_ok=True)
|
||||
path2.mkdir(parents=True, exist_ok=True)
|
||||
for file in file1:
|
||||
shutil.copy2(file, path1)
|
||||
for file in file2:
|
||||
shutil.copy2(file, path2)
|
||||
|
||||
if i==99:
|
||||
break
|
||||
|
||||
th1, corr = [c[0] for c in Correct], [c[1] for c in Correct]
|
||||
th2, recl = [c[0] for c in Recall], [c[1] for c in Recall]
|
||||
th3, prep = [c[0] for c in PrecisePos], [c[1] for c in PrecisePos]
|
||||
th4, pren = [c[0] for c in PreciseNeg], [c[1] for c in PreciseNeg]
|
||||
|
||||
'''================= Correct ==================='''
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(th1, corr, 'b', label='Correct = (TP+TN)/(TP+TN+FP+FN)')
|
||||
max_corr = max(corr)
|
||||
max_index = corr.index(max_corr)
|
||||
max_thresh = th1[max_index]
|
||||
ax.plot([0, max_thresh], [max_corr, max_corr], 'r--')
|
||||
ax.plot([max_thresh, max_thresh], [0, max_corr], 'r--')
|
||||
ax.set_xlim([0, 1])
|
||||
ax.set_ylim([0, 1])
|
||||
ax.grid(True)
|
||||
ax.set_title('Correct')
|
||||
ax.legend()
|
||||
plt.show()
|
||||
|
||||
|
||||
'''================= PrecisePos & PreciseNeg & Recall ================='''
|
||||
fig, ax = plt.subplots()
|
||||
ax.plot(th2, recl, 'g', label='Recall = TP/(TP+FN)')
|
||||
ax.plot(th3, prep, 'c', label='PrecisePos = TP/(TP+FP)')
|
||||
ax.plot(th4, pren, 'm', label='PreciseNeg = TN/(TN+FN)')
|
||||
|
||||
ax.set_xlim([0, 1])
|
||||
ax.set_ylim([0, 1])
|
||||
ax.grid(True)
|
||||
ax.set_title('PrecisePos & PreciseNeg')
|
||||
ax.legend()
|
||||
plt.show()
|
||||
|
||||
|
||||
def have_tracked():
|
||||
trackdir = r"./data/tracks"
|
||||
|
||||
# =============================================================================
|
||||
# FileList = []
|
||||
# with open(r'./matching/视频分类/单.txt', 'r') as file:
|
||||
# lines = file.readlines()
|
||||
# for line in lines:
|
||||
# file = line.split('.')[0]
|
||||
# FileList.append(file)
|
||||
# FileList = list(set(FileList))
|
||||
# =============================================================================
|
||||
|
||||
MatchingDict = {}
|
||||
k, gt = 0, Profile()
|
||||
for filename in os.listdir(trackdir):
|
||||
file, ext = os.path.splitext(filename)
|
||||
|
||||
# if file not in FileList: continue
|
||||
if file.find('20240508')<0: continue
|
||||
filepath = os.path.join(trackdir, filename)
|
||||
|
||||
|
||||
tracksDict = np.load(filepath, allow_pickle=True)
|
||||
bboxes = tracksDict['TrackBoxes']
|
||||
with gt:
|
||||
if filename.find("front") >= 0:
|
||||
vts = doFrontTracks(bboxes, tracksDict)
|
||||
vts.classify()
|
||||
|
||||
elif filename.find("back") >= 0:
|
||||
vts = doBackTracks(bboxes, tracksDict)
|
||||
vts.classify()
|
||||
|
||||
print(file+f" need time: {gt.dt:.2f}s")
|
||||
|
||||
elements = file.split('_')
|
||||
assert len(elements) == 7, f"{filename} fields num: {len(elements)}"
|
||||
BarCode = elements[0]
|
||||
|
||||
## ====================================== 只用于在images文件夹下保存图片
|
||||
SampleTime = elements[1].split('-')[1]
|
||||
CameraType = elements[2]
|
||||
if elements[3]=="addGood":
|
||||
ActionType = "add"
|
||||
elif elements[3]=="returnGood":
|
||||
ActionType = "return"
|
||||
else:
|
||||
ActionType = "x"
|
||||
|
||||
subimg_dir = Path(f'./matching/images/{BarCode}_{SampleTime}_{ActionType}/')
|
||||
if not subimg_dir.exists():
|
||||
subimg_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# boxes: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
# 0, 1, 2, 3, 4, 5, 6, 7, 8
|
||||
for track in vts.Residual:
|
||||
boxes = track.boxes
|
||||
for i in range(boxes.shape[0]):
|
||||
box = boxes[i, :]
|
||||
tid, fid, bid = int(box[4]), int(box[7]), int(box[8])
|
||||
|
||||
feat_dict = tracksDict[fid]
|
||||
feature = feat_dict[bid]
|
||||
img = feat_dict[f'{bid}_img']
|
||||
|
||||
sub_img_file = subimg_dir.joinpath(f"{BarCode}_{SampleTime}_{CameraType}_{ActionType}_{tid}_{fid}_{bid}.png")
|
||||
cv2.imwrite(str(sub_img_file), img)
|
||||
|
||||
condict = {f"{file}_{tid}_{fid}_{bid}": {'img': img, 'feature': feature}}
|
||||
|
||||
MatchingDict.update(condict)
|
||||
# k += 1
|
||||
# if k == 100:
|
||||
# break
|
||||
|
||||
featpath = Path('./matching/featdata/')
|
||||
if not featpath.exists():
|
||||
featpath.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
featdata = featpath.joinpath('imgs_feats_data_noplane.pkl')
|
||||
with open(featdata, 'wb') as file:
|
||||
pickle.dump(MatchingDict, file)
|
||||
|
||||
def imgsample_cleaning():
|
||||
ContrastDict = np.load('./matching/featdata/imgs_feats_data.pkl', allow_pickle=True)
|
||||
print(f"The Num of imgsample: {len(ContrastDict)}")
|
||||
|
||||
|
||||
MatchingDict_refined = {}
|
||||
for filename, value in ContrastDict.items():
|
||||
elements = filename.split('_')
|
||||
|
||||
|
||||
tid = elements[7]
|
||||
fid = elements[8]
|
||||
bid = elements[9]
|
||||
|
||||
BarCode = elements[0]
|
||||
SampleTime = elements[1].split('-')[1]
|
||||
CameraType = elements[2]
|
||||
if elements[3]=="addGood":
|
||||
ActionType = "add"
|
||||
elif elements[3]=="returnGood":
|
||||
ActionType = "return"
|
||||
else:
|
||||
ActionType = "x"
|
||||
refimgdir = f'.\matching\images_refined\{BarCode}_{SampleTime}_{ActionType}'
|
||||
|
||||
file = '_'.join(elements[0:7])
|
||||
if os.path.exists(refimgdir) and os.path.isdir(refimgdir):
|
||||
imgpath = os.path.join(refimgdir, f"{BarCode}_{SampleTime}_{CameraType}_{ActionType}_{tid}_{fid}_{bid}.png")
|
||||
if os.path.isfile(imgpath):
|
||||
condict = {f"{file}_{tid}_{fid}_{bid}": value}
|
||||
|
||||
MatchingDict_refined.update(condict)
|
||||
|
||||
featpath = Path('./matching/featdata/')
|
||||
if not featpath.exists():
|
||||
featpath.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
featdata = featpath.joinpath('imgs_feats_data_refined.pkl')
|
||||
with open(featdata, 'wb') as file:
|
||||
pickle.dump(MatchingDict_refined, file)
|
||||
|
||||
print(f"The Num of ContrastDict: {len(ContrastDict)}")
|
||||
print(f"The Num of MatchingDict_refined: {len(MatchingDict_refined)}")
|
||||
print(f"The Num of cleaned img: {len(ContrastDict)} - {len(MatchingDict_refined)}")
|
||||
|
||||
|
||||
def main():
|
||||
'''1. 提取运动商品轨迹'''
|
||||
# have_tracked()
|
||||
|
||||
'''2. 清除一次事件中包含多件商品的事件'''
|
||||
# imgsample_cleaning()
|
||||
|
||||
'''3.1 计算事件间相似度: 将 front、back 的所有 track 特征合并'''
|
||||
calculate_similarity()
|
||||
|
||||
'''3.2 计算事件间相似度: 考虑前后摄的不同组合,或 track 间的不同组合'''
|
||||
# calculate_similarity_track()
|
||||
|
||||
|
||||
'''4.1 事件间匹配的总体性能评估'''
|
||||
filename = r'./matching/featdata/MatchDict_plane.pkl'
|
||||
match_evaluate(filename)
|
||||
|
||||
filename = r'./matching/featdata/MatchDict_noplane.pkl'
|
||||
match_evaluate(filename)
|
||||
|
||||
'''4.2 模拟实际场景,任选N件作为一组作为加购,取出其中一件时的性能评估'''
|
||||
# filename = r'./matching/featdata/MatchDict_refined.pkl'
|
||||
# sortN_matching(filename)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# save_dir = Path(f'./result/')
|
||||
# read_csv_file()
|
||||
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
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
|
807
tracking/matching/视频分类/单.txt
Normal file
807
tracking/matching/视频分类/单.txt
Normal file
@ -0,0 +1,807 @@
|
||||
230537101280010007_20240411-144918_back_addGood_70f75407b7ae_570_17788571404.mp4
|
||||
230537101280010007_20240411-144918_front_addGood_70f75407b7ae_570_17788571404.mp4
|
||||
230537101280010007_20240411-144945_back_returnGood_70f75407b7ae_565_17788571404.mp4
|
||||
230537101280010007_20240411-144945_front_returnGood_70f75407b7ae_565_17788571404.mp4
|
||||
230538001280010009_20240411-144924_back_addGood_70f754088050_550_17327712807.mp4
|
||||
230538001280010009_20240411-144924_front_addGood_70f754088050_550_17327712807.mp4
|
||||
230538001280010009_20240411-144934_back_returnGood_70f754088050_550_17327712807.mp4
|
||||
230538001280010009_20240411-144934_front_returnGood_70f754088050_550_17327712807.mp4
|
||||
2500456001326_20240411-145321_back_addGood_70f75407b7ae_155_17788571404.mp4
|
||||
2500456001326_20240411-145321_front_addGood_70f75407b7ae_155_17788571404.mp4
|
||||
2500456001326_20240411-145327_back_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
2500456001326_20240411-145327_front_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
2500456001326_20240411-145330_back_addGood_70f754088050_155_17327712807.mp4
|
||||
2500456001326_20240411-145330_front_addGood_70f754088050_155_17327712807.mp4
|
||||
2500456001326_20240411-145338_back_returnGood_70f754088050_155_17327712807.mp4
|
||||
2500456001326_20240411-145338_front_returnGood_70f754088050_155_17327712807.mp4
|
||||
2500458675341_20240411-144658_back_addGood_70f75407b7ae_140_17788571404.mp4
|
||||
2500458675341_20240411-144658_front_addGood_70f75407b7ae_140_17788571404.mp4
|
||||
2500458675341_20240411-144707_back_returnGood_70f75407b7ae_140_17788571404.mp4
|
||||
2500458675341_20240411-144707_front_returnGood_70f75407b7ae_140_17788571404.mp4
|
||||
2500458675341_20240411-144711_back_addGood_70f754088050_135_17327712807.mp4
|
||||
2500458675341_20240411-144711_front_addGood_70f754088050_135_17327712807.mp4
|
||||
2500458675341_20240411-144718_back_returnGood_70f754088050_135_17327712807.mp4
|
||||
2500458675341_20240411-144718_front_returnGood_70f754088050_135_17327712807.mp4
|
||||
2500463464671_20240411-145041_back_addGood_70f75407b7ae_805_17788571404.mp4
|
||||
2500463464671_20240411-145041_front_addGood_70f75407b7ae_805_17788571404.mp4
|
||||
2500463464671_20240411-145042_back_addGood_70f754088050_815_17327712807.mp4
|
||||
2500463464671_20240411-145042_front_addGood_70f754088050_815_17327712807.mp4
|
||||
2500463464671_20240411-145049_back_returnGood_70f754088050_815_17327712807.mp4
|
||||
2500463464671_20240411-145049_front_returnGood_70f754088050_815_17327712807.mp4
|
||||
2500463464671_20240411-145053_back_returnGood_70f75407b7ae_810_17788571404.mp4
|
||||
2500463464671_20240411-145053_front_returnGood_70f75407b7ae_810_17788571404.mp4
|
||||
6901070613142_20240411-142722_back_addGood_70f754088050_240_17327712807.mp4
|
||||
6901070613142_20240411-142722_front_addGood_70f754088050_240_17327712807.mp4
|
||||
6901070613142_20240411-142725_back_addGood_70f75407b7ae_240_17788571404.mp4
|
||||
6901070613142_20240411-142725_front_addGood_70f75407b7ae_240_17788571404.mp4
|
||||
6901070613142_20240411-142730_back_returnGood_70f754088050_240_17327712807.mp4
|
||||
6901070613142_20240411-142730_front_returnGood_70f754088050_240_17327712807.mp4
|
||||
6901070613142_20240411-142734_back_returnGood_70f75407b7ae_240_17788571404.mp4
|
||||
6901070613142_20240411-142734_front_returnGood_70f75407b7ae_240_17788571404.mp4
|
||||
6901668053893_20240411-143608_back_addGood_70f75407b7ae_70_17788571404.mp4
|
||||
6901668053893_20240411-143608_back_addGood_70f754088050_70_17327712807.mp4
|
||||
6901668053893_20240411-143608_front_addGood_70f75407b7ae_70_17788571404.mp4
|
||||
6901668053893_20240411-143608_front_addGood_70f754088050_70_17327712807.mp4
|
||||
6901668053893_20240411-143616_back_returnGood_70f754088050_70_17327712807.mp4
|
||||
6901668053893_20240411-143616_front_returnGood_70f754088050_70_17327712807.mp4
|
||||
6901668053893_20240411-143617_back_returnGood_70f75407b7ae_70_17788571404.mp4
|
||||
6901668053893_20240411-143617_front_returnGood_70f75407b7ae_70_17788571404.mp4
|
||||
6902007010249_20240411-142528_back_addGood_70f75407b7ae_755_17788571404.mp4
|
||||
6902007010249_20240411-142528_back_addGood_70f754088050_755_17327712807.mp4
|
||||
6902007010249_20240411-142528_front_addGood_70f75407b7ae_755_17788571404.mp4
|
||||
6902007010249_20240411-142528_front_addGood_70f754088050_755_17327712807.mp4
|
||||
6902007010249_20240411-142535_back_returnGood_70f75407b7ae_755_17788571404.mp4
|
||||
6902007010249_20240411-142535_front_returnGood_70f75407b7ae_755_17788571404.mp4
|
||||
6902007010249_20240411-142541_back_returnGood_70f754088050_755_17327712807.mp4
|
||||
6902007010249_20240411-142541_front_returnGood_70f754088050_755_17327712807.mp4
|
||||
6902022135514_20240411-142819_back_addGood_70f75407b7ae_3180_17788571404.mp4
|
||||
6902022135514_20240411-142819_front_addGood_70f75407b7ae_3180_17788571404.mp4
|
||||
6902022135514_20240411-142828_back_addGood_70f754088050_3185_17327712807.mp4
|
||||
6902022135514_20240411-142828_front_addGood_70f754088050_3185_17327712807.mp4
|
||||
6902022135514_20240411-142830_back_returnGood_70f75407b7ae_3180_17788571404.mp4
|
||||
6902022135514_20240411-142830_front_returnGood_70f75407b7ae_3180_17788571404.mp4
|
||||
6902022135514_20240411-142840_back_returnGood_70f754088050_3185_17327712807.mp4
|
||||
6902022135514_20240411-142840_front_returnGood_70f754088050_3185_17327712807.mp4
|
||||
6902265114369_20240411-142331_back_addGood_70f75407b7ae_715_17788571404.mp4
|
||||
6902265114369_20240411-142331_front_addGood_70f75407b7ae_715_17788571404.mp4
|
||||
6902265114369_20240411-142338_back_returnGood_70f75407b7ae_720_17788571404.mp4
|
||||
6902265114369_20240411-142338_front_returnGood_70f75407b7ae_720_17788571404.mp4
|
||||
6902265114369_20240411-142355_back_addGood_70f754088050_720_17327712807.mp4
|
||||
6902265114369_20240411-142355_front_addGood_70f754088050_720_17327712807.mp4
|
||||
6902265114369_20240411-142403_back_returnGood_70f754088050_715_17327712807.mp4
|
||||
6902265114369_20240411-142403_front_returnGood_70f754088050_715_17327712807.mp4
|
||||
6902265908012_20240411-142446_back_addGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6902265908012_20240411-142446_front_addGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6902265908012_20240411-142447_back_addGood_70f754088050_1150_17327712807.mp4
|
||||
6902265908012_20240411-142447_front_addGood_70f754088050_1150_17327712807.mp4
|
||||
6902265908012_20240411-142456_back_returnGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6902265908012_20240411-142456_front_returnGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6902265908012_20240411-142459_back_returnGood_70f754088050_1150_17327712807.mp4
|
||||
6902265908012_20240411-142459_front_returnGood_70f754088050_1150_17327712807.mp4
|
||||
69025143_20240411-163325_back_addGood_70f75407b7ae_3385_17788571404.mp4
|
||||
69025143_20240411-163325_front_addGood_70f75407b7ae_3385_17788571404.mp4
|
||||
69025143_20240411-163352_back_returnGood_70f75407b7ae_3390_17788571404.mp4
|
||||
69025143_20240411-163352_front_returnGood_70f75407b7ae_3390_17788571404.mp4
|
||||
69025143_20240411-163417_back_addGood_70f754088050_3380_17327712807.mp4
|
||||
69025143_20240411-163417_front_addGood_70f754088050_3380_17327712807.mp4
|
||||
69025143_20240411-163428_back_returnGood_70f754088050_3380_17327712807.mp4
|
||||
69025143_20240411-163428_front_returnGood_70f754088050_3380_17327712807.mp4
|
||||
6902538007367_20240411-144030_back_addGood_70f754088050_660_17327712807.mp4
|
||||
6902538007367_20240411-144030_front_addGood_70f754088050_660_17327712807.mp4
|
||||
6902538007367_20240411-144031_back_addGood_70f75407b7ae_660_17788571404.mp4
|
||||
6902538007367_20240411-144031_front_addGood_70f75407b7ae_660_17788571404.mp4
|
||||
6902538007367_20240411-144037_back_returnGood_70f75407b7ae_660_17788571404.mp4
|
||||
6902538007367_20240411-144037_front_returnGood_70f75407b7ae_660_17788571404.mp4
|
||||
6902538007367_20240411-144040_back_returnGood_70f754088050_660_17327712807.mp4
|
||||
6902538007367_20240411-144040_front_returnGood_70f754088050_660_17327712807.mp4
|
||||
69028571_20240411-163728_back_addGood_70f75407b7ae_1845_17788571404.mp4
|
||||
69028571_20240411-163728_front_addGood_70f75407b7ae_1845_17788571404.mp4
|
||||
69028571_20240411-163739_back_returnGood_70f75407b7ae_1845_17788571404.mp4
|
||||
69028571_20240411-163739_front_returnGood_70f75407b7ae_1845_17788571404.mp4
|
||||
69028571_20240411-163853_back_addGood_70f754088050_1840_17327712807.mp4
|
||||
69028571_20240411-163853_front_addGood_70f754088050_1840_17327712807.mp4
|
||||
69028571_20240411-163904_back_returnGood_70f754088050_1840_17327712807.mp4
|
||||
69028571_20240411-163904_front_returnGood_70f754088050_1840_17327712807.mp4
|
||||
6907992103952_20240411-142013_back_addGood_70f75407b7ae_190_17788571404.mp4
|
||||
6907992103952_20240411-142013_front_addGood_70f75407b7ae_190_17788571404.mp4
|
||||
6907992103952_20240411-142021_back_returnGood_70f75407b7ae_190_17788571404.mp4
|
||||
6907992103952_20240411-142021_front_returnGood_70f75407b7ae_190_17788571404.mp4
|
||||
6907992103952_20240411-142029_back_addGood_70f754088050_190_17327712807.mp4
|
||||
6907992103952_20240411-142029_front_addGood_70f754088050_190_17327712807.mp4
|
||||
6907992103952_20240411-142036_back_returnGood_70f754088050_190_17327712807.mp4
|
||||
6907992103952_20240411-142036_front_returnGood_70f754088050_190_17327712807.mp4
|
||||
6907992104157_20240411-141633_back_addGood_70f754088050_1120_17327712807.mp4
|
||||
6907992104157_20240411-141633_front_addGood_70f754088050_1120_17327712807.mp4
|
||||
6907992104157_20240411-141637_back_addGood_70f75407b7ae_1120_17788571404.mp4
|
||||
6907992104157_20240411-141637_front_addGood_70f75407b7ae_1120_17788571404.mp4
|
||||
6907992104157_20240411-141646_back_returnGood_70f75407b7ae_1120_17788571404.mp4
|
||||
6907992104157_20240411-141646_back_returnGood_70f754088050_1120_17327712807.mp4
|
||||
6907992104157_20240411-141646_front_returnGood_70f75407b7ae_1120_17788571404.mp4
|
||||
6907992104157_20240411-141646_front_returnGood_70f754088050_1120_17327712807.mp4
|
||||
6907992105765_20240411-142214_back_addGood_70f754088050_1860_17327712807.mp4
|
||||
6907992105765_20240411-142214_front_addGood_70f754088050_1860_17327712807.mp4
|
||||
6907992105765_20240411-142220_back_addGood_70f75407b7ae_1855_17788571404.mp4
|
||||
6907992105765_20240411-142220_front_addGood_70f75407b7ae_1855_17788571404.mp4
|
||||
6907992105765_20240411-142228_back_returnGood_70f754088050_1860_17327712807.mp4
|
||||
6907992105765_20240411-142228_front_returnGood_70f754088050_1860_17327712807.mp4
|
||||
6907992105765_20240411-142230_back_returnGood_70f75407b7ae_1855_17788571404.mp4
|
||||
6907992105765_20240411-142230_front_returnGood_70f75407b7ae_1855_17788571404.mp4
|
||||
6907992106113_20240411-142100_back_addGood_70f754088050_3085_17327712807.mp4
|
||||
6907992106113_20240411-142100_front_addGood_70f754088050_3085_17327712807.mp4
|
||||
6907992106113_20240411-142133_back_returnGood_70f754088050_3085_17327712807.mp4
|
||||
6907992106113_20240411-142133_front_returnGood_70f754088050_3085_17327712807.mp4
|
||||
6907992106113_20240411-142149_back_addGood_70f75407b7ae_3085_17788571404.mp4
|
||||
6907992106113_20240411-142149_front_addGood_70f75407b7ae_3085_17788571404.mp4
|
||||
6907992106113_20240411-142157_back_returnGood_70f75407b7ae_3080_17788571404.mp4
|
||||
6907992106113_20240411-142157_front_returnGood_70f75407b7ae_3080_17788571404.mp4
|
||||
6907992106205_20240411-141741_back_addGood_70f75407b7ae_795_17788571404.mp4
|
||||
6907992106205_20240411-141741_front_addGood_70f75407b7ae_795_17788571404.mp4
|
||||
6907992106205_20240411-141750_back_returnGood_70f75407b7ae_795_17788571404.mp4
|
||||
6907992106205_20240411-141750_front_returnGood_70f75407b7ae_795_17788571404.mp4
|
||||
6907992106205_20240411-141806_back_addGood_70f754088050_795_17327712807.mp4
|
||||
6907992106205_20240411-141806_front_addGood_70f754088050_795_17327712807.mp4
|
||||
6907992106205_20240411-141815_back_returnGood_70f754088050_795_17327712807.mp4
|
||||
6907992106205_20240411-141815_front_returnGood_70f754088050_795_17327712807.mp4
|
||||
6907992106311_20240411-141711_back_addGood_70f754088050_880_17327712807.mp4
|
||||
6907992106311_20240411-141711_front_addGood_70f754088050_880_17327712807.mp4
|
||||
6907992106311_20240411-141713_back_addGood_70f75407b7ae_885_17788571404.mp4
|
||||
6907992106311_20240411-141713_front_addGood_70f75407b7ae_885_17788571404.mp4
|
||||
6907992106311_20240411-141721_back_returnGood_70f75407b7ae_885_17788571404.mp4
|
||||
6907992106311_20240411-141721_front_returnGood_70f75407b7ae_885_17788571404.mp4
|
||||
6907992106311_20240411-141726_back_returnGood_70f754088050_880_17327712807.mp4
|
||||
6907992106311_20240411-141726_front_returnGood_70f754088050_880_17327712807.mp4
|
||||
6914973602908_20240411-162105_back_returnGood_70f75407b7ae_5_17788571404.mp4
|
||||
6914973602908_20240411-162105_front_returnGood_70f75407b7ae_5_17788571404.mp4
|
||||
6914973602908_20240411-162113_back_returnGood_70f75407b7ae_740_17788571404.mp4
|
||||
6914973602908_20240411-162113_front_returnGood_70f75407b7ae_740_17788571404.mp4
|
||||
6914973602908_20240411-162420_back_addGood_70f754088050_740_17327712807.mp4
|
||||
6914973602908_20240411-162420_front_addGood_70f754088050_740_17327712807.mp4
|
||||
6914973602908_20240411-162434_back_returnGood_70f754088050_740_17327712807.mp4
|
||||
6914973602908_20240411-162434_front_returnGood_70f754088050_740_17327712807.mp4
|
||||
6914973606340_20240411-161927_back_addGood_70f75407b7ae_350_17788571404.mp4
|
||||
6914973606340_20240411-161927_front_addGood_70f75407b7ae_350_17788571404.mp4
|
||||
6914973606340_20240411-161936_back_returnGood_70f75407b7ae_355_17788571404.mp4
|
||||
6914973606340_20240411-161936_front_returnGood_70f75407b7ae_355_17788571404.mp4
|
||||
6914973606340_20240411-162001_back_addGood_70f754088050_5_17327712807.mp4
|
||||
6914973606340_20240411-162001_front_addGood_70f754088050_5_17327712807.mp4
|
||||
6914973606340_20240411-162009_back_returnGood_70f754088050_355_17327712807.mp4
|
||||
6914973606340_20240411-162009_front_returnGood_70f754088050_355_17327712807.mp4
|
||||
6920152400630_20240411-144222_back_addGood_70f75407b7ae_580_17788571404.mp4
|
||||
6920152400630_20240411-144222_front_addGood_70f75407b7ae_580_17788571404.mp4
|
||||
6920152400630_20240411-144223_back_addGood_70f754088050_570_17327712807.mp4
|
||||
6920152400630_20240411-144223_front_addGood_70f754088050_570_17327712807.mp4
|
||||
6920152400630_20240411-144228_back_returnGood_70f75407b7ae_575_17788571404.mp4
|
||||
6920152400630_20240411-144228_front_returnGood_70f75407b7ae_575_17788571404.mp4
|
||||
6920152400630_20240411-144236_back_returnGood_70f754088050_570_17327712807.mp4
|
||||
6920152400630_20240411-144236_front_returnGood_70f754088050_570_17327712807.mp4
|
||||
6920174757101_20240411-143201_back_addGood_70f75407b7ae_1305_17788571404.mp4
|
||||
6920174757101_20240411-143201_front_addGood_70f75407b7ae_1305_17788571404.mp4
|
||||
6920174757101_20240411-143202_back_addGood_70f754088050_1305_17327712807.mp4
|
||||
6920174757101_20240411-143202_front_addGood_70f754088050_1305_17327712807.mp4
|
||||
6920174757101_20240411-143208_back_returnGood_70f75407b7ae_1305_17788571404.mp4
|
||||
6920174757101_20240411-143208_front_returnGood_70f75407b7ae_1305_17788571404.mp4
|
||||
6920174757101_20240411-143211_back_returnGood_70f754088050_1305_17327712807.mp4
|
||||
6920174757101_20240411-143211_front_returnGood_70f754088050_1305_17327712807.mp4
|
||||
6920459905012_20240411-143922_back_addGood_70f75407b7ae_550_17788571404.mp4
|
||||
6920459905012_20240411-143922_front_addGood_70f75407b7ae_550_17788571404.mp4
|
||||
6920459905012_20240411-143930_back_returnGood_70f75407b7ae_555_17788571404.mp4
|
||||
6920459905012_20240411-143930_front_returnGood_70f75407b7ae_555_17788571404.mp4
|
||||
6920459905012_20240411-143947_back_addGood_70f754088050_550_17327712807.mp4
|
||||
6920459905012_20240411-143947_front_addGood_70f754088050_550_17327712807.mp4
|
||||
6920459905012_20240411-143956_back_returnGood_70f754088050_550_17327712807.mp4
|
||||
6920459905012_20240411-143956_front_returnGood_70f754088050_550_17327712807.mp4
|
||||
6920907810707_20240411-143338_back_addGood_70f754088050_75_17327712807.mp4
|
||||
6920907810707_20240411-143338_front_addGood_70f754088050_75_17327712807.mp4
|
||||
6920907810707_20240411-143339_back_addGood_70f75407b7ae_80_17788571404.mp4
|
||||
6920907810707_20240411-143339_front_addGood_70f75407b7ae_80_17788571404.mp4
|
||||
6920907810707_20240411-143347_back_returnGood_70f754088050_80_17327712807.mp4
|
||||
6920907810707_20240411-143347_front_returnGood_70f754088050_80_17327712807.mp4
|
||||
6920907810707_20240411-143356_back_returnGood_70f75407b7ae_80_17788571404.mp4
|
||||
6920907810707_20240411-143356_front_returnGood_70f75407b7ae_80_17788571404.mp4
|
||||
6922130119213_20240411-142631_back_addGood_70f754088050_1020_17327712807.mp4
|
||||
6922130119213_20240411-142631_front_addGood_70f754088050_1020_17327712807.mp4
|
||||
6922130119213_20240411-142636_back_addGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6922130119213_20240411-142636_front_addGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6922130119213_20240411-142641_back_returnGood_70f754088050_1020_17327712807.mp4
|
||||
6922130119213_20240411-142641_front_returnGood_70f754088050_1020_17327712807.mp4
|
||||
6922130119213_20240411-142647_back_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6922130119213_20240411-142647_front_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6922577700968_20240411-141822_back_addGood_70f75407b7ae_1040_17788571404.mp4
|
||||
6922577700968_20240411-141822_front_addGood_70f75407b7ae_1040_17788571404.mp4
|
||||
6922577700968_20240411-141834_back_addGood_70f754088050_1045_17327712807.mp4
|
||||
6922577700968_20240411-141834_front_addGood_70f754088050_1045_17327712807.mp4
|
||||
6922577700968_20240411-141839_back_returnGood_70f75407b7ae_1045_17788571404.mp4
|
||||
6922577700968_20240411-141839_front_returnGood_70f75407b7ae_1045_17788571404.mp4
|
||||
6922577700968_20240411-141844_back_returnGood_70f754088050_1045_17327712807.mp4
|
||||
6922577700968_20240411-141844_front_returnGood_70f754088050_1045_17327712807.mp4
|
||||
6922868291168_20240411-142913_back_addGood_70f754088050_1160_17327712807.mp4
|
||||
6922868291168_20240411-142913_front_addGood_70f754088050_1160_17327712807.mp4
|
||||
6922868291168_20240411-142921_back_addGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6922868291168_20240411-142921_front_addGood_70f75407b7ae_1150_17788571404.mp4
|
||||
6922868291168_20240411-142929_back_returnGood_70f754088050_1160_17327712807.mp4
|
||||
6922868291168_20240411-142929_front_returnGood_70f754088050_1160_17327712807.mp4
|
||||
6922868291168_20240411-142933_back_returnGood_70f75407b7ae_1155_17788571404.mp4
|
||||
6922868291168_20240411-142933_front_returnGood_70f75407b7ae_1155_17788571404.mp4
|
||||
6923450601549_20240411-162014_back_addGood_70f75407b7ae_600_17788571404.mp4
|
||||
6923450601549_20240411-162014_front_addGood_70f75407b7ae_600_17788571404.mp4
|
||||
6923450601549_20240411-162024_back_returnGood_70f75407b7ae_600_17788571404.mp4
|
||||
6923450601549_20240411-162024_front_returnGood_70f75407b7ae_600_17788571404.mp4
|
||||
6923450601549_20240411-162216_back_addGood_70f754088050_595_17327712807.mp4
|
||||
6923450601549_20240411-162216_front_addGood_70f754088050_595_17327712807.mp4
|
||||
6923450601549_20240411-162227_back_returnGood_70f754088050_595_17327712807.mp4
|
||||
6923450601549_20240411-162227_front_returnGood_70f754088050_595_17327712807.mp4
|
||||
6923450603574_20240411-163042_back_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450603574_20240411-163042_front_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450603574_20240411-163049_back_returnGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450603574_20240411-163049_front_returnGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450603574_20240411-163104_back_addGood_70f754088050_865_17327712807.mp4
|
||||
6923450603574_20240411-163104_front_addGood_70f754088050_865_17327712807.mp4
|
||||
6923450603574_20240411-163114_back_returnGood_70f754088050_865_17327712807.mp4
|
||||
6923450603574_20240411-163114_front_returnGood_70f754088050_865_17327712807.mp4
|
||||
6923450605288_20240411-161704_back_addGood_70f75407b7ae_250_17788571404.mp4
|
||||
6923450605288_20240411-161704_front_addGood_70f75407b7ae_250_17788571404.mp4
|
||||
6923450605288_20240411-161715_back_returnGood_70f75407b7ae_250_17788571404.mp4
|
||||
6923450605288_20240411-161715_front_returnGood_70f75407b7ae_250_17788571404.mp4
|
||||
6923450605288_20240411-161748_back_addGood_70f754088050_245_17327712807.mp4
|
||||
6923450605288_20240411-161748_front_addGood_70f754088050_245_17327712807.mp4
|
||||
6923450605288_20240411-161800_back_returnGood_70f754088050_245_17327712807.mp4
|
||||
6923450605288_20240411-161800_front_returnGood_70f754088050_245_17327712807.mp4
|
||||
6923450610428_20240411-161209_back_addGood_70f75407b7ae_275_17788571404.mp4
|
||||
6923450610428_20240411-161209_front_addGood_70f75407b7ae_275_17788571404.mp4
|
||||
6923450610428_20240411-161217_back_returnGood_70f75407b7ae_275_17788571404.mp4
|
||||
6923450610428_20240411-161217_front_returnGood_70f75407b7ae_275_17788571404.mp4
|
||||
6923450610428_20240411-161235_back_addGood_70f754088050_270_17327712807.mp4
|
||||
6923450610428_20240411-161235_front_addGood_70f754088050_270_17327712807.mp4
|
||||
6923450610428_20240411-161249_back_returnGood_70f754088050_275_17327712807.mp4
|
||||
6923450610428_20240411-161249_front_returnGood_70f754088050_275_17327712807.mp4
|
||||
6923450610459_20240411-162814_back_addGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450610459_20240411-162814_front_addGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450610459_20240411-162822_back_returnGood_70f75407b7ae_450_17788571404.mp4
|
||||
6923450610459_20240411-162822_front_returnGood_70f75407b7ae_450_17788571404.mp4
|
||||
6923450610459_20240411-162854_back_addGood_70f754088050_445_17327712807.mp4
|
||||
6923450610459_20240411-162854_front_addGood_70f754088050_445_17327712807.mp4
|
||||
6923450610459_20240411-162906_back_returnGood_70f754088050_445_17327712807.mp4
|
||||
6923450610459_20240411-162906_front_returnGood_70f754088050_445_17327712807.mp4
|
||||
6923450611067_20240411-162639_back_addGood_70f75407b7ae_625_17788571404.mp4
|
||||
6923450611067_20240411-162639_front_addGood_70f75407b7ae_625_17788571404.mp4
|
||||
6923450611067_20240411-162648_back_returnGood_70f75407b7ae_625_17788571404.mp4
|
||||
6923450611067_20240411-162648_front_returnGood_70f75407b7ae_625_17788571404.mp4
|
||||
6923450611067_20240411-162752_back_addGood_70f754088050_620_17327712807.mp4
|
||||
6923450611067_20240411-162752_front_addGood_70f754088050_620_17327712807.mp4
|
||||
6923450611067_20240411-162808_back_returnGood_70f754088050_625_17327712807.mp4
|
||||
6923450611067_20240411-162808_front_returnGood_70f754088050_625_17327712807.mp4
|
||||
6923450612415_20240411-160636_back_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450612415_20240411-160636_front_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6923450612415_20240411-160724_back_addGood_70f754088050_865_17327712807.mp4
|
||||
6923450612415_20240411-160724_front_addGood_70f754088050_865_17327712807.mp4
|
||||
6923450612415_20240411-160735_back_returnGood_70f754088050_865_17327712807.mp4
|
||||
6923450612415_20240411-160735_front_returnGood_70f754088050_865_17327712807.mp4
|
||||
6923450612415_20240411-161013_back_returnGood_70f75407b7ae_865_17788571404.mp4
|
||||
6923450612415_20240411-161013_front_returnGood_70f75407b7ae_865_17788571404.mp4
|
||||
6923450612484_20240411-161509_back_addGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450612484_20240411-161509_front_addGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450612484_20240411-161517_back_returnGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450612484_20240411-161517_front_returnGood_70f75407b7ae_445_17788571404.mp4
|
||||
6923450612484_20240411-161535_back_addGood_70f754088050_450_17327712807.mp4
|
||||
6923450612484_20240411-161535_front_addGood_70f754088050_450_17327712807.mp4
|
||||
6923450612484_20240411-161546_back_returnGood_70f754088050_450_17327712807.mp4
|
||||
6923450612484_20240411-161546_front_returnGood_70f754088050_450_17327712807.mp4
|
||||
6923450657829_20240411-163522_back_addGood_70f75407b7ae_985_17788571404.mp4
|
||||
6923450657829_20240411-163522_front_addGood_70f75407b7ae_985_17788571404.mp4
|
||||
6923450657829_20240411-163532_back_returnGood_70f75407b7ae_985_17788571404.mp4
|
||||
6923450657829_20240411-163532_front_returnGood_70f75407b7ae_985_17788571404.mp4
|
||||
6923450657829_20240411-163554_back_addGood_70f754088050_990_17327712807.mp4
|
||||
6923450657829_20240411-163554_front_addGood_70f754088050_990_17327712807.mp4
|
||||
6923450657829_20240411-163606_back_returnGood_70f754088050_990_17327712807.mp4
|
||||
6923450657829_20240411-163606_front_returnGood_70f754088050_990_17327712807.mp4
|
||||
6923450659441_20240411-163152_back_addGood_70f75407b7ae_1695_17788571404.mp4
|
||||
6923450659441_20240411-163152_front_addGood_70f75407b7ae_1695_17788571404.mp4
|
||||
6923450659441_20240411-163204_back_returnGood_70f75407b7ae_1695_17788571404.mp4
|
||||
6923450659441_20240411-163204_front_returnGood_70f75407b7ae_1695_17788571404.mp4
|
||||
6923450659441_20240411-163217_back_addGood_70f754088050_1690_17327712807.mp4
|
||||
6923450659441_20240411-163217_front_addGood_70f754088050_1690_17327712807.mp4
|
||||
6923450659441_20240411-163231_back_returnGood_70f754088050_1690_17327712807.mp4
|
||||
6923450659441_20240411-163231_front_returnGood_70f754088050_1690_17327712807.mp4
|
||||
6923450666838_20240411-162541_back_addGood_70f75407b7ae_285_17788571404.mp4
|
||||
6923450666838_20240411-162541_front_addGood_70f75407b7ae_285_17788571404.mp4
|
||||
6923450666838_20240411-162549_back_returnGood_70f75407b7ae_285_17788571404.mp4
|
||||
6923450666838_20240411-162549_front_returnGood_70f75407b7ae_285_17788571404.mp4
|
||||
6923450666838_20240411-162637_back_addGood_70f754088050_285_17327712807.mp4
|
||||
6923450666838_20240411-162637_front_addGood_70f754088050_285_17327712807.mp4
|
||||
6923450666838_20240411-162704_back_returnGood_70f754088050_285_17327712807.mp4
|
||||
6923450666838_20240411-162704_front_returnGood_70f754088050_285_17327712807.mp4
|
||||
6923450668207_20240411-161832_back_addGood_70f75407b7ae_350_17788571404.mp4
|
||||
6923450668207_20240411-161832_front_addGood_70f75407b7ae_350_17788571404.mp4
|
||||
6923450668207_20240411-161839_back_returnGood_70f75407b7ae_350_17788571404.mp4
|
||||
6923450668207_20240411-161839_front_returnGood_70f75407b7ae_350_17788571404.mp4
|
||||
6923450668207_20240411-161904_back_addGood_70f754088050_350_17327712807.mp4
|
||||
6923450668207_20240411-161904_front_addGood_70f754088050_350_17327712807.mp4
|
||||
6923450668207_20240411-161913_back_returnGood_70f754088050_355_17327712807.mp4
|
||||
6923450668207_20240411-161913_front_returnGood_70f754088050_355_17327712807.mp4
|
||||
6923450677858_20240411-163609_back_addGood_70f75407b7ae_5_17788571404.mp4
|
||||
6923450677858_20240411-163609_front_addGood_70f75407b7ae_5_17788571404.mp4
|
||||
6923450677858_20240411-163618_back_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6923450677858_20240411-163618_front_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6923450677858_20240411-163726_back_addGood_70f754088050_1025_17327712807.mp4
|
||||
6923450677858_20240411-163726_front_addGood_70f754088050_1025_17327712807.mp4
|
||||
6923450677858_20240411-163737_back_returnGood_70f754088050_1025_17327712807.mp4
|
||||
6923450677858_20240411-163737_front_returnGood_70f754088050_1025_17327712807.mp4
|
||||
6923644286293_20240411-141557_back_addGood_70f754088050_795_17327712807.mp4
|
||||
6923644286293_20240411-141557_front_addGood_70f754088050_795_17327712807.mp4
|
||||
6923644286293_20240411-141601_back_addGood_70f75407b7ae_800_17788571404.mp4
|
||||
6923644286293_20240411-141601_front_addGood_70f75407b7ae_800_17788571404.mp4
|
||||
6923644286293_20240411-141605_back_returnGood_70f754088050_795_17327712807.mp4
|
||||
6923644286293_20240411-141605_front_returnGood_70f754088050_795_17327712807.mp4
|
||||
6923644286293_20240411-141609_back_returnGood_70f75407b7ae_800_17788571404.mp4
|
||||
6923644286293_20240411-141609_front_returnGood_70f75407b7ae_800_17788571404.mp4
|
||||
6923644298760_20240411-141459_back_addGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6923644298760_20240411-141459_front_addGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6923644298760_20240411-141500_back_addGood_70f754088050_1020_17327712807.mp4
|
||||
6923644298760_20240411-141500_front_addGood_70f754088050_1020_17327712807.mp4
|
||||
6923644298760_20240411-141511_back_returnGood_70f754088050_1020_17327712807.mp4
|
||||
6923644298760_20240411-141511_front_returnGood_70f754088050_1020_17327712807.mp4
|
||||
6923644298760_20240411-141516_back_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6923644298760_20240411-141516_front_returnGood_70f75407b7ae_1020_17788571404.mp4
|
||||
6924743915824_20240411-143302_back_addGood_70f75407b7ae_150_17788571404.mp4
|
||||
6924743915824_20240411-143302_back_addGood_70f754088050_150_17327712807.mp4
|
||||
6924743915824_20240411-143302_front_addGood_70f75407b7ae_150_17788571404.mp4
|
||||
6924743915824_20240411-143302_front_addGood_70f754088050_150_17327712807.mp4
|
||||
6924743915824_20240411-143310_back_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
6924743915824_20240411-143310_back_returnGood_70f754088050_150_17327712807.mp4
|
||||
6924743915824_20240411-143310_front_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
6924743915824_20240411-143310_front_returnGood_70f754088050_150_17327712807.mp4
|
||||
6924882497106_20240411-143858_back_addGood_70f75407b7ae_360_17788571404.mp4
|
||||
6924882497106_20240411-143858_front_addGood_70f75407b7ae_360_17788571404.mp4
|
||||
6924882497106_20240411-143904_back_returnGood_70f75407b7ae_360_17788571404.mp4
|
||||
6924882497106_20240411-143904_front_returnGood_70f75407b7ae_360_17788571404.mp4
|
||||
6924882497106_20240411-143915_back_addGood_70f754088050_355_17327712807.mp4
|
||||
6924882497106_20240411-143915_front_addGood_70f754088050_355_17327712807.mp4
|
||||
6924882497106_20240411-143924_back_returnGood_70f754088050_355_17327712807.mp4
|
||||
6924882497106_20240411-143924_front_returnGood_70f754088050_355_17327712807.mp4
|
||||
6925307305525_20240411-141034_back_addGood_70f75407b7ae_1670_17788571404.mp4
|
||||
6925307305525_20240411-141034_front_addGood_70f75407b7ae_1670_17788571404.mp4
|
||||
6925307305525_20240411-141044_back_returnGood_70f75407b7ae_1670_17788571404.mp4
|
||||
6925307305525_20240411-141044_front_returnGood_70f75407b7ae_1670_17788571404.mp4
|
||||
6925307305525_20240411-141055_back_addGood_70f754088050_1670_17327712807.mp4
|
||||
6925307305525_20240411-141055_front_addGood_70f754088050_1670_17327712807.mp4
|
||||
6925307305525_20240411-141106_back_returnGood_70f754088050_1670_17327712807.mp4
|
||||
6925307305525_20240411-141106_front_returnGood_70f754088050_1670_17327712807.mp4
|
||||
6928804011173_20240411-143817_back_addGood_70f75407b7ae_555_17788571404.mp4
|
||||
6928804011173_20240411-143817_front_addGood_70f75407b7ae_555_17788571404.mp4
|
||||
6928804011173_20240411-143825_back_returnGood_70f75407b7ae_550_17788571404.mp4
|
||||
6928804011173_20240411-143825_front_returnGood_70f75407b7ae_550_17788571404.mp4
|
||||
6928804011173_20240411-143833_back_addGood_70f754088050_545_17327712807.mp4
|
||||
6928804011173_20240411-143833_front_addGood_70f754088050_545_17327712807.mp4
|
||||
6928804011173_20240411-143841_back_returnGood_70f754088050_550_17327712807.mp4
|
||||
6928804011173_20240411-143841_front_returnGood_70f754088050_550_17327712807.mp4
|
||||
6931925828032_20240411-162501_back_addGood_70f75407b7ae_405_17788571404.mp4
|
||||
6931925828032_20240411-162501_front_addGood_70f75407b7ae_405_17788571404.mp4
|
||||
6931925828032_20240411-162511_back_returnGood_70f75407b7ae_405_17788571404.mp4
|
||||
6931925828032_20240411-162511_front_returnGood_70f75407b7ae_405_17788571404.mp4
|
||||
6931925828032_20240411-162532_back_addGood_70f754088050_405_17327712807.mp4
|
||||
6931925828032_20240411-162532_front_addGood_70f754088050_405_17327712807.mp4
|
||||
6931925828032_20240411-162540_back_returnGood_70f754088050_405_17327712807.mp4
|
||||
6931925828032_20240411-162540_front_returnGood_70f754088050_405_17327712807.mp4
|
||||
6933620900051_20240411-150018_back_addGood_70f75407b7ae_5_17788571404.mp4
|
||||
6933620900051_20240411-150018_front_addGood_70f75407b7ae_5_17788571404.mp4
|
||||
6933620900051_20240411-150035_back_returnGood_70f75407b7ae_380_17788571404.mp4
|
||||
6933620900051_20240411-150035_front_returnGood_70f75407b7ae_380_17788571404.mp4
|
||||
6933620900051_20240411-150103_back_addGood_70f754088050_455_17327712807.mp4
|
||||
6933620900051_20240411-150103_front_addGood_70f754088050_455_17327712807.mp4
|
||||
6933620900051_20240411-150116_back_returnGood_70f754088050_455_17327712807.mp4
|
||||
6933620900051_20240411-150116_front_returnGood_70f754088050_455_17327712807.mp4
|
||||
6934665095108_20240411-141931_back_addGood_70f754088050_360_17327712807.mp4
|
||||
6934665095108_20240411-141931_front_addGood_70f754088050_360_17327712807.mp4
|
||||
6934665095108_20240411-141936_back_addGood_70f75407b7ae_360_17788571404.mp4
|
||||
6934665095108_20240411-141936_front_addGood_70f75407b7ae_360_17788571404.mp4
|
||||
6934665095108_20240411-141940_back_returnGood_70f754088050_365_17327712807.mp4
|
||||
6934665095108_20240411-141940_front_returnGood_70f754088050_365_17327712807.mp4
|
||||
6934665095108_20240411-141945_back_returnGood_70f75407b7ae_360_17788571404.mp4
|
||||
6934665095108_20240411-141945_front_returnGood_70f75407b7ae_360_17788571404.mp4
|
||||
6935270642121_20240411-144253_back_addGood_70f75407b7ae_155_17788571404.mp4
|
||||
6935270642121_20240411-144253_front_addGood_70f75407b7ae_155_17788571404.mp4
|
||||
6935270642121_20240411-144255_back_addGood_70f754088050_155_17327712807.mp4
|
||||
6935270642121_20240411-144255_front_addGood_70f754088050_155_17327712807.mp4
|
||||
6935270642121_20240411-144300_back_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
6935270642121_20240411-144300_front_returnGood_70f75407b7ae_155_17788571404.mp4
|
||||
6935270642121_20240411-144304_back_returnGood_70f754088050_155_17327712807.mp4
|
||||
6935270642121_20240411-144304_front_returnGood_70f754088050_155_17327712807.mp4
|
||||
6935284417326_20240411-143504_back_addGood_70f754088050_420_17327712807.mp4
|
||||
6935284417326_20240411-143504_front_addGood_70f754088050_420_17327712807.mp4
|
||||
6935284417326_20240411-143508_back_addGood_70f75407b7ae_415_17788571404.mp4
|
||||
6935284417326_20240411-143508_front_addGood_70f75407b7ae_415_17788571404.mp4
|
||||
6935284417326_20240411-143517_back_returnGood_70f754088050_420_17327712807.mp4
|
||||
6935284417326_20240411-143517_front_returnGood_70f754088050_420_17327712807.mp4
|
||||
6935284417326_20240411-143521_back_returnGood_70f75407b7ae_415_17788571404.mp4
|
||||
6935284417326_20240411-143521_front_returnGood_70f75407b7ae_415_17788571404.mp4
|
||||
6941025140798_20240411-135647_back_addGood_70f75407b7ae_1760_17788571404.mp4
|
||||
6941025140798_20240411-135647_front_addGood_70f75407b7ae_1760_17788571404.mp4
|
||||
6941025140798_20240411-135655_back_returnGood_70f75407b7ae_1760_17788571404.mp4
|
||||
6941025140798_20240411-135655_front_returnGood_70f75407b7ae_1760_17788571404.mp4
|
||||
6941025140798_20240411-140026_back_addGood_70f754088050_1765_17327712807.mp4
|
||||
6941025140798_20240411-140026_front_addGood_70f754088050_1765_17327712807.mp4
|
||||
6941025140798_20240411-140105_back_returnGood_70f754088050_1760_17327712807.mp4
|
||||
6941025140798_20240411-140105_front_returnGood_70f754088050_1760_17327712807.mp4
|
||||
6952074634794_20240411-143701_back_addGood_70f75407b7ae_275_17788571404.mp4
|
||||
6952074634794_20240411-143701_front_addGood_70f75407b7ae_275_17788571404.mp4
|
||||
6952074634794_20240411-143707_back_returnGood_70f75407b7ae_275_17788571404.mp4
|
||||
6952074634794_20240411-143709_back_returnGood_70f754088050_5_17327712807.mp4
|
||||
6952074634794_20240411-143709_front_returnGood_70f754088050_5_17327712807.mp4
|
||||
6952074634794_20240411-143720_back_returnGood_70f754088050_265_17327712807.mp4
|
||||
6952074634794_20240411-143720_front_returnGood_70f754088050_265_17327712807.mp4
|
||||
6954432711307_20240411-161612_back_addGood_70f75407b7ae_460_17788571404.mp4
|
||||
6954432711307_20240411-161612_front_addGood_70f75407b7ae_460_17788571404.mp4
|
||||
6954432711307_20240411-161622_back_returnGood_70f75407b7ae_460_17788571404.mp4
|
||||
6954432711307_20240411-161622_front_returnGood_70f75407b7ae_460_17788571404.mp4
|
||||
6954432711307_20240411-161647_back_addGood_70f754088050_460_17327712807.mp4
|
||||
6954432711307_20240411-161647_front_addGood_70f754088050_460_17327712807.mp4
|
||||
6954432711307_20240411-161700_back_returnGood_70f754088050_460_17327712807.mp4
|
||||
6954432711307_20240411-161700_front_returnGood_70f754088050_460_17327712807.mp4
|
||||
6959546100993_20240411-135257_back_addGood_70f75407b7ae_295_17788571404.mp4
|
||||
6959546100993_20240411-135257_front_addGood_70f75407b7ae_295_17788571404.mp4
|
||||
6959546100993_20240411-135339_back_returnGood_70f75407b7ae_295_17788571404.mp4
|
||||
6959546100993_20240411-135339_front_returnGood_70f75407b7ae_295_17788571404.mp4
|
||||
6959546100993_20240411-141255_back_addGood_70f754088050_295_17327712807.mp4
|
||||
6959546100993_20240411-141255_front_addGood_70f754088050_295_17327712807.mp4
|
||||
6959546100993_20240411-141309_back_returnGood_70f754088050_295_17327712807.mp4
|
||||
6959546100993_20240411-141309_front_returnGood_70f754088050_295_17327712807.mp4
|
||||
6971075127463_20240411-135002_back_addGood_70f75407b7ae_210_17788571404.mp4
|
||||
6971075127463_20240411-135002_front_addGood_70f75407b7ae_210_17788571404.mp4
|
||||
6971075127463_20240411-135058_back_returnGood_70f75407b7ae_210_17788571404.mp4
|
||||
6971075127463_20240411-135058_front_returnGood_70f75407b7ae_210_17788571404.mp4
|
||||
6971075127463_20240411-141154_back_addGood_70f754088050_215_17327712807.mp4
|
||||
6971075127463_20240411-141154_front_addGood_70f754088050_215_17327712807.mp4
|
||||
6971075127463_20240411-141214_back_returnGood_70f754088050_210_17327712807.mp4
|
||||
6971075127463_20240411-141214_front_returnGood_70f754088050_210_17327712807.mp4
|
||||
6971328580533_20240411-135748_back_addGood_70f75407b7ae_405_17788571404.mp4
|
||||
6971328580533_20240411-135748_front_addGood_70f75407b7ae_405_17788571404.mp4
|
||||
6971328580533_20240411-135757_back_returnGood_70f75407b7ae_405_17788571404.mp4
|
||||
6971328580533_20240411-135757_front_returnGood_70f75407b7ae_405_17788571404.mp4
|
||||
6971328580533_20240411-140231_back_addGood_70f754088050_405_17327712807.mp4
|
||||
6971328580533_20240411-140231_front_addGood_70f754088050_405_17327712807.mp4
|
||||
6971328580533_20240411-140529_back_addGood_70f754088050_410_17327712807.mp4
|
||||
6971328580533_20240411-140529_front_addGood_70f754088050_410_17327712807.mp4
|
||||
6971328580533_20240411-140745_back_returnGood_70f754088050_405_17327712807.mp4
|
||||
6971328580533_20240411-140745_front_returnGood_70f754088050_405_17327712807.mp4
|
||||
6971738655333_20240411-144616_back_addGood_70f75407b7ae_270_17788571404.mp4
|
||||
6971738655333_20240411-144616_back_addGood_70f754088050_260_17327712807.mp4
|
||||
6971738655333_20240411-144616_front_addGood_70f75407b7ae_270_17788571404.mp4
|
||||
6971738655333_20240411-144616_front_addGood_70f754088050_260_17327712807.mp4
|
||||
6971738655333_20240411-144633_back_returnGood_70f75407b7ae_270_17788571404.mp4
|
||||
6971738655333_20240411-144633_front_returnGood_70f75407b7ae_270_17788571404.mp4
|
||||
6971738655333_20240411-144635_back_returnGood_70f754088050_260_17327712807.mp4
|
||||
6971738655333_20240411-144635_front_returnGood_70f754088050_260_17327712807.mp4
|
||||
6972378998200_20240411-142603_back_addGood_70f754088050_410_17327712807.mp4
|
||||
6972378998200_20240411-142603_front_addGood_70f754088050_410_17327712807.mp4
|
||||
6972378998200_20240411-142604_back_addGood_70f75407b7ae_410_17788571404.mp4
|
||||
6972378998200_20240411-142604_front_addGood_70f75407b7ae_410_17788571404.mp4
|
||||
6972378998200_20240411-142613_back_returnGood_70f75407b7ae_410_17788571404.mp4
|
||||
6972378998200_20240411-142613_back_returnGood_70f754088050_410_17327712807.mp4
|
||||
6972378998200_20240411-142613_front_returnGood_70f75407b7ae_410_17788571404.mp4
|
||||
6972378998200_20240411-142613_front_returnGood_70f754088050_410_17327712807.mp4
|
||||
6972790052733_20240411-162926_back_addGood_70f75407b7ae_690_17788571404.mp4
|
||||
6972790052733_20240411-162926_front_addGood_70f75407b7ae_690_17788571404.mp4
|
||||
6972790052733_20240411-162933_back_returnGood_70f75407b7ae_690_17788571404.mp4
|
||||
6972790052733_20240411-162933_front_returnGood_70f75407b7ae_690_17788571404.mp4
|
||||
6972790052733_20240411-162948_back_addGood_70f754088050_690_17327712807.mp4
|
||||
6972790052733_20240411-162948_front_addGood_70f754088050_690_17327712807.mp4
|
||||
6972790052733_20240411-163001_back_returnGood_70f754088050_690_17327712807.mp4
|
||||
6972790052733_20240411-163001_front_returnGood_70f754088050_690_17327712807.mp4
|
||||
6974627182033_20240411-150747_back_addGood_70f75407b7ae_485_17788571404.mp4
|
||||
6974627182033_20240411-150747_front_addGood_70f75407b7ae_485_17788571404.mp4
|
||||
6974627182033_20240411-150756_back_returnGood_70f75407b7ae_480_17788571404.mp4
|
||||
6974627182033_20240411-150756_front_returnGood_70f75407b7ae_480_17788571404.mp4
|
||||
6974627182033_20240411-150825_back_addGood_70f754088050_495_17327712807.mp4
|
||||
6974627182033_20240411-150825_front_addGood_70f754088050_495_17327712807.mp4
|
||||
6974627182033_20240411-150835_back_returnGood_70f754088050_495_17327712807.mp4
|
||||
6974627182033_20240411-150835_front_returnGood_70f754088050_495_17327712807.mp4
|
||||
6974913231612_20240411-135543_back_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6974913231612_20240411-135543_front_addGood_70f75407b7ae_870_17788571404.mp4
|
||||
6974913231612_20240411-135552_back_returnGood_70f75407b7ae_865_17788571404.mp4
|
||||
6974913231612_20240411-135552_front_returnGood_70f75407b7ae_865_17788571404.mp4
|
||||
6974913231612_20240411-140906_back_addGood_70f754088050_755_17327712807.mp4
|
||||
6974913231612_20240411-140906_front_addGood_70f754088050_755_17327712807.mp4
|
||||
6974913231612_20240411-140917_back_returnGood_70f754088050_755_17327712807.mp4
|
||||
6974913231612_20240411-140917_front_returnGood_70f754088050_755_17327712807.mp4
|
||||
6974995172711_20240411-135456_back_addGood_70f75407b7ae_1455_17788571404.mp4
|
||||
6974995172711_20240411-135456_front_addGood_70f75407b7ae_1455_17788571404.mp4
|
||||
6974995172711_20240411-135506_back_returnGood_70f75407b7ae_1460_17788571404.mp4
|
||||
6974995172711_20240411-135506_front_returnGood_70f75407b7ae_1460_17788571404.mp4
|
||||
6974995172711_20240411-140831_back_addGood_70f754088050_1460_17327712807.mp4
|
||||
6974995172711_20240411-140831_front_addGood_70f754088050_1460_17327712807.mp4
|
||||
6974995172711_20240411-140844_back_returnGood_70f754088050_1460_17327712807.mp4
|
||||
6974995172711_20240411-140844_front_returnGood_70f754088050_1460_17327712807.mp4
|
||||
6976090230303_20240411-144457_back_addGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976090230303_20240411-144457_front_addGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976090230303_20240411-144458_back_addGood_70f754088050_390_17327712807.mp4
|
||||
6976090230303_20240411-144458_front_addGood_70f754088050_390_17327712807.mp4
|
||||
6976090230303_20240411-144508_back_returnGood_70f754088050_390_17327712807.mp4
|
||||
6976090230303_20240411-144508_front_returnGood_70f754088050_390_17327712807.mp4
|
||||
6976090230303_20240411-144509_back_returnGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976090230303_20240411-144509_front_returnGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976371220276_20240411-150456_back_addGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976371220276_20240411-150456_front_addGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976371220276_20240411-150505_back_returnGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976371220276_20240411-150505_front_returnGood_70f75407b7ae_390_17788571404.mp4
|
||||
6976371220276_20240411-150559_back_addGood_70f754088050_405_17327712807.mp4
|
||||
6976371220276_20240411-150559_front_addGood_70f754088050_405_17327712807.mp4
|
||||
6976371220276_20240411-150610_back_returnGood_70f754088050_405_17327712807.mp4
|
||||
6976371220276_20240411-150610_front_returnGood_70f754088050_405_17327712807.mp4
|
||||
230537101280010007_20240412-140824_back_addGood_70f754088050_565_13725988807.mp4
|
||||
230537101280010007_20240412-140824_front_addGood_70f754088050_565_13725988807.mp4
|
||||
230537101280010007_20240412-140835_back_returnGood_70f754088050_565_13725988807.mp4
|
||||
230537101280010007_20240412-140835_front_returnGood_70f754088050_565_13725988807.mp4
|
||||
2500456001326_20240412-140949_back_addGood_70f754088050_150_13725988807.mp4
|
||||
2500456001326_20240412-140949_front_addGood_70f754088050_150_13725988807.mp4
|
||||
2500456001326_20240412-140958_back_returnGood_70f754088050_155_13725988807.mp4
|
||||
2500456001326_20240412-140958_front_returnGood_70f754088050_155_13725988807.mp4
|
||||
2500458675341_20240412-140915_back_addGood_70f754088050_130_13725988807.mp4
|
||||
2500458675341_20240412-140915_front_addGood_70f754088050_130_13725988807.mp4
|
||||
2500458675341_20240412-140924_back_returnGood_70f754088050_130_13725988807.mp4
|
||||
2500458675341_20240412-140924_front_returnGood_70f754088050_130_13725988807.mp4
|
||||
2500463464671_20240412-140718_back_addGood_70f754088050_810_13725988807.mp4
|
||||
2500463464671_20240412-140718_front_addGood_70f754088050_810_13725988807.mp4
|
||||
2500463464671_20240412-140731_back_returnGood_70f754088050_810_13725988807.mp4
|
||||
2500463464671_20240412-140731_front_returnGood_70f754088050_810_13725988807.mp4
|
||||
6901070613142_20240412-144631_back_addGood_70f754088050_240_13725988807.mp4
|
||||
6901070613142_20240412-144631_front_addGood_70f754088050_240_13725988807.mp4
|
||||
6901070613142_20240412-144643_back_returnGood_70f754088050_240_13725988807.mp4
|
||||
6901070613142_20240412-144643_front_returnGood_70f754088050_240_13725988807.mp4
|
||||
6901586000993_20240412-152404_back_addGood_70f754088050_890_13725988807.mp4
|
||||
6901586000993_20240412-152404_front_addGood_70f754088050_890_13725988807.mp4
|
||||
6901586000993_20240412-152440_back_returnGood_70f754088050_885_13725988807.mp4
|
||||
6901586000993_20240412-152440_front_returnGood_70f754088050_885_13725988807.mp4
|
||||
6901668053893_20240412-150115_back_addGood_70f754088050_70_13725988807.mp4
|
||||
6901668053893_20240412-150115_front_addGood_70f754088050_70_13725988807.mp4
|
||||
6901668053893_20240412-150125_back_returnGood_70f754088050_70_13725988807.mp4
|
||||
6901668053893_20240412-150125_front_returnGood_70f754088050_70_13725988807.mp4
|
||||
6902007010249_20240412-144518_back_addGood_70f754088050_755_13725988807.mp4
|
||||
6902007010249_20240412-144518_front_addGood_70f754088050_755_13725988807.mp4
|
||||
6902007010249_20240412-144529_back_returnGood_70f754088050_755_13725988807.mp4
|
||||
6902007010249_20240412-144529_front_returnGood_70f754088050_755_13725988807.mp4
|
||||
6902022135514_20240412-144740_back_addGood_70f754088050_3180_13725988807.mp4
|
||||
6902022135514_20240412-144740_front_addGood_70f754088050_3180_13725988807.mp4
|
||||
6902022135514_20240412-144750_back_returnGood_70f754088050_3180_13725988807.mp4
|
||||
6902022135514_20240412-144750_front_returnGood_70f754088050_3180_13725988807.mp4
|
||||
6902265114369_20240412-144022_back_addGood_70f754088050_720_13725988807.mp4
|
||||
6902265114369_20240412-144022_front_addGood_70f754088050_720_13725988807.mp4
|
||||
6902265114369_20240412-144034_back_returnGood_70f754088050_720_13725988807.mp4
|
||||
6902265114369_20240412-144034_front_returnGood_70f754088050_720_13725988807.mp4
|
||||
6902265908012_20240412-144141_back_addGood_70f754088050_1145_13725988807.mp4
|
||||
6902265908012_20240412-144141_front_addGood_70f754088050_1145_13725988807.mp4
|
||||
6902265908012_20240412-144149_back_returnGood_70f754088050_1145_13725988807.mp4
|
||||
6902265908012_20240412-144149_front_returnGood_70f754088050_1145_13725988807.mp4
|
||||
69025143_20240412-135423_back_addGood_70f754088050_3015_13725988807.mp4
|
||||
69025143_20240412-135423_front_addGood_70f754088050_3015_13725988807.mp4
|
||||
69025143_20240412-135439_back_returnGood_70f754088050_3015_13725988807.mp4
|
||||
69025143_20240412-135439_front_returnGood_70f754088050_3015_13725988807.mp4
|
||||
6907992103952_20240412-141542_back_addGood_70f754088050_190_13725988807.mp4
|
||||
6907992103952_20240412-141542_front_addGood_70f754088050_190_13725988807.mp4
|
||||
6907992103952_20240412-141554_back_returnGood_70f754088050_190_13725988807.mp4
|
||||
6907992103952_20240412-141554_front_returnGood_70f754088050_190_13725988807.mp4
|
||||
6907992104157_20240412-141255_back_addGood_70f754088050_1120_13725988807.mp4
|
||||
6907992104157_20240412-141255_front_addGood_70f754088050_1120_13725988807.mp4
|
||||
6907992104157_20240412-141307_back_returnGood_70f754088050_1120_13725988807.mp4
|
||||
6907992104157_20240412-141307_front_returnGood_70f754088050_1120_13725988807.mp4
|
||||
6907992105260_20240412-142144_back_addGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105260_20240412-142144_front_addGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105260_20240412-142156_back_returnGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105260_20240412-142156_front_returnGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105260_20240412-143214_back_addGood_70f754088050_2770_13725988807.mp4
|
||||
6907992105260_20240412-143214_front_addGood_70f754088050_2770_13725988807.mp4
|
||||
6907992105260_20240412-143225_back_returnGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105260_20240412-143225_front_returnGood_70f754088050_2775_13725988807.mp4
|
||||
6907992105765_20240412-141505_back_addGood_70f754088050_2085_13725988807.mp4
|
||||
6907992105765_20240412-141505_front_addGood_70f754088050_2085_13725988807.mp4
|
||||
6907992105765_20240412-141523_back_returnGood_70f754088050_2085_13725988807.mp4
|
||||
6907992105765_20240412-141523_front_returnGood_70f754088050_2085_13725988807.mp4
|
||||
6907992106205_20240412-141729_back_addGood_70f754088050_790_13725988807.mp4
|
||||
6907992106205_20240412-141729_front_addGood_70f754088050_790_13725988807.mp4
|
||||
6907992106205_20240412-141741_back_returnGood_70f754088050_790_13725988807.mp4
|
||||
6907992106205_20240412-141741_front_returnGood_70f754088050_790_13725988807.mp4
|
||||
6907992106311_20240412-141326_back_addGood_70f754088050_890_13725988807.mp4
|
||||
6907992106311_20240412-141326_front_addGood_70f754088050_890_13725988807.mp4
|
||||
6907992106311_20240412-141341_back_returnGood_70f754088050_890_13725988807.mp4
|
||||
6907992106311_20240412-141341_front_returnGood_70f754088050_890_13725988807.mp4
|
||||
6914973602908_20240412-134547_back_addGood_70f754088050_505_13725988807.mp4
|
||||
6914973602908_20240412-134547_front_addGood_70f754088050_505_13725988807.mp4
|
||||
6914973602908_20240412-134600_back_returnGood_70f754088050_505_13725988807.mp4
|
||||
6914973602908_20240412-134600_front_returnGood_70f754088050_505_13725988807.mp4
|
||||
6914973604223_20240412-143609_back_addGood_70f754088050_230_13725988807.mp4
|
||||
6914973604223_20240412-143609_front_addGood_70f754088050_230_13725988807.mp4
|
||||
6914973604223_20240412-143619_back_returnGood_70f754088050_230_13725988807.mp4
|
||||
6914973604223_20240412-143619_front_returnGood_70f754088050_230_13725988807.mp4
|
||||
6914973606340_20240412-134352_back_addGood_70f754088050_325_13725988807.mp4
|
||||
6914973606340_20240412-134352_front_addGood_70f754088050_325_13725988807.mp4
|
||||
6914973606340_20240412-134406_back_returnGood_70f754088050_330_13725988807.mp4
|
||||
6914973606340_20240412-134406_front_returnGood_70f754088050_330_13725988807.mp4
|
||||
6919188092377_20240412-141841_back_addGood_70f754088050_515_13725988807.mp4
|
||||
6919188092377_20240412-141841_front_addGood_70f754088050_515_13725988807.mp4
|
||||
6919188092377_20240412-141850_back_returnGood_70f754088050_515_13725988807.mp4
|
||||
6919188092377_20240412-141850_front_returnGood_70f754088050_515_13725988807.mp4
|
||||
6920152400630_20240412-151036_back_returnGood_70f754088050_580_13725988807.mp4
|
||||
6920174757101_20240412-144934_back_addGood_70f754088050_1300_13725988807.mp4
|
||||
6920174757101_20240412-144934_front_addGood_70f754088050_1300_13725988807.mp4
|
||||
6920174757101_20240412-144946_back_returnGood_70f754088050_1300_13725988807.mp4
|
||||
6920174757101_20240412-144946_front_returnGood_70f754088050_1300_13725988807.mp4
|
||||
6920459905012_20240412-150826_back_returnGood_70f754088050_550_13725988807.mp4
|
||||
6920907810707_20240412-145330_back_addGood_70f754088050_75_13725988807.mp4
|
||||
6920907810707_20240412-145330_front_addGood_70f754088050_75_13725988807.mp4
|
||||
6920907810707_20240412-145340_back_returnGood_70f754088050_75_13725988807.mp4
|
||||
6920907810707_20240412-145340_front_returnGood_70f754088050_75_13725988807.mp4
|
||||
6922130119213_20240412-144416_back_addGood_70f754088050_1020_13725988807.mp4
|
||||
6922130119213_20240412-144416_front_addGood_70f754088050_1020_13725988807.mp4
|
||||
6922130119213_20240412-144426_back_returnGood_70f754088050_1020_13725988807.mp4
|
||||
6922130119213_20240412-144426_front_returnGood_70f754088050_1020_13725988807.mp4
|
||||
6922577700968_20240412-141700_back_addGood_70f754088050_1040_13725988807.mp4
|
||||
6922577700968_20240412-141700_front_addGood_70f754088050_1040_13725988807.mp4
|
||||
6922577700968_20240412-141710_back_returnGood_70f754088050_1040_13725988807.mp4
|
||||
6922577700968_20240412-141710_front_returnGood_70f754088050_1040_13725988807.mp4
|
||||
6922868291168_20240412-145057_back_addGood_70f754088050_1160_13725988807.mp4
|
||||
6922868291168_20240412-145057_front_addGood_70f754088050_1160_13725988807.mp4
|
||||
6922868291168_20240412-145113_back_returnGood_70f754088050_1160_13725988807.mp4
|
||||
6922868291168_20240412-145113_front_returnGood_70f754088050_1160_13725988807.mp4
|
||||
6923450601549_20240412-134454_back_addGood_70f754088050_640_13725988807.mp4
|
||||
6923450601549_20240412-134454_front_addGood_70f754088050_640_13725988807.mp4
|
||||
6923450601549_20240412-134504_back_returnGood_70f754088050_635_13725988807.mp4
|
||||
6923450601549_20240412-134504_front_returnGood_70f754088050_635_13725988807.mp4
|
||||
6923450603574_20240412-135226_back_addGood_70f754088050_970_13725988807.mp4
|
||||
6923450603574_20240412-135226_front_addGood_70f754088050_970_13725988807.mp4
|
||||
6923450603574_20240412-135240_back_returnGood_70f754088050_965_13725988807.mp4
|
||||
6923450603574_20240412-135240_front_returnGood_70f754088050_965_13725988807.mp4
|
||||
6923450605288_20240412-134133_back_addGood_70f754088050_470_13725988807.mp4
|
||||
6923450605288_20240412-134133_front_addGood_70f754088050_470_13725988807.mp4
|
||||
6923450605288_20240412-134143_back_returnGood_70f754088050_470_13725988807.mp4
|
||||
6923450605288_20240412-134143_front_returnGood_70f754088050_470_13725988807.mp4
|
||||
6923450605332_20240412-135742_back_addGood_70f754088050_1785_13725988807.mp4
|
||||
6923450605332_20240412-135742_front_addGood_70f754088050_1785_13725988807.mp4
|
||||
6923450605332_20240412-135756_back_returnGood_70f754088050_1785_13725988807.mp4
|
||||
6923450605332_20240412-135756_front_returnGood_70f754088050_1785_13725988807.mp4
|
||||
6923450610428_20240412-133618_back_addGood_70f754088050_320_13725988807.mp4
|
||||
6923450610428_20240412-133618_front_addGood_70f754088050_320_13725988807.mp4
|
||||
6923450610428_20240412-133632_back_returnGood_70f754088050_320_13725988807.mp4
|
||||
6923450610428_20240412-133632_front_returnGood_70f754088050_320_13725988807.mp4
|
||||
6923450610459_20240412-135040_back_addGood_70f754088050_555_13725988807.mp4
|
||||
6923450610459_20240412-135040_front_addGood_70f754088050_555_13725988807.mp4
|
||||
6923450610459_20240412-135057_back_returnGood_70f754088050_555_13725988807.mp4
|
||||
6923450610459_20240412-135057_front_returnGood_70f754088050_555_13725988807.mp4
|
||||
6923450611067_20240412-134949_back_addGood_70f754088050_800_13725988807.mp4
|
||||
6923450611067_20240412-134949_front_addGood_70f754088050_800_13725988807.mp4
|
||||
6923450611067_20240412-135002_back_returnGood_70f754088050_800_13725988807.mp4
|
||||
6923450611067_20240412-135002_front_returnGood_70f754088050_800_13725988807.mp4
|
||||
6923450612415_20240412-133515_back_addGood_70f754088050_820_13725988807.mp4
|
||||
6923450612415_20240412-133515_front_addGood_70f754088050_820_13725988807.mp4
|
||||
6923450612415_20240412-133532_back_returnGood_70f754088050_820_13725988807.mp4
|
||||
6923450612415_20240412-133532_front_returnGood_70f754088050_820_13725988807.mp4
|
||||
6923450612484_20240412-133746_back_addGood_70f754088050_255_13725988807.mp4
|
||||
6923450612484_20240412-133746_front_addGood_70f754088050_255_13725988807.mp4
|
||||
6923450612484_20240412-133758_back_returnGood_70f754088050_255_13725988807.mp4
|
||||
6923450612484_20240412-133758_front_returnGood_70f754088050_255_13725988807.mp4
|
||||
6923450657829_20240412-135628_back_addGood_70f754088050_965_13725988807.mp4
|
||||
6923450657829_20240412-135628_front_addGood_70f754088050_965_13725988807.mp4
|
||||
6923450657829_20240412-135640_back_returnGood_70f754088050_965_13725988807.mp4
|
||||
6923450657829_20240412-135640_front_returnGood_70f754088050_965_13725988807.mp4
|
||||
6923450659441_20240412-135319_back_addGood_70f754088050_1825_13725988807.mp4
|
||||
6923450659441_20240412-135319_front_addGood_70f754088050_1825_13725988807.mp4
|
||||
6923450659441_20240412-135334_back_returnGood_70f754088050_1825_13725988807.mp4
|
||||
6923450659441_20240412-135334_front_returnGood_70f754088050_1825_13725988807.mp4
|
||||
6923450666838_20240412-134807_back_addGood_70f754088050_760_13725988807.mp4
|
||||
6923450666838_20240412-134807_front_addGood_70f754088050_760_13725988807.mp4
|
||||
6923450666838_20240412-134818_back_returnGood_70f754088050_760_13725988807.mp4
|
||||
6923450666838_20240412-134818_front_returnGood_70f754088050_760_13725988807.mp4
|
||||
6923450668207_20240412-134250_back_addGood_70f754088050_700_13725988807.mp4
|
||||
6923450668207_20240412-134250_front_addGood_70f754088050_700_13725988807.mp4
|
||||
6923450668207_20240412-134302_back_returnGood_70f754088050_700_13725988807.mp4
|
||||
6923450668207_20240412-134302_front_returnGood_70f754088050_700_13725988807.mp4
|
||||
6923450677858_20240412-135523_back_addGood_70f754088050_910_13725988807.mp4
|
||||
6923450677858_20240412-135523_front_addGood_70f754088050_910_13725988807.mp4
|
||||
6923450677858_20240412-135537_back_returnGood_70f754088050_910_13725988807.mp4
|
||||
6923450677858_20240412-135537_front_returnGood_70f754088050_910_13725988807.mp4
|
||||
6923644286293_20240412-141148_back_addGood_70f754088050_810_13725988807.mp4
|
||||
6923644286293_20240412-141148_front_addGood_70f754088050_810_13725988807.mp4
|
||||
6923644286293_20240412-141157_back_returnGood_70f754088050_805_13725988807.mp4
|
||||
6923644286293_20240412-141157_front_returnGood_70f754088050_805_13725988807.mp4
|
||||
6923644298760_20240412-141219_back_addGood_70f754088050_1020_13725988807.mp4
|
||||
6923644298760_20240412-141219_front_addGood_70f754088050_1020_13725988807.mp4
|
||||
6923644298760_20240412-141227_back_returnGood_70f754088050_1020_13725988807.mp4
|
||||
6923644298760_20240412-141227_front_returnGood_70f754088050_1020_13725988807.mp4
|
||||
6924743915824_20240412-145245_back_addGood_70f754088050_155_13725988807.mp4
|
||||
6924743915824_20240412-145245_front_addGood_70f754088050_155_13725988807.mp4
|
||||
6924743915824_20240412-145254_back_returnGood_70f754088050_155_13725988807.mp4
|
||||
6924743915824_20240412-145254_front_returnGood_70f754088050_155_13725988807.mp4
|
||||
6924882497106_20240412-150553_back_addGood_70f754088050_355_13725988807.mp4
|
||||
6924882497106_20240412-150553_front_addGood_70f754088050_355_13725988807.mp4
|
||||
6924882497106_20240412-150604_back_returnGood_70f754088050_355_13725988807.mp4
|
||||
6924882497106_20240412-150604_front_returnGood_70f754088050_355_13725988807.mp4
|
||||
6925307305525_20240412-152608_back_addGood_70f754088050_590_13725988807.mp4
|
||||
6925307305525_20240412-152608_front_addGood_70f754088050_590_13725988807.mp4
|
||||
6925307305525_20240412-152627_back_returnGood_70f754088050_590_13725988807.mp4
|
||||
6925307305525_20240412-152627_front_returnGood_70f754088050_590_13725988807.mp4
|
||||
6928033404968_20240412-143452_back_addGood_70f754088050_400_13725988807.mp4
|
||||
6928033404968_20240412-143452_front_addGood_70f754088050_400_13725988807.mp4
|
||||
6928033404968_20240412-143502_back_returnGood_70f754088050_400_13725988807.mp4
|
||||
6928033404968_20240412-143502_front_returnGood_70f754088050_400_13725988807.mp4
|
||||
6928804011173_20240412-152735_back_addGood_70f754088050_545_13725988807.mp4
|
||||
6928804011173_20240412-152735_front_addGood_70f754088050_545_13725988807.mp4
|
||||
6928804011173_20240412-152746_back_returnGood_70f754088050_545_13725988807.mp4
|
||||
6928804011173_20240412-152746_front_returnGood_70f754088050_545_13725988807.mp4
|
||||
6931925828032_20240412-134659_back_addGood_70f754088050_415_13725988807.mp4
|
||||
6931925828032_20240412-134659_front_addGood_70f754088050_415_13725988807.mp4
|
||||
6931925828032_20240412-134711_back_returnGood_70f754088050_415_13725988807.mp4
|
||||
6931925828032_20240412-134711_front_returnGood_70f754088050_415_13725988807.mp4
|
||||
6933620900051_20240412-140306_back_addGood_70f754088050_385_13725988807.mp4
|
||||
6933620900051_20240412-140306_front_addGood_70f754088050_385_13725988807.mp4
|
||||
6933620900051_20240412-140326_back_returnGood_70f754088050_390_13725988807.mp4
|
||||
6933620900051_20240412-140326_front_returnGood_70f754088050_390_13725988807.mp4
|
||||
6934665095108_20240412-141758_back_addGood_70f754088050_355_13725988807.mp4
|
||||
6934665095108_20240412-141758_front_addGood_70f754088050_355_13725988807.mp4
|
||||
6934665095108_20240412-141807_back_returnGood_70f754088050_355_13725988807.mp4
|
||||
6934665095108_20240412-141807_front_returnGood_70f754088050_355_13725988807.mp4
|
||||
6935270642121_20240412-151112_back_addGood_70f754088050_165_13725988807.mp4
|
||||
6935270642121_20240412-151112_front_addGood_70f754088050_165_13725988807.mp4
|
||||
6935270642121_20240412-151124_back_returnGood_70f754088050_165_13725988807.mp4
|
||||
6935270642121_20240412-151124_front_returnGood_70f754088050_165_13725988807.mp4
|
||||
6935284417326_20240412-145558_back_addGood_70f754088050_410_13725988807.mp4
|
||||
6935284417326_20240412-145558_front_addGood_70f754088050_410_13725988807.mp4
|
||||
6935284417326_20240412-145610_back_returnGood_70f754088050_410_13725988807.mp4
|
||||
6935284417326_20240412-145610_front_returnGood_70f754088050_410_13725988807.mp4
|
||||
6941025140798_20240412-152222_back_addGood_70f754088050_1160_13725988807.mp4
|
||||
6941025140798_20240412-152222_front_addGood_70f754088050_1160_13725988807.mp4
|
||||
6941025140798_20240412-152235_back_returnGood_70f754088050_1155_13725988807.mp4
|
||||
6941025140798_20240412-152235_front_returnGood_70f754088050_1155_13725988807.mp4
|
||||
6952074634794_20240412-153052_back_addGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153052_front_addGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153105_back_returnGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153105_front_returnGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153233_back_addGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153233_front_addGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153245_back_returnGood_70f754088050_265_13725988807.mp4
|
||||
6952074634794_20240412-153245_front_returnGood_70f754088050_265_13725988807.mp4
|
||||
6954432711307_20240412-134028_back_addGood_70f754088050_355_13725988807.mp4
|
||||
6954432711307_20240412-134028_front_addGood_70f754088050_355_13725988807.mp4
|
||||
6954432711307_20240412-134040_back_returnGood_70f754088050_350_13725988807.mp4
|
||||
6954432711307_20240412-134040_front_returnGood_70f754088050_350_13725988807.mp4
|
||||
6959546100993_20240412-142438_back_addGood_70f754088050_295_13725988807.mp4
|
||||
6959546100993_20240412-142438_front_addGood_70f754088050_295_13725988807.mp4
|
||||
6959546100993_20240412-142455_back_returnGood_70f754088050_295_13725988807.mp4
|
||||
6959546100993_20240412-142455_front_returnGood_70f754088050_295_13725988807.mp4
|
||||
6971075127463_20240412-142330_back_addGood_70f754088050_210_13725988807.mp4
|
||||
6971075127463_20240412-142330_front_addGood_70f754088050_210_13725988807.mp4
|
||||
6971075127463_20240412-142347_back_returnGood_70f754088050_215_13725988807.mp4
|
||||
6971075127463_20240412-142347_front_returnGood_70f754088050_215_13725988807.mp4
|
||||
6971075127470_20240412-142650_back_addGood_70f754088050_215_13725988807.mp4
|
||||
6971075127470_20240412-142650_front_addGood_70f754088050_215_13725988807.mp4
|
||||
6971075127470_20240412-142700_back_returnGood_70f754088050_215_13725988807.mp4
|
||||
6971075127470_20240412-142700_front_returnGood_70f754088050_215_13725988807.mp4
|
||||
6971328580533_20240412-152303_back_addGood_70f754088050_655_13725988807.mp4
|
||||
6971328580533_20240412-152303_front_addGood_70f754088050_655_13725988807.mp4
|
||||
6971328580533_20240412-152322_back_returnGood_70f754088050_650_13725988807.mp4
|
||||
6971328580533_20240412-152322_front_returnGood_70f754088050_650_13725988807.mp4
|
||||
6971738655333_20240412-141033_back_addGood_70f754088050_270_13725988807.mp4
|
||||
6971738655333_20240412-141033_front_addGood_70f754088050_270_13725988807.mp4
|
||||
6971738655333_20240412-141042_back_returnGood_70f754088050_270_13725988807.mp4
|
||||
6971738655333_20240412-141042_front_returnGood_70f754088050_270_13725988807.mp4
|
||||
6972378998200_20240412-144314_back_addGood_70f754088050_410_13725988807.mp4
|
||||
6972378998200_20240412-144314_front_addGood_70f754088050_410_13725988807.mp4
|
||||
6972378998200_20240412-144326_back_returnGood_70f754088050_410_13725988807.mp4
|
||||
6972378998200_20240412-144326_front_returnGood_70f754088050_410_13725988807.mp4
|
||||
6972790052733_20240412-135134_back_addGood_70f754088050_525_13725988807.mp4
|
||||
6972790052733_20240412-135134_front_addGood_70f754088050_525_13725988807.mp4
|
||||
6972790052733_20240412-135145_back_returnGood_70f754088050_525_13725988807.mp4
|
||||
6972790052733_20240412-135145_front_returnGood_70f754088050_525_13725988807.mp4
|
||||
6974913231612_20240412-142848_back_addGood_70f754088050_500_13725988807.mp4
|
||||
6974913231612_20240412-142848_front_addGood_70f754088050_500_13725988807.mp4
|
||||
6974913231612_20240412-142900_back_returnGood_70f754088050_495_13725988807.mp4
|
||||
6974913231612_20240412-142900_front_returnGood_70f754088050_495_13725988807.mp4
|
||||
6974995172711_20240412-152143_back_addGood_70f754088050_455_13725988807.mp4
|
||||
6974995172711_20240412-152143_front_addGood_70f754088050_455_13725988807.mp4
|
||||
6974995172711_20240412-152158_back_returnGood_70f754088050_455_13725988807.mp4
|
||||
6974995172711_20240412-152158_front_returnGood_70f754088050_455_13725988807.mp4
|
||||
6976371220276_20240412-140448_back_addGood_70f754088050_295_13725988807.mp4
|
||||
6976371220276_20240412-140448_front_addGood_70f754088050_295_13725988807.mp4
|
||||
6976371220276_20240412-140459_back_returnGood_70f754088050_295_13725988807.mp4
|
||||
6976371220276_20240412-140459_front_returnGood_70f754088050_295_13725988807.mp4
|
||||
850009021632_20240412-152522_back_addGood_70f754088050_470_13725988807.mp4
|
||||
850009021632_20240412-152522_front_addGood_70f754088050_470_13725988807.mp4
|
||||
850009021632_20240412-152543_back_returnGood_70f754088050_470_13725988807.mp4
|
||||
850009021632_20240412-152543_front_returnGood_70f754088050_470_13725988807.mp4
|
208
tracking/matching/视频分类/双.txt
Normal file
208
tracking/matching/视频分类/双.txt
Normal file
@ -0,0 +1,208 @@
|
||||
6901070613142_20240411-170415_back_addGood_70f75407b7ae_430_17788571404.mp4
|
||||
6901070613142_20240411-170415_front_addGood_70f75407b7ae_430_17788571404.mp4
|
||||
6901070613142_20240411-170424_back_returnGood_70f75407b7ae_430_17788571404.mp4
|
||||
6901070613142_20240411-170424_front_returnGood_70f75407b7ae_430_17788571404.mp4
|
||||
6901070613142_20240411-170441_back_addGood_70f754088050_430_17327712807.mp4
|
||||
6901070613142_20240411-170441_front_addGood_70f754088050_430_17327712807.mp4
|
||||
6901070613142_20240411-170450_back_returnGood_70f754088050_430_17327712807.mp4
|
||||
6901070613142_20240411-170450_front_returnGood_70f754088050_430_17327712807.mp4
|
||||
6902538007367_20240411-165931_back_addGood_70f75407b7ae_995_17788571404.mp4
|
||||
6902538007367_20240411-165931_front_addGood_70f75407b7ae_995_17788571404.mp4
|
||||
6902538007367_20240411-165942_back_returnGood_70f75407b7ae_995_17788571404.mp4
|
||||
6902538007367_20240411-165942_front_returnGood_70f75407b7ae_995_17788571404.mp4
|
||||
6902538007367_20240411-165954_back_addGood_70f754088050_1000_17327712807.mp4
|
||||
6902538007367_20240411-165954_front_addGood_70f754088050_1000_17327712807.mp4
|
||||
6902538007367_20240411-170005_back_returnGood_70f754088050_1000_17327712807.mp4
|
||||
6902538007367_20240411-170005_front_returnGood_70f754088050_1000_17327712807.mp4
|
||||
6920152400630_20240411-165136_back_addGood_70f75407b7ae_720_17788571404.mp4
|
||||
6920152400630_20240411-165136_front_addGood_70f75407b7ae_720_17788571404.mp4
|
||||
6920152400630_20240411-165151_back_returnGood_70f75407b7ae_715_17788571404.mp4
|
||||
6920152400630_20240411-165151_front_returnGood_70f75407b7ae_715_17788571404.mp4
|
||||
6920152400630_20240411-165213_back_addGood_70f754088050_720_17327712807.mp4
|
||||
6920152400630_20240411-165213_front_addGood_70f754088050_720_17327712807.mp4
|
||||
6920152400630_20240411-165224_back_returnGood_70f754088050_720_17327712807.mp4
|
||||
6920152400630_20240411-165224_front_returnGood_70f754088050_720_17327712807.mp4
|
||||
6920907810707_20240411-165615_back_addGood_70f75407b7ae_225_17788571404.mp4
|
||||
6920907810707_20240411-165615_front_addGood_70f75407b7ae_225_17788571404.mp4
|
||||
6920907810707_20240411-165625_back_returnGood_70f75407b7ae_225_17788571404.mp4
|
||||
6920907810707_20240411-165625_front_returnGood_70f75407b7ae_225_17788571404.mp4
|
||||
6920907810707_20240411-165635_back_addGood_70f754088050_225_17327712807.mp4
|
||||
6920907810707_20240411-165635_front_addGood_70f754088050_225_17327712807.mp4
|
||||
6920907810707_20240411-165646_back_returnGood_70f754088050_225_17327712807.mp4
|
||||
6920907810707_20240411-165646_front_returnGood_70f754088050_225_17327712807.mp4
|
||||
6923450605288_20240411-172045_back_addGood_70f75407b7ae_750_17788571404.mp4
|
||||
6923450605288_20240411-172045_front_addGood_70f75407b7ae_750_17788571404.mp4
|
||||
6923450605288_20240411-172058_back_returnGood_70f75407b7ae_750_17788571404.mp4
|
||||
6923450605288_20240411-172058_front_returnGood_70f75407b7ae_750_17788571404.mp4
|
||||
6923450605288_20240411-172134_back_addGood_70f754088050_750_17327712807.mp4
|
||||
6923450605288_20240411-172134_front_addGood_70f754088050_750_17327712807.mp4
|
||||
6923450605288_20240411-172147_back_returnGood_70f754088050_750_17327712807.mp4
|
||||
6923450605288_20240411-172147_front_returnGood_70f754088050_750_17327712807.mp4
|
||||
6923450610428_20240411-171842_back_addGood_70f75407b7ae_270_17788571404.mp4
|
||||
6923450610428_20240411-171842_front_addGood_70f75407b7ae_270_17788571404.mp4
|
||||
6923450610428_20240411-171900_back_returnGood_70f75407b7ae_755_17788571404.mp4
|
||||
6923450610428_20240411-171900_front_returnGood_70f75407b7ae_755_17788571404.mp4
|
||||
6923450610428_20240411-171918_back_addGood_70f754088050_755_17327712807.mp4
|
||||
6923450610428_20240411-171918_front_addGood_70f754088050_755_17327712807.mp4
|
||||
6923450610428_20240411-172000_back_returnGood_70f754088050_755_17327712807.mp4
|
||||
6923450610428_20240411-172000_front_returnGood_70f754088050_755_17327712807.mp4
|
||||
6923450659441_20240411-171417_back_addGood_70f75407b7ae_2340_17788571404.mp4
|
||||
6923450659441_20240411-171417_front_addGood_70f75407b7ae_2340_17788571404.mp4
|
||||
6923450659441_20240411-171430_back_returnGood_70f75407b7ae_2335_17788571404.mp4
|
||||
6923450659441_20240411-171430_front_returnGood_70f75407b7ae_2335_17788571404.mp4
|
||||
6923450659441_20240411-171445_back_addGood_70f754088050_2340_17327712807.mp4
|
||||
6923450659441_20240411-171445_front_addGood_70f754088050_2340_17327712807.mp4
|
||||
6923450659441_20240411-171456_back_returnGood_70f754088050_2340_17327712807.mp4
|
||||
6923450659441_20240411-171456_front_returnGood_70f754088050_2340_17327712807.mp4
|
||||
6923450677858_20240411-171658_back_addGood_70f75407b7ae_1310_17788571404.mp4
|
||||
6923450677858_20240411-171658_front_addGood_70f75407b7ae_1310_17788571404.mp4
|
||||
6923450677858_20240411-171709_back_returnGood_70f75407b7ae_1310_17788571404.mp4
|
||||
6923450677858_20240411-171709_front_returnGood_70f75407b7ae_1310_17788571404.mp4
|
||||
6923450677858_20240411-171720_back_addGood_70f754088050_1310_17327712807.mp4
|
||||
6923450677858_20240411-171720_front_addGood_70f754088050_1310_17327712807.mp4
|
||||
6923450677858_20240411-171730_back_returnGood_70f754088050_1315_17327712807.mp4
|
||||
6923450677858_20240411-171730_front_returnGood_70f754088050_1315_17327712807.mp4
|
||||
6928804011173_20240411-165756_back_addGood_70f75407b7ae_615_17788571404.mp4
|
||||
6928804011173_20240411-165756_front_addGood_70f75407b7ae_615_17788571404.mp4
|
||||
6928804011173_20240411-165808_back_returnGood_70f75407b7ae_620_17788571404.mp4
|
||||
6928804011173_20240411-165808_front_returnGood_70f75407b7ae_620_17788571404.mp4
|
||||
6928804011173_20240411-165822_back_addGood_70f754088050_620_17327712807.mp4
|
||||
6928804011173_20240411-165822_front_addGood_70f754088050_620_17327712807.mp4
|
||||
6928804011173_20240411-165830_back_returnGood_70f754088050_620_17327712807.mp4
|
||||
6928804011173_20240411-165830_front_returnGood_70f754088050_620_17327712807.mp4
|
||||
6976371220276_20240411-170159_back_addGood_70f75407b7ae_745_17788571404.mp4
|
||||
6976371220276_20240411-170159_front_addGood_70f75407b7ae_745_17788571404.mp4
|
||||
6976371220276_20240411-170211_back_returnGood_70f75407b7ae_745_17788571404.mp4
|
||||
6976371220276_20240411-170211_front_returnGood_70f75407b7ae_745_17788571404.mp4
|
||||
6976371220276_20240411-170230_back_addGood_70f754088050_745_17327712807.mp4
|
||||
6976371220276_20240411-170230_front_addGood_70f754088050_745_17327712807.mp4
|
||||
6976371220276_20240411-170240_back_returnGood_70f754088050_745_17327712807.mp4
|
||||
6976371220276_20240411-170240_front_returnGood_70f754088050_745_17327712807.mp4
|
||||
230537101280010007_20240412-114205_back_addGood_70f75407b7ae_720_17327712807.mp4
|
||||
230537101280010007_20240412-114205_front_addGood_70f75407b7ae_720_17327712807.mp4
|
||||
230537101280010007_20240412-114214_back_returnGood_70f75407b7ae_720_17327712807.mp4
|
||||
230537101280010007_20240412-114214_front_returnGood_70f75407b7ae_720_17327712807.mp4
|
||||
2500456001326_20240412-110503_back_addGood_70f75407b7ae_1085_17327712807.mp4
|
||||
2500456001326_20240412-110503_front_addGood_70f75407b7ae_1085_17327712807.mp4
|
||||
2500456001326_20240412-110513_back_returnGood_70f75407b7ae_1085_17327712807.mp4
|
||||
2500456001326_20240412-110513_front_returnGood_70f75407b7ae_1085_17327712807.mp4
|
||||
6901070613142_20240412-110200_back_addGood_70f754088050_1180_17788571404.mp4
|
||||
6901070613142_20240412-110200_front_addGood_70f754088050_1180_17788571404.mp4
|
||||
6901070613142_20240412-110207_back_returnGood_70f754088050_1180_17788571404.mp4
|
||||
6901070613142_20240412-110207_front_returnGood_70f754088050_1180_17788571404.mp4
|
||||
6901070613142_20240412-112959_back_addGood_70f75407b7ae_655_17327712807.mp4
|
||||
6901070613142_20240412-112959_front_addGood_70f75407b7ae_655_17327712807.mp4
|
||||
6901070613142_20240412-113011_back_returnGood_70f75407b7ae_655_17327712807.mp4
|
||||
6901070613142_20240412-113011_front_returnGood_70f75407b7ae_655_17327712807.mp4
|
||||
6901668053893_20240412-113635_back_addGood_70f75407b7ae_565_17327712807.mp4
|
||||
6901668053893_20240412-113635_front_addGood_70f75407b7ae_565_17327712807.mp4
|
||||
6901668053893_20240412-113645_back_returnGood_70f75407b7ae_640_17327712807.mp4
|
||||
6901668053893_20240412-113645_front_returnGood_70f75407b7ae_640_17327712807.mp4
|
||||
6902007010249_20240412-113050_back_addGood_70f75407b7ae_1460_17327712807.mp4
|
||||
6902007010249_20240412-113050_front_addGood_70f75407b7ae_1460_17327712807.mp4
|
||||
6902007010249_20240412-113103_back_returnGood_70f75407b7ae_1460_17327712807.mp4
|
||||
6902007010249_20240412-113103_front_returnGood_70f75407b7ae_1460_17327712807.mp4
|
||||
6902022135514_20240412-112914_back_addGood_70f75407b7ae_3640_17327712807.mp4
|
||||
6902022135514_20240412-112914_front_addGood_70f75407b7ae_3640_17327712807.mp4
|
||||
6902022135514_20240412-112926_back_returnGood_70f75407b7ae_3640_17327712807.mp4
|
||||
6902022135514_20240412-112926_front_returnGood_70f75407b7ae_3640_17327712807.mp4
|
||||
6902265114369_20240412-110609_back_addGood_70f75407b7ae_990_17327712807.mp4
|
||||
6902265114369_20240412-110609_front_addGood_70f75407b7ae_990_17327712807.mp4
|
||||
6902265114369_20240412-110618_back_returnGood_70f75407b7ae_990_17327712807.mp4
|
||||
6902265114369_20240412-110618_front_returnGood_70f75407b7ae_990_17327712807.mp4
|
||||
6902265908012_20240412-111958_back_addGood_70f75407b7ae_1520_17327712807.mp4
|
||||
6902265908012_20240412-111958_front_addGood_70f75407b7ae_1520_17327712807.mp4
|
||||
6902265908012_20240412-112012_back_returnGood_70f75407b7ae_1520_17327712807.mp4
|
||||
6902265908012_20240412-112012_front_returnGood_70f75407b7ae_1520_17327712807.mp4
|
||||
6902538007367_20240412-105046_back_addGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105046_front_addGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105057_back_returnGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105057_front_returnGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105130_back_addGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105130_front_addGood_70f754088050_930_17788571404.mp4
|
||||
6902538007367_20240412-105143_back_returnGood_70f754088050_935_17788571404.mp4
|
||||
6902538007367_20240412-105143_front_returnGood_70f754088050_935_17788571404.mp4
|
||||
6907992103952_20240412-114032_back_addGood_70f75407b7ae_545_17327712807.mp4
|
||||
6907992103952_20240412-114032_front_addGood_70f75407b7ae_545_17327712807.mp4
|
||||
6907992103952_20240412-114043_back_returnGood_70f75407b7ae_540_17327712807.mp4
|
||||
6907992103952_20240412-114043_front_returnGood_70f75407b7ae_540_17327712807.mp4
|
||||
6907992106311_20240412-110328_back_addGood_70f75407b7ae_2565_17327712807.mp4
|
||||
6907992106311_20240412-110328_front_addGood_70f75407b7ae_2565_17327712807.mp4
|
||||
6907992106311_20240412-110342_back_returnGood_70f75407b7ae_2570_17327712807.mp4
|
||||
6907992106311_20240412-110342_front_returnGood_70f75407b7ae_2570_17327712807.mp4
|
||||
6920152400630_20240412-112256_back_addGood_70f75407b7ae_1230_17327712807.mp4
|
||||
6920152400630_20240412-112256_front_addGood_70f75407b7ae_1230_17327712807.mp4
|
||||
6920152400630_20240412-112308_back_returnGood_70f75407b7ae_1230_17327712807.mp4
|
||||
6920152400630_20240412-112308_front_returnGood_70f75407b7ae_1230_17327712807.mp4
|
||||
6920174757101_20240412-112806_back_addGood_70f75407b7ae_2120_17327712807.mp4
|
||||
6920174757101_20240412-112806_front_addGood_70f75407b7ae_2120_17327712807.mp4
|
||||
6920174757101_20240412-112823_back_returnGood_70f75407b7ae_2125_17327712807.mp4
|
||||
6920174757101_20240412-112823_front_returnGood_70f75407b7ae_2125_17327712807.mp4
|
||||
6920459905012_20240412-104811_back_addGood_70f754088050_840_17788571404.mp4
|
||||
6920459905012_20240412-104811_front_addGood_70f754088050_840_17788571404.mp4
|
||||
6920459905012_20240412-104906_back_returnGood_70f754088050_840_17788571404.mp4
|
||||
6920459905012_20240412-104906_front_returnGood_70f754088050_840_17788571404.mp4
|
||||
6920459905012_20240412-105345_back_addGood_70f754088050_830_17788571404.mp4
|
||||
6920459905012_20240412-105345_front_addGood_70f754088050_830_17788571404.mp4
|
||||
6920459905012_20240412-105356_back_returnGood_70f754088050_830_17788571404.mp4
|
||||
6920459905012_20240412-105356_front_returnGood_70f754088050_830_17788571404.mp4
|
||||
6920459905012_20240412-113755_back_addGood_70f75407b7ae_970_17327712807.mp4
|
||||
6920459905012_20240412-113755_front_addGood_70f75407b7ae_970_17327712807.mp4
|
||||
6920459905012_20240412-113808_back_returnGood_70f75407b7ae_970_17327712807.mp4
|
||||
6920459905012_20240412-113808_front_returnGood_70f75407b7ae_970_17327712807.mp4
|
||||
6920907810707_20240412-105728_back_addGood_70f754088050_150_17788571404.mp4
|
||||
6920907810707_20240412-105728_front_addGood_70f754088050_150_17788571404.mp4
|
||||
6920907810707_20240412-105739_back_returnGood_70f754088050_145_17788571404.mp4
|
||||
6920907810707_20240412-105739_front_returnGood_70f754088050_145_17788571404.mp4
|
||||
6920907810707_20240412-113710_back_addGood_70f75407b7ae_900_17327712807.mp4
|
||||
6920907810707_20240412-113710_front_addGood_70f75407b7ae_900_17327712807.mp4
|
||||
6920907810707_20240412-113720_back_returnGood_70f75407b7ae_900_17327712807.mp4
|
||||
6920907810707_20240412-113720_front_returnGood_70f75407b7ae_900_17327712807.mp4
|
||||
6922130119213_20240412-111854_back_addGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922130119213_20240412-111854_front_addGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922130119213_20240412-111904_back_returnGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922130119213_20240412-111904_front_returnGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922577700968_20240412-114323_back_addGood_70f75407b7ae_1045_17327712807.mp4
|
||||
6922577700968_20240412-114323_front_addGood_70f75407b7ae_1045_17327712807.mp4
|
||||
6922577700968_20240412-114333_back_returnGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922577700968_20240412-114333_front_returnGood_70f75407b7ae_1310_17327712807.mp4
|
||||
6922868291168_20240412-112724_back_addGood_70f75407b7ae_4540_17327712807.mp4
|
||||
6922868291168_20240412-112724_front_addGood_70f75407b7ae_4540_17327712807.mp4
|
||||
6922868291168_20240412-112736_back_returnGood_70f75407b7ae_4540_17327712807.mp4
|
||||
6922868291168_20240412-112736_front_returnGood_70f75407b7ae_4540_17327712807.mp4
|
||||
6923644286293_20240412-114002_back_addGood_70f75407b7ae_1535_17327712807.mp4
|
||||
6923644286293_20240412-114002_front_addGood_70f75407b7ae_1535_17327712807.mp4
|
||||
6923644286293_20240412-114012_back_returnGood_70f75407b7ae_1535_17327712807.mp4
|
||||
6923644286293_20240412-114012_front_returnGood_70f75407b7ae_1535_17327712807.mp4
|
||||
6924743915824_20240412-104437_back_addGood_70f754088050_455_17788571404.mp4
|
||||
6924743915824_20240412-104437_front_addGood_70f754088050_455_17788571404.mp4
|
||||
6924743915824_20240412-104448_back_returnGood_70f754088050_455_17788571404.mp4
|
||||
6924743915824_20240412-104448_front_returnGood_70f754088050_455_17788571404.mp4
|
||||
6924743915824_20240412-113553_back_addGood_70f75407b7ae_920_17327712807.mp4
|
||||
6924743915824_20240412-113553_front_addGood_70f75407b7ae_920_17327712807.mp4
|
||||
6924743915824_20240412-113603_back_returnGood_70f75407b7ae_920_17327712807.mp4
|
||||
6924743915824_20240412-113603_front_returnGood_70f75407b7ae_920_17327712807.mp4
|
||||
6928804011173_20240412-105808_back_addGood_70f754088050_915_17788571404.mp4
|
||||
6928804011173_20240412-105808_front_addGood_70f754088050_915_17788571404.mp4
|
||||
6928804011173_20240412-105818_back_returnGood_70f754088050_910_17788571404.mp4
|
||||
6928804011173_20240412-105818_front_returnGood_70f754088050_910_17788571404.mp4
|
||||
6934665095108_20240412-114106_back_addGood_70f75407b7ae_885_17327712807.mp4
|
||||
6934665095108_20240412-114106_front_addGood_70f75407b7ae_885_17327712807.mp4
|
||||
6934665095108_20240412-114116_back_returnGood_70f75407b7ae_885_17327712807.mp4
|
||||
6934665095108_20240412-114116_front_returnGood_70f75407b7ae_885_17327712807.mp4
|
||||
6935270642121_20240412-111602_back_addGood_70f75407b7ae_540_17327712807.mp4
|
||||
6935270642121_20240412-111602_front_addGood_70f75407b7ae_540_17327712807.mp4
|
||||
6935270642121_20240412-111614_back_returnGood_70f75407b7ae_540_17327712807.mp4
|
||||
6935270642121_20240412-111614_front_returnGood_70f75407b7ae_540_17327712807.mp4
|
||||
6952074634794_20240412-104633_back_addGood_70f754088050_855_17788571404.mp4
|
||||
6952074634794_20240412-104633_front_addGood_70f754088050_855_17788571404.mp4
|
||||
6952074634794_20240412-104700_back_returnGood_70f754088050_855_17788571404.mp4
|
||||
6952074634794_20240412-104700_front_returnGood_70f754088050_855_17788571404.mp4
|
||||
6952074634794_20240412-113515_back_addGood_70f75407b7ae_595_17327712807.mp4
|
||||
6952074634794_20240412-113515_front_addGood_70f75407b7ae_595_17327712807.mp4
|
||||
6952074634794_20240412-113524_back_returnGood_70f75407b7ae_595_17327712807.mp4
|
||||
6952074634794_20240412-113524_front_returnGood_70f75407b7ae_595_17327712807.mp4
|
||||
6972378998200_20240412-111721_back_addGood_70f75407b7ae_870_17327712807.mp4
|
||||
6972378998200_20240412-111721_front_addGood_70f75407b7ae_870_17327712807.mp4
|
||||
6972378998200_20240412-111741_back_returnGood_70f75407b7ae_865_17327712807.mp4
|
||||
6972378998200_20240412-111741_front_returnGood_70f75407b7ae_865_17327712807.mp4
|
12
tracking/matching/视频分类/视频文件命名规则.txt
Normal file
12
tracking/matching/视频分类/视频文件命名规则.txt
Normal file
@ -0,0 +1,12 @@
|
||||
采集文件名字段规则:
|
||||
230537101280010007_20240411-144945_back_returnGood_70f75407b7ae_565_17788571404.mp4
|
||||
|
||||
String targetName =
|
||||
barCode + "_" (条形码字段)
|
||||
+ recordFileName + "_" (文件名字段:时间格式精确到秒)
|
||||
+ "back/front"+ "_" (后/前摄字段)
|
||||
+ "addGood/returnGood"+ "_"(加/退购字段)
|
||||
+ macId + "_" (mac地址字段:去除中间冒号)
|
||||
+ Math.abs(goodsWeight) + "_" (商品重量字段:变化的绝对值)
|
||||
+ user.phone (采集人手机号字段)
|
||||
+ ".mp4";
|
6
tracking/matching/视频分类/较暗.txt
Normal file
6
tracking/matching/视频分类/较暗.txt
Normal file
@ -0,0 +1,6 @@
|
||||
6920152400630_20240412-151024_back_addGood_70f754088050_580_13725988807.mp4
|
||||
6920152400630_20240412-151024_front_addGood_70f754088050_580_13725988807.mp4
|
||||
6920152400630_20240412-151036_front_returnGood_70f754088050_580_13725988807.mp4
|
||||
6920459905012_20240412-150815_back_addGood_70f754088050_550_13725988807.mp4
|
||||
6920459905012_20240412-150815_front_addGood_70f754088050_550_13725988807.mp4
|
||||
6920459905012_20240412-150826_front_returnGood_70f754088050_550_13725988807.mp4
|
173
tracking/merge_track_test.py
Normal file
173
tracking/merge_track_test.py
Normal file
@ -0,0 +1,173 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Feb 23 11:04:48 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import cv2
|
||||
from scipy.spatial.distance import cdist
|
||||
# from trackers.utils import matching
|
||||
|
||||
def readDict(boxes, feat_dicts):
|
||||
feat = []
|
||||
for i in range(boxes.shape[0]):
|
||||
tid, fid, bid = int(boxes[i, 4]), int(boxes[i, 7]), int(boxes[i, 8])
|
||||
feat.append(feat_dicts[fid][bid])
|
||||
|
||||
# img = feat_dicts[fid][f'{bid}_img']
|
||||
# cv2.imwrite(f'./result/imgs/{tid}_{fid}_{bid}.png', img)
|
||||
|
||||
return np.asarray(feat, dtype=np.float32)
|
||||
|
||||
|
||||
|
||||
def track_equal_track(atrack, btrack, feat_dicts):
|
||||
# boxes: [x, y, w, h, track_id, score, cls, frame_index, box_index]
|
||||
aboxes = atrack.boxes
|
||||
bboxes = btrack.boxes
|
||||
|
||||
''' 1. 判断轨迹在时序上是否有交集 '''
|
||||
afids = aboxes[:, 7].astype(np.int_)
|
||||
bfids = bboxes[:, 7].astype(np.int_)
|
||||
# 帧索引交集
|
||||
interfid = set(afids).intersection(set(bfids))
|
||||
|
||||
# 或者直接判断帧索引是否有交集,返回 Ture or False
|
||||
# interfid = set(afids).isdisjoint(set(bfids))
|
||||
|
||||
|
||||
''' 2. 轨迹空间iou'''
|
||||
alabel = np.array([0] * afids.size, dtype=np.int_)
|
||||
blabel = np.array([1] * bfids.size, dtype=np.int_)
|
||||
|
||||
label = np.concatenate((alabel, blabel), axis=0)
|
||||
fids = np.concatenate((afids, bfids), axis=0)
|
||||
indices = np.argsort(fids)
|
||||
idx_pair = []
|
||||
for i in range(len(indices)-1):
|
||||
idx1, idx2 = indices[i], indices[i+1]
|
||||
if label[idx1] != label[idx2] and fids[idx2] - fids[idx1] == 1:
|
||||
if label[idx1] == 0:
|
||||
a_idx = idx1
|
||||
b_idx = idx2-alabel.size
|
||||
else:
|
||||
a_idx = idx2
|
||||
b_idx = idx1-alabel.size
|
||||
|
||||
idx_pair.append((a_idx, b_idx))
|
||||
|
||||
ious = []
|
||||
for a, b in idx_pair:
|
||||
abox, bbox = aboxes[a, :], bboxes[b, :]
|
||||
|
||||
xa1, ya1 = abox[0] - abox[2]/2, abox[1] - abox[3]/2
|
||||
xa2, ya2 = abox[0] + abox[2]/2, abox[1] + abox[3]/2
|
||||
|
||||
xb1, yb1 = bbox[0] - bbox[2]/2, bbox[1] - bbox[3]/2
|
||||
xb2, yb2 = bbox[0] + bbox[2]/2, bbox[1] + bbox[3]/2
|
||||
|
||||
|
||||
inter = (np.minimum(xb2, xa2) - np.maximum(xb1, xa1)).clip(0) * \
|
||||
(np.minimum(yb2, ya2) - np.maximum(yb1, ya1)).clip(0)
|
||||
|
||||
# Union Area
|
||||
box1_area = abox[2] * abox[3]
|
||||
box2_area = bbox[2] * bbox[3]
|
||||
union = box1_area + box2_area - inter + 1e-6
|
||||
|
||||
ious.append(inter/union)
|
||||
|
||||
''' 3. 轨迹特征相似度判断'''
|
||||
afeat = readDict(aboxes, feat_dicts)
|
||||
bfeat = readDict(bboxes, feat_dicts)
|
||||
feat = np.concatenate((afeat, bfeat), axis=0)
|
||||
|
||||
emb_simil = 1-np.maximum(0.0, cdist(feat, feat, 'cosine'))
|
||||
emb_ = 1-cdist(np.mean(afeat, axis=0)[None, :], np.mean(bfeat, axis=0)[None, :], 'cosine')
|
||||
|
||||
cont1 = False if len(interfid) else True
|
||||
cont2 = all(iou>0.5 for iou in ious)
|
||||
cont3 = emb_[0, 0]>0.75
|
||||
|
||||
cont = cont1 and cont2 and cont3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return cont
|
||||
|
||||
|
||||
|
||||
def track_equal_str(atrack, btrack):
|
||||
if atrack == btrack:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def merge_track(Residual):
|
||||
out_list = []
|
||||
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:
|
||||
if track_equal_str(atrack, btrack):
|
||||
cur_list.append(btrack)
|
||||
else:
|
||||
alist.append(btrack)
|
||||
|
||||
out_list.append(cur_list)
|
||||
return out_list
|
||||
|
||||
def main():
|
||||
Residual = ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'b', 'c', 'd']
|
||||
out_list = merge_track(Residual)
|
||||
|
||||
print(Residual)
|
||||
print(out_list)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# for i, atrack in enumerate(input_list):
|
||||
# cur_list = []
|
||||
# cur_list.append(atrack)
|
||||
# del input_list[i]
|
||||
#
|
||||
# for j, btrack in enumerate(input_list):
|
||||
# if track_equal(atrack, btrack):
|
||||
# cur_list.append(btrack)
|
||||
# del input_list[j]
|
||||
#
|
||||
# out_list.append(cur_list)
|
||||
# =============================================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
470
tracking/module_analysis.py
Normal file
470
tracking/module_analysis.py
Normal file
@ -0,0 +1,470 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu May 30 14:03:03 2024
|
||||
|
||||
轨迹分析现场测试性能分析:
|
||||
(1) 读取 data 文件中的轨迹数据,绘制轨迹图
|
||||
(2) 读取本地运行 Yolo+Rsenet+Tracker+Tracking 的数据,绘制轨迹图
|
||||
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
import warnings
|
||||
import sys
|
||||
sys.path.append(r"D:\DetectTracking")
|
||||
|
||||
from tracking.utils.read_data import extract_data_realtime, read_tracking_output_realtime
|
||||
|
||||
from tracking.utils.plotting import Annotator, colors, draw_tracking_boxes
|
||||
from tracking.utils import Boxes, IterableSimpleNamespace, yaml_load
|
||||
from tracking.trackers import BOTSORT, BYTETracker
|
||||
from tracking.dotrack.dotracks_back import doBackTracks
|
||||
from tracking.dotrack.dotracks_front import doFrontTracks
|
||||
from tracking.utils.drawtracks import plot_frameID_y2, draw_all_trajectories
|
||||
|
||||
from tracking.utils.read_data import extract_data, read_deletedBarcode_file, read_tracking_output, read_returnGoods_file
|
||||
|
||||
from contrast.one2n_contrast import get_contrast_paths, one2n_return
|
||||
from tracking.utils.annotator import TrackAnnotator
|
||||
|
||||
W, H = 1024, 1280
|
||||
Mode = 'front' #'back'
|
||||
ImgFormat = ['.jpg', '.jpeg', '.png', '.bmp']
|
||||
|
||||
|
||||
|
||||
'''调用tracking()函数,利用本地跟踪算法获取各目标轨迹,可以比较本地跟踪算法与现场跟踪算法的区别。'''
|
||||
def init_tracker(tracker_yaml = None, bs=1):
|
||||
"""
|
||||
Initialize tracker for object tracking during prediction.
|
||||
"""
|
||||
TRACKER_MAP = {'bytetrack': BYTETracker, 'botsort': BOTSORT}
|
||||
cfg = IterableSimpleNamespace(**yaml_load(tracker_yaml))
|
||||
|
||||
tracker = TRACKER_MAP[cfg.tracker_type](args=cfg, frame_rate=30)
|
||||
|
||||
return tracker
|
||||
|
||||
def tracking(bboxes, ffeats):
|
||||
tracker_yaml = r"./trackers/cfg/botsort.yaml"
|
||||
tracker = init_tracker(tracker_yaml)
|
||||
|
||||
TrackBoxes = np.empty((0, 9), dtype = np.float32)
|
||||
TracksDict = {}
|
||||
|
||||
'''========================== 执行跟踪处理 ============================='''
|
||||
# dets 与 feats 应保持严格对应
|
||||
for dets, feats in zip(bboxes, ffeats):
|
||||
det_tracking = Boxes(dets).cpu().numpy()
|
||||
tracks = tracker.update(det_tracking, features=feats)
|
||||
|
||||
|
||||
'''tracks: [x1, y1, x2, y2, track_id, score, cls, frame_index, box_index]
|
||||
0 1 2 3 4 5 6 7 8
|
||||
这里,frame_index 也可以用视频的 帧ID 代替, box_index 保持不变
|
||||
'''
|
||||
|
||||
if len(tracks):
|
||||
TrackBoxes = np.concatenate([TrackBoxes, tracks], axis=0)
|
||||
|
||||
FeatDict = {}
|
||||
for track in tracks:
|
||||
tid = int(track[8])
|
||||
FeatDict.update({tid: feats[tid, :]})
|
||||
|
||||
frameID = tracks[0, 7]
|
||||
|
||||
# print(f"frameID: {int(frameID)}")
|
||||
assert len(tracks) == len(FeatDict), f"Please check the func: tracker.update() at frameID({int(frameID)})"
|
||||
|
||||
TracksDict[f"frame_{int(frameID)}"] = {"feats":FeatDict}
|
||||
|
||||
|
||||
return TrackBoxes, TracksDict
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def read_imgs(imgspath, CamerType):
|
||||
'''
|
||||
inputs:
|
||||
imgspath;序列图像地址
|
||||
CamerType:相机类型,0:后摄,1:前摄
|
||||
outputs:
|
||||
imgs:图像序列
|
||||
功能:
|
||||
根据CamerType类型读取imgspath文件夹中的图像,并根据帧索引进行排序。
|
||||
do_tracking()中调用该函数,实现(1)读取imgs并绘制各目标轨迹框;(2)获取subimgs
|
||||
'''
|
||||
imgs, frmIDs = [], []
|
||||
for filename in os.listdir(imgspath):
|
||||
file, ext = os.path.splitext(filename)
|
||||
flist = file.split('_')
|
||||
if len(flist)==4 and ext in ImgFormat:
|
||||
camID, frmID = flist[0], int(flist[-1])
|
||||
if camID==CamerType:
|
||||
img = cv2.imread(os.path.join(imgspath, filename))
|
||||
imgs.append(img)
|
||||
frmIDs.append(frmID)
|
||||
if len(frmIDs):
|
||||
indice = np.argsort(np.array(frmIDs))
|
||||
imgs = [imgs[i] for i in indice]
|
||||
|
||||
return imgs
|
||||
|
||||
def do_tracking(fpath, savedir, event_name='images'):
|
||||
'''
|
||||
args:
|
||||
fpath: 算法各模块输出的data文件地址,匹配;
|
||||
savedir: 对 fpath 各模块输出的复现;
|
||||
分析具体视频时,需指定 fpath 和 savedir
|
||||
outputs:
|
||||
img_tracking:目标跟踪轨迹、本地轨迹分析算法的轨迹对比图
|
||||
abimg:现场轨迹分析算法、轨迹选择输出的对比图
|
||||
'''
|
||||
# fpath = r'D:\contrast\dataset\1_to_n\709\20240709-102758_6971558612189\1_track.data'
|
||||
# savedir = r'D:\contrast\dataset\result\20240709-102843_6958770005357_6971558612189\error_6971558612189'
|
||||
|
||||
imgpath, dfname = os.path.split(fpath)
|
||||
CamerType = dfname.split('_')[0]
|
||||
|
||||
|
||||
'''1.1 构造 0/1_tracking_output.data 文件地址,读取文件数据'''
|
||||
tracking_output_path = os.path.join(imgpath, CamerType + '_tracking_output.data')
|
||||
|
||||
basename = os.path.basename(imgpath)
|
||||
if not os.path.isfile(fpath):
|
||||
print(f"{basename}: Can't find {dfname} file!")
|
||||
return None, None
|
||||
if not os.path.isfile(tracking_output_path):
|
||||
print(f"{basename}: Can't find {CamerType}_tracking_output.data file!")
|
||||
return None, None
|
||||
|
||||
bboxes, ffeats, trackerboxes, tracker_feat_dict, trackingboxes, tracking_feat_dict = extract_data(fpath)
|
||||
tracking_output_boxes, _ = read_tracking_output(tracking_output_path)
|
||||
|
||||
'''1.2 利用本地跟踪算法生成各商品轨迹'''
|
||||
# trackerboxes, tracker_feat_dict = tracking(bboxes, ffeats)
|
||||
|
||||
'''1.3 分别构造 2 个文件夹,(1) 存储画框后的图像; (2) 运动轨迹对应的 boxes子图'''
|
||||
save_dir = os.path.join(savedir, event_name + '_images')
|
||||
subimg_dir = os.path.join(savedir, event_name + '_subimgs')
|
||||
if not os.path.exists(save_dir):
|
||||
os.makedirs(save_dir)
|
||||
if not os.path.exists(subimg_dir):
|
||||
os.makedirs(subimg_dir)
|
||||
|
||||
|
||||
'''2. 执行轨迹分析, 保存轨迹分析前后的对比图示'''
|
||||
traj_graphic = event_name + '_' + CamerType
|
||||
if CamerType == '1':
|
||||
vts = doFrontTracks(trackerboxes, tracker_feat_dict)
|
||||
vts.classify()
|
||||
|
||||
plt = plot_frameID_y2(vts)
|
||||
# ftpath = os.path.join(savedir, f"{traj_graphic}_front_y2.png")
|
||||
# plt.savefig(ftpath)
|
||||
plt.close()
|
||||
|
||||
edgeline = cv2.imread("./shopcart/cart_tempt/board_ftmp_line.png")
|
||||
img_tracking = draw_all_trajectories(vts, edgeline, savedir, CamerType, draw5p=True)
|
||||
|
||||
|
||||
elif CamerType == '0':
|
||||
vts = doBackTracks(trackerboxes, tracker_feat_dict)
|
||||
vts.classify()
|
||||
|
||||
edgeline = cv2.imread("./shopcart/cart_tempt/edgeline.png")
|
||||
img_tracking = draw_all_trajectories(vts, edgeline, savedir, CamerType, draw5p=True)
|
||||
|
||||
# imgpth = os.path.join(savedir, f"{traj_graphic}_.png")
|
||||
# cv2.imwrite(str(imgpth), img)
|
||||
else:
|
||||
print("Please check data file!")
|
||||
|
||||
|
||||
'''3 tracking() 算法输出后多轨迹选择问题分析'''
|
||||
if CamerType == '1':
|
||||
aline = cv2.imread("./shopcart/cart_tempt/board_ftmp_line.png")
|
||||
elif CamerType == '0':
|
||||
aline = cv2.imread("./shopcart/cart_tempt/edgeline.png")
|
||||
else:
|
||||
print("Please check data file!")
|
||||
|
||||
bline = aline.copy()
|
||||
|
||||
annotator = TrackAnnotator(aline, line_width=2)
|
||||
for track in trackingboxes:
|
||||
annotator.plotting_track(track)
|
||||
aline = annotator.result()
|
||||
|
||||
annotator = TrackAnnotator(bline, line_width=2)
|
||||
if not isinstance(tracking_output_boxes, list):
|
||||
tracking_output_boxes = [tracking_output_boxes]
|
||||
|
||||
for track in tracking_output_boxes:
|
||||
annotator.plotting_track(track)
|
||||
bline = annotator.result()
|
||||
|
||||
abimg = np.concatenate((aline, bline), axis = 1)
|
||||
abH, abW = abimg.shape[:2]
|
||||
cv2.line(abimg, (int(abW/2), 0), (int(abW/2), abH), (128, 255, 128), 2)
|
||||
|
||||
# algpath = os.path.join(savedir, f"{traj_graphic}_alg.png")
|
||||
# cv2.imwrite(str(algpath), abimg)
|
||||
|
||||
'''4. 画框后的图像和子图保存,若imgs数与tracker中fid数不匹配,只保存原图,不保存子图'''
|
||||
'''4.0 读取 fpath 中对应的图像 imgs '''
|
||||
imgs = read_imgs(imgpath, CamerType)
|
||||
|
||||
'''4.1 imgs数 < trackerboxes 的 max(fid),返回原图'''
|
||||
if len(imgs) < np.max(trackerboxes[:,7]):
|
||||
for i in range(len(imgs)):
|
||||
img_savepath = os.path.join(save_dir, CamerType + "_" + f"{i}.png")
|
||||
cv2.imwrite(img_savepath, imgs[i])
|
||||
print(f"{basename}: len(imgs) = {len(imgs)} < Tracker max(fid) = {int(np.max(trackerboxes[:,7]))}, 无法匹配画框")
|
||||
return img_tracking, abimg
|
||||
|
||||
'''4.2 在 imgs 上画框并保存'''
|
||||
imgs_dw = draw_tracking_boxes(imgs, trackerboxes)
|
||||
for fid, img in imgs_dw:
|
||||
img_savepath = os.path.join(save_dir, CamerType + "_fid_" + f"{int(fid)}.png")
|
||||
cv2.imwrite(img_savepath, img)
|
||||
|
||||
'''4.3.2 保存轨迹选择对应的子图'''
|
||||
# for track in tracking_output_boxes:
|
||||
for track in vts.Residual:
|
||||
for *xyxy, tid, conf, cls, fid, bid in track.boxes:
|
||||
img = imgs[int(fid-1)]
|
||||
x1, y1, x2, y2 = int(xyxy[0]/2), int(xyxy[1]/2), int(xyxy[2]/2), int(xyxy[3]/2)
|
||||
subimg = img[y1:y2, x1:x2]
|
||||
|
||||
subimg_path = os.path.join(subimg_dir, f'{CamerType}_tid{int(tid)}_{int(fid)}_{int(bid)}.png' )
|
||||
cv2.imwrite(subimg_path, subimg)
|
||||
|
||||
for track in tracking_output_boxes:
|
||||
for *xyxy, tid, conf, cls, fid, bid in track:
|
||||
img = imgs[int(fid-1)]
|
||||
x1, y1, x2, y2 = int(xyxy[0]/2), int(xyxy[1]/2), int(xyxy[2]/2), int(xyxy[3]/2)
|
||||
subimg = img[y1:y2, x1:x2]
|
||||
|
||||
subimg_path = os.path.join(subimg_dir, f'x_{CamerType}_tid{int(tid)}_{int(fid)}_{int(bid)}.png' )
|
||||
cv2.imwrite(subimg_path, subimg)
|
||||
|
||||
|
||||
return img_tracking, abimg
|
||||
|
||||
|
||||
def tracking_simulate(eventpath, savepath):
|
||||
'''args:
|
||||
eventpath: 事件文件夹
|
||||
savepath: 存储文件夹
|
||||
遍历eventpath
|
||||
'''
|
||||
|
||||
# =============================================================================
|
||||
# '''1. 获取事件名'''
|
||||
# event_names = os.path.basename(eventpath).strip().split('_')
|
||||
# if len(event_names)==2 and len(event_names[1])>=8:
|
||||
# enent_name = event_names[1]
|
||||
# elif len(event_names)==2 and len(event_names[1])==0:
|
||||
# enent_name = event_names[0]
|
||||
# else:
|
||||
# return
|
||||
# =============================================================================
|
||||
enent_name = os.path.basename(eventpath)
|
||||
|
||||
## only for simplify the filename
|
||||
idx = enent_name.find('2024')
|
||||
if idx>=0:
|
||||
enent_name = enent_name[idx:(idx+15)]
|
||||
|
||||
|
||||
'''2. 依次读取 0/1_track.data 中数据,进行仿真'''
|
||||
illu_tracking, illu_select = [], []
|
||||
for filename in os.listdir(eventpath):
|
||||
# filename = '1_track.data'
|
||||
if filename.find("track.data") < 0: continue
|
||||
|
||||
fpath = os.path.join(eventpath, filename)
|
||||
if not os.path.isfile(fpath): continue
|
||||
|
||||
img_tracking, img_select = do_tracking(fpath, savepath, enent_name)
|
||||
|
||||
if img_select is not None:
|
||||
illu_select.append(img_select)
|
||||
if img_tracking is not None:
|
||||
illu_tracking.append(img_tracking)
|
||||
|
||||
'''3. 共幅8图,上下子图显示的是前后摄,每一行4个子图,分别为:
|
||||
(1) tracker输出原始轨迹; (2)本地tracking输出; (3)现场算法轨迹选择前轨迹; (4)现场算法轨迹选择后的轨迹
|
||||
'''
|
||||
if len(illu_select)==2:
|
||||
Img_s = np.concatenate((illu_select[0], illu_select[1]), axis = 0)
|
||||
H, W = Img_s.shape[:2]
|
||||
cv2.line(Img_s, (0, int(H/2)), (int(W), int(H/2)), (128, 255, 128), 2)
|
||||
elif len(illu_select)==1:
|
||||
Img_s = illu_select[0]
|
||||
else:
|
||||
Img_s = None
|
||||
|
||||
if len(illu_tracking)==2:
|
||||
Img_t = np.concatenate((illu_tracking[0], illu_tracking[1]), axis = 0)
|
||||
H, W = Img_t.shape[:2]
|
||||
cv2.line(Img_t, (0, int(H/2)), (int(W), int(H/2)), (128, 255, 128), 2)
|
||||
elif len(illu_tracking)==1:
|
||||
Img_t = illu_tracking[0]
|
||||
else:
|
||||
Img_t = None
|
||||
|
||||
|
||||
'''3.1 保存输出轨迹图,若tracking、select的shape相同,则合并输出,否则单独输出'''
|
||||
imgpath_tracking = os.path.join(savepath, enent_name + '_tracking.png')
|
||||
imgpath_select = os.path.join(savepath, enent_name + '_select.png')
|
||||
imgpath_ts = os.path.join(savepath, enent_name + '_tracking_select.png')
|
||||
|
||||
if Img_t is not None and Img_s is not None and np.all(Img_s.shape==Img_t.shape):
|
||||
Img_ts = np.concatenate((Img_t, Img_s), axis = 1)
|
||||
H, W = Img_ts.shape[:2]
|
||||
cv2.line(Img_ts, (int(W/2), 0), (int(W/2), int(H)), (0, 0, 255), 4)
|
||||
cv2.imwrite(imgpath_ts, Img_ts)
|
||||
|
||||
else:
|
||||
if Img_s: cv2.imwrite(imgpath_select, Img_s) # 不会执行到该处
|
||||
if Img_t: cv2.imwrite(imgpath_tracking, Img_t) # 不会执行到该处
|
||||
Img_ts = None
|
||||
|
||||
'''3.2 单独另存保存完好的 8 轨迹图'''
|
||||
if Img_ts is not None:
|
||||
basepath, _ = os.path.split(savepath)
|
||||
trajpath = os.path.join(basepath, 'trajs')
|
||||
if not os.path.exists(trajpath):
|
||||
os.makedirs(trajpath)
|
||||
traj_path = os.path.join(trajpath, enent_name+'.png')
|
||||
cv2.imwrite(traj_path, Img_ts)
|
||||
|
||||
|
||||
return Img_ts
|
||||
|
||||
|
||||
|
||||
|
||||
# warnings.simplefilter("error", category=np.VisibleDeprecationWarning)
|
||||
|
||||
def main_loop():
|
||||
|
||||
|
||||
del_barcode_file = r'\\192.168.1.28\share\测试_202406\0723\0723_3\deletedBarcode.txt'
|
||||
basepath = r'\\192.168.1.28\share\测试_202406\0723\0723_3' # 测试数据文件夹地址
|
||||
|
||||
# del_barcode_file = r'\\192.168.1.28\share\测试_202406\1030\images\returnGoods.txt'
|
||||
# basepath = r'\\192.168.1.28\share\测试_202406\1030\images' # 测试数据文件夹地址
|
||||
|
||||
'''获取性能测试数据相关路径'''
|
||||
SavePath = r'D:\contrast\dataset\resultx' # 结果保存地址
|
||||
saveimgs = True
|
||||
|
||||
if os.path.basename(del_barcode_file).find('deletedBarcode'):
|
||||
relative_paths = get_contrast_paths(del_barcode_file, basepath, SavePath, saveimgs)
|
||||
elif os.path.basename(del_barcode_file).find('returnGoods'):
|
||||
blist = read_returnGoods_file(del_barcode_file)
|
||||
errpairs, corrpairs, err_similarity, correct_similarity = one2n_return(blist)
|
||||
relative_paths = []
|
||||
for getoutevent, inputevent, errevent in errpairs:
|
||||
relative_paths.append(os.path.join(basepath, getoutevent))
|
||||
relative_paths.append(os.path.join(basepath, inputevent))
|
||||
relative_paths.append(os.path.join(basepath, errevent))
|
||||
|
||||
# prefix = ["getout_", "input_", "error_"]
|
||||
'''开始循环执行每次测试过任务'''
|
||||
k = 0
|
||||
for tuple_paths in relative_paths:
|
||||
|
||||
'''1. 生成存储结果图像的文件夹'''
|
||||
namedirs = []
|
||||
for data_path in tuple_paths:
|
||||
base_name = os.path.basename(data_path).strip().split('_')
|
||||
if len(base_name[-1]):
|
||||
name = base_name[-1]
|
||||
else:
|
||||
name = base_name[0]
|
||||
namedirs.append(name)
|
||||
sdir = "_".join(namedirs)
|
||||
savepath = os.path.join(SavePath, sdir)
|
||||
|
||||
# if os.path.exists(savepath):
|
||||
# continue
|
||||
if not os.path.exists(savepath):
|
||||
os.makedirs(savepath)
|
||||
|
||||
'''2. 循环执行操作事件:取出、放入、错误匹配'''
|
||||
for eventpath in tuple_paths:
|
||||
try:
|
||||
tracking_simulate(eventpath, savepath)
|
||||
except Exception as e:
|
||||
print(f'Error! {eventpath}, {e}')
|
||||
|
||||
# k +=1
|
||||
# if k==1:
|
||||
# break
|
||||
|
||||
|
||||
def main():
|
||||
'''
|
||||
eventPaths: data文件地址,该 data 文件包括 Pipeline 各模块输出
|
||||
SavePath: 包含二级目录,一级目录为轨迹图像;二级目录为与data文件对应的序列图像存储地址。
|
||||
'''
|
||||
# eventPaths = r'\\192.168.1.28\share\测试_202406\0723\0723_3'
|
||||
eventPaths = r'\\192.168.1.28\share\测试视频数据以及日志\各模块测试记录\展厅测试\1120_展厅模型v801测试\扫A放A'
|
||||
savePath = r'D:\exhibition\result'
|
||||
|
||||
k=0
|
||||
for pathname in os.listdir(eventPaths):
|
||||
pathname = "20241121-144901-fdba61c6-aefa-4b50-876d-5e05998befdc_6920459905012_6920459905012"
|
||||
|
||||
eventpath = os.path.join(eventPaths, pathname)
|
||||
savepath = os.path.join(savePath, pathname)
|
||||
if not os.path.exists(savepath):
|
||||
os.makedirs(savepath)
|
||||
|
||||
tracking_simulate(eventpath, savepath)
|
||||
|
||||
# try:
|
||||
# tracking_simulate(eventpath, savepath)
|
||||
# except Exception as e:
|
||||
# print(f'Error! {eventpath}, {e}')
|
||||
|
||||
k += 1
|
||||
if k==1:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# main_loop()
|
||||
main()
|
||||
# try:
|
||||
# main_loop()
|
||||
# except Exception as e:
|
||||
# print(f'Error: {e}')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1
tracking/shopcart/cart_program/新建文本文档.txt
Normal file
1
tracking/shopcart/cart_program/新建文本文档.txt
Normal file
@ -0,0 +1 @@
|
||||
求取购物车轮廓,判断任一点是否在购物车内、是否在购物车边框附近
|
6
tracking/shopcart/cart_tempt/说明.txt
Normal file
6
tracking/shopcart/cart_tempt/说明.txt
Normal file
@ -0,0 +1,6 @@
|
||||
5幅图:
|
||||
incart.png
|
||||
outcart.png
|
||||
incart_ftmp.png
|
||||
outcart_ftmp.png
|
||||
cartboarder.png
|
151
tracking/shopcart/carttempt.py
Normal file
151
tracking/shopcart/carttempt.py
Normal file
@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Sep 19 18:17:55 2023
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
|
||||
import cv2
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
|
||||
def tempt_add_adjc():
|
||||
|
||||
temp = cv2.imread("img.png")
|
||||
|
||||
path = r"D:\DeepLearning\yolov5\runs\trajectory"
|
||||
patr = r"D:\DeepLearning\yolov5\tracking\result"
|
||||
for filename in os.listdir(path):
|
||||
imgpath = os.path.join(path, filename)
|
||||
|
||||
img = cv2.imread(imgpath)
|
||||
|
||||
img1 = cv2.add(img, temp)
|
||||
|
||||
img1path = os.path.join(patr, filename)
|
||||
cv2.imwrite(img1path, img1)
|
||||
|
||||
def temp_add_boarder():
|
||||
temp = cv2.imread("cartedge.png")
|
||||
temp[640:, 0:20, :] = 255
|
||||
temp[640:, -20:, :] = 255
|
||||
temp[-20:, :, :] = 255
|
||||
|
||||
cv2.imwrite("cartboarder.png", temp)
|
||||
|
||||
|
||||
def create_front_temp():
|
||||
image = cv2.imread("./iCart4/b.png")
|
||||
Height, Width = image.shape[:2]
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
thresh, binary = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY_INV)
|
||||
board = cv2.bitwise_not(binary)
|
||||
contours, _ = cv2.findContours(board, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
|
||||
|
||||
k = 0
|
||||
for cnt in contours:
|
||||
img = np.zeros((Height, Width), dtype=np.uint8)
|
||||
cv2.drawContours(img, [cnt], -1, 255, 3)
|
||||
k += 1
|
||||
cv2.imwrite(f"./iCart4/back{k}.png", img)
|
||||
|
||||
imgshow = cv2.drawContours(image, contours, -1, (0,255,0), 3)
|
||||
cv2.imwrite("./iCart4/board_back_line.png", imgshow)
|
||||
|
||||
# cv2.imwrite("./iCart4/4.png", board)
|
||||
# cv2.imwrite("1.png", gray)
|
||||
# cv2.imwrite("2.png", binary)
|
||||
|
||||
|
||||
|
||||
|
||||
def create_back_temp():
|
||||
'''
|
||||
image1.png:从中获取轮廓的初始图像
|
||||
image2.png:主要用于显示效果
|
||||
Return:img.png
|
||||
'''
|
||||
|
||||
image = cv2.imread("image1.png")
|
||||
|
||||
Height, Width = image.shape[:2]
|
||||
|
||||
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
gray[:405, :] = 0
|
||||
thresh, binary = cv2.threshold(gray, 254, 255, cv2.THRESH_BINARY)
|
||||
cv2.imwrite("shopcart.png", binary)
|
||||
|
||||
imgshow = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
|
||||
|
||||
|
||||
|
||||
contours, _ = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
|
||||
|
||||
imgshow = cv2.drawContours(imgshow, contours, -1, (0,255,0), 1)
|
||||
cv2.imwrite("imgshow.png", imgshow)
|
||||
|
||||
|
||||
image2 = cv2.imread("image2.png")
|
||||
image2 = cv2.drawContours(image2, contours, -1, (0,255,0), 3)
|
||||
|
||||
|
||||
for cnt in contours:
|
||||
_, start, _, num = cv2.boundingRect(cnt)
|
||||
|
||||
x1 = (cnt[:, 0, 0] != 0)
|
||||
x2 = (cnt[:, 0, 0] != Width-1)
|
||||
x3 = (cnt[:, 0, 1] != Height-1)
|
||||
x = (x1 & x2) & x3
|
||||
idx = np.where(x)
|
||||
cntx = cnt[idx, :, :][0]
|
||||
|
||||
cnt1 = cntx[:,0,:].copy()
|
||||
|
||||
cntx[:, 0, 1] -= 60
|
||||
cnt2 = cntx[:,0,:].copy()
|
||||
|
||||
|
||||
cv2.drawContours(image2,[cntx], 0, (0,0,255), 2)
|
||||
|
||||
|
||||
|
||||
img = np.zeros(gray.shape, np.uint8)
|
||||
for i in range(len(cnt1)):
|
||||
x1, y1 = cnt1[i]
|
||||
x2, y2 = cnt2[i]
|
||||
cv2.rectangle(img, (x1-1, y1-1), (x1+1, y1+1), 255, 1)
|
||||
cv2.rectangle(img, (x2-1, y2-1), (x2+1, y2+1), 255, 1)
|
||||
|
||||
|
||||
|
||||
cv2.imwrite("img.png", img)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create_back_temp()
|
||||
# temp_add_boarder()
|
||||
# tempt_add_adjc()
|
||||
|
||||
create_front_temp()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
BIN
tracking/shopcart/iCart4.zip
Normal file
BIN
tracking/shopcart/iCart4.zip
Normal file
Binary file not shown.
98
tracking/time_test.py
Normal file
98
tracking/time_test.py
Normal file
@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 13 09:39:42 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import numpy as np
|
||||
import sys
|
||||
sys.path.append(r"D:\DetectTracking")
|
||||
from tracking.utils.read_data import extract_data, read_weight_timeConsuming
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
directory = r"\\192.168.1.28\share\测试_202406\0821\images"
|
||||
|
||||
TimeConsuming = []
|
||||
DayHMS = []
|
||||
for root, dirs, files in os.walk(directory):
|
||||
if root.find('20240821') == -1: continue
|
||||
for name in files:
|
||||
if name.find('process.data') == -1: continue
|
||||
datename = os.path.basename(root)[:15]
|
||||
|
||||
fpath = os.path.join(root, name)
|
||||
WeightDict, SensorDict, ProcessTimeDict = read_weight_timeConsuming(fpath)
|
||||
try:
|
||||
t1 = ProcessTimeDict['algroDoStart'] # 算法处理的第一帧图像时间
|
||||
t2 = ProcessTimeDict['breakinFirst'] # 第一次入侵时间
|
||||
t3 = ProcessTimeDict['algroLastFrame'] # 算法处理的最后一帧图像时间
|
||||
t4 = ProcessTimeDict['breakinLast'] # 最后一次入侵时间
|
||||
t5 = ProcessTimeDict['weightStablityTime'] # 重力稳定时间
|
||||
wv = ProcessTimeDict['weightValue'] # 重力值
|
||||
t6 = ProcessTimeDict['YoloResnetTrackerEnd'] # Yolo、Resnet、tracker执行结束时间
|
||||
t7 = ProcessTimeDict['trackingEnd'] # 轨迹分析结束时间
|
||||
t8 = ProcessTimeDict['contrastEnd'] # 比对结束时间
|
||||
t9 = ProcessTimeDict['algroStartToEnd'] # 算法总执行时间
|
||||
t10 = ProcessTimeDict['weightstablityToEnd'] # 重力稳定至算法结束时间
|
||||
t11 = ProcessTimeDict['frameEndToEnd'] # 最后一帧图像至算法结束时间
|
||||
|
||||
TimeConsuming.append((t1, t2, t3, t4, t5, wv, t6, t7, t8, t9, t10, t11))
|
||||
DayHMS.append(datename)
|
||||
except Exception as e:
|
||||
print(f'Error! {datename}, {e}')
|
||||
|
||||
TimeConsuming = np.array(TimeConsuming, dtype = np.int64)
|
||||
|
||||
TimeTotal = np.concatenate((TimeConsuming,
|
||||
TimeConsuming[:,4][:, None] - TimeConsuming[:,0][:, None],
|
||||
TimeConsuming[:,4][:, None] - TimeConsuming[:,2][:, None]), axis=1)
|
||||
|
||||
tt = TimeTotal[:, 3]==0
|
||||
|
||||
TimeTotal0 = TimeTotal[tt]
|
||||
DayHMS0 = [DayHMS[ti] for i, ti in enumerate(tt) if ti]
|
||||
|
||||
TimeTotalMinus = TimeTotal[TimeTotal[:, 5]<0]
|
||||
TimeTotalAdd = TimeTotal[TimeTotal[:, 5]>=0]
|
||||
|
||||
TimeTotalAdd0 = TimeTotalAdd[TimeTotalAdd[:,3] == 0]
|
||||
TimeTotalAdd1 = TimeTotalAdd[TimeTotalAdd[:,3] != 0]
|
||||
|
||||
TimeTotalMinus0 = TimeTotalMinus[TimeTotalMinus[:,3] == 0]
|
||||
TimeTotalMinus1 = TimeTotalMinus[TimeTotalMinus[:,3] != 0]
|
||||
|
||||
print(f"Total number is {len(TimeConsuming)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
94
tracking/trackers/README.md
Normal file
94
tracking/trackers/README.md
Normal file
@ -0,0 +1,94 @@
|
||||
# Tracker
|
||||
|
||||
## Supported Trackers
|
||||
|
||||
- [x] ByteTracker
|
||||
- [x] BoT-SORT
|
||||
|
||||
## Usage
|
||||
|
||||
### python interface:
|
||||
|
||||
You can use the Python interface to track objects using the YOLO model.
|
||||
|
||||
```python
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt") # or a segmentation model .i.e yolov8n-seg.pt
|
||||
model.track(
|
||||
source="video/streams",
|
||||
stream=True,
|
||||
tracker="botsort.yaml", # or 'bytetrack.yaml'
|
||||
show=True,
|
||||
)
|
||||
```
|
||||
|
||||
You can get the IDs of the tracked objects using the following code:
|
||||
|
||||
```python
|
||||
from ultralytics import YOLO
|
||||
|
||||
model = YOLO("yolov8n.pt")
|
||||
|
||||
for result in model.track(source="video.mp4"):
|
||||
print(
|
||||
result.boxes.id.cpu().numpy().astype(int)
|
||||
) # this will print the IDs of the tracked objects in the frame
|
||||
```
|
||||
|
||||
If you want to use the tracker with a folder of images or when you loop on the video frames, you should use the `persist` parameter to tell the model that these frames are related to each other so the IDs will be fixed for the same objects. Otherwise, the IDs will be different in each frame because in each loop, the model creates a new object for tracking, but the `persist` parameter makes it use the same object for tracking.
|
||||
|
||||
```python
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
|
||||
cap = cv2.VideoCapture("video.mp4")
|
||||
model = YOLO("yolov8n.pt")
|
||||
while True:
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
break
|
||||
results = model.track(frame, persist=True)
|
||||
boxes = results[0].boxes.xyxy.cpu().numpy().astype(int)
|
||||
ids = results[0].boxes.id.cpu().numpy().astype(int)
|
||||
for box, id in zip(boxes, ids):
|
||||
cv2.rectangle(frame, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2)
|
||||
cv2.putText(
|
||||
frame,
|
||||
f"Id {id}",
|
||||
(box[0], box[1]),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
1,
|
||||
(0, 0, 255),
|
||||
2,
|
||||
)
|
||||
cv2.imshow("frame", frame)
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
```
|
||||
|
||||
## Change tracker parameters
|
||||
|
||||
You can change the tracker parameters by editing the `tracker.yaml` file which is located in the ultralytics/cfg/trackers folder.
|
||||
|
||||
## Command Line Interface (CLI)
|
||||
|
||||
You can also use the command line interface to track objects using the YOLO model.
|
||||
|
||||
```bash
|
||||
yolo detect track source=... tracker=...
|
||||
yolo segment track source=... tracker=...
|
||||
yolo pose track source=... tracker=...
|
||||
```
|
||||
|
||||
By default, trackers will use the configuration in `ultralytics/cfg/trackers`. We also support using a modified tracker config file. Please refer to the tracker config files in `ultralytics/cfg/trackers`.
|
||||
|
||||
## Contribute to Our Trackers Section
|
||||
|
||||
Are you proficient in multi-object tracking and have successfully implemented or adapted a tracking algorithm with Ultralytics YOLO? We invite you to contribute to our Trackers section! Your real-world applications and solutions could be invaluable for users working on tracking tasks.
|
||||
|
||||
By contributing to this section, you help expand the scope of tracking solutions available within the Ultralytics YOLO framework, adding another layer of functionality and utility for the community.
|
||||
|
||||
To initiate your contribution, please refer to our [Contributing Guide](https://docs.ultralytics.com/help/contributing) for comprehensive instructions on submitting a Pull Request (PR) 🛠️. We are excited to see what you bring to the table!
|
||||
|
||||
Together, let's enhance the tracking capabilities of the Ultralytics YOLO ecosystem 🙏!
|
10
tracking/trackers/__init__.py
Normal file
10
tracking/trackers/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
from .bot_sort import BOTSORT
|
||||
from .byte_tracker import BYTETracker
|
||||
from .track import register_tracker
|
||||
|
||||
|
||||
|
||||
__all__ = 'register_tracker', 'BOTSORT', 'BYTETracker' # allow simpler import
|
||||
|
BIN
tracking/trackers/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tracking/trackers/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/trackers/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/basetrack.cpython-312.pyc
Normal file
BIN
tracking/trackers/__pycache__/basetrack.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/basetrack.cpython-39.pyc
Normal file
BIN
tracking/trackers/__pycache__/basetrack.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/bot_sort.cpython-312.pyc
Normal file
BIN
tracking/trackers/__pycache__/bot_sort.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/bot_sort.cpython-39.pyc
Normal file
BIN
tracking/trackers/__pycache__/bot_sort.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/byte_tracker.cpython-312.pyc
Normal file
BIN
tracking/trackers/__pycache__/byte_tracker.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/byte_tracker.cpython-39.pyc
Normal file
BIN
tracking/trackers/__pycache__/byte_tracker.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/track.cpython-312.pyc
Normal file
BIN
tracking/trackers/__pycache__/track.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/__pycache__/track.cpython-39.pyc
Normal file
BIN
tracking/trackers/__pycache__/track.cpython-39.pyc
Normal file
Binary file not shown.
71
tracking/trackers/basetrack.py
Normal file
71
tracking/trackers/basetrack.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class TrackState:
|
||||
"""Enumeration of possible object tracking states."""
|
||||
|
||||
New = 0
|
||||
Tracked = 1
|
||||
Lost = 2
|
||||
Removed = 3
|
||||
|
||||
|
||||
class BaseTrack:
|
||||
"""Base class for object tracking, handling basic track attributes and operations."""
|
||||
|
||||
_count = 0
|
||||
|
||||
track_id = 0
|
||||
is_activated = False
|
||||
state = TrackState.New
|
||||
|
||||
history = OrderedDict()
|
||||
features = []
|
||||
curr_feature = None
|
||||
score = 0
|
||||
start_frame = 0
|
||||
frame_id = 0
|
||||
time_since_update = 0
|
||||
|
||||
# Multi-camera
|
||||
location = (np.inf, np.inf)
|
||||
|
||||
@property
|
||||
def end_frame(self):
|
||||
"""Return the last frame ID of the track."""
|
||||
return self.frame_id
|
||||
|
||||
@staticmethod
|
||||
def next_id():
|
||||
"""Increment and return the global track ID counter."""
|
||||
BaseTrack._count += 1
|
||||
return BaseTrack._count
|
||||
|
||||
def activate(self, *args):
|
||||
"""Activate the track with the provided arguments."""
|
||||
raise NotImplementedError
|
||||
|
||||
def predict(self):
|
||||
"""Predict the next state of the track."""
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
"""Update the track with new observations."""
|
||||
raise NotImplementedError
|
||||
|
||||
def mark_lost(self):
|
||||
"""Mark the track as lost."""
|
||||
self.state = TrackState.Lost
|
||||
|
||||
def mark_removed(self):
|
||||
"""Mark the track as removed."""
|
||||
self.state = TrackState.Removed
|
||||
|
||||
@staticmethod
|
||||
def reset_id():
|
||||
"""Reset the global track ID counter."""
|
||||
BaseTrack._count = 0
|
211
tracking/trackers/bot_sort.py
Normal file
211
tracking/trackers/bot_sort.py
Normal file
@ -0,0 +1,211 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
from collections import deque
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .basetrack import TrackState
|
||||
from .byte_tracker import BYTETracker, STrack
|
||||
from .utils import matching
|
||||
# from .utils.gmc import GMC
|
||||
from .utils.kalman_filter import KalmanFilterXYWH
|
||||
|
||||
# from .reid.reid_interface import ReIDInterface
|
||||
# from .reid.config import config
|
||||
|
||||
from contrast.feat_extract.inference import FeatsInterface
|
||||
from contrast.feat_extract.config import config as conf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BOTrack(STrack):
|
||||
shared_kalman = KalmanFilterXYWH()
|
||||
|
||||
def __init__(self, tlwh, score, cls, feat=None, feat_history=50):
|
||||
"""Initialize YOLOv8 object with temporal parameters, such as feature history, alpha and current features."""
|
||||
super().__init__(tlwh, score, cls)
|
||||
|
||||
self.smooth_feat = None
|
||||
self.curr_feat = None
|
||||
if feat is not None:
|
||||
self.update_features(feat)
|
||||
self.features = deque([], maxlen=feat_history)
|
||||
self.alpha = 0.9
|
||||
|
||||
def update_features(self, feat):
|
||||
"""Update features vector and smooth it using exponential moving average."""
|
||||
feat /= np.linalg.norm(feat)
|
||||
self.curr_feat = feat
|
||||
if self.smooth_feat is None:
|
||||
self.smooth_feat = feat
|
||||
else:
|
||||
self.smooth_feat = self.alpha * self.smooth_feat + (1 - self.alpha) * feat
|
||||
self.features.append(feat)
|
||||
self.smooth_feat /= np.linalg.norm(self.smooth_feat)
|
||||
|
||||
def predict(self):
|
||||
"""Predicts the mean and covariance using Kalman filter."""
|
||||
mean_state = self.mean.copy()
|
||||
if self.state != TrackState.Tracked:
|
||||
mean_state[6] = 0
|
||||
mean_state[7] = 0
|
||||
|
||||
self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance)
|
||||
|
||||
def re_activate(self, new_track, frame_id, new_id=False):
|
||||
"""Reactivates a track with updated features and optionally assigns a new ID."""
|
||||
if new_track.curr_feat is not None:
|
||||
self.update_features(new_track.curr_feat)
|
||||
super().re_activate(new_track, frame_id, new_id)
|
||||
|
||||
def update(self, new_track, frame_id):
|
||||
"""Update the YOLOv8 instance with new track and frame ID."""
|
||||
if new_track.curr_feat is not None:
|
||||
self.update_features(new_track.curr_feat)
|
||||
super().update(new_track, frame_id)
|
||||
|
||||
@property
|
||||
def tlwh(self):
|
||||
"""Get current position in bounding box format `(top left x, top left y,
|
||||
width, height)`.
|
||||
"""
|
||||
if self.mean is None:
|
||||
return self._tlwh.copy()
|
||||
ret = self.mean[:4].copy()
|
||||
ret[:2] -= ret[2:] / 2
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def multi_predict(stracks):
|
||||
"""Predicts the mean and covariance of multiple object tracks using shared Kalman filter."""
|
||||
if len(stracks) <= 0:
|
||||
return
|
||||
multi_mean = np.asarray([st.mean.copy() for st in stracks])
|
||||
multi_covariance = np.asarray([st.covariance for st in stracks])
|
||||
for i, st in enumerate(stracks):
|
||||
if st.state != TrackState.Tracked:
|
||||
multi_mean[i][6] = 0
|
||||
multi_mean[i][7] = 0
|
||||
multi_mean, multi_covariance = BOTrack.shared_kalman.multi_predict(multi_mean, multi_covariance)
|
||||
for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)):
|
||||
stracks[i].mean = mean
|
||||
stracks[i].covariance = cov
|
||||
|
||||
def convert_coords(self, tlwh):
|
||||
"""Converts Top-Left-Width-Height bounding box coordinates to X-Y-Width-Height format."""
|
||||
return self.tlwh_to_xywh(tlwh)
|
||||
|
||||
@staticmethod
|
||||
def tlwh_to_xywh(tlwh):
|
||||
"""Convert bounding box to format `(center x, center y, width,
|
||||
height)`.
|
||||
"""
|
||||
ret = np.asarray(tlwh).copy()
|
||||
ret[:2] += ret[2:] / 2
|
||||
return ret
|
||||
|
||||
|
||||
class BOTSORT(BYTETracker):
|
||||
|
||||
def __init__(self, args, frame_rate=30):
|
||||
"""Initialize YOLOv8 object with ReID module and GMC algorithm."""
|
||||
super().__init__(args, frame_rate)
|
||||
# ReID module
|
||||
self.proximity_thresh = args.proximity_thresh
|
||||
self.appearance_thresh = args.appearance_thresh
|
||||
|
||||
# if args.with_reid:
|
||||
# # Haven't supported BoT-SORT(reid) yet
|
||||
# # self.encoder = ReIDInterface(config)
|
||||
|
||||
# self.encoder = FeatsInterface(conf)
|
||||
|
||||
# print('load model {} in BOTSORT'.format(conf.testbackbone))
|
||||
|
||||
# self.gmc = GMC(method=args.gmc_method) # commented by WQG
|
||||
|
||||
def get_kalmanfilter(self):
|
||||
"""Returns an instance of KalmanFilterXYWH for object tracking."""
|
||||
return KalmanFilterXYWH()
|
||||
|
||||
def init_track(self, dets, scores, cls, image, features_keep):
|
||||
"""Initialize track with detections, scores, and classes."""
|
||||
if len(dets) == 0:
|
||||
return []
|
||||
if self.args.with_reid and self.encoder is not None:
|
||||
if features_keep is None:
|
||||
imgs, features_keep = self.encoder.inference(image, dets)
|
||||
|
||||
return [BOTrack(xyxy, s, c, f) for (xyxy, s, c, f) in zip(dets, scores, cls, features_keep)] # detections
|
||||
else:
|
||||
return [BOTrack(xyxy, s, c) for (xyxy, s, c) in zip(dets, scores, cls)] # detections
|
||||
|
||||
def get_dists(self, tracks, detections):
|
||||
"""Get distances between tracks and detections using IoU and (optionally) ReID embeddings."""
|
||||
dists = matching.iou_distance(tracks, detections)
|
||||
# proximity_thresh 应该设较大的值,表示只有两个boxes离得较远时,不考虑reid特征
|
||||
dists_mask = (dists > self.proximity_thresh)
|
||||
|
||||
# TODO: mot20
|
||||
# if not self.args.mot20:
|
||||
dists = matching.fuse_score(dists, detections)
|
||||
|
||||
if self.args.with_reid and self.encoder is not None:
|
||||
emb_dists = matching.embedding_distance(tracks, detections) / 2.0
|
||||
emb_dists[emb_dists > self.appearance_thresh] = 1.0
|
||||
emb_dists[dists_mask] = 1.0
|
||||
dists = np.minimum(dists, emb_dists)
|
||||
|
||||
return dists
|
||||
|
||||
def get_dists_1(self, tracks, detections):
|
||||
"""Get distances between tracks and detections using IoU and (optionally) ReID embeddings."""
|
||||
iou_dists = matching.iou_distance(tracks, detections)
|
||||
iou_dists_mask = (iou_dists>0.9)
|
||||
|
||||
iou_dists = matching.fuse_score(iou_dists, detections)
|
||||
weight = 0.4
|
||||
if self.args.with_reid and self.encoder is not None:
|
||||
emb_dists = matching.embedding_distance(tracks, detections)
|
||||
|
||||
'''============ iou_dists 和 emb_dists 融合有两种策略 ==========='''
|
||||
'''1. reid 相似度阈值,低于该值的两 boxes 图像不可能是同一对象,需要确定一个合理的可信阈值
|
||||
2. iou 的约束为若约束,故 iou_dists 应设置为较大的值
|
||||
'''
|
||||
emb_dists_mask = (emb_dists > 0.8)
|
||||
iou_dists[emb_dists_mask] = 1
|
||||
emb_dists[iou_dists_mask] = 1
|
||||
|
||||
dists = np.minimum(iou_dists, emb_dists)
|
||||
'''2. embed 阈值'''
|
||||
# dists = (1-weight)*iou_dists + weight*emb_dists
|
||||
else:
|
||||
|
||||
dists = iou_dists.copy()
|
||||
|
||||
return dists
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def multi_predict(self, tracks):
|
||||
"""Predict and track multiple objects with YOLOv8 model."""
|
||||
BOTrack.multi_predict(tracks)
|
||||
|
||||
|
||||
def get_result(self):
|
||||
'''written by WQG'''
|
||||
activate_tracks = np.asarray([x.tlbr.tolist() + [x.track_id, x.score, x.cls, x.idx]
|
||||
for x in self.tracked_stracks if x.is_activated], dtype=np.float32)
|
||||
|
||||
track_features = []
|
||||
if self.args.with_reid and self.encoder is not None:
|
||||
track_features = np.asarray([x.curr_feat for x in self.tracked_stracks if x.is_activated], dtype=np.float32)
|
||||
|
||||
|
||||
return (activate_tracks, track_features)
|
496
tracking/trackers/byte_tracker.py
Normal file
496
tracking/trackers/byte_tracker.py
Normal file
@ -0,0 +1,496 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
import numpy as np
|
||||
|
||||
from .basetrack import BaseTrack, TrackState
|
||||
from .utils import matching
|
||||
from .utils.kalman_filter import KalmanFilterXYAH
|
||||
|
||||
|
||||
def dists_update(dists, strack_pool, detections):
|
||||
'''written by WQG'''
|
||||
|
||||
if len(strack_pool) and len(detections):
|
||||
# alabel = np.array([int(stack.cls) if int(stack.cls)==0 or int(stack.cls)==9 else -1 for stack in strack_pool])
|
||||
# blabel = np.array([int(stack.cls) if int(stack.cls)==0 or int(stack.cls)==9 else -1 for stack in detections])
|
||||
|
||||
alabel = np.array([int(stack.cls) for stack in strack_pool])
|
||||
blabel = np.array([int(stack.cls) for stack in detections])
|
||||
amlabel = np.expand_dims(alabel, axis=1).repeat(len(detections),axis=1)
|
||||
bmlabel = np.expand_dims(blabel, axis=0).repeat(len(strack_pool),axis=0)
|
||||
|
||||
mlabel = bmlabel == amlabel
|
||||
iou_dist = matching.iou_distance(strack_pool, detections) > 0.1 #boxes iou>0.9时,可以不考虑类别
|
||||
dist_label = (1 - mlabel) & iou_dist # 不同类,且不是严格重叠,需考虑类别距离
|
||||
|
||||
dist_label = 1 - mlabel
|
||||
dists = np.where(dists > dist_label, dists, dist_label)
|
||||
return dists
|
||||
|
||||
|
||||
class STrack(BaseTrack):
|
||||
shared_kalman = KalmanFilterXYAH()
|
||||
|
||||
def __init__(self, tlwh, score, cls):
|
||||
"""wait activate."""
|
||||
self._tlwh = np.asarray(self.tlbr_to_tlwh(tlwh[:-1]), dtype=np.float32)
|
||||
self.kalman_filter = None
|
||||
self.mean, self.covariance = None, None
|
||||
self.is_activated = False
|
||||
|
||||
self.first_find = False ###
|
||||
|
||||
self.score = score
|
||||
self.tracklet_len = 0
|
||||
self.cls = cls
|
||||
self.idx = tlwh[-1]
|
||||
|
||||
def predict(self):
|
||||
"""Predicts mean and covariance using Kalman filter."""
|
||||
mean_state = self.mean.copy()
|
||||
if self.state != TrackState.Tracked:
|
||||
mean_state[7] = 0
|
||||
self.mean, self.covariance = self.kalman_filter.predict(mean_state, self.covariance)
|
||||
|
||||
@staticmethod
|
||||
def multi_predict(stracks):
|
||||
"""Perform multi-object predictive tracking using Kalman filter for given stracks."""
|
||||
if len(stracks) <= 0:
|
||||
return
|
||||
multi_mean = np.asarray([st.mean.copy() for st in stracks])
|
||||
multi_covariance = np.asarray([st.covariance for st in stracks])
|
||||
for i, st in enumerate(stracks):
|
||||
if st.state != TrackState.Tracked:
|
||||
multi_mean[i][7] = 0
|
||||
multi_mean, multi_covariance = STrack.shared_kalman.multi_predict(multi_mean, multi_covariance)
|
||||
for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)):
|
||||
stracks[i].mean = mean
|
||||
stracks[i].covariance = cov
|
||||
|
||||
@staticmethod
|
||||
def multi_gmc(stracks, H=np.eye(2, 3)):
|
||||
"""Update state tracks positions and covariances using a homography matrix."""
|
||||
if len(stracks) > 0:
|
||||
multi_mean = np.asarray([st.mean.copy() for st in stracks])
|
||||
multi_covariance = np.asarray([st.covariance for st in stracks])
|
||||
|
||||
R = H[:2, :2]
|
||||
R8x8 = np.kron(np.eye(4, dtype=float), R)
|
||||
t = H[:2, 2]
|
||||
|
||||
for i, (mean, cov) in enumerate(zip(multi_mean, multi_covariance)):
|
||||
mean = R8x8.dot(mean)
|
||||
mean[:2] += t
|
||||
cov = R8x8.dot(cov).dot(R8x8.transpose())
|
||||
|
||||
stracks[i].mean = mean
|
||||
stracks[i].covariance = cov
|
||||
|
||||
def activate(self, kalman_filter, frame_id):
|
||||
"""Start a new tracklet."""
|
||||
self.kalman_filter = kalman_filter
|
||||
self.track_id = self.next_id()
|
||||
self.mean, self.covariance = self.kalman_filter.initiate(self.convert_coords(self._tlwh))
|
||||
|
||||
self.tracklet_len = 0
|
||||
self.state = TrackState.Tracked
|
||||
if frame_id == 1:
|
||||
self.is_activated = True
|
||||
else:
|
||||
self.first_find = True ### Add by WQG
|
||||
self.frame_id = frame_id
|
||||
self.start_frame = frame_id
|
||||
|
||||
def re_activate(self, new_track, frame_id, new_id=False):
|
||||
"""Reactivates a previously lost track with a new detection."""
|
||||
self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance,
|
||||
self.convert_coords(new_track.tlwh))
|
||||
self.tracklet_len = 0
|
||||
self.state = TrackState.Tracked
|
||||
self.is_activated = True
|
||||
self.first_find = False
|
||||
self.frame_id = frame_id
|
||||
if new_id:
|
||||
self.track_id = self.next_id()
|
||||
self.score = new_track.score
|
||||
self.cls = new_track.cls
|
||||
self.idx = new_track.idx
|
||||
|
||||
self._tlwh = new_track._tlwh
|
||||
|
||||
def update(self, new_track, frame_id):
|
||||
"""
|
||||
Update a matched track
|
||||
:type new_track: STrack
|
||||
:type frame_id: int
|
||||
:return:
|
||||
"""
|
||||
self.frame_id = frame_id
|
||||
self.tracklet_len += 1
|
||||
|
||||
new_tlwh = new_track.tlwh
|
||||
self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance,
|
||||
self.convert_coords(new_tlwh))
|
||||
self.state = TrackState.Tracked
|
||||
self.is_activated = True
|
||||
self.first_find = False
|
||||
|
||||
self.score = new_track.score
|
||||
self.cls = new_track.cls
|
||||
self.idx = new_track.idx
|
||||
|
||||
self._tlwh = new_track._tlwh
|
||||
|
||||
|
||||
def convert_coords(self, tlwh):
|
||||
"""Convert a bounding box's top-left-width-height format to its x-y-angle-height equivalent."""
|
||||
return self.tlwh_to_xyah(tlwh)
|
||||
|
||||
@property
|
||||
def tlwh(self):
|
||||
"""Get current position in bounding box format `(top left x, top left y,
|
||||
width, height)`.
|
||||
"""
|
||||
if self.mean is None:
|
||||
return self._tlwh.copy()
|
||||
ret = self.mean[:4].copy()
|
||||
ret[2] *= ret[3]
|
||||
ret[:2] -= ret[2:] / 2
|
||||
return ret
|
||||
|
||||
@property
|
||||
def tlbr(self):
|
||||
"""Convert bounding box to format `(min x, min y, max x, max y)`, i.e.,
|
||||
`(top left, bottom right)`.
|
||||
"""
|
||||
ret = self.tlwh.copy()
|
||||
ret[2:] += ret[:2]
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def tlwh_to_xyah(tlwh):
|
||||
"""Convert bounding box to format `(center x, center y, aspect ratio,
|
||||
height)`, where the aspect ratio is `width / height`.
|
||||
"""
|
||||
ret = np.asarray(tlwh).copy()
|
||||
ret[:2] += ret[2:] / 2
|
||||
ret[2] /= ret[3]
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def tlbr_to_tlwh(tlbr):
|
||||
"""Converts top-left bottom-right format to top-left width height format."""
|
||||
ret = np.asarray(tlbr).copy()
|
||||
ret[2:] -= ret[:2]
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def tlwh_to_tlbr(tlwh):
|
||||
"""Converts tlwh bounding box format to tlbr format."""
|
||||
ret = np.asarray(tlwh).copy()
|
||||
ret[2:] += ret[:2]
|
||||
return ret
|
||||
|
||||
def __repr__(self):
|
||||
"""Return a string representation of the BYTETracker object with start and end frames and track ID."""
|
||||
return f'OT_{self.track_id}_({self.start_frame}-{self.end_frame})'
|
||||
|
||||
|
||||
class BYTETracker:
|
||||
|
||||
def __init__(self, args, frame_rate=30):
|
||||
"""Initialize a YOLOv8 object to track objects with given arguments and frame rate."""
|
||||
self.tracked_stracks = [] # type: list[STrack]
|
||||
self.lost_stracks = [] # type: list[STrack]
|
||||
self.removed_stracks = [] # type: list[STrack]
|
||||
|
||||
self.frame_id = 0
|
||||
self.args = args
|
||||
self.max_time_lost = int(frame_rate / 30.0 * args.track_buffer)
|
||||
self.kalman_filter = self.get_kalmanfilter()
|
||||
self.reset_id()
|
||||
|
||||
# Add by WQG
|
||||
self.args.new_track_thresh = 0.5
|
||||
|
||||
|
||||
def update(self, results, img=None, features=None):
|
||||
"""Updates object tracker with new detections and returns tracked object bounding boxes."""
|
||||
self.frame_id += 1
|
||||
activated_stracks = []
|
||||
refind_stracks = []
|
||||
lost_stracks = []
|
||||
removed_stracks = []
|
||||
|
||||
first_finded = []
|
||||
|
||||
scores = results.conf
|
||||
cls = results.cls
|
||||
|
||||
# =============================================================================
|
||||
# # get xyxy and add index
|
||||
# bboxes = results.xyxy
|
||||
# bboxes = np.concatenate([bboxes, np.arange(len(bboxes)).reshape(-1, 1)], axis=-1)
|
||||
# =============================================================================
|
||||
bboxes = results.xyxyb
|
||||
|
||||
|
||||
remain_inds = scores > self.args.track_high_thresh
|
||||
inds_low = scores > self.args.track_low_thresh
|
||||
inds_high = scores < self.args.track_high_thresh
|
||||
|
||||
inds_second = np.logical_and(inds_low, inds_high)
|
||||
dets_second = bboxes[inds_second]
|
||||
dets = bboxes[remain_inds]
|
||||
scores_keep = scores[remain_inds]
|
||||
scores_second = scores[inds_second]
|
||||
cls_keep = cls[remain_inds]
|
||||
cls_second = cls[inds_second]
|
||||
|
||||
detections = self.init_track(dets, scores_keep, cls_keep, img, features)
|
||||
|
||||
# Add newly detected tracklets to tracked_stracks
|
||||
unconfirmed = []
|
||||
tracked_stracks = [] # type: list[STrack]
|
||||
for track in self.tracked_stracks:
|
||||
if not track.is_activated:
|
||||
unconfirmed.append(track)
|
||||
else:
|
||||
tracked_stracks.append(track)
|
||||
|
||||
|
||||
# Step 2: First association, with high score detection boxes
|
||||
strack_pool = self.joint_stracks(tracked_stracks, self.lost_stracks)
|
||||
# Predict the current location with KF
|
||||
self.multi_predict(strack_pool)
|
||||
|
||||
# ============================================================= 没必要gmc,WQG
|
||||
# if hasattr(self, 'gmc') and img is not None:
|
||||
# warp = self.gmc.apply(img, dets)
|
||||
# STrack.multi_gmc(strack_pool, warp)
|
||||
# STrack.multi_gmc(unconfirmed, warp)
|
||||
# =============================================================================
|
||||
|
||||
dists = self.get_dists_1(strack_pool, detections)
|
||||
|
||||
'''written by WQG for different class'''
|
||||
dists = dists_update(dists, strack_pool, detections)
|
||||
|
||||
matches, u_track, u_detection = matching.linear_assignment(dists, thresh=self.args.match_thresh)
|
||||
for itracked, idet in matches:
|
||||
track = strack_pool[itracked]
|
||||
det = detections[idet]
|
||||
if track.state == TrackState.Tracked:
|
||||
track.update(det, self.frame_id)
|
||||
activated_stracks.append(track)
|
||||
else:
|
||||
track.re_activate(det, self.frame_id, new_id=False)
|
||||
refind_stracks.append(track)
|
||||
|
||||
|
||||
# Step 3: Second association, with low score detection boxes
|
||||
# association the untrack to the low score detections
|
||||
detections_second = self.init_track(dets_second, scores_second, cls_second, img, features)
|
||||
r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked]
|
||||
|
||||
# TODO
|
||||
dists = matching.iou_distance(r_tracked_stracks, detections_second)
|
||||
'''written by WQG for different class'''
|
||||
dists = dists_update(dists, r_tracked_stracks, detections_second)
|
||||
|
||||
matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)
|
||||
for itracked, idet in matches:
|
||||
track = r_tracked_stracks[itracked]
|
||||
det = detections_second[idet]
|
||||
if track.state == TrackState.Tracked:
|
||||
track.update(det, self.frame_id)
|
||||
activated_stracks.append(track)
|
||||
else:
|
||||
track.re_activate(det, self.frame_id, new_id=False)
|
||||
refind_stracks.append(track)
|
||||
|
||||
for it in u_track:
|
||||
track = r_tracked_stracks[it]
|
||||
if track.state != TrackState.Lost:
|
||||
track.mark_lost()
|
||||
lost_stracks.append(track)
|
||||
|
||||
# Deal with unconfirmed tracks, usually tracks with only one beginning frame
|
||||
detections = [detections[i] for i in u_detection]
|
||||
dists = self.get_dists_1(unconfirmed, detections)
|
||||
'''written by WQG for different class'''
|
||||
dists = dists_update(dists, unconfirmed, detections)
|
||||
|
||||
matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7)
|
||||
for itracked, idet in matches:
|
||||
unconfirmed[itracked].update(detections[idet], self.frame_id)
|
||||
activated_stracks.append(unconfirmed[itracked])
|
||||
for it in u_unconfirmed:
|
||||
track = unconfirmed[it]
|
||||
if self.frame_id - track.end_frame > 2: # Add by WQG
|
||||
track.mark_removed()
|
||||
removed_stracks.append(track)
|
||||
# Step 4: Init new stracks
|
||||
for inew in u_detection:
|
||||
track = detections[inew]
|
||||
if track.score < self.args.new_track_thresh:
|
||||
continue
|
||||
track.activate(self.kalman_filter, self.frame_id)
|
||||
activated_stracks.append(track)
|
||||
|
||||
first_finded.append(track)
|
||||
|
||||
# Step 5: Update state
|
||||
for track in self.lost_stracks:
|
||||
if self.frame_id - track.end_frame > self.max_time_lost:
|
||||
track.mark_removed()
|
||||
removed_stracks.append(track)
|
||||
|
||||
self.tracked_stracks = [t for t in self.tracked_stracks if t.state == TrackState.Tracked]
|
||||
self.tracked_stracks = self.joint_stracks(self.tracked_stracks, activated_stracks)
|
||||
self.tracked_stracks = self.joint_stracks(self.tracked_stracks, refind_stracks)
|
||||
self.lost_stracks = self.sub_stracks(self.lost_stracks, self.tracked_stracks)
|
||||
self.lost_stracks.extend(lost_stracks)
|
||||
self.lost_stracks = self.sub_stracks(self.lost_stracks, self.removed_stracks)
|
||||
self.tracked_stracks, self.lost_stracks = self.remove_duplicate_stracks(self.tracked_stracks, self.lost_stracks)
|
||||
self.removed_stracks.extend(removed_stracks)
|
||||
if len(self.removed_stracks) > 1000:
|
||||
self.removed_stracks = self.removed_stracks[-999:] # clip remove stracks to 1000 maximum
|
||||
|
||||
'''x.tlbr have update by function:
|
||||
@property
|
||||
def tlwh(self):
|
||||
'''
|
||||
|
||||
##================ 原算法输出
|
||||
# output = np.asarray([x.tlbr.tolist() + [x.track_id, x.score, x.cls, x.frame_id, x.idx]
|
||||
# for x in self.tracked_stracks if x.is_activated], dtype=np.float32)
|
||||
|
||||
## ===== write by WQG
|
||||
output1 = [x.tlwh_to_tlbr(x._tlwh).tolist() + [x.track_id, x.score, x.cls, x.frame_id, x.idx]
|
||||
for x in self.tracked_stracks if x.is_activated]
|
||||
|
||||
output2 = [x.tlwh_to_tlbr(x._tlwh).tolist() + [x.track_id, x.score, x.cls, x.frame_id, x.idx]
|
||||
for x in first_finded if x.first_find]
|
||||
|
||||
output = np.asarray(output1 + output2, dtype=np.float32)
|
||||
|
||||
|
||||
out_feat1 = [(x.frame_id, x.idx, x.smooth_feat, x.curr_feat, x.features) for x in self.tracked_stracks if x.is_activated]
|
||||
out_feat2 = [(x.frame_id, x.idx, x.smooth_feat, x.curr_feat, x.features) for x in first_finded if x.first_find]
|
||||
|
||||
|
||||
return output, out_feat1 + out_feat2
|
||||
|
||||
|
||||
def get_result(self):
|
||||
'''written by WQG'''
|
||||
# =============================================================================
|
||||
# activate_tracks = np.asarray([x.tlbr.tolist() + [x.track_id, x.score, x.cls, x.idx]
|
||||
# for x in self.tracked_stracks if x.is_activated], dtype=np.float32)
|
||||
#
|
||||
# track_features = []
|
||||
# =============================================================================
|
||||
tracks = []
|
||||
feats = []
|
||||
for t in self.tracked_stracks:
|
||||
if t.is_activated or t.first_find:
|
||||
track = t.tlbr.tolist() + [t.track_id, t.score, t.cls, t.idx]
|
||||
feat = t.curr_feature
|
||||
|
||||
tracks.append(track)
|
||||
feats.append(feat)
|
||||
|
||||
tracks = np.asarray(tracks, dtype=np.float32)
|
||||
|
||||
return (tracks, feats)
|
||||
|
||||
|
||||
def get_kalmanfilter(self):
|
||||
"""Returns a Kalman filter object for tracking bounding boxes."""
|
||||
return KalmanFilterXYAH()
|
||||
|
||||
def init_track(self, dets, scores, cls, img=None, feats=None):
|
||||
"""Initialize object tracking with detections and scores using STrack algorithm."""
|
||||
return [STrack(xyxy, s, c) for (xyxy, s, c) in zip(dets, scores, cls)] if len(dets) else [] # detections
|
||||
|
||||
def get_dists(self, tracks, detections):
|
||||
"""Calculates the distance between tracks and detections using IOU and fuses scores."""
|
||||
dists = matching.iou_distance(tracks, detections)
|
||||
# TODO: mot20
|
||||
# if not self.args.mot20:
|
||||
dists = matching.fuse_score(dists, detections)
|
||||
return dists
|
||||
def get_dists_1(self, tracks, detections):
|
||||
"""Calculates the distance between tracks and detections using IOU and fuses scores."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def multi_predict(self, tracks):
|
||||
"""Returns the predicted tracks using the YOLOv8 network."""
|
||||
STrack.multi_predict(tracks)
|
||||
|
||||
def reset_id(self):
|
||||
"""Resets the ID counter of STrack."""
|
||||
STrack.reset_id()
|
||||
|
||||
@staticmethod
|
||||
def joint_stracks(tlista, tlistb):
|
||||
"""Combine two lists of stracks into a single one."""
|
||||
exists = {}
|
||||
res = []
|
||||
for t in tlista:
|
||||
exists[t.track_id] = 1
|
||||
res.append(t)
|
||||
for t in tlistb:
|
||||
tid = t.track_id
|
||||
if not exists.get(tid, 0):
|
||||
exists[tid] = 1
|
||||
res.append(t)
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def sub_stracks(tlista, tlistb):
|
||||
"""DEPRECATED CODE in https://github.com/ultralytics/ultralytics/pull/1890/
|
||||
stracks = {t.track_id: t for t in tlista}
|
||||
for t in tlistb:
|
||||
tid = t.track_id
|
||||
if stracks.get(tid, 0):
|
||||
del stracks[tid]
|
||||
return list(stracks.values())
|
||||
"""
|
||||
track_ids_b = {t.track_id for t in tlistb}
|
||||
return [t for t in tlista if t.track_id not in track_ids_b]
|
||||
|
||||
@staticmethod
|
||||
def remove_duplicate_stracks(stracksa, stracksb):
|
||||
"""Remove duplicate stracks with non-maximum IOU distance."""
|
||||
pdist = matching.iou_distance(stracksa, stracksb)
|
||||
|
||||
#### ===================================== written by WQG
|
||||
mlabel = []
|
||||
if len(stracksa) and len(stracksb):
|
||||
alabel = np.array([int(stack.cls) for stack in stracksa])
|
||||
blabel = np.array([int(stack.cls) for stack in stracksb])
|
||||
amlabel = np.expand_dims(alabel, axis=1).repeat(len(stracksb),axis=1)
|
||||
bmlabel = np.expand_dims(blabel, axis=0).repeat(len(stracksa),axis=0)
|
||||
mlabel = bmlabel == amlabel
|
||||
if len(mlabel):
|
||||
condt = (pdist<0.15) & mlabel # 需满足iou足够小,且类别相同,才予以排除
|
||||
else:
|
||||
condt = pdist<0.15
|
||||
|
||||
|
||||
pairs = np.where(condt)
|
||||
dupa, dupb = [], []
|
||||
for p, q in zip(*pairs):
|
||||
timep = stracksa[p].frame_id - stracksa[p].start_frame
|
||||
timeq = stracksb[q].frame_id - stracksb[q].start_frame
|
||||
if timep > timeq:
|
||||
dupb.append(q)
|
||||
else:
|
||||
dupa.append(p)
|
||||
resa = [t for i, t in enumerate(stracksa) if i not in dupa]
|
||||
resb = [t for i, t in enumerate(stracksb) if i not in dupb]
|
||||
return resa, resb
|
18
tracking/trackers/cfg/botsort.yaml
Normal file
18
tracking/trackers/cfg/botsort.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
# Default YOLO tracker settings for BoT-SORT tracker https://github.com/NirAharon/BoT-SORT
|
||||
|
||||
tracker_type: botsort # tracker type, ['botsort', 'bytetrack']
|
||||
track_high_thresh: 0.5 # threshold for the first association
|
||||
track_low_thresh: 0.1 # threshold for the second association
|
||||
new_track_thresh: 0.6 # threshold for init new track if the detection does not match any tracks
|
||||
track_buffer: 30 # buffer to calculate the time when to remove tracks
|
||||
match_thresh: 0.8 # threshold for matching tracks
|
||||
# min_box_area: 10 # threshold for min box areas(for tracker evaluation, not used for now)
|
||||
# mot20: False # for tracker evaluation(not used for now)
|
||||
|
||||
# BoT-SORT settings
|
||||
gmc_method: sparseOptFlow # method of global motion compensation
|
||||
# ReID model related thresh (not supported yet)
|
||||
proximity_thresh: 0.5
|
||||
appearance_thresh: 0.25
|
||||
with_reid: True
|
11
tracking/trackers/cfg/bytetrack.yaml
Normal file
11
tracking/trackers/cfg/bytetrack.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
# Default YOLO tracker settings for ByteTrack tracker https://github.com/ifzhang/ByteTrack
|
||||
|
||||
tracker_type: bytetrack # tracker type, ['botsort', 'bytetrack']
|
||||
track_high_thresh: 0.5 # threshold for the first association
|
||||
track_low_thresh: 0.1 # threshold for the second association
|
||||
new_track_thresh: 0.6 # threshold for init new track if the detection does not match any tracks
|
||||
track_buffer: 30 # buffer to calculate the time when to remove tracks
|
||||
match_thresh: 0.8 # threshold for matching tracks
|
||||
# min_box_area: 10 # threshold for min box areas(for tracker evaluation, not used for now)
|
||||
# mot20: False # for tracker evaluation(not used for now)
|
7
tracking/trackers/reid/__init__.py
Normal file
7
tracking/trackers/reid/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Jan 19 16:15:35 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
|
BIN
tracking/trackers/reid/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/reid/__pycache__/config.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/__pycache__/config.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/reid/__pycache__/reid_interface.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/__pycache__/reid_interface.cpython-39.pyc
Normal file
Binary file not shown.
45
tracking/trackers/reid/config.py
Normal file
45
tracking/trackers/reid/config.py
Normal file
@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Jan 19 14:01:46 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
|
||||
import torch
|
||||
import os
|
||||
# import torchvision.transforms as T
|
||||
class Config:
|
||||
# network settings
|
||||
backbone = 'resnet18' # [resnet18, mobilevit_s, mobilenet_v2, mobilenetv3]
|
||||
batch_size = 8
|
||||
embedding_size = 256
|
||||
img_size = 224
|
||||
|
||||
ckpt_path = r"ckpts\resnet18_1220\best.pth"
|
||||
ckpt_path = r"ckpts\best_resnet18_1887_0311.pth"
|
||||
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
model_path = os.path.join(current_path, ckpt_path)
|
||||
|
||||
# model_path = "./trackers/reid/ckpts/resnet18_1220/best.pth"
|
||||
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||
|
||||
# =============================================================================
|
||||
# metric = 'arcface' # [cosface, arcface]
|
||||
# drop_ratio = 0.5
|
||||
#
|
||||
# # training settings
|
||||
# checkpoints = "checkpoints/Mobilev3Large_1225" # [resnet18, mobilevit_s, mobilenet_v2, mobilenetv3]
|
||||
# restore = False
|
||||
#
|
||||
# test_model = "./checkpoints/resnet18_1220/best.pth"
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||
# pin_memory = True # if memory is large, set it True to speed up a bit
|
||||
# num_workers = 4 # dataloader
|
||||
# =============================================================================
|
||||
|
||||
config = Config()
|
83
tracking/trackers/reid/model/BAM.py
Normal file
83
tracking/trackers/reid/model/BAM.py
Normal file
@ -0,0 +1,83 @@
|
||||
import torch.nn as nn
|
||||
import torchvision
|
||||
from torch.nn import init
|
||||
|
||||
class Flatten(nn.Module):
|
||||
def forward(self, x):
|
||||
return x.view(x.shape[0], -1)
|
||||
|
||||
class ChannelAttention(nn.Module):
|
||||
def __int__(self,channel,reduction, num_layers):
|
||||
super(ChannelAttention,self).__init__()
|
||||
self.avgpool = nn.AdaptiveAvgPool2d(1)
|
||||
gate_channels = [channel]
|
||||
gate_channels += [len(channel)//reduction]*num_layers
|
||||
gate_channels += [channel]
|
||||
|
||||
self.ca = nn.Sequential()
|
||||
self.ca.add_module('flatten', Flatten())
|
||||
for i in range(len(gate_channels)-2):
|
||||
self.ca.add_module('',nn.Linear(gate_channels[i], gate_channels[i+1]))
|
||||
self.ca.add_module('',nn.BatchNorm1d(gate_channels[i+1]))
|
||||
self.ca.add_module('',nn.ReLU())
|
||||
self.ca.add_module('',nn.Linear(gate_channels[-2], gate_channels[-1]))
|
||||
|
||||
def forward(self, x):
|
||||
res = self.avgpool(x)
|
||||
res = self.ca(res)
|
||||
res = res.unsqueeze(-1).unsqueeze(-1).expand_as(x)
|
||||
return res
|
||||
|
||||
class SpatialAttention(nn.Module):
|
||||
def __int__(self, channel,reduction=16,num_lay=3,dilation=2):
|
||||
super(SpatialAttention).__init__()
|
||||
self.sa = nn.Sequential()
|
||||
self.sa.add_module('', nn.Conv2d(kernel_size=1, in_channels=channel, out_channels=(channel//reduction)*3))
|
||||
self.sa.add_module('',nn.BatchNorm2d(num_features=(channel//reduction)))
|
||||
self.sa.add_module('',nn.ReLU())
|
||||
for i in range(num_lay):
|
||||
self.sa.add_module('', nn.Conv2d(kernel_size=3,
|
||||
in_channels=(channel//reduction),
|
||||
out_channels=(channel//reduction),
|
||||
padding=1,
|
||||
dilation= 2))
|
||||
self.sa.add_module('',nn.BatchNorm2d(channel//reduction))
|
||||
self.sa.add_module('',nn.ReLU())
|
||||
self.sa.add_module('',nn.Conv2d(channel//reduction, 1, kernel_size=1))
|
||||
def forward(self,x):
|
||||
res = self.sa(x)
|
||||
res = res.expand_as(x)
|
||||
return res
|
||||
|
||||
class BAMblock(nn.Module):
|
||||
def __init__(self,channel=512, reduction=16, dia_val=2):
|
||||
super(BAMblock, self).__init__()
|
||||
self.ca = ChannelAttention(channel, reduction)
|
||||
self.sa = SpatialAttention(channel,reduction,dia_val)
|
||||
self.sigmoid = nn.Sigmoid()
|
||||
|
||||
def init_weights(self):
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
init.kaiming_normal(m.weight, mode='fan_out')
|
||||
if m.bais is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
init.constant_(m.weight, 1)
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.Linear):
|
||||
init.normal_(m.weight, std=0.001)
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
|
||||
def forward(self,x):
|
||||
b, c, _, _ = x.size()
|
||||
sa_out=self.sa(x)
|
||||
ca_out=self.ca(x)
|
||||
weight=self.sigmoid(sa_out+ca_out)
|
||||
out=(1+weight)*x
|
||||
return out
|
||||
|
||||
if __name__ =="__main__":
|
||||
|
||||
print(512//14)
|
68
tracking/trackers/reid/model/CBAM.py
Normal file
68
tracking/trackers/reid/model/CBAM.py
Normal file
@ -0,0 +1,68 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.init as init
|
||||
|
||||
class channelAttention(nn.Module):
|
||||
def __init__(self, channel, reduction=16):
|
||||
super(channelAttention, self).__init__()
|
||||
self.Maxpooling = nn.AdaptiveMaxPool2d(1)
|
||||
self.Avepooling = nn.AdaptiveAvgPool2d(1)
|
||||
self.ca = nn.Sequential()
|
||||
self.ca.add_module('conv1',nn.Conv2d(channel, channel//reduction, 1, bias=False))
|
||||
self.ca.add_module('Relu', nn.ReLU())
|
||||
self.ca.add_module('conv2',nn.Conv2d(channel//reduction, channel, 1, bias=False))
|
||||
self.sigmod = nn.Sigmoid()
|
||||
|
||||
def forward(self, x):
|
||||
M_out = self.Maxpooling(x)
|
||||
A_out = self.Avepooling(x)
|
||||
M_out = self.ca(M_out)
|
||||
A_out = self.ca(A_out)
|
||||
out = self.sigmod(M_out+A_out)
|
||||
return out
|
||||
|
||||
class SpatialAttention(nn.Module):
|
||||
def __init__(self, kernel_size=7):
|
||||
super().__init__()
|
||||
self.conv = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=kernel_size, padding=kernel_size // 2)
|
||||
self.sigmoid = nn.Sigmoid()
|
||||
|
||||
def forward(self, x):
|
||||
max_result, _ = torch.max(x, dim=1, keepdim=True)
|
||||
avg_result = torch.mean(x, dim=1, keepdim=True)
|
||||
result = torch.cat([max_result, avg_result], dim=1)
|
||||
output = self.conv(result)
|
||||
output = self.sigmoid(output)
|
||||
return output
|
||||
class CBAM(nn.Module):
|
||||
def __init__(self, channel=512, reduction=16, kernel_size=7):
|
||||
super().__init__()
|
||||
self.ca = channelAttention(channel, reduction)
|
||||
self.sa = SpatialAttention(kernel_size)
|
||||
|
||||
def init_weights(self):
|
||||
for m in self.modules():#权重初始化
|
||||
if isinstance(m, nn.Conv2d):
|
||||
init.kaiming_normal_(m.weight, mode='fan_out')
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
init.constant_(m.weight, 1)
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.Linear):
|
||||
init.normal_(m.weight, std=0.001)
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
|
||||
def forward(self, x):
|
||||
# b,c_,_ = x.size()
|
||||
# residual = x
|
||||
out = x*self.ca(x)
|
||||
out = out*self.sa(out)
|
||||
return out
|
||||
if __name__ == '__main__':
|
||||
input=torch.randn(50,512,7,7)
|
||||
kernel_size=input.shape[2]
|
||||
cbam = CBAM(channel=512,reduction=16,kernel_size=kernel_size)
|
||||
output=cbam(input)
|
||||
print(output.shape)
|
33
tracking/trackers/reid/model/Tool.py
Normal file
33
tracking/trackers/reid/model/Tool.py
Normal file
@ -0,0 +1,33 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
class GeM(nn.Module):
|
||||
def __init__(self, p=3, eps=1e-6):
|
||||
super(GeM, self).__init__()
|
||||
self.p = nn.Parameter(torch.ones(1) * p)
|
||||
self.eps = eps
|
||||
|
||||
def forward(self, x):
|
||||
return self.gem(x, p=self.p, eps=self.eps, stride = 2)
|
||||
|
||||
def gem(self, x, p=3, eps=1e-6, stride = 2):
|
||||
return F.avg_pool2d(x.clamp(min=eps).pow(p), (x.size(-2), x.size(-1)), stride=2).pow(1. / p)
|
||||
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__ + \
|
||||
'(' + 'p=' + '{:.4f}'.format(self.p.data.tolist()[0]) + \
|
||||
', ' + 'eps=' + str(self.eps) + ')'
|
||||
|
||||
class TripletLoss(nn.Module):
|
||||
def __init__(self, margin):
|
||||
super(TripletLoss, self).__init__()
|
||||
self.margin = margin
|
||||
|
||||
def forward(self, anchor, positive, negative, size_average = True):
|
||||
distance_positive = (anchor-positive).pow(2).sum(1)
|
||||
distance_negative = (anchor-negative).pow(2).sum(1)
|
||||
losses = F.relu(distance_negative-distance_positive+self.margin)
|
||||
return losses.mean() if size_average else losses.sum()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('')
|
9
tracking/trackers/reid/model/__init__.py
Normal file
9
tracking/trackers/reid/model/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from .fmobilenet import FaceMobileNet
|
||||
from .resnet_face import ResIRSE
|
||||
from .mobilevit import mobilevit_s
|
||||
from .metric import ArcFace, CosFace
|
||||
from .loss import FocalLoss
|
||||
from .resbam import resnet
|
||||
from .resnet_pre import resnet18, resnet34, resnet50
|
||||
from .mobilenet_v2 import mobilenet_v2
|
||||
from .mobilenet_v3 import MobileNetV3_Small, MobileNetV3_Large
|
BIN
tracking/trackers/reid/model/__pycache__/CBAM.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/CBAM.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/Tool.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/Tool.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/loss.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/loss.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/metric.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/metric.cpython-39.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/resbam.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/resbam.cpython-39.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tracking/trackers/reid/model/__pycache__/utils.cpython-39.pyc
Normal file
BIN
tracking/trackers/reid/model/__pycache__/utils.cpython-39.pyc
Normal file
Binary file not shown.
124
tracking/trackers/reid/model/fmobilenet.py
Normal file
124
tracking/trackers/reid/model/fmobilenet.py
Normal file
@ -0,0 +1,124 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
|
||||
class Flatten(nn.Module):
|
||||
def forward(self, x):
|
||||
return x.view(x.shape[0], -1)
|
||||
|
||||
class ConvBn(nn.Module):
|
||||
|
||||
def __init__(self, in_c, out_c, kernel=(1, 1), stride=1, padding=0, groups=1):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.Conv2d(in_c, out_c, kernel, stride, padding, groups=groups, bias=False),
|
||||
nn.BatchNorm2d(out_c)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class ConvBnPrelu(nn.Module):
|
||||
|
||||
def __init__(self, in_c, out_c, kernel=(1, 1), stride=1, padding=0, groups=1):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
ConvBn(in_c, out_c, kernel, stride, padding, groups),
|
||||
nn.PReLU(out_c)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class DepthWise(nn.Module):
|
||||
|
||||
def __init__(self, in_c, out_c, kernel=(3, 3), stride=2, padding=1, groups=1):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
ConvBnPrelu(in_c, groups, kernel=(1, 1), stride=1, padding=0),
|
||||
ConvBnPrelu(groups, groups, kernel=kernel, stride=stride, padding=padding, groups=groups),
|
||||
ConvBn(groups, out_c, kernel=(1, 1), stride=1, padding=0),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class DepthWiseRes(nn.Module):
|
||||
"""DepthWise with Residual"""
|
||||
|
||||
def __init__(self, in_c, out_c, kernel=(3, 3), stride=2, padding=1, groups=1):
|
||||
super().__init__()
|
||||
self.net = DepthWise(in_c, out_c, kernel, stride, padding, groups)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x) + x
|
||||
|
||||
|
||||
class MultiDepthWiseRes(nn.Module):
|
||||
|
||||
def __init__(self, num_block, channels, kernel=(3, 3), stride=1, padding=1, groups=1):
|
||||
super().__init__()
|
||||
|
||||
self.net = nn.Sequential(*[
|
||||
DepthWiseRes(channels, channels, kernel, stride, padding, groups)
|
||||
for _ in range(num_block)
|
||||
])
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class FaceMobileNet(nn.Module):
|
||||
|
||||
def __init__(self, embedding_size):
|
||||
super().__init__()
|
||||
self.conv1 = ConvBnPrelu(1, 64, kernel=(3, 3), stride=2, padding=1)
|
||||
self.conv2 = ConvBn(64, 64, kernel=(3, 3), stride=1, padding=1, groups=64)
|
||||
self.conv3 = DepthWise(64, 64, kernel=(3, 3), stride=2, padding=1, groups=128)
|
||||
self.conv4 = MultiDepthWiseRes(num_block=4, channels=64, kernel=3, stride=1, padding=1, groups=128)
|
||||
self.conv5 = DepthWise(64, 128, kernel=(3, 3), stride=2, padding=1, groups=256)
|
||||
self.conv6 = MultiDepthWiseRes(num_block=6, channels=128, kernel=(3, 3), stride=1, padding=1, groups=256)
|
||||
self.conv7 = DepthWise(128, 128, kernel=(3, 3), stride=2, padding=1, groups=512)
|
||||
self.conv8 = MultiDepthWiseRes(num_block=2, channels=128, kernel=(3, 3), stride=1, padding=1, groups=256)
|
||||
self.conv9 = ConvBnPrelu(128, 512, kernel=(1, 1))
|
||||
self.conv10 = ConvBn(512, 512, groups=512, kernel=(7, 7))
|
||||
self.flatten = Flatten()
|
||||
self.linear = nn.Linear(2048, embedding_size, bias=False)
|
||||
self.bn = nn.BatchNorm1d(embedding_size)
|
||||
|
||||
def forward(self, x):
|
||||
#print('x',x.shape)
|
||||
out = self.conv1(x)
|
||||
out = self.conv2(out)
|
||||
out = self.conv3(out)
|
||||
out = self.conv4(out)
|
||||
out = self.conv5(out)
|
||||
out = self.conv6(out)
|
||||
out = self.conv7(out)
|
||||
out = self.conv8(out)
|
||||
out = self.conv9(out)
|
||||
out = self.conv10(out)
|
||||
out = self.flatten(out)
|
||||
out = self.linear(out)
|
||||
out = self.bn(out)
|
||||
return out
|
||||
|
||||
if __name__ == "__main__":
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
x = Image.open("../samples/009.jpg").convert('L')
|
||||
x = x.resize((128, 128))
|
||||
x = np.asarray(x, dtype=np.float32)
|
||||
x = x[None, None, ...]
|
||||
x = torch.from_numpy(x)
|
||||
net = FaceMobileNet(512)
|
||||
net.eval()
|
||||
with torch.no_grad():
|
||||
out = net(x)
|
||||
print(out.shape)
|
18
tracking/trackers/reid/model/loss.py
Normal file
18
tracking/trackers/reid/model/loss.py
Normal file
@ -0,0 +1,18 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
|
||||
class FocalLoss(nn.Module):
|
||||
|
||||
def __init__(self, gamma=2):
|
||||
super().__init__()
|
||||
self.gamma = gamma
|
||||
self.ce = torch.nn.CrossEntropyLoss()
|
||||
|
||||
def forward(self, input, target):
|
||||
|
||||
#print(f'theta {input.shape, input[0]}, target {target.shape, target}')
|
||||
logp = self.ce(input, target)
|
||||
p = torch.exp(-logp)
|
||||
loss = (1 - p) ** self.gamma * logp
|
||||
return loss.mean()
|
83
tracking/trackers/reid/model/metric.py
Normal file
83
tracking/trackers/reid/model/metric.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Definition of ArcFace loss and CosFace loss
|
||||
|
||||
import math
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class ArcFace(nn.Module):
|
||||
|
||||
def __init__(self, embedding_size, class_num, s=30.0, m=0.50):
|
||||
"""ArcFace formula:
|
||||
cos(m + theta) = cos(m)cos(theta) - sin(m)sin(theta)
|
||||
Note that:
|
||||
0 <= m + theta <= Pi
|
||||
So if (m + theta) >= Pi, then theta >= Pi - m. In [0, Pi]
|
||||
we have:
|
||||
cos(theta) < cos(Pi - m)
|
||||
So we can use cos(Pi - m) as threshold to check whether
|
||||
(m + theta) go out of [0, Pi]
|
||||
|
||||
Args:
|
||||
embedding_size: usually 128, 256, 512 ...
|
||||
class_num: num of people when training
|
||||
s: scale, see normface https://arxiv.org/abs/1704.06369
|
||||
m: margin, see SphereFace, CosFace, and ArcFace paper
|
||||
"""
|
||||
super().__init__()
|
||||
self.in_features = embedding_size
|
||||
self.out_features = class_num
|
||||
self.s = s
|
||||
self.m = m
|
||||
self.weight = nn.Parameter(torch.FloatTensor(class_num, embedding_size))
|
||||
nn.init.xavier_uniform_(self.weight)
|
||||
|
||||
self.cos_m = math.cos(m)
|
||||
self.sin_m = math.sin(m)
|
||||
self.th = math.cos(math.pi - m)
|
||||
self.mm = math.sin(math.pi - m) * m
|
||||
|
||||
def forward(self, input, label):
|
||||
#print(f"embding {self.in_features}, class_num {self.out_features}, input {len(input)}, label {len(label)}")
|
||||
cosine = F.linear(F.normalize(input), F.normalize(self.weight))
|
||||
# print('F.normalize(input)',input.shape)
|
||||
# print('F.normalize(self.weight)',F.normalize(self.weight).shape)
|
||||
sine = ((1.0 - cosine.pow(2)).clamp(0, 1)).sqrt()
|
||||
phi = cosine * self.cos_m - sine * self.sin_m
|
||||
phi = torch.where(cosine > self.th, phi, cosine - self.mm) # drop to CosFace
|
||||
#print(f'consine {cosine.shape, cosine}, sine {sine.shape, sine}, phi {phi.shape, phi}')
|
||||
# update y_i by phi in cosine
|
||||
output = cosine * 1.0 # make backward works
|
||||
batch_size = len(output)
|
||||
output[range(batch_size), label] = phi[range(batch_size), label]
|
||||
# print(f'output {(output * self.s).shape}')
|
||||
# print(f'phi[range(batch_size), label] {phi[range(batch_size), label]}')
|
||||
return output * self.s
|
||||
|
||||
|
||||
class CosFace(nn.Module):
|
||||
|
||||
def __init__(self, in_features, out_features, s=30.0, m=0.40):
|
||||
"""
|
||||
Args:
|
||||
embedding_size: usually 128, 256, 512 ...
|
||||
class_num: num of people when training
|
||||
s: scale, see normface https://arxiv.org/abs/1704.06369
|
||||
m: margin, see SphereFace, CosFace, and ArcFace paper
|
||||
"""
|
||||
super().__init__()
|
||||
self.in_features = in_features
|
||||
self.out_features = out_features
|
||||
self.s = s
|
||||
self.m = m
|
||||
self.weight = nn.Parameter(torch.FloatTensor(out_features, in_features))
|
||||
nn.init.xavier_uniform_(self.weight)
|
||||
|
||||
def forward(self, input, label):
|
||||
cosine = F.linear(F.normalize(input), F.normalize(self.weight))
|
||||
phi = cosine - self.m
|
||||
output = cosine * 1.0 # make backward works
|
||||
batch_size = len(output)
|
||||
output[range(batch_size), label] = phi[range(batch_size), label]
|
||||
return output * self.s
|
200
tracking/trackers/reid/model/mobilenet_v2.py
Normal file
200
tracking/trackers/reid/model/mobilenet_v2.py
Normal file
@ -0,0 +1,200 @@
|
||||
from torch import nn
|
||||
from .utils import load_state_dict_from_url
|
||||
from ..config import config as conf
|
||||
|
||||
__all__ = ['MobileNetV2', 'mobilenet_v2']
|
||||
|
||||
|
||||
model_urls = {
|
||||
'mobilenet_v2': 'https://download.pytorch.org/models/mobilenet_v2-b0353104.pth',
|
||||
}
|
||||
|
||||
|
||||
def _make_divisible(v, divisor, min_value=None):
|
||||
"""
|
||||
This function is taken from the original tf repo.
|
||||
It ensures that all layers have a channel number that is divisible by 8
|
||||
It can be seen here:
|
||||
https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
|
||||
:param v:
|
||||
:param divisor:
|
||||
:param min_value:
|
||||
:return:
|
||||
"""
|
||||
if min_value is None:
|
||||
min_value = divisor
|
||||
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
|
||||
# Make sure that round down does not go down by more than 10%.
|
||||
if new_v < 0.9 * v:
|
||||
new_v += divisor
|
||||
return new_v
|
||||
|
||||
|
||||
class ConvBNReLU(nn.Sequential):
|
||||
def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, groups=1, norm_layer=None):
|
||||
padding = (kernel_size - 1) // 2
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
super(ConvBNReLU, self).__init__(
|
||||
nn.Conv2d(in_planes, out_planes, kernel_size, stride, padding, groups=groups, bias=False),
|
||||
norm_layer(out_planes),
|
||||
nn.ReLU6(inplace=True)
|
||||
)
|
||||
|
||||
|
||||
class InvertedResidual(nn.Module):
|
||||
def __init__(self, inp, oup, stride, expand_ratio, norm_layer=None):
|
||||
super(InvertedResidual, self).__init__()
|
||||
self.stride = stride
|
||||
assert stride in [1, 2]
|
||||
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
|
||||
hidden_dim = int(round(inp * expand_ratio))
|
||||
self.use_res_connect = self.stride == 1 and inp == oup
|
||||
|
||||
layers = []
|
||||
if expand_ratio != 1:
|
||||
# pw
|
||||
layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1, norm_layer=norm_layer))
|
||||
layers.extend([
|
||||
# dw
|
||||
ConvBNReLU(hidden_dim, hidden_dim, stride=stride, groups=hidden_dim, norm_layer=norm_layer),
|
||||
# pw-linear
|
||||
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
|
||||
norm_layer(oup),
|
||||
])
|
||||
self.conv = nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
if self.use_res_connect:
|
||||
return x + self.conv(x)
|
||||
else:
|
||||
return self.conv(x)
|
||||
|
||||
|
||||
class MobileNetV2(nn.Module):
|
||||
def __init__(self,
|
||||
num_classes=conf.embedding_size,
|
||||
width_mult=1.0,
|
||||
inverted_residual_setting=None,
|
||||
round_nearest=8,
|
||||
block=None,
|
||||
norm_layer=None):
|
||||
"""
|
||||
MobileNet V2 main class
|
||||
|
||||
Args:
|
||||
num_classes (int): Number of classes
|
||||
width_mult (float): Width multiplier - adjusts number of channels in each layer by this amount
|
||||
inverted_residual_setting: Network structure
|
||||
round_nearest (int): Round the number of channels in each layer to be a multiple of this number
|
||||
Set to 1 to turn off rounding
|
||||
block: Module specifying inverted residual building block for mobilenet
|
||||
norm_layer: Module specifying the normalization layer to use
|
||||
|
||||
"""
|
||||
super(MobileNetV2, self).__init__()
|
||||
|
||||
if block is None:
|
||||
block = InvertedResidual
|
||||
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
|
||||
input_channel = 32
|
||||
last_channel = 1280
|
||||
|
||||
if inverted_residual_setting is None:
|
||||
inverted_residual_setting = [
|
||||
# t, c, n, s
|
||||
[1, 16, 1, 1],
|
||||
[6, 24, 2, 2],
|
||||
[6, 32, 3, 2],
|
||||
[6, 64, 4, 2],
|
||||
[6, 96, 3, 1],
|
||||
[6, 160, 3, 2],
|
||||
[6, 320, 1, 1],
|
||||
]
|
||||
|
||||
# only check the first element, assuming user knows t,c,n,s are required
|
||||
if len(inverted_residual_setting) == 0 or len(inverted_residual_setting[0]) != 4:
|
||||
raise ValueError("inverted_residual_setting should be non-empty "
|
||||
"or a 4-element list, got {}".format(inverted_residual_setting))
|
||||
|
||||
# building first layer
|
||||
input_channel = _make_divisible(input_channel * width_mult, round_nearest)
|
||||
self.last_channel = _make_divisible(last_channel * max(1.0, width_mult), round_nearest)
|
||||
features = [ConvBNReLU(3, input_channel, stride=2, norm_layer=norm_layer)]
|
||||
# building inverted residual blocks
|
||||
for t, c, n, s in inverted_residual_setting:
|
||||
output_channel = _make_divisible(c * width_mult, round_nearest)
|
||||
for i in range(n):
|
||||
stride = s if i == 0 else 1
|
||||
features.append(block(input_channel, output_channel, stride, expand_ratio=t, norm_layer=norm_layer))
|
||||
input_channel = output_channel
|
||||
# building last several layers
|
||||
features.append(ConvBNReLU(input_channel, self.last_channel, kernel_size=1, norm_layer=norm_layer))
|
||||
# make it nn.Sequential
|
||||
self.features = nn.Sequential(*features)
|
||||
|
||||
# building classifier
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Dropout(0.2),
|
||||
nn.Linear(self.last_channel, num_classes),
|
||||
)
|
||||
|
||||
# weight initialization
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal_(m.weight, mode='fan_out')
|
||||
if m.bias is not None:
|
||||
nn.init.zeros_(m.bias)
|
||||
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
|
||||
nn.init.ones_(m.weight)
|
||||
nn.init.zeros_(m.bias)
|
||||
elif isinstance(m, nn.Linear):
|
||||
nn.init.normal_(m.weight, 0, 0.01)
|
||||
nn.init.zeros_(m.bias)
|
||||
|
||||
def _forward_impl(self, x):
|
||||
# This exists since TorchScript doesn't support inheritance, so the superclass method
|
||||
# (this one) needs to have a name other than `forward` that can be accessed in a subclass
|
||||
x = self.features(x)
|
||||
# Cannot use "squeeze" as batch-size can be 1 => must use reshape with x.shape[0]
|
||||
x = nn.functional.adaptive_avg_pool2d(x, 1).reshape(x.shape[0], -1)
|
||||
x = self.classifier(x)
|
||||
return x
|
||||
|
||||
def forward(self, x):
|
||||
return self._forward_impl(x)
|
||||
|
||||
|
||||
def mobilenet_v2(pretrained=True, progress=True, **kwargs):
|
||||
"""
|
||||
Constructs a MobileNetV2 architecture from
|
||||
`"MobileNetV2: Inverted Residuals and Linear Bottlenecks" <https://arxiv.org/abs/1801.04381>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
model = MobileNetV2(**kwargs)
|
||||
if pretrained:
|
||||
state_dict = load_state_dict_from_url(model_urls['mobilenet_v2'],
|
||||
progress=progress)
|
||||
src_state_dict = state_dict
|
||||
target_state_dict = model.state_dict()
|
||||
skip_keys = []
|
||||
# skip mismatch size tensors in case of pretraining
|
||||
for k in src_state_dict.keys():
|
||||
if k not in target_state_dict:
|
||||
continue
|
||||
if src_state_dict[k].size() != target_state_dict[k].size():
|
||||
skip_keys.append(k)
|
||||
for k in skip_keys:
|
||||
del src_state_dict[k]
|
||||
missing_keys, unexpected_keys = model.load_state_dict(src_state_dict, strict=False)
|
||||
#.load_state_dict(state_dict)
|
||||
return model
|
200
tracking/trackers/reid/model/mobilenet_v3.py
Normal file
200
tracking/trackers/reid/model/mobilenet_v3.py
Normal file
@ -0,0 +1,200 @@
|
||||
'''MobileNetV3 in PyTorch.
|
||||
|
||||
See the paper "Inverted Residuals and Linear Bottlenecks:
|
||||
Mobile Networks for Classification, Detection and Segmentation" for more details.
|
||||
'''
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from torch.nn import init
|
||||
from ..config import config as conf
|
||||
|
||||
|
||||
class hswish(nn.Module):
|
||||
def forward(self, x):
|
||||
out = x * F.relu6(x + 3, inplace=True) / 6
|
||||
return out
|
||||
|
||||
|
||||
class hsigmoid(nn.Module):
|
||||
def forward(self, x):
|
||||
out = F.relu6(x + 3, inplace=True) / 6
|
||||
return out
|
||||
|
||||
|
||||
class SeModule(nn.Module):
|
||||
def __init__(self, in_size, reduction=4):
|
||||
super(SeModule, self).__init__()
|
||||
self.se = nn.Sequential(
|
||||
nn.AdaptiveAvgPool2d(1),
|
||||
nn.Conv2d(in_size, in_size // reduction, kernel_size=1, stride=1, padding=0, bias=False),
|
||||
nn.BatchNorm2d(in_size // reduction),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(in_size // reduction, in_size, kernel_size=1, stride=1, padding=0, bias=False),
|
||||
nn.BatchNorm2d(in_size),
|
||||
hsigmoid()
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return x * self.se(x)
|
||||
|
||||
|
||||
class Block(nn.Module):
|
||||
'''expand + depthwise + pointwise'''
|
||||
def __init__(self, kernel_size, in_size, expand_size, out_size, nolinear, semodule, stride):
|
||||
super(Block, self).__init__()
|
||||
self.stride = stride
|
||||
self.se = semodule
|
||||
|
||||
self.conv1 = nn.Conv2d(in_size, expand_size, kernel_size=1, stride=1, padding=0, bias=False)
|
||||
self.bn1 = nn.BatchNorm2d(expand_size)
|
||||
self.nolinear1 = nolinear
|
||||
self.conv2 = nn.Conv2d(expand_size, expand_size, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, groups=expand_size, bias=False)
|
||||
self.bn2 = nn.BatchNorm2d(expand_size)
|
||||
self.nolinear2 = nolinear
|
||||
self.conv3 = nn.Conv2d(expand_size, out_size, kernel_size=1, stride=1, padding=0, bias=False)
|
||||
self.bn3 = nn.BatchNorm2d(out_size)
|
||||
|
||||
self.shortcut = nn.Sequential()
|
||||
if stride == 1 and in_size != out_size:
|
||||
self.shortcut = nn.Sequential(
|
||||
nn.Conv2d(in_size, out_size, kernel_size=1, stride=1, padding=0, bias=False),
|
||||
nn.BatchNorm2d(out_size),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
out = self.nolinear1(self.bn1(self.conv1(x)))
|
||||
out = self.nolinear2(self.bn2(self.conv2(out)))
|
||||
out = self.bn3(self.conv3(out))
|
||||
if self.se != None:
|
||||
out = self.se(out)
|
||||
out = out + self.shortcut(x) if self.stride==1 else out
|
||||
return out
|
||||
|
||||
|
||||
class MobileNetV3_Large(nn.Module):
|
||||
def __init__(self, num_classes=conf.embedding_size):
|
||||
super(MobileNetV3_Large, self).__init__()
|
||||
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False)
|
||||
self.bn1 = nn.BatchNorm2d(16)
|
||||
self.hs1 = hswish()
|
||||
|
||||
self.bneck = nn.Sequential(
|
||||
Block(3, 16, 16, 16, nn.ReLU(inplace=True), None, 1),
|
||||
Block(3, 16, 64, 24, nn.ReLU(inplace=True), None, 2),
|
||||
Block(3, 24, 72, 24, nn.ReLU(inplace=True), None, 1),
|
||||
Block(5, 24, 72, 40, nn.ReLU(inplace=True), SeModule(40), 2),
|
||||
Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1),
|
||||
Block(5, 40, 120, 40, nn.ReLU(inplace=True), SeModule(40), 1),
|
||||
Block(3, 40, 240, 80, hswish(), None, 2),
|
||||
Block(3, 80, 200, 80, hswish(), None, 1),
|
||||
Block(3, 80, 184, 80, hswish(), None, 1),
|
||||
Block(3, 80, 184, 80, hswish(), None, 1),
|
||||
Block(3, 80, 480, 112, hswish(), SeModule(112), 1),
|
||||
Block(3, 112, 672, 112, hswish(), SeModule(112), 1),
|
||||
Block(5, 112, 672, 160, hswish(), SeModule(160), 1),
|
||||
Block(5, 160, 672, 160, hswish(), SeModule(160), 2),
|
||||
Block(5, 160, 960, 160, hswish(), SeModule(160), 1),
|
||||
)
|
||||
|
||||
|
||||
self.conv2 = nn.Conv2d(160, 960, kernel_size=1, stride=1, padding=0, bias=False)
|
||||
self.bn2 = nn.BatchNorm2d(960)
|
||||
self.hs2 = hswish()
|
||||
self.linear3 = nn.Linear(960, 1280)
|
||||
self.bn3 = nn.BatchNorm1d(1280)
|
||||
self.hs3 = hswish()
|
||||
self.linear4 = nn.Linear(1280, num_classes)
|
||||
self.init_params()
|
||||
|
||||
def init_params(self):
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
init.kaiming_normal_(m.weight, mode='fan_out')
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
init.constant_(m.weight, 1)
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.Linear):
|
||||
init.normal_(m.weight, std=0.001)
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
|
||||
def forward(self, x):
|
||||
out = self.hs1(self.bn1(self.conv1(x)))
|
||||
out = self.bneck(out)
|
||||
out = self.hs2(self.bn2(self.conv2(out)))
|
||||
out = F.avg_pool2d(out, conf.img_size // 32)
|
||||
out = out.view(out.size(0), -1)
|
||||
out = self.hs3(self.bn3(self.linear3(out)))
|
||||
out = self.linear4(out)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
class MobileNetV3_Small(nn.Module):
|
||||
def __init__(self, num_classes=conf.embedding_size):
|
||||
super(MobileNetV3_Small, self).__init__()
|
||||
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=2, padding=1, bias=False)
|
||||
self.bn1 = nn.BatchNorm2d(16)
|
||||
self.hs1 = hswish()
|
||||
|
||||
self.bneck = nn.Sequential(
|
||||
Block(3, 16, 16, 16, nn.ReLU(inplace=True), SeModule(16), 2),
|
||||
Block(3, 16, 72, 24, nn.ReLU(inplace=True), None, 2),
|
||||
Block(3, 24, 88, 24, nn.ReLU(inplace=True), None, 1),
|
||||
Block(5, 24, 96, 40, hswish(), SeModule(40), 2),
|
||||
Block(5, 40, 240, 40, hswish(), SeModule(40), 1),
|
||||
Block(5, 40, 240, 40, hswish(), SeModule(40), 1),
|
||||
Block(5, 40, 120, 48, hswish(), SeModule(48), 1),
|
||||
Block(5, 48, 144, 48, hswish(), SeModule(48), 1),
|
||||
Block(5, 48, 288, 96, hswish(), SeModule(96), 2),
|
||||
Block(5, 96, 576, 96, hswish(), SeModule(96), 1),
|
||||
Block(5, 96, 576, 96, hswish(), SeModule(96), 1),
|
||||
)
|
||||
|
||||
|
||||
self.conv2 = nn.Conv2d(96, 576, kernel_size=1, stride=1, padding=0, bias=False)
|
||||
self.bn2 = nn.BatchNorm2d(576)
|
||||
self.hs2 = hswish()
|
||||
self.linear3 = nn.Linear(576, 1280)
|
||||
self.bn3 = nn.BatchNorm1d(1280)
|
||||
self.hs3 = hswish()
|
||||
self.linear4 = nn.Linear(1280, num_classes)
|
||||
self.init_params()
|
||||
|
||||
def init_params(self):
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
init.kaiming_normal_(m.weight, mode='fan_out')
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.BatchNorm2d):
|
||||
init.constant_(m.weight, 1)
|
||||
init.constant_(m.bias, 0)
|
||||
elif isinstance(m, nn.Linear):
|
||||
init.normal_(m.weight, std=0.001)
|
||||
if m.bias is not None:
|
||||
init.constant_(m.bias, 0)
|
||||
|
||||
def forward(self, x):
|
||||
out = self.hs1(self.bn1(self.conv1(x)))
|
||||
out = self.bneck(out)
|
||||
out = self.hs2(self.bn2(self.conv2(out)))
|
||||
out = F.avg_pool2d(out, conf.img_size // 32)
|
||||
out = out.view(out.size(0), -1)
|
||||
|
||||
out = self.hs3(self.bn3(self.linear3(out)))
|
||||
out = self.linear4(out)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
def test():
|
||||
net = MobileNetV3_Small()
|
||||
x = torch.randn(2,3,224,224)
|
||||
y = net(x)
|
||||
print(y.size())
|
||||
|
||||
# test()
|
265
tracking/trackers/reid/model/mobilevit.py
Normal file
265
tracking/trackers/reid/model/mobilevit.py
Normal file
@ -0,0 +1,265 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
from einops import rearrange
|
||||
from ..config import config as conf
|
||||
|
||||
|
||||
def conv_1x1_bn(inp, oup):
|
||||
return nn.Sequential(
|
||||
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
|
||||
nn.BatchNorm2d(oup),
|
||||
nn.SiLU()
|
||||
)
|
||||
|
||||
|
||||
def conv_nxn_bn(inp, oup, kernal_size=3, stride=1):
|
||||
return nn.Sequential(
|
||||
nn.Conv2d(inp, oup, kernal_size, stride, 1, bias=False),
|
||||
nn.BatchNorm2d(oup),
|
||||
nn.SiLU()
|
||||
)
|
||||
|
||||
|
||||
class PreNorm(nn.Module):
|
||||
def __init__(self, dim, fn):
|
||||
super().__init__()
|
||||
self.norm = nn.LayerNorm(dim)
|
||||
self.fn = fn
|
||||
|
||||
def forward(self, x, **kwargs):
|
||||
return self.fn(self.norm(x), **kwargs)
|
||||
|
||||
|
||||
class FeedForward(nn.Module):
|
||||
def __init__(self, dim, hidden_dim, dropout=0.):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.Linear(dim, hidden_dim),
|
||||
nn.SiLU(),
|
||||
nn.Dropout(dropout),
|
||||
nn.Linear(hidden_dim, dim),
|
||||
nn.Dropout(dropout)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x)
|
||||
|
||||
|
||||
class Attention(nn.Module):
|
||||
def __init__(self, dim, heads=8, dim_head=64, dropout=0.):
|
||||
super().__init__()
|
||||
inner_dim = dim_head * heads
|
||||
project_out = not (heads == 1 and dim_head == dim)
|
||||
|
||||
self.heads = heads
|
||||
self.scale = dim_head ** -0.5
|
||||
|
||||
self.attend = nn.Softmax(dim=-1)
|
||||
self.to_qkv = nn.Linear(dim, inner_dim * 3, bias=False)
|
||||
|
||||
self.to_out = nn.Sequential(
|
||||
nn.Linear(inner_dim, dim),
|
||||
nn.Dropout(dropout)
|
||||
) if project_out else nn.Identity()
|
||||
|
||||
def forward(self, x):
|
||||
qkv = self.to_qkv(x).chunk(3, dim=-1)
|
||||
q, k, v = map(lambda t: rearrange(t, 'b p n (h d) -> b p h n d', h=self.heads), qkv)
|
||||
|
||||
dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale
|
||||
attn = self.attend(dots)
|
||||
out = torch.matmul(attn, v)
|
||||
out = rearrange(out, 'b p h n d -> b p n (h d)')
|
||||
return self.to_out(out)
|
||||
|
||||
|
||||
class Transformer(nn.Module):
|
||||
def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout=0.):
|
||||
super().__init__()
|
||||
self.layers = nn.ModuleList([])
|
||||
for _ in range(depth):
|
||||
self.layers.append(nn.ModuleList([
|
||||
PreNorm(dim, Attention(dim, heads, dim_head, dropout)),
|
||||
PreNorm(dim, FeedForward(dim, mlp_dim, dropout))
|
||||
]))
|
||||
|
||||
def forward(self, x):
|
||||
for attn, ff in self.layers:
|
||||
x = attn(x) + x
|
||||
x = ff(x) + x
|
||||
return x
|
||||
|
||||
|
||||
class MV2Block(nn.Module):
|
||||
def __init__(self, inp, oup, stride=1, expansion=4):
|
||||
super().__init__()
|
||||
self.stride = stride
|
||||
assert stride in [1, 2]
|
||||
|
||||
hidden_dim = int(inp * expansion)
|
||||
self.use_res_connect = self.stride == 1 and inp == oup
|
||||
|
||||
if expansion == 1:
|
||||
self.conv = nn.Sequential(
|
||||
# dw
|
||||
nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
|
||||
nn.BatchNorm2d(hidden_dim),
|
||||
nn.SiLU(),
|
||||
# pw-linear
|
||||
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
|
||||
nn.BatchNorm2d(oup),
|
||||
)
|
||||
else:
|
||||
self.conv = nn.Sequential(
|
||||
# pw
|
||||
nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False),
|
||||
nn.BatchNorm2d(hidden_dim),
|
||||
nn.SiLU(),
|
||||
# dw
|
||||
nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False),
|
||||
nn.BatchNorm2d(hidden_dim),
|
||||
nn.SiLU(),
|
||||
# pw-linear
|
||||
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
|
||||
nn.BatchNorm2d(oup),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
if self.use_res_connect:
|
||||
return x + self.conv(x)
|
||||
else:
|
||||
return self.conv(x)
|
||||
|
||||
|
||||
class MobileViTBlock(nn.Module):
|
||||
def __init__(self, dim, depth, channel, kernel_size, patch_size, mlp_dim, dropout=0.):
|
||||
super().__init__()
|
||||
self.ph, self.pw = patch_size
|
||||
|
||||
self.conv1 = conv_nxn_bn(channel, channel, kernel_size)
|
||||
self.conv2 = conv_1x1_bn(channel, dim)
|
||||
|
||||
self.transformer = Transformer(dim, depth, 4, 8, mlp_dim, dropout)
|
||||
|
||||
self.conv3 = conv_1x1_bn(dim, channel)
|
||||
self.conv4 = conv_nxn_bn(2 * channel, channel, kernel_size)
|
||||
|
||||
def forward(self, x):
|
||||
y = x.clone()
|
||||
|
||||
# Local representations
|
||||
x = self.conv1(x)
|
||||
x = self.conv2(x)
|
||||
|
||||
# Global representations
|
||||
_, _, h, w = x.shape
|
||||
x = rearrange(x, 'b d (h ph) (w pw) -> b (ph pw) (h w) d', ph=self.ph, pw=self.pw)
|
||||
x = self.transformer(x)
|
||||
x = rearrange(x, 'b (ph pw) (h w) d -> b d (h ph) (w pw)', h=h // self.ph, w=w // self.pw, ph=self.ph,
|
||||
pw=self.pw)
|
||||
|
||||
# Fusion
|
||||
x = self.conv3(x)
|
||||
x = torch.cat((x, y), 1)
|
||||
x = self.conv4(x)
|
||||
return x
|
||||
|
||||
|
||||
class MobileViT(nn.Module):
|
||||
def __init__(self, image_size, dims, channels, num_classes, expansion=4, kernel_size=3, patch_size=(2, 2)):
|
||||
super().__init__()
|
||||
ih, iw = image_size
|
||||
ph, pw = patch_size
|
||||
assert ih % ph == 0 and iw % pw == 0
|
||||
|
||||
L = [2, 4, 3]
|
||||
|
||||
self.conv1 = conv_nxn_bn(3, channels[0], stride=2)
|
||||
|
||||
self.mv2 = nn.ModuleList([])
|
||||
self.mv2.append(MV2Block(channels[0], channels[1], 1, expansion))
|
||||
self.mv2.append(MV2Block(channels[1], channels[2], 2, expansion))
|
||||
self.mv2.append(MV2Block(channels[2], channels[3], 1, expansion))
|
||||
self.mv2.append(MV2Block(channels[2], channels[3], 1, expansion)) # Repeat
|
||||
self.mv2.append(MV2Block(channels[3], channels[4], 2, expansion))
|
||||
self.mv2.append(MV2Block(channels[5], channels[6], 2, expansion))
|
||||
self.mv2.append(MV2Block(channels[7], channels[8], 2, expansion))
|
||||
|
||||
self.mvit = nn.ModuleList([])
|
||||
self.mvit.append(MobileViTBlock(dims[0], L[0], channels[5], kernel_size, patch_size, int(dims[0] * 2)))
|
||||
self.mvit.append(MobileViTBlock(dims[1], L[1], channels[7], kernel_size, patch_size, int(dims[1] * 4)))
|
||||
self.mvit.append(MobileViTBlock(dims[2], L[2], channels[9], kernel_size, patch_size, int(dims[2] * 4)))
|
||||
|
||||
self.conv2 = conv_1x1_bn(channels[-2], channels[-1])
|
||||
|
||||
self.pool = nn.AvgPool2d(ih // 32, 1)
|
||||
self.fc = nn.Linear(channels[-1], num_classes, bias=False)
|
||||
|
||||
def forward(self, x):
|
||||
#print('x',x.shape)
|
||||
x = self.conv1(x)
|
||||
x = self.mv2[0](x)
|
||||
|
||||
x = self.mv2[1](x)
|
||||
x = self.mv2[2](x)
|
||||
x = self.mv2[3](x) # Repeat
|
||||
|
||||
x = self.mv2[4](x)
|
||||
x = self.mvit[0](x)
|
||||
|
||||
x = self.mv2[5](x)
|
||||
x = self.mvit[1](x)
|
||||
|
||||
x = self.mv2[6](x)
|
||||
x = self.mvit[2](x)
|
||||
x = self.conv2(x)
|
||||
|
||||
|
||||
#print('pool_before',x.shape)
|
||||
x = self.pool(x).view(-1, x.shape[1])
|
||||
#print('self_pool',self.pool)
|
||||
#print('pool_after',x.shape)
|
||||
x = self.fc(x)
|
||||
return x
|
||||
|
||||
|
||||
def mobilevit_xxs():
|
||||
dims = [64, 80, 96]
|
||||
channels = [16, 16, 24, 24, 48, 48, 64, 64, 80, 80, 320]
|
||||
return MobileViT((256, 256), dims, channels, num_classes=1000, expansion=2)
|
||||
|
||||
|
||||
def mobilevit_xs():
|
||||
dims = [96, 120, 144]
|
||||
channels = [16, 32, 48, 48, 64, 64, 80, 80, 96, 96, 384]
|
||||
return MobileViT((256, 256), dims, channels, num_classes=1000)
|
||||
|
||||
|
||||
def mobilevit_s():
|
||||
dims = [144, 192, 240]
|
||||
channels = [16, 32, 64, 64, 96, 96, 128, 128, 160, 160, 640]
|
||||
return MobileViT((conf.img_size, conf.img_size), dims, channels, num_classes=conf.embedding_size)
|
||||
|
||||
|
||||
def count_parameters(model):
|
||||
return sum(p.numel() for p in model.parameters() if p.requires_grad)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
img = torch.randn(5, 3, 256, 256)
|
||||
|
||||
vit = mobilevit_xxs()
|
||||
out = vit(img)
|
||||
print(out.shape)
|
||||
print(count_parameters(vit))
|
||||
|
||||
vit = mobilevit_xs()
|
||||
out = vit(img)
|
||||
print(out.shape)
|
||||
print(count_parameters(vit))
|
||||
|
||||
vit = mobilevit_s()
|
||||
out = vit(img)
|
||||
print(out.shape)
|
||||
print(count_parameters(vit))
|
134
tracking/trackers/reid/model/resbam.py
Normal file
134
tracking/trackers/reid/model/resbam.py
Normal file
@ -0,0 +1,134 @@
|
||||
from .CBAM import CBAM
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from .Tool import GeM as gem
|
||||
|
||||
class Bottleneck(nn.Module):
|
||||
expansion = 4
|
||||
def __init__(self, inchannel, outchannel,stride =1,dowsample=None):
|
||||
# super(Bottleneck, self).__init__()
|
||||
super().__init__()
|
||||
self.conv1 = nn.Conv2d(in_channels=inchannel,out_channels=outchannel, kernel_size=1, stride=1, bias=False)
|
||||
self.bn1 = nn.BatchNorm2d(outchannel)
|
||||
self.conv2 = nn.Conv2d(in_channels=outchannel, out_channels=outchannel,kernel_size=3,bias=False, stride=stride,padding=1)
|
||||
self.bn2 = nn.BatchNorm2d(outchannel)
|
||||
self.conv3 =nn.Conv2d(in_channels=outchannel, out_channels=outchannel*self.expansion,stride=1,bias=False,kernel_size=1)
|
||||
self.bn3 = nn.BatchNorm2d(outchannel*self.expansion)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = dowsample
|
||||
|
||||
def forward(self, x):
|
||||
self.identity = x
|
||||
# print('>>>>>>>>',type(x))
|
||||
if self.downsample is not None:
|
||||
# print('>>>>downsample>>>>', type(self.downsample))
|
||||
self.identity = self.downsample(x)
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
out = self.relu(out)
|
||||
out = self.conv3(out)
|
||||
out = self.bn3(out)
|
||||
# print('>>>>out>>>identity',out.size(),self.identity.size())
|
||||
out = out+self.identity
|
||||
out = self.relu(out)
|
||||
return out
|
||||
|
||||
class resnet(nn.Module):
|
||||
def __init__(self,block=Bottleneck, block_num=[3,4,6,3], num_class=1000):
|
||||
super().__init__()
|
||||
self.in_channel = 64
|
||||
self.conv1 = nn.Conv2d(in_channels=3,
|
||||
out_channels=self.in_channel,
|
||||
stride=2,
|
||||
kernel_size=7,
|
||||
padding=3,
|
||||
bias=False)
|
||||
self.bn1 = nn.BatchNorm2d(self.in_channel)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.cbam = CBAM(self.in_channel)
|
||||
self.cbam1 = CBAM(2048)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
self.layer1 = self._make_layer(block, 64, block_num[0],stride=1)
|
||||
self.layer2 = self._make_layer(block, 128, block_num[1],stride=2)
|
||||
self.layer3 = self._make_layer(block, 256, block_num[2],stride=2)
|
||||
self.layer4 = self._make_layer(block, 512, block_num[3],stride=2)
|
||||
self.avgpool = nn.AdaptiveAvgPool2d((1,1))
|
||||
self.gem = gem()
|
||||
self.fc = nn.Linear(512*block.expansion, num_class)
|
||||
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal(m.weight,mode = 'fan_out',
|
||||
nonlinearity='relu')
|
||||
if isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
|
||||
nn.init.constant_(m.weight, 1.0)
|
||||
nn.init.constant_(m.bias, 1.0)
|
||||
|
||||
def _make_layer(self,block ,channel, block_num, stride=1):
|
||||
downsample = None
|
||||
if stride !=1 or self.in_channel != channel*block.expansion:
|
||||
downsample = nn.Sequential(
|
||||
nn.Conv2d(self.in_channel, channel*block.expansion,kernel_size=1,stride=stride,bias=False),
|
||||
nn.BatchNorm2d(channel*block.expansion))
|
||||
layer = []
|
||||
layer.append(block(self.in_channel, channel, stride, downsample))
|
||||
self.in_channel = channel*block.expansion
|
||||
for _ in range(1, block_num):
|
||||
layer.append(block(self.in_channel, channel))
|
||||
return nn.Sequential(*layer)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
x = self.cbam(x)
|
||||
|
||||
x = self.layer1(x)
|
||||
x = self.layer2(x)
|
||||
x = self.layer3(x)
|
||||
x = self.layer4(x)
|
||||
|
||||
x = self.cbam1(x)
|
||||
# x = self.avgpool(x)
|
||||
x = self.gem(x)
|
||||
x = torch.flatten(x, 1)
|
||||
x = self.fc(x)
|
||||
return x
|
||||
|
||||
class TripletNet(nn.Module):
|
||||
def __init__(self, num_class, flag=True):
|
||||
super(TripletNet, self).__init__()
|
||||
self.initnet = rescbam(num_class)
|
||||
self.flag = flag
|
||||
|
||||
def forward(self, x1, x2=None, x3=None):
|
||||
if self.flag:
|
||||
output1 = self.initnet(x1)
|
||||
output2 = self.initnet(x2)
|
||||
output3 = self.initnet(x3)
|
||||
return output1, output2, output3
|
||||
else:
|
||||
output = self.initnet(x1)
|
||||
return output
|
||||
|
||||
def rescbam(num_class):
|
||||
return resnet(block=Bottleneck, block_num=[3,4,6,3],num_class=num_class)
|
||||
|
||||
if __name__ =='__main__':
|
||||
input1 = torch.randn(4,3,640,640)
|
||||
input2 = torch.randn(4,3,640,640)
|
||||
input3 = torch.randn(4,3,640,640)
|
||||
|
||||
#rescbam测试
|
||||
# Resnet50 = rescbam(512)
|
||||
# output = Resnet50.forward(input1)
|
||||
# print(Resnet50)
|
||||
|
||||
#trnet测试
|
||||
trnet = TripletNet(512)
|
||||
output = trnet(input1, input2, input3)
|
||||
print(output)
|
182
tracking/trackers/reid/model/resnet.py
Normal file
182
tracking/trackers/reid/model/resnet.py
Normal file
@ -0,0 +1,182 @@
|
||||
"""resnet in pytorch
|
||||
|
||||
|
||||
|
||||
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun.
|
||||
|
||||
Deep Residual Learning for Image Recognition
|
||||
https://arxiv.org/abs/1512.03385v1
|
||||
"""
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from config import config as conf
|
||||
|
||||
class BasicBlock(nn.Module):
|
||||
"""Basic Block for resnet 18 and resnet 34
|
||||
|
||||
"""
|
||||
|
||||
#BasicBlock and BottleNeck block
|
||||
#have different output size
|
||||
#we use class attribute expansion
|
||||
#to distinct
|
||||
expansion = 1
|
||||
|
||||
def __init__(self, in_channels, out_channels, stride=1):
|
||||
super().__init__()
|
||||
|
||||
#residual function
|
||||
self.residual_function = nn.Sequential(
|
||||
nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(out_channels, out_channels * BasicBlock.expansion, kernel_size=3, padding=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels * BasicBlock.expansion)
|
||||
)
|
||||
|
||||
#shortcut
|
||||
self.shortcut = nn.Sequential()
|
||||
|
||||
#the shortcut output dimension is not the same with residual function
|
||||
#use 1*1 convolution to match the dimension
|
||||
if stride != 1 or in_channels != BasicBlock.expansion * out_channels:
|
||||
self.shortcut = nn.Sequential(
|
||||
nn.Conv2d(in_channels, out_channels * BasicBlock.expansion, kernel_size=1, stride=stride, bias=False),
|
||||
nn.BatchNorm2d(out_channels * BasicBlock.expansion)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return nn.ReLU(inplace=True)(self.residual_function(x) + self.shortcut(x))
|
||||
|
||||
class BottleNeck(nn.Module):
|
||||
"""Residual block for resnet over 50 layers
|
||||
|
||||
"""
|
||||
expansion = 4
|
||||
def __init__(self, in_channels, out_channels, stride=1):
|
||||
super().__init__()
|
||||
self.residual_function = nn.Sequential(
|
||||
nn.Conv2d(in_channels, out_channels, kernel_size=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(out_channels, out_channels, stride=stride, kernel_size=3, padding=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(out_channels, out_channels * BottleNeck.expansion, kernel_size=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels * BottleNeck.expansion),
|
||||
)
|
||||
|
||||
self.shortcut = nn.Sequential()
|
||||
|
||||
if stride != 1 or in_channels != out_channels * BottleNeck.expansion:
|
||||
self.shortcut = nn.Sequential(
|
||||
nn.Conv2d(in_channels, out_channels * BottleNeck.expansion, stride=stride, kernel_size=1, bias=False),
|
||||
nn.BatchNorm2d(out_channels * BottleNeck.expansion)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return nn.ReLU(inplace=True)(self.residual_function(x) + self.shortcut(x))
|
||||
|
||||
class ResNet(nn.Module):
|
||||
|
||||
def __init__(self, block, num_block, num_classes=conf.embedding_size):
|
||||
super().__init__()
|
||||
|
||||
self.in_channels = 64
|
||||
|
||||
# self.conv1 = nn.Sequential(
|
||||
# nn.Conv2d(3, 64, kernel_size=3, padding=1, bias=False),
|
||||
# nn.BatchNorm2d(64),
|
||||
# nn.ReLU(inplace=True))
|
||||
|
||||
self.conv1 = nn.Sequential(
|
||||
nn.Conv2d(3, 64,stride=2,kernel_size=7,padding=3,bias=False),
|
||||
nn.BatchNorm2d(64),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
|
||||
|
||||
|
||||
#we use a different inputsize than the original paper
|
||||
#so conv2_x's stride is 1
|
||||
self.conv2_x = self._make_layer(block, 64, num_block[0], 1)
|
||||
self.conv3_x = self._make_layer(block, 128, num_block[1], 2)
|
||||
self.conv4_x = self._make_layer(block, 256, num_block[2], 2)
|
||||
self.conv5_x = self._make_layer(block, 512, num_block[3], 2)
|
||||
self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
|
||||
self.fc = nn.Linear(512 * block.expansion, num_classes)
|
||||
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal(m.weight,mode = 'fan_out',
|
||||
nonlinearity='relu')
|
||||
if isinstance(m, (nn.BatchNorm2d)):
|
||||
nn.init.constant_(m.weight, 1.0)
|
||||
nn.init.constant_(m.bias, 1.0)
|
||||
|
||||
def _make_layer(self, block, out_channels, num_blocks, stride):
|
||||
"""make resnet layers(by layer i didnt mean this 'layer' was the
|
||||
same as a neuron netowork layer, ex. conv layer), one layer may
|
||||
contain more than one residual block
|
||||
|
||||
Args:
|
||||
block: block type, basic block or bottle neck block
|
||||
out_channels: output depth channel number of this layer
|
||||
num_blocks: how many blocks per layer
|
||||
stride: the stride of the first block of this layer
|
||||
|
||||
Return:
|
||||
return a resnet layer
|
||||
"""
|
||||
|
||||
# we have num_block blocks per layer, the first block
|
||||
# could be 1 or 2, other blocks would always be 1
|
||||
strides = [stride] + [1] * (num_blocks - 1)
|
||||
layers = []
|
||||
for stride in strides:
|
||||
layers.append(block(self.in_channels, out_channels, stride))
|
||||
self.in_channels = out_channels * block.expansion
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x):
|
||||
output = self.conv1(x)
|
||||
output = self.conv2_x(output)
|
||||
output = self.conv3_x(output)
|
||||
output = self.conv4_x(output)
|
||||
output = self.conv5_x(output)
|
||||
print('pollBefore',output.shape)
|
||||
output = self.avg_pool(output)
|
||||
print('poolAfter',output.shape)
|
||||
output = output.view(output.size(0), -1)
|
||||
print('fcBefore',output.shape)
|
||||
output = self.fc(output)
|
||||
|
||||
return output
|
||||
|
||||
def resnet18():
|
||||
""" return a ResNet 18 object
|
||||
"""
|
||||
return ResNet(BasicBlock, [2, 2, 2, 2])
|
||||
|
||||
def resnet34():
|
||||
""" return a ResNet 34 object
|
||||
"""
|
||||
return ResNet(BasicBlock, [3, 4, 6, 3])
|
||||
|
||||
def resnet50():
|
||||
""" return a ResNet 50 object
|
||||
"""
|
||||
return ResNet(BottleNeck, [3, 4, 6, 3])
|
||||
|
||||
def resnet101():
|
||||
""" return a ResNet 101 object
|
||||
"""
|
||||
return ResNet(BottleNeck, [3, 4, 23, 3])
|
||||
|
||||
def resnet152():
|
||||
""" return a ResNet 152 object
|
||||
"""
|
||||
return ResNet(BottleNeck, [3, 8, 36, 3])
|
||||
|
||||
|
120
tracking/trackers/reid/model/resnet_face.py
Normal file
120
tracking/trackers/reid/model/resnet_face.py
Normal file
@ -0,0 +1,120 @@
|
||||
""" Resnet_IR_SE in ArcFace """
|
||||
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
class Flatten(nn.Module):
|
||||
def forward(self, x):
|
||||
return x.reshape(x.shape[0], -1)
|
||||
|
||||
|
||||
class SEConv(nn.Module):
|
||||
"""Use Convolution instead of FullyConnection in SE"""
|
||||
|
||||
def __init__(self, channels, reduction):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.AdaptiveAvgPool2d(1),
|
||||
nn.Conv2d(channels, channels // reduction, kernel_size=1, bias=False),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Conv2d(channels // reduction, channels, kernel_size=1, bias=False),
|
||||
nn.Sigmoid(),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x) * x
|
||||
|
||||
|
||||
class SE(nn.Module):
|
||||
|
||||
def __init__(self, channels, reduction):
|
||||
super().__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.AdaptiveAvgPool2d(1),
|
||||
nn.Linear(channels, channels // reduction),
|
||||
nn.ReLU(inplace=True),
|
||||
nn.Linear(channels // reduction, channels),
|
||||
nn.Sigmoid(),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.net(x) * x
|
||||
|
||||
|
||||
class IRSE(nn.Module):
|
||||
|
||||
def __init__(self, channels, depth, stride):
|
||||
super().__init__()
|
||||
if channels == depth:
|
||||
self.shortcut = nn.MaxPool2d(kernel_size=1, stride=stride)
|
||||
else:
|
||||
self.shortcut = nn.Sequential(
|
||||
nn.Conv2d(channels, depth, (1, 1), stride, bias=False),
|
||||
nn.BatchNorm2d(depth),
|
||||
)
|
||||
self.residual = nn.Sequential(
|
||||
nn.BatchNorm2d(channels),
|
||||
nn.Conv2d(channels, depth, (3, 3), 1, 1, bias=False),
|
||||
nn.PReLU(depth),
|
||||
nn.Conv2d(depth, depth, (3, 3), stride, 1, bias=False),
|
||||
nn.BatchNorm2d(depth),
|
||||
SEConv(depth, 16),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
return self.shortcut(x) + self.residual(x)
|
||||
|
||||
class ResIRSE(nn.Module):
|
||||
"""Resnet50-IRSE backbone"""
|
||||
|
||||
def __init__(self, ih,embedding_size, drop_ratio):
|
||||
super().__init__()
|
||||
ih_last = ih // 16
|
||||
self.input_layer = nn.Sequential(
|
||||
nn.Conv2d(3, 64, (3, 3), 1, 1, bias=False),
|
||||
nn.BatchNorm2d(64),
|
||||
nn.PReLU(64),
|
||||
)
|
||||
self.output_layer = nn.Sequential(
|
||||
nn.BatchNorm2d(512),
|
||||
nn.Dropout(drop_ratio),
|
||||
Flatten(),
|
||||
nn.Linear(512 * ih_last * ih_last, embedding_size),
|
||||
nn.BatchNorm1d(embedding_size),
|
||||
)
|
||||
|
||||
# ["channels", "depth", "stride"],
|
||||
self.res50_arch = [
|
||||
[64, 64, 2], [64, 64, 1], [64, 64, 1],
|
||||
[64, 128, 2], [128, 128, 1], [128, 128, 1], [128, 128, 1],
|
||||
[128, 256, 2], [256, 256, 1], [256, 256, 1], [256, 256, 1], [256, 256, 1],
|
||||
[256, 256, 1], [256, 256, 1], [256, 256, 1], [256, 256, 1], [256, 256, 1],
|
||||
[256, 256, 1], [256, 256, 1], [256, 256, 1], [256, 256, 1],
|
||||
[256, 512, 2], [512, 512, 1], [512, 512, 1],
|
||||
]
|
||||
|
||||
self.body = nn.Sequential(*[ IRSE(a,b,c) for (a,b,c) in self.res50_arch ])
|
||||
|
||||
def forward(self, x):
|
||||
x = self.input_layer(x)
|
||||
x = self.body(x)
|
||||
x = self.output_layer(x)
|
||||
return x
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
x = Image.open("../samples/009.jpg").convert('L')
|
||||
x = x.resize((128, 128))
|
||||
x = np.asarray(x, dtype=np.float32)
|
||||
x = x[None, None, ...]
|
||||
x = torch.from_numpy(x)
|
||||
net = ResIRSE(512, 0.6)
|
||||
net.eval()
|
||||
with torch.no_grad():
|
||||
out = net(x)
|
||||
print(out.shape)
|
384
tracking/trackers/reid/model/resnet_pre.py
Normal file
384
tracking/trackers/reid/model/resnet_pre.py
Normal file
@ -0,0 +1,384 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
# from config import config as conf
|
||||
from ..config import config as conf
|
||||
|
||||
try:
|
||||
from torch.hub import load_state_dict_from_url
|
||||
except ImportError:
|
||||
from torch.utils.model_zoo import load_url as load_state_dict_from_url
|
||||
#from .utils import load_state_dict_from_url
|
||||
|
||||
|
||||
__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',
|
||||
'resnet152', 'resnext50_32x4d', 'resnext101_32x8d',
|
||||
'wide_resnet50_2', 'wide_resnet101_2']
|
||||
|
||||
|
||||
model_urls = {
|
||||
'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
|
||||
'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
|
||||
'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
|
||||
'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
|
||||
'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
|
||||
'resnext50_32x4d': 'https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth',
|
||||
'resnext101_32x8d': 'https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth',
|
||||
'wide_resnet50_2': 'https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth',
|
||||
'wide_resnet101_2': 'https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth',
|
||||
}
|
||||
|
||||
|
||||
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
|
||||
"""3x3 convolution with padding"""
|
||||
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
|
||||
padding=dilation, groups=groups, bias=False, dilation=dilation)
|
||||
|
||||
|
||||
def conv1x1(in_planes, out_planes, stride=1):
|
||||
"""1x1 convolution"""
|
||||
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
|
||||
|
||||
|
||||
class BasicBlock(nn.Module):
|
||||
expansion = 1
|
||||
|
||||
def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
|
||||
base_width=64, dilation=1, norm_layer=None):
|
||||
super(BasicBlock, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
if groups != 1 or base_width != 64:
|
||||
raise ValueError('BasicBlock only supports groups=1 and base_width=64')
|
||||
if dilation > 1:
|
||||
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")
|
||||
# Both self.conv1 and self.downsample layers downsample the input when stride != 1
|
||||
self.conv1 = conv3x3(inplanes, planes, stride)
|
||||
self.bn1 = norm_layer(planes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.conv2 = conv3x3(planes, planes)
|
||||
self.bn2 = norm_layer(planes)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
|
||||
def forward(self, x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Bottleneck(nn.Module):
|
||||
# Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)
|
||||
# while original implementation places the stride at the first 1x1 convolution(self.conv1)
|
||||
# according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385.
|
||||
# This variant is also known as ResNet V1.5 and improves accuracy according to
|
||||
# https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.
|
||||
|
||||
expansion = 4
|
||||
|
||||
def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
|
||||
base_width=64, dilation=1, norm_layer=None):
|
||||
super(Bottleneck, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
width = int(planes * (base_width / 64.)) * groups
|
||||
# Both self.conv2 and self.downsample layers downsample the input when stride != 1
|
||||
self.conv1 = conv1x1(inplanes, width)
|
||||
self.bn1 = norm_layer(width)
|
||||
self.conv2 = conv3x3(width, width, stride, groups, dilation)
|
||||
self.bn2 = norm_layer(width)
|
||||
self.conv3 = conv1x1(width, planes * self.expansion)
|
||||
self.bn3 = norm_layer(planes * self.expansion)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
|
||||
def forward(self, x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.bn3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class ResNet(nn.Module):
|
||||
|
||||
def __init__(self, block, layers, num_classes=conf.embedding_size, zero_init_residual=False,
|
||||
groups=1, width_per_group=64, replace_stride_with_dilation=None,
|
||||
norm_layer=None):
|
||||
super(ResNet, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
self._norm_layer = norm_layer
|
||||
|
||||
self.inplanes = 64
|
||||
self.dilation = 1
|
||||
if replace_stride_with_dilation is None:
|
||||
# each element in the tuple indicates if we should replace
|
||||
# the 2x2 stride with a dilated convolution instead
|
||||
replace_stride_with_dilation = [False, False, False]
|
||||
if len(replace_stride_with_dilation) != 3:
|
||||
raise ValueError("replace_stride_with_dilation should be None "
|
||||
"or a 3-element tuple, got {}".format(replace_stride_with_dilation))
|
||||
self.groups = groups
|
||||
self.base_width = width_per_group
|
||||
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,
|
||||
bias=False)
|
||||
self.bn1 = norm_layer(self.inplanes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
self.layer1 = self._make_layer(block, 64, layers[0])
|
||||
self.layer2 = self._make_layer(block, 128, layers[1], stride=2,
|
||||
dilate=replace_stride_with_dilation[0])
|
||||
self.layer3 = self._make_layer(block, 256, layers[2], stride=2,
|
||||
dilate=replace_stride_with_dilation[1])
|
||||
self.layer4 = self._make_layer(block, 512, layers[3], stride=2,
|
||||
dilate=replace_stride_with_dilation[2])
|
||||
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
|
||||
self.fc = nn.Linear(512 * block.expansion, num_classes)
|
||||
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
|
||||
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
|
||||
nn.init.constant_(m.weight, 1)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
|
||||
# Zero-initialize the last BN in each residual branch,
|
||||
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
|
||||
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
|
||||
if zero_init_residual:
|
||||
for m in self.modules():
|
||||
if isinstance(m, Bottleneck):
|
||||
nn.init.constant_(m.bn3.weight, 0)
|
||||
elif isinstance(m, BasicBlock):
|
||||
nn.init.constant_(m.bn2.weight, 0)
|
||||
|
||||
def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
|
||||
norm_layer = self._norm_layer
|
||||
downsample = None
|
||||
previous_dilation = self.dilation
|
||||
if dilate:
|
||||
self.dilation *= stride
|
||||
stride = 1
|
||||
if stride != 1 or self.inplanes != planes * block.expansion:
|
||||
downsample = nn.Sequential(
|
||||
conv1x1(self.inplanes, planes * block.expansion, stride),
|
||||
norm_layer(planes * block.expansion),
|
||||
)
|
||||
|
||||
layers = []
|
||||
layers.append(block(self.inplanes, planes, stride, downsample, self.groups,
|
||||
self.base_width, previous_dilation, norm_layer))
|
||||
self.inplanes = planes * block.expansion
|
||||
for _ in range(1, blocks):
|
||||
layers.append(block(self.inplanes, planes, groups=self.groups,
|
||||
base_width=self.base_width, dilation=self.dilation,
|
||||
norm_layer=norm_layer))
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _forward_impl(self, x):
|
||||
# See note [TorchScript super()]
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
|
||||
x = self.layer1(x)
|
||||
x = self.layer2(x)
|
||||
x = self.layer3(x)
|
||||
x = self.layer4(x)
|
||||
|
||||
#print('poolBefore', x.shape)
|
||||
x = self.avgpool(x)
|
||||
#print('poolAfter', x.shape)
|
||||
x = torch.flatten(x, 1)
|
||||
#print('fcBefore',x.shape)
|
||||
x = self.fc(x)
|
||||
# print('fcAfter',x.shape)
|
||||
|
||||
return x
|
||||
|
||||
def forward(self, x):
|
||||
return self._forward_impl(x)
|
||||
|
||||
|
||||
# def _resnet(arch, block, layers, pretrained, progress, **kwargs):
|
||||
# model = ResNet(block, layers, **kwargs)
|
||||
# if pretrained:
|
||||
# state_dict = load_state_dict_from_url(model_urls[arch],
|
||||
# progress=progress)
|
||||
# model.load_state_dict(state_dict, strict=False)
|
||||
# return model
|
||||
def _resnet(arch, block, layers, pretrained, progress, **kwargs):
|
||||
model = ResNet(block, layers, **kwargs)
|
||||
if pretrained:
|
||||
state_dict = load_state_dict_from_url(model_urls[arch],
|
||||
progress=progress)
|
||||
#print('state_dict',state_dict)
|
||||
src_state_dict = state_dict
|
||||
target_state_dict = model.state_dict()
|
||||
skip_keys = []
|
||||
# skip mismatch size tensors in case of pretraining
|
||||
for k in src_state_dict.keys():
|
||||
if k not in target_state_dict:
|
||||
continue
|
||||
if src_state_dict[k].size() != target_state_dict[k].size():
|
||||
skip_keys.append(k)
|
||||
for k in skip_keys:
|
||||
del src_state_dict[k]
|
||||
missing_keys, unexpected_keys = model.load_state_dict(src_state_dict, strict=False)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def resnet18(pretrained=True, progress=True, **kwargs):
|
||||
r"""ResNet-18 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet34(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-34 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet50(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-50 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet101(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-101 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet152(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-152 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnext50_32x4d(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNeXt-50 32x4d model from
|
||||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width_per_group'] = 4
|
||||
return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def resnext101_32x8d(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNeXt-101 32x8d model from
|
||||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width_per_group'] = 8
|
||||
return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def wide_resnet50_2(pretrained=False, progress=True, **kwargs):
|
||||
r"""Wide ResNet-50-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_
|
||||
|
||||
The model is the same as ResNet except for the bottleneck number of channels
|
||||
which is twice larger in every block. The number of channels in outer 1x1
|
||||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
|
||||
channels, and in Wide ResNet-50-2 has 2048-1024-2048.
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['width_per_group'] = 64 * 2
|
||||
return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def wide_resnet101_2(pretrained=False, progress=True, **kwargs):
|
||||
r"""Wide ResNet-101-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_
|
||||
|
||||
The model is the same as ResNet except for the bottleneck number of channels
|
||||
which is twice larger in every block. The number of channels in outer 1x1
|
||||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
|
||||
channels, and in Wide ResNet-50-2 has 2048-1024-2048.
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['width_per_group'] = 64 * 2
|
||||
return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3],
|
||||
pretrained, progress, **kwargs)
|
4
tracking/trackers/reid/model/utils.py
Normal file
4
tracking/trackers/reid/model/utils.py
Normal file
@ -0,0 +1,4 @@
|
||||
try:
|
||||
from torch.hub import load_state_dict_from_url
|
||||
except ImportError:
|
||||
from torch.utils.model_zoo import load_url as load_state_dict_from_url
|
144
tracking/trackers/reid/reid_interface.py
Normal file
144
tracking/trackers/reid/reid_interface.py
Normal file
@ -0,0 +1,144 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu Jan 18 17:21:01 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import numpy as np
|
||||
import torch
|
||||
import cv2
|
||||
import torch.nn as nn
|
||||
import torchvision.transforms as T
|
||||
from .model import mobilevit_s, resnet18, resnet34, resnet50, mobilenet_v2, MobileNetV3_Small
|
||||
from .config import config as conf
|
||||
|
||||
|
||||
class ReIDInterface:
|
||||
def __init__(self, config):
|
||||
self.device = conf.device
|
||||
if conf.backbone == 'resnet18':
|
||||
# model = ResIRSE(img_size, embedding_size, conf.drop_ratio).to(device)
|
||||
model = resnet18().to(self.device)
|
||||
elif conf.backbone == 'resnet34':
|
||||
model = resnet34().to(self.device)
|
||||
elif conf.backbone == 'resnet50':
|
||||
model = resnet50().to(self.device)
|
||||
elif conf.backbone == 'mobilevit_s':
|
||||
model = mobilevit_s().to(self.device)
|
||||
elif conf.backbone == 'mobilenetv3':
|
||||
model = MobileNetV3_Small().to(self.device)
|
||||
else:
|
||||
model = mobilenet_v2().to(self.device)
|
||||
|
||||
self.batch_size = conf.batch_size
|
||||
self.embedding_size = conf.embedding_size
|
||||
self.img_size = conf.img_size
|
||||
|
||||
self.model_path = conf.model_path
|
||||
|
||||
# 原输入为PIL
|
||||
self.transform = T.Compose([
|
||||
T.ToTensor(),
|
||||
T.Resize((self.img_size, self.img_size)),
|
||||
T.ConvertImageDtype(torch.float32),
|
||||
T.Normalize(mean=[0.5], std=[0.5]),
|
||||
])
|
||||
|
||||
|
||||
# self.model = nn.DataParallel(model).to(self.device)
|
||||
self.model = model
|
||||
self.model.load_state_dict(torch.load(self.model_path, map_location=self.device))
|
||||
self.model.eval()
|
||||
|
||||
def inference(self, images, detections):
|
||||
if isinstance(images, np.ndarray):
|
||||
features = self.inference_image(images, detections)
|
||||
return features
|
||||
|
||||
batch_patches = []
|
||||
patches = []
|
||||
for i, img in enumerate(images):
|
||||
img = img.copy()
|
||||
patch = self.transform(img)
|
||||
if str(self.device) != "cpu":
|
||||
patch = patch.to(device=self.device).half()
|
||||
else:
|
||||
patch = patch.to(device=self.device)
|
||||
|
||||
patches.append(patch)
|
||||
if (i + 1) % self.batch_size == 0:
|
||||
patches = torch.stack(patches, dim=0)
|
||||
batch_patches.append(patches)
|
||||
patches = []
|
||||
|
||||
if len(patches):
|
||||
patches = torch.stack(patches, dim=0)
|
||||
batch_patches.append(patches)
|
||||
|
||||
features = np.zeros((0, self.embedding_size))
|
||||
for patches in batch_patches:
|
||||
pred=self.model(patches)
|
||||
pred[torch.isinf(pred)] = 1.0
|
||||
feat = pred.cpu().data.numpy()
|
||||
features = np.vstack((features, feat))
|
||||
return features
|
||||
|
||||
def inference_image(self, image, detections):
|
||||
H, W, _ = np.shape(image)
|
||||
|
||||
batch_patches = []
|
||||
patches = []
|
||||
for d in range(np.size(detections, 0)):
|
||||
tlbr = detections[d, :4].astype(np.int_)
|
||||
tlbr[0] = max(0, tlbr[0])
|
||||
tlbr[1] = max(0, tlbr[1])
|
||||
tlbr[2] = min(W - 1, tlbr[2])
|
||||
tlbr[3] = min(H - 1, tlbr[3])
|
||||
img = image[tlbr[1]:tlbr[3], tlbr[0]:tlbr[2], :]
|
||||
|
||||
img = img[:, :, ::-1].copy() # the model expects RGB inputs
|
||||
patch = self.transform(img)
|
||||
|
||||
# patch = patch.to(device=self.device).half()
|
||||
if str(self.device) != "cpu":
|
||||
patch = patch.to(device=self.device).half()
|
||||
else:
|
||||
patch = patch.to(device=self.device)
|
||||
|
||||
patches.append(patch)
|
||||
if (d + 1) % self.batch_size == 0:
|
||||
patches = torch.stack(patches, dim=0)
|
||||
batch_patches.append(patches)
|
||||
patches = []
|
||||
|
||||
if len(patches):
|
||||
patches = torch.stack(patches, dim=0)
|
||||
batch_patches.append(patches)
|
||||
|
||||
features = np.zeros((0, self.embedding_size))
|
||||
for patches in batch_patches:
|
||||
pred = self.model(patches)
|
||||
pred[torch.isinf(pred)] = 1.0
|
||||
feat = pred.cpu().data.numpy()
|
||||
features = np.vstack((features, feat))
|
||||
|
||||
return features
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
462
tracking/trackers/reid/resnet_pre_lc.py
Normal file
462
tracking/trackers/reid/resnet_pre_lc.py
Normal file
@ -0,0 +1,462 @@
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
from tools.config import config as conf
|
||||
|
||||
try:
|
||||
from torch.hub import load_state_dict_from_url
|
||||
except ImportError:
|
||||
from torch.utils.model_zoo import load_url as load_state_dict_from_url
|
||||
# from .utils import load_state_dict_from_url
|
||||
|
||||
__all__ = ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101',
|
||||
'resnet152', 'resnext50_32x4d', 'resnext101_32x8d',
|
||||
'wide_resnet50_2', 'wide_resnet101_2']
|
||||
|
||||
model_urls = {
|
||||
'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
|
||||
'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
|
||||
'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
|
||||
'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
|
||||
'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
|
||||
'resnext50_32x4d': 'https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pth',
|
||||
'resnext101_32x8d': 'https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pth',
|
||||
'wide_resnet50_2': 'https://download.pytorch.org/models/wide_resnet50_2-95faca4d.pth',
|
||||
'wide_resnet101_2': 'https://download.pytorch.org/models/wide_resnet101_2-32ee1156.pth',
|
||||
}
|
||||
|
||||
|
||||
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
|
||||
"""3x3 convolution with padding"""
|
||||
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
|
||||
padding=dilation, groups=groups, bias=False, dilation=dilation)
|
||||
|
||||
def conv1x1(in_planes, out_planes, stride=1):
|
||||
"""1x1 convolution"""
|
||||
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
|
||||
|
||||
|
||||
class SpatialAttention(nn.Module):
|
||||
def __init__(self, kernel_size=7):
|
||||
super(SpatialAttention, self).__init__()
|
||||
|
||||
assert kernel_size in (3, 7), 'kernel size must be 3 or 7'
|
||||
padding = 3 if kernel_size == 7 else 1
|
||||
|
||||
self.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
|
||||
self.sigmoid = nn.Sigmoid()
|
||||
|
||||
def forward(self, x):
|
||||
avg_out = torch.mean(x, dim=1, keepdim=True)
|
||||
max_out, _ = torch.max(x, dim=1, keepdim=True)
|
||||
x = torch.cat([avg_out, max_out], dim=1)
|
||||
x = self.conv1(x)
|
||||
return self.sigmoid(x)
|
||||
|
||||
class BasicBlock(nn.Module):
|
||||
expansion = 1
|
||||
|
||||
def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
|
||||
base_width=64, dilation=1, norm_layer=None, cam=False, bam=False):
|
||||
super(BasicBlock, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
if groups != 1 or base_width != 64:
|
||||
raise ValueError('BasicBlock only supports groups=1 and base_width=64')
|
||||
if dilation > 1:
|
||||
raise NotImplementedError("Dilation > 1 not supported in BasicBlock")
|
||||
self.cam = cam
|
||||
self.bam = bam
|
||||
# Both self.conv1 and self.downsample layers downsample the input when stride != 1
|
||||
self.conv1 = conv3x3(inplanes, planes, stride)
|
||||
self.bn1 = norm_layer(planes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.conv2 = conv3x3(planes, planes)
|
||||
self.bn2 = norm_layer(planes)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
if self.cam:
|
||||
if planes == 64:
|
||||
self.globalAvgPool = nn.AvgPool2d(56, stride=1)
|
||||
elif planes == 128:
|
||||
self.globalAvgPool = nn.AvgPool2d(28, stride=1)
|
||||
elif planes == 256:
|
||||
self.globalAvgPool = nn.AvgPool2d(14, stride=1)
|
||||
elif planes == 512:
|
||||
self.globalAvgPool = nn.AvgPool2d(7, stride=1)
|
||||
|
||||
self.fc1 = nn.Linear(in_features=planes, out_features=round(planes / 16))
|
||||
self.fc2 = nn.Linear(in_features=round(planes / 16), out_features=planes)
|
||||
self.sigmod = nn.Sigmoid()
|
||||
if self.bam:
|
||||
self.bam = SpatialAttention()
|
||||
|
||||
def forward(self, x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
if self.cam:
|
||||
ori_out = self.globalAvgPool(out)
|
||||
out = out.view(out.size(0), -1)
|
||||
out = self.fc1(out)
|
||||
out = self.relu(out)
|
||||
out = self.fc2(out)
|
||||
out = self.sigmod(out)
|
||||
out = out.view(out.size(0), out.size(-1), 1, 1)
|
||||
out = out * ori_out
|
||||
|
||||
if self.bam:
|
||||
out = out*self.bam(out)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class Bottleneck(nn.Module):
|
||||
# Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)
|
||||
# while original implementation places the stride at the first 1x1 convolution(self.conv1)
|
||||
# according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385.
|
||||
# This variant is also known as ResNet V1.5 and improves accuracy according to
|
||||
# https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.
|
||||
|
||||
expansion = 4
|
||||
|
||||
def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
|
||||
base_width=64, dilation=1, norm_layer=None, cam=False, bam=False):
|
||||
super(Bottleneck, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
width = int(planes * (base_width / 64.)) * groups
|
||||
self.cam = cam
|
||||
self.bam = bam
|
||||
# Both self.conv2 and self.downsample layers downsample the input when stride != 1
|
||||
self.conv1 = conv1x1(inplanes, width)
|
||||
self.bn1 = norm_layer(width)
|
||||
self.conv2 = conv3x3(width, width, stride, groups, dilation)
|
||||
self.bn2 = norm_layer(width)
|
||||
self.conv3 = conv1x1(width, planes * self.expansion)
|
||||
self.bn3 = norm_layer(planes * self.expansion)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
if self.cam:
|
||||
if planes == 64:
|
||||
self.globalAvgPool = nn.AvgPool2d(56, stride=1)
|
||||
elif planes == 128:
|
||||
self.globalAvgPool = nn.AvgPool2d(28, stride=1)
|
||||
elif planes == 256:
|
||||
self.globalAvgPool = nn.AvgPool2d(14, stride=1)
|
||||
elif planes == 512:
|
||||
self.globalAvgPool = nn.AvgPool2d(7, stride=1)
|
||||
|
||||
self.fc1 = nn.Linear(planes * self.expansion, round(planes / 4))
|
||||
self.fc2 = nn.Linear(round(planes / 4), planes * self.expansion)
|
||||
self.sigmod = nn.Sigmoid()
|
||||
|
||||
def forward(self, x):
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.bn3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
if self.cam:
|
||||
ori_out = self.globalAvgPool(out)
|
||||
out = out.view(out.size(0), -1)
|
||||
out = self.fc1(out)
|
||||
out = self.relu(out)
|
||||
out = self.fc2(out)
|
||||
out = self.sigmod(out)
|
||||
out = out.view(out.size(0), out.size(-1), 1, 1)
|
||||
out = out * ori_out
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
return out
|
||||
|
||||
|
||||
class ResNet(nn.Module):
|
||||
|
||||
def __init__(self, block, layers, num_classes=conf.embedding_size, zero_init_residual=False,
|
||||
groups=1, width_per_group=64, replace_stride_with_dilation=None,
|
||||
norm_layer=None, scale=0.75):
|
||||
super(ResNet, self).__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2d
|
||||
self._norm_layer = norm_layer
|
||||
|
||||
self.inplanes = 64
|
||||
self.dilation = 1
|
||||
if replace_stride_with_dilation is None:
|
||||
# each element in the tuple indicates if we should replace
|
||||
# the 2x2 stride with a dilated convolution instead
|
||||
replace_stride_with_dilation = [False, False, False]
|
||||
if len(replace_stride_with_dilation) != 3:
|
||||
raise ValueError("replace_stride_with_dilation should be None "
|
||||
"or a 3-element tuple, got {}".format(replace_stride_with_dilation))
|
||||
self.groups = groups
|
||||
self.base_width = width_per_group
|
||||
self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,
|
||||
bias=False)
|
||||
self.bn1 = norm_layer(self.inplanes)
|
||||
self.relu = nn.ReLU(inplace=True)
|
||||
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
||||
self.layer1 = self._make_layer(block, int(64*scale), layers[0])
|
||||
self.layer2 = self._make_layer(block, int(128*scale), layers[1], stride=2,
|
||||
dilate=replace_stride_with_dilation[0])
|
||||
self.layer3 = self._make_layer(block, int(256*scale), layers[2], stride=2,
|
||||
dilate=replace_stride_with_dilation[1])
|
||||
self.layer4 = self._make_layer(block, int(512*scale), layers[3], stride=2,
|
||||
dilate=replace_stride_with_dilation[2])
|
||||
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
|
||||
self.fc = nn.Linear(int(512 * block.expansion*scale), num_classes)
|
||||
|
||||
for m in self.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
|
||||
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
|
||||
nn.init.constant_(m.weight, 1)
|
||||
nn.init.constant_(m.bias, 0)
|
||||
|
||||
# Zero-initialize the last BN in each residual branch,
|
||||
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
|
||||
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
|
||||
if zero_init_residual:
|
||||
for m in self.modules():
|
||||
if isinstance(m, Bottleneck):
|
||||
nn.init.constant_(m.bn3.weight, 0)
|
||||
elif isinstance(m, BasicBlock):
|
||||
nn.init.constant_(m.bn2.weight, 0)
|
||||
|
||||
def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
|
||||
norm_layer = self._norm_layer
|
||||
downsample = None
|
||||
previous_dilation = self.dilation
|
||||
if dilate:
|
||||
self.dilation *= stride
|
||||
stride = 1
|
||||
if stride != 1 or self.inplanes != planes * block.expansion:
|
||||
downsample = nn.Sequential(
|
||||
conv1x1(self.inplanes, planes * block.expansion, stride),
|
||||
norm_layer(planes * block.expansion),
|
||||
)
|
||||
|
||||
layers = []
|
||||
layers.append(block(self.inplanes, planes, stride, downsample, self.groups,
|
||||
self.base_width, previous_dilation, norm_layer))
|
||||
self.inplanes = planes * block.expansion
|
||||
for _ in range(1, blocks):
|
||||
layers.append(block(self.inplanes, planes, groups=self.groups,
|
||||
base_width=self.base_width, dilation=self.dilation,
|
||||
norm_layer=norm_layer))
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def _forward_impl(self, x):
|
||||
# See note [TorchScript super()]
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
|
||||
x = self.layer1(x)
|
||||
x = self.layer2(x)
|
||||
x = self.layer3(x)
|
||||
x = self.layer4(x)
|
||||
|
||||
# print('poolBefore', x.shape)
|
||||
x = self.avgpool(x)
|
||||
# print('poolAfter', x.shape)
|
||||
x = torch.flatten(x, 1)
|
||||
# print('fcBefore',x.shape)
|
||||
x = self.fc(x)
|
||||
|
||||
# print('fcAfter',x.shape)
|
||||
|
||||
return x
|
||||
|
||||
def forward(self, x):
|
||||
return self._forward_impl(x)
|
||||
|
||||
|
||||
# def _resnet(arch, block, layers, pretrained, progress, **kwargs):
|
||||
# model = ResNet(block, layers, **kwargs)
|
||||
# if pretrained:
|
||||
# state_dict = load_state_dict_from_url(model_urls[arch],
|
||||
# progress=progress)
|
||||
# model.load_state_dict(state_dict, strict=False)
|
||||
# return model
|
||||
def _resnet(arch, block, layers, pretrained, progress, **kwargs):
|
||||
model = ResNet(block, layers, **kwargs)
|
||||
if pretrained:
|
||||
state_dict = load_state_dict_from_url(model_urls[arch],
|
||||
progress=progress)
|
||||
|
||||
src_state_dict = state_dict
|
||||
target_state_dict = model.state_dict()
|
||||
skip_keys = []
|
||||
# skip mismatch size tensors in case of pretraining
|
||||
for k in src_state_dict.keys():
|
||||
if k not in target_state_dict:
|
||||
continue
|
||||
if src_state_dict[k].size() != target_state_dict[k].size():
|
||||
skip_keys.append(k)
|
||||
for k in skip_keys:
|
||||
del src_state_dict[k]
|
||||
missing_keys, unexpected_keys = model.load_state_dict(src_state_dict, strict=False)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def resnet14(pretrained=True, progress=True, **kwargs):
|
||||
r"""ResNet-14 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet18', BasicBlock, [2, 1, 1, 2], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet18(pretrained=True, progress=True, **kwargs):
|
||||
r"""ResNet-18 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet34(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-34 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet50(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-50 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet101(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-101 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnet152(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNet-152 model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, progress,
|
||||
**kwargs)
|
||||
|
||||
|
||||
def resnext50_32x4d(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNeXt-50 32x4d model from
|
||||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width_per_group'] = 4
|
||||
return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def resnext101_32x8d(pretrained=False, progress=True, **kwargs):
|
||||
r"""ResNeXt-101 32x8d model from
|
||||
`"Aggregated Residual Transformation for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width_per_group'] = 8
|
||||
return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def wide_resnet50_2(pretrained=False, progress=True, **kwargs):
|
||||
r"""Wide ResNet-50-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_
|
||||
|
||||
The model is the same as ResNet except for the bottleneck number of channels
|
||||
which is twice larger in every block. The number of channels in outer 1x1
|
||||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
|
||||
channels, and in Wide ResNet-50-2 has 2048-1024-2048.
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['width_per_group'] = 64 * 2
|
||||
return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3],
|
||||
pretrained, progress, **kwargs)
|
||||
|
||||
|
||||
def wide_resnet101_2(pretrained=False, progress=True, **kwargs):
|
||||
r"""Wide ResNet-101-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_
|
||||
|
||||
The model is the same as ResNet except for the bottleneck number of channels
|
||||
which is twice larger in every block. The number of channels in outer 1x1
|
||||
convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048
|
||||
channels, and in Wide ResNet-50-2 has 2048-1024-2048.
|
||||
|
||||
Args:
|
||||
pretrained (bool): If True, returns a model pre-trained on ImageNet
|
||||
progress (bool): If True, displays a progress bar of the download to stderr
|
||||
"""
|
||||
kwargs['width_per_group'] = 64 * 2
|
||||
return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3],
|
||||
pretrained, progress, **kwargs)
|
21
tracking/trackers/reid/test.py
Normal file
21
tracking/trackers/reid/test.py
Normal file
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Jan 19 16:10:39 2024
|
||||
|
||||
@author: ym
|
||||
"""
|
||||
import torch
|
||||
from model.resnet_pre import resnet18
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
model_path = "best.pth"
|
||||
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||
model = resnet18().to(device)
|
||||
model.load_state_dict(torch.load(model_path, map_location=device))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
66
tracking/trackers/track.py
Normal file
66
tracking/trackers/track.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
from functools import partial
|
||||
|
||||
import torch
|
||||
|
||||
from ultralytics.utils import IterableSimpleNamespace, yaml_load
|
||||
from ultralytics.utils.checks import check_yaml
|
||||
|
||||
from .bot_sort import BOTSORT
|
||||
from .byte_tracker import BYTETracker
|
||||
|
||||
TRACKER_MAP = {'bytetrack': BYTETracker, 'botsort': BOTSORT}
|
||||
|
||||
|
||||
def on_predict_start(predictor, persist=False):
|
||||
"""
|
||||
Initialize trackers for object tracking during prediction.
|
||||
|
||||
Args:
|
||||
predictor (object): The predictor object to initialize trackers for.
|
||||
persist (bool, optional): Whether to persist the trackers if they already exist. Defaults to False.
|
||||
|
||||
Raises:
|
||||
AssertionError: If the tracker_type is not 'bytetrack' or 'botsort'.
|
||||
"""
|
||||
if hasattr(predictor, 'trackers') and persist:
|
||||
return
|
||||
tracker = check_yaml(predictor.args.tracker)
|
||||
cfg = IterableSimpleNamespace(**yaml_load(tracker))
|
||||
assert cfg.tracker_type in ['bytetrack', 'botsort'], \
|
||||
f"Only support 'bytetrack' and 'botsort' for now, but got '{cfg.tracker_type}'"
|
||||
trackers = []
|
||||
for _ in range(predictor.dataset.bs):
|
||||
tracker = TRACKER_MAP[cfg.tracker_type](args=cfg, frame_rate=30)
|
||||
trackers.append(tracker)
|
||||
predictor.trackers = trackers
|
||||
|
||||
|
||||
def on_predict_postprocess_end(predictor):
|
||||
"""Postprocess detected boxes and update with object tracking."""
|
||||
bs = predictor.dataset.bs
|
||||
im0s = predictor.batch[1]
|
||||
for i in range(bs):
|
||||
det = predictor.results[i].boxes.cpu().numpy()
|
||||
if len(det) == 0:
|
||||
continue
|
||||
tracks = predictor.trackers[i].update(det, im0s[i])
|
||||
if len(tracks) == 0:
|
||||
continue
|
||||
idx = tracks[:, -1].astype(int)
|
||||
predictor.results[i] = predictor.results[i][idx]
|
||||
predictor.results[i].update(boxes=torch.as_tensor(tracks[:, :-1]))
|
||||
|
||||
|
||||
def register_tracker(model, persist):
|
||||
"""
|
||||
Register tracking callbacks to the model for object tracking during prediction.
|
||||
|
||||
Args:
|
||||
model (object): The model object to register tracking callbacks for.
|
||||
persist (bool): Whether to persist the trackers if they already exist.
|
||||
|
||||
"""
|
||||
model.add_callback('on_predict_start', partial(on_predict_start, persist=persist))
|
||||
model.add_callback('on_predict_postprocess_end', on_predict_postprocess_end)
|
3
tracking/trackers/utils/__init__.py
Normal file
3
tracking/trackers/utils/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
# Ultralytics YOLO 🚀, AGPL-3.0 license
|
||||
|
||||
|
BIN
tracking/trackers/utils/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/gmc.cpython-39.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/gmc.cpython-39.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/kalman_filter.cpython-39.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/kalman_filter.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/matching.cpython-312.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/matching.cpython-312.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/matching.cpython-39.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/matching.cpython-39.pyc
Normal file
Binary file not shown.
BIN
tracking/trackers/utils/__pycache__/results.cpython-39.pyc
Normal file
BIN
tracking/trackers/utils/__pycache__/results.cpython-39.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user