474 lines
16 KiB
Python
474 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Created on Fri Aug 30 17:53:03 2024
|
||
|
||
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 pickle
|
||
import torch
|
||
import time
|
||
import json
|
||
from config import config as conf
|
||
from model import resnet18
|
||
from inference import load_contrast_model
|
||
from inference import featurize
|
||
|
||
|
||
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']
|
||
|
||
model = load_contrast_model()
|
||
|
||
def creat_shopping_event(basePath, savePath, subimgPath=False):
|
||
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
|
||
|
||
'''================ 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)
|
||
|
||
|
||
'''================= 2. 读取 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]
|
||
''' 2.1 读取 0/1_track.data 中数据,暂不考虑'''
|
||
# if dataname.find("_track.data")>0:
|
||
# bboxes, ffeats, trackerboxes, tracker_feat_dict, trackingboxes, tracking_feat_dict = extract_data(datapath)
|
||
|
||
''' 2.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
|
||
|
||
'''2.3 事件的特征表征方式: 特征选择、特征集成'''
|
||
bk_feats = event['back_feats']
|
||
ft_feats = event['front_feats']
|
||
|
||
'''2.3.1 特征集成'''
|
||
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
|
||
|
||
'''2.3.1 特征选择'''
|
||
if len(ft_feats):
|
||
event['feats_select'] = ft_feats
|
||
|
||
pickpath = os.path.join(savePath, f"{filename}.pickle")
|
||
with open(pickpath, 'wb') as f:
|
||
pickle.dump(event, f)
|
||
print(f"Event: {filename}")
|
||
|
||
if subimgPath==False:
|
||
eventList.append(event)
|
||
continue
|
||
|
||
'''================ 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, bpath):
|
||
'''
|
||
inputs:
|
||
bcdpath: 已清洗的barcode样本图像,如果barcode下有'base'文件夹,只选用该文件夹下图像
|
||
(default = r'\\192.168.1.28\share\已标注数据备份\对比数据\barcode\barcode_1771')
|
||
功能:
|
||
生成并保存只有一个key值的字典 {barcode: [imgpath1, imgpath1, ...]},
|
||
bpath: 字典存储地址
|
||
'''
|
||
|
||
# bpath = r'\\192.168.1.28\share\测试_202406\contrast\barcodes'
|
||
|
||
'''读取数据集中 barcode 列表'''
|
||
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]
|
||
|
||
'''遍历数据集,针对每一个barcode,生成并保存字典{barcode: [imgpath1, imgpath1, ...]}'''
|
||
k = 0
|
||
for barcode, bpath in bcdpaths:
|
||
stdBarcodeDict = {}
|
||
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)
|
||
|
||
pickpath = os.path.join(bpath, f"{barcode}.pickle")
|
||
with open(pickpath, 'wb') as f:
|
||
pickle.dump(stdBarcodeDict, f)
|
||
print(f"Barcode: {barcode}")
|
||
|
||
# k += 1
|
||
# if k == 10:
|
||
# break
|
||
|
||
return
|
||
|
||
|
||
def extract_save_trajture_subimgs(shoppingEventPath, shoppingFeatPath, subimgPath):
|
||
'''用于保存一次购物事件的轨迹图像子图'''
|
||
|
||
shoppingFeatPath = r"\\192.168.1.28\share\测试_202406\contrast\events"
|
||
subimgPath = r'\\192.168.1.28\share\测试_202406\contrast\subimgs'
|
||
|
||
|
||
eventList = creat_shopping_event(shoppingEventPath, shoppingFeatPath, subimgPath=True)
|
||
print("======= eventList have generated and features have saved! =======")
|
||
|
||
|
||
barcodeDict = {}
|
||
for event in eventList:
|
||
'''9 items: barcode, type, filepath, back_imgpaths, front_imgpaths,
|
||
back_boxes, front_boxes, back_feats, front_feats
|
||
'''
|
||
if len(event['feats_select']):
|
||
event_feats = event['feats_select']
|
||
elif len(event['back_feats']):
|
||
event_feats = event['back_feats']
|
||
else:
|
||
continue
|
||
|
||
|
||
'''保存一次购物事件的轨迹子图'''
|
||
basename = os.path.basename(event['filepath'])
|
||
spath = os.path.join(subimgPath, 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 batch_inference(imgpaths, batch):
|
||
size = len(imgpaths)
|
||
groups = []
|
||
for i in range(0, size, batch):
|
||
end = min(batch + i, size)
|
||
groups.append(imgpaths[i: end])
|
||
|
||
features = []
|
||
for group in groups:
|
||
feature = featurize(group, conf.test_transform, model, conf.device)
|
||
features.append(feature)
|
||
features = np.concatenate(features, axis=0)
|
||
|
||
return features
|
||
|
||
def stdfeat_infer(imgPath, featPath):
|
||
'''
|
||
inputs:
|
||
imgPath: 该文件夹下的 pickle 文件格式 {barcode: [imgpath1, imgpath1, ...]}
|
||
featPath: imgPath图像对应特征的存储地址
|
||
功能:
|
||
对 imgPath中图像进行特征提取,生成只有一个key值的字典,
|
||
{barcode: features},features.shape=(nsample, 256),并保存至 featPath 中
|
||
|
||
'''
|
||
|
||
# imgPath = r"\\192.168.1.28\share\测试_202406\contrast\barcodes"
|
||
# featPath = r"\\192.168.1.28\share\测试_202406\contrast\features"
|
||
stdBarcodeDict = {}
|
||
k = 0
|
||
for filename in os.listdir(imgPath):
|
||
filepath = os.path.join(imgPath, filename)
|
||
|
||
stdbDict = {}
|
||
t1 = time.time()
|
||
|
||
try:
|
||
with open(filepath, 'rb') as f:
|
||
bpDict = pickle.load(f)
|
||
for barcode, imgpaths in bpDict.items():
|
||
feature = batch_inference(imgpaths, 8)
|
||
except Exception as e:
|
||
print(f"Error accured at: {filename}, with Exception is: {e}")
|
||
|
||
'''================ 保存单个barcode特征 ================'''
|
||
stdbDict[barcode] = feature
|
||
pkpath = os.path.join(featPath, f"{barcode}.pickle")
|
||
with open(pkpath, 'wb') as f:
|
||
pickle.dump(stdbDict, f)
|
||
|
||
stdBarcodeDict[barcode] = feature
|
||
t2 = time.time()
|
||
print(f"Barcode: {barcode}, need time: {t2-t1:.1f} secs")
|
||
k += 1
|
||
if k == 10:
|
||
break
|
||
|
||
pickpath = os.path.join(featPath, f"barcode_features_{k}.pickle")
|
||
with open(pickpath, 'wb') as f:
|
||
pickle.dump(stdBarcodeDict, f)
|
||
|
||
def contrast_performance_evaluate():
|
||
eventFeatPath = r"\\192.168.1.28\share\测试_202406\contrast\events"
|
||
stdFeatPath = r"\\192.168.1.28\share\测试_202406\contrast\features"
|
||
|
||
|
||
|
||
|
||
def generate_event_and_standard_features():
|
||
stdSamplePath = r"\\192.168.1.28\share\已标注数据备份\对比数据\barcode\barcode_1771"
|
||
stdBarcodePath = r"\\192.168.1.28\share\测试_202406\contrast\barcodes"
|
||
stdFeaturePath = r"\\192.168.1.28\share\测试_202406\contrast\features"
|
||
|
||
'''=========================== 1. 生成标准特征集 ========================'''
|
||
'''1.1 提取并保存标准特征库原始图像文件地址字典'''
|
||
# get_std_barcodeDict(stdSamplePath, stdBarcodePath)
|
||
# print("standard imgpath have extracted and saved")
|
||
|
||
|
||
'''1.2 特征提取,并保存至文件夹 stdFeaturePath 中'''
|
||
stdfeat_infer(stdBarcodePath, stdFeaturePath)
|
||
# print("standard features have generated!")
|
||
|
||
|
||
'''=========================== 2. 提取并存储事件特征 ========================'''
|
||
shoppingEventPath = [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']
|
||
shoppingFeatPath = r"\\192.168.1.28\share\测试_202406\contrast\events"
|
||
# for sPath in shoppingEventPath:
|
||
# eventList = creat_shopping_event(sPath, shoppingFeatPath)
|
||
|
||
|
||
|
||
|
||
def shopping_event_test():
|
||
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'
|
||
]
|
||
|
||
shoppingFeatPath = r"\\192.168.1.28\share\测试_202406\contrast\events"
|
||
subimgPath = r'\\192.168.1.28\share\测试_202406\contrast\subimgs'
|
||
|
||
for filepath in fplist:
|
||
'''用于保存一次购物事件的轨迹轨迹特征、及对应的图像子图'''
|
||
extract_save_trajture_subimgs(filepath, shoppingFeatPath, subimgPath)
|
||
|
||
|
||
|
||
def main():
|
||
generate_event_and_standard_features()
|
||
# shopping_event_test()
|
||
|
||
pass
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|