Video-ReID

This commit is contained in:
li-zhenyun
2022-09-03 01:47:35 +08:00
parent be25035bd6
commit 9ebe625276
16 changed files with 1825 additions and 0 deletions

265
Video-ReID/README.md Normal file
View File

@@ -0,0 +1,265 @@
# MindXSDK 视频行人重识别
## 1 简介
本开发样例基于MindX SDK实现了对图片和视频流的行人重识别Person Re-identification, ReID支持检索给定照片、视频中的行人ID。其主要流程为
- 构建行人特征库将底库图片调整大小利用目标检测模型YOLOv3推理检测图片中的行人检测结果经过抠图与调整大小再利用OSNet模型提取图片中每个行人的特征向量并保存特征向量用于与后续的待查询图片或者视频中的行人作比较。
- 对于查询图片或视频帧利用目标检测模型YOLOv3推理检测图片中的行人检测结果经过抠图与调整大小再利用OSNet模型提取图片中每个行人的特征向量。
- 行人检索将查询图片中行人的特征向量与底库中的特征向量做比较为每个查询图片中的行人检索最有可能的ID通过识别框和文字信息进行可视化标记。
- 如果输入是图片最终得到标记过的图片文件如果是视频流得到标记过的H264格式视频文件
## 2 目录结构
本工程名称为Video-ReID工程目录如下图所示
```
video-ReID
|---- config
| | |---- coco.names
| | |---- yolov3_tf_bs1_fp16.cfg
|---- data
| |---- gallery // 行人底库图片文件夹
| |---- query
| |---- images // 查询场景图片文件夹
| |---- videos // 查询场景视频文件夹,也可根据推流时实际环境配置存放位置
|---- models // 目标检测、OSNet模型与配置文件夹
| |---- OSNet
| | | |---- aipp.config
|---- YOLOv3
| | | |---- tf_aipp.config
|---- pipeline // 流水线配置文件夹
| | |---- image.pipeline
| | |---- gallery.pipeline
| | |---- video.pipeline
|---- plugins // 自定义插件目录
| |---- PluginFeatureMatch
|---- output // 结果保存文件夹
| |---- gallery
| |---- query
| |---- images
| |---- videos //需自行创建
|---- image.py
|---- gallery.py
|---- video.py
|---- run.sh
|---- README.md
```
> 由于无法在Gitee上创建空文件夹请按照该工程目录自行创建output文件夹、data文件夹与其内部的文件夹
## 3 依赖
| 软件名称 | 版本 |
| :--------: | :------: |
|ubuntu 18.04|18.04.1 LTS |
|CANN|5.0.4|
|MindX SDK|2.0.4|
|Python| 3.9.2|
|numpy | 1.21.0 |
|opencv_python|4.5.2|
- 设置环境变量请确认install_path路径是否正确
```
#执行如下命令
. ${SDK-path}/set_env.sh
. ${ascend_toolkit_path}/set_env.sh
```
请注意MindX SDK使用python版本为3.9.2如出现无法找到python对应lib库请在root下安装python3.9开发库
```
apt-get install libpython3.9
```
- 推理中涉及到第三方软件依赖如下表所示。
| 依赖软件 | 版本 | 说明 | 使用教程 |
| -------- | ---------- | -------------------------------- | ------------------------------------------------------------ |
| live555 | 1.10 | 实现视频转 rstp 进行推流 | [链接](https://gitee.com/ascend/mindxsdk-referenceapps/blob/master/docs/参考资料/Live555离线视频转RTSP说明文档.md) |
| ffmpeg | 2022-06-27 | 实现 mp4 格式视频转为264格式视频 | [链接](https://gitee.com/ascend/mindxsdk-referenceapps/blob/master/docs/参考资料/pc端ffmpeg安装教程.md) |
## 4 模型转换
行人重识别先采用了yolov3模型将图片中的行人检测出来然后利用OsNet模型获取行人的特征向量。由于yolov3模型和OsNet模型分别是基于Pytorch和Tensorflow的深度模型我们需要借助ATC工具分别将其转换成对应的.om模型。
### 4.1 yolov3的模型转换
**步骤1** 获取yolov3的原始模型(.pb文件)和相应的配置文件(.cfg文件)
      [原始模型下载链接](https://www.hiascend.com/zh/software/modelzoo/models/detail/1/ba2a4c054a094ef595da288ecbc7d7b4)
**步骤2** 将获取到的yolov3模型.pb文件和.cfg文件存放至“项目所在目录/models/YOLOv3/”
**步骤3** .om模型转换
进入“项目所在目录/models/YOLOv3”
- 使用ATC将.pb文件转成为.om文件
```
atc --model=yolov3_tf.pb --framework=3 --output=yolov3 --output_type=FP32 --soc_version=Ascend310 --input_shape="input:1,416,416,3" --out_nodes="yolov3/yolov3_head/Conv_6/BiasAdd:0;yolov3/yolov3_head/Conv_14/BiasAdd:0;yolov3/yolov3_head/Conv_22/BiasAdd:0" --log=info --insert_op_conf=tf_aipp.cfg
```
- 执行完模型转换脚本后若提示如下信息说明模型转换成功可以在该路径下找到名为yolov3.om模型文件。
可以通过修改output参数来重命名这个.om文件
```
ATC run success, welcome to the next use.
```
### 4.2 OSNet的模型转换
#### 4.2.1 模型概述
      [OSNet论文地址](https://arxiv.org/pdf/1905.00953.pdf)
      [OSNet代码地址](https://github.com/KaiyangZhou/deep-person-reid)
#### 4.2.2 模型转换步骤
**步骤1** 从ModelZoo源码包中获取OSNet的onnx模型文件(osnet_x1_0.onnx)
      [权重文件源码包下载链接](https://www.hiascend.com/zh/software/modelzoo/models/detail/1/43a754e306c6461d86dafced5046121f)
**步骤2** 将获取到的onnx模型存放至“项目所在目录/models/OSNet/”
**步骤3** .om模型转换
进入“项目所在目录/models/OSNet”
- 使用ATC将.onnx文件转成为.om文件
```
atc --framework=5 --model=./osnet_x1_0.onnx --input_format=NCHW --insert_op_conf=./aipp.config --input_shape="image:-1,3,256,128" --dynamic_batch_size="1,2,3,4,5,6,7,8" --output=osnet --log=debug --soc_version=Ascend310
// dynamic参数为支持的动态batchsize,可根据实际图片中可能出现的行人数目更改
```
- 执行完模型转换脚本后若提示如下信息说明模型转换成功可以在该路径下找到名为yolov3.om模型文件。
可以通过修改output参数来重命名这个.om文件
```
ATC run success, welcome to the next use.
```
经过上述操作,可以在“项目所在目录/models”的子目录下找到yolov3.om模型和osnet.om模型模型转换操作已全部完成
### 4.3 参考链接
> 模型转换使用了ATC工具如需更多信息请参考[ATC工具使用指南-快速入门](https://support.huaweicloud.com/tg-cannApplicationDev330/atlasatc_16_0005.html)
> Yolov3模型转换的参考链接[ATC YOLOv3(FP16)](https://www.hiascend.com/zh/software/modelzoo/models/detail/1/ba2a4c054a094ef595da288ecbc7d7b4)
> OSNet模型转换的参考链接[OSNet](https://gitee.com/ascend/ModelZoo-PyTorch/tree/master/ACL_PyTorch/contrib/cv/classfication/OSNet)
## 5 准备
### 5.1 数据
为适配网络输入以及性能要求建议输入图片或视频流长宽比接近11图像长宽均需为偶数待检测行人范围像素面积大于5120且宽度大于32像素长度大于16像素,且图像后缀为jpg/JPG。
涉及文件夹
> “项目所在目录/data/gallery”用于存放制作行人底库的场景图片
建议针对待检测的行人单人正面,侧面,背面各采集图片作为其底库。图片中行人清晰醒目且身体完整。
> “项目所在目录/data/query/images”用于存放待查询行人图片
可检测行人正面,侧面,背面。要求图片中行人清晰醒目且身体尽量完整。
> “项目所在目录/data/query/videos”用于存放待查询行人视频
可检测行人正面,侧面,背面。要求视频帧中行人清晰醒目且身体尽量完整。
### 5.2 编译插件
项目需要用到自定义插件进行特征匹配与重标定,自定义插件源码目录为“项目所在目录/plugins/PluginFeatureMatch”
#### 5.2.1 编译插件
```
> bash build.sh
```
编译后会在${MX_SDK_HOME}/lib/plugins/目录下生成libmxpi_featurematch.so文件
#### 5.2.2 为编译获得的.so文件授予640权限.
编译参考[插件编译指南](https://support.huawei.com/enterprise/zh/doc/EDOC1100234263/21d24289)
### 5.3 视频推流
按照第 3 小结软件依赖安装 live555 和 ffmpeg按照 Live555离线视频转RTSP说明文档 将 mp4 视频转换为 h264 格式。并将生成的 264 格式的视频上传到 live/mediaServer 目录下,然后修改 项目所在目录/pipeline 目录下的 video.pipeline 文件中 mxpi_rtspsrc0 的内容。
```
"mxpi_rtspsrc0": {
"props": {
"rtspUrl":"rtsp://xxx.xxx.xxx.xxx:xxxx/xxx.264", // 修改为自己开发板的地址和文件名
"channelId": "0",
"timeout": "30"
},
"factory": "mxpi_rtspsrc",
"next": "mxpi_videodecoder0"
},
```
### 5.4 适用场景
项目适用于大部分行人目标较完整且醒目可见的场景,对于行人不完整程度高,或者行人底库中缺少同一行人对应机位的参照时,检测框或行人重标定可能会出现误差,请根据实际应用进行数据选择和处理。
对于视频每帧的处理时长约为200ms建议拉流视频帧率在5左右若出现内存不足等情况请适当降低帧率。
## 6 测试
### 6.1 获取om模型
```
步骤详见4 模型转换
```
### 6.2 准备
```
步骤详见5 准备
```
### 6.3 配置pipeline
根据所需场景配置pipeline文件调整路径参数等。
```
# 配置mxpi_tensorinfer插件的yolov3.om模型加载路径三个pipeline均需配置
"mxpi_tensorinfer0": {
"props": {
"dataSource": "mxpi_imageresize0",
"modelPath": "models/YOLOv3/yolov3.om(这里根据你的命名或路径进行更改)"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_objectpostprocessor0"
},
# 配置mxpi_objectpostprocessor插件的yolov3.cfg配置文件加载路径以及SDN的安装路径三个pipeline均需配置
"mxpi_objectpostprocessor0": {
"props": {
"dataSource": "mxpi_tensorinfer0",
"postProcessConfigPath": "config/yolov3_tf_bs1_fp16.cfg(这里根据你的命名或路径进行更改)",
"labelPath": "config/coco.names",
"postProcessLibPath": "libyolov3postprocess.so"
},
"factory": "mxpi_objectpostprocessor",
"next": "mxpi_objectselector0"
},
# 配置mxpi_tensorinfer插件的OsNet.om模型加载路径三个pipeline均需配置
"mxpi_tensorinfer1": {
"props": {
"dataSource": "mxpi_imagecrop0",
"dynamicStrategy": "Upper",
"modelPath": "models/OSNet/osnet.om",
"waitingTime": "1"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_featurematch0"
},
```
### 6.4 执行
#### 6.4.1 构建行人特征库
```
bash run.sh gallery
```
执行成功后会打印单张图片处理耗时并在output/gallery目录下生成.txt标签文件和.bin特征向量存储文件。
#### 6.4.2 图片查询
```
bash run.sh image
```
执行成功后会打印单张图片处理耗时并在output/query/images目录下生成标记了Reid目标的图片输出。
经测试端到端处理耗时在200ms以内。
#### 6.4.3 视频查询
```
bash run.sh video 60 # 60s为处理视频流时长参数可根据实际情况修改
```
执行成功后会在output/query/videos目录下获得标记了Reid目标的h264格式视频文件。
### 6.5 查看结果
执行命令后,可在“项目所在目录/output”路径下查看结果。
## 7 参考链接
> 特定行人检索:[Person Search Demo](https://github.com/KaiyangZhou/deep-person-reid)
## 8 Q&A
### 8.1 在运行查询脚本时出现"[DVPP: image width out of range] The crop width of image No.[0] is out of range [32,4096]"
> 这里的错误是因为yolov3模型检测到的目标过小调大“mxpi_objectselector0”插件的MinArea参数或者更新“项目所在目录/config/yolov3_tf_bs1_fp16.cfg”文件将OBJECTNESS_THRESH适度调大可解决该问题
### 8.2 运行video.py时出现"[6003][stream change state fail] create stream(queryVideoProcess) failed."
> 可能是因为video.pipeline中filelink插件的保存路径文件夹未创建请手动创建(output/query/videos).
### 8.3 运行脚本时出现 streamInstance GetResult return nullptr.
> 可能是因为图像/视频里没有检测到行人,请更换有显著行人的图像输入。
### 8.4 视频处理时出现 "[Error code unknown] Failed to send frame.","[DVPP: decode H264 or H265 fail] Decode video failed.",且在销毁stream时出现 “Failed to destroy vdec channel.", "Failed to destroy stream:queryVideoProcess"或出现 "Malloc device memory failed."
> 此错误可能是因为视频帧率过高,请适当降低.264视频帧率。

View File

@@ -0,0 +1,81 @@
# This file is originally from https://github.com/pjreddie/darknet/blob/master/data/coco.names
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

View File

@@ -0,0 +1,10 @@
CLASS_NUM=80
BIASES_NUM=18
BIASES=10,13,16,30,33,23,30,61,62,45,59,119,116,90,156,198,373,326
SCORE_THRESH=0.3
OBJECTNESS_THRESH=0.3
IOU_THRESH=0.45
YOLO_TYPE=3
ANCHOR_DIM=3
MODEL_TYPE=0
RESIZE_FLAG=0

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python
# coding=utf-8
"""
Copyright(C) Huawei Technologies Co.,Ltd. 2012-2021 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import argparse
import os
import time
import numpy as np
import MxpiDataType_pb2 as MxpiDataType
from StreamManagerApi import StreamManagerApi, MxDataInput, StringVector
GALLERY_STREAM_NAME = b'galleryProcess'
IN_PLUGIN_ID = 0
OUT_PLUGIN_ID = 0
def initialize_stream():
"""
Initialize stream galleryImageProcess for detecting and re-identifying persons in galley images
:arg:
None
:return:
Stream api
"""
stream_pi = StreamManagerApi()
ret = stream_pi.InitManager()
if ret != 0:
error_message = "Failed to init Stream manager, ret=%s" % str(ret)
print(error_message)
exit()
# creating stream based on json strings in the pipeline file: 'ReID.pipeline'
with open("pipeline/gallery.pipeline", 'rb') as f:
pipeline = f.read()
ret = stream_pi.CreateMultipleStreams(pipeline)
if ret != 0:
error_message = "Failed to create Stream, ret=%s" % str(ret)
print(error_message)
exit()
return stream_pi
def get_gallery_feature(input_dir, output_dir, stream_api):
"""
Extract the features of gallery images, save the feature vector and the Pids to files
:arg:
imgPath: the directory of gallery images
outputDir: the directory of gallery output files
streamApi: stream api
:return:
None
"""
# constructing the results returned by the queryImageProcess stream
key_vec = StringVector()
key_vec.push_back(b"mxpi_tensorinfer1")
# check the query file
if os.path.exists(input_dir) != 1:
error_message = 'The img dir does not exist.'
print(error_message)
exit()
if len(os.listdir(input_dir)) == 0:
error_message = 'The img file is empty.'
print(error_message)
exit()
if os.path.exists(output_dir) != 1:
root = os.getcwd()
os.makedirs(os.path.join(root, output_dir))
features = []
pids = []
# extract the features for all images in gallery file
for root, dirs, files in os.walk(input_dir):
for file in files:
if file.endswith('.jpg') or file.endswith('.JPG'):
data_input = MxDataInput()
file_path = os.path.join(root, file)
with open(file_path, 'rb') as f:
data_input.data = f.read()
start = time.time()
# send the prepared data to the stream
unique_id = stream_api.SendData(GALLERY_STREAM_NAME, IN_PLUGIN_ID, data_input)
if unique_id < 0:
error_message = 'Failed to send data to queryImageProcess stream.'
print(error_message)
exit()
# get infer result
infer_result = stream_api.GetProtobuf(GALLERY_STREAM_NAME, OUT_PLUGIN_ID, key_vec)
end = time.time()
print("time:", end-start)
# checking whether the infer results is valid or not
if infer_result.size() == 0:
error_message = 'Unable to get effective infer results, please check the stream log for details'
print(error_message)
exit()
tensor_packages = MxpiDataType.MxpiTensorPackageList()
tensor_packages.ParseFromString(infer_result[0].messageBuf)
feature = np.frombuffer(tensor_packages.tensorPackageVec[0].tensorVec[0].dataStr,
dtype=np.float32)
features.append(feature)
pids.append(file.split('.')[0])
else:
print('Input image only support jpg')
exit()
features = np.array(features)
features.tofile(os.path.join(output_dir, 'gallery_features.bin'))
pids = np.array(pids).T
np.savetxt(os.path.join(output_dir, 'persons.txt'), pids, fmt='%s')
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--galleryInputDir', type=str, default='data/gallery', help="Gallery File Path")
parser.add_argument('--galleryOutputDir', type=str, default='output/gallery', help="Gallery Features Output Path")
opt = parser.parse_args()
stream_manager_api = initialize_stream()
get_gallery_feature(opt.galleryInputDir, opt.galleryOutputDir, stream_manager_api)
stream_manager_api.DestroyAllStreams()

131
Video-ReID/image.py Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python
# coding=utf-8
"""
Copyright(C) Huawei Technologies Co.,Ltd. 2012-2021 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import argparse
import stat
import os
import time
from StreamManagerApi import StreamManagerApi, MxDataInput
QUERY_STREAM_NAME = b'queryImageProcess'
IN_PLUGIN_ID = 0
OUT_PLUGIN_ID = 0
def initialize_stream():
"""
Initialize stream queryImageProcess for detecting and re-identifying persons in image
:arg:
None
:return:
Stream api
"""
stream_pi = StreamManagerApi()
ret = stream_pi.InitManager()
if ret != 0:
error_message = "Failed to init Stream manager, ret=%s" % str(ret)
print(error_message)
exit()
# creating stream based on json strings in the pipeline file: 'ReID.pipeline'
with open("pipeline/image.pipeline", 'rb') as f:
pipeline = f.read()
ret = stream_pi.CreateMultipleStreams(pipeline)
if ret != 0:
error_message = "Failed to create Stream, ret=%s" % str(ret)
print(error_message)
exit()
return stream_pi
def reid(input_dir, output_dir, stream_api):
"""
detecting and re-identifying persons in videos
:arg:
inputDir: the directory of query images
outputDir: the directory of output images
streamApi: stream api
:return:
None
"""
if os.path.exists(input_dir) != 1:
error_message = 'The img dir does not exist.'
print(error_message)
exit()
if len(os.listdir(input_dir)) == 0:
error_message = 'The img file is empty.'
print(error_message)
exit()
if os.path.exists(output_dir) != 1:
root = os.getcwd()
os.makedirs(os.path.join(root, output_dir))
for root, dirs, files in os.walk(input_dir):
for file in files:
if file.endswith('.jpg') or file.endswith('.JPG'):
data_input = MxDataInput()
file_path = os.path.join(root, file)
with open(file_path, 'rb') as f:
data_input.data = f.read()
start = time.time()
unique_id = stream_api.SendData(QUERY_STREAM_NAME, IN_PLUGIN_ID, data_input)
if unique_id < 0:
error_message = 'Failed to send data to queryImageProcess stream.'
print(error_message)
exit()
# get infer result
infer_result = stream_api.GetResult(QUERY_STREAM_NAME, OUT_PLUGIN_ID)
if infer_result.errorCode:
error_message = 'Unable to get effective infer results, please check the stream log for details'
print(error_message)
exit()
end = time.time()
out_path = os.path.join(output_dir, file)
flags = os.O_WRONLY | os.O_CREAT # Sets how files are read and written
modes = stat.S_IWUSR | stat.S_IRUSR # Set file permissions
with os.fdopen(os.open(out_path, flags, modes), 'wb') as f:
f.write(infer_result.data)
print("time:", end - start)
else:
print('Input image only support jpg')
exit()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--queryInputDir', type=str, default='data/query/images', help="Query Images Input Dir")
parser.add_argument('--queryOutputDir', type=str, default='output/query/images', help="Query Images Output Dir")
opt = parser.parse_args()
stream_manager_api = initialize_stream()
reid(opt.queryInputDir, opt.queryOutputDir, stream_manager_api)
stream_manager_api.DestroyAllStreams()

View File

@@ -0,0 +1,21 @@
aipp_op {
aipp_mode: static
input_format : YUV420SP_U8
csc_switch : true
rbuv_swap_switch : false
matrix_r0c0 : 256
matrix_r0c1 : 0
matrix_r0c2 : 359
matrix_r1c0 : 256
matrix_r1c1 : -88
matrix_r1c2 : -183
matrix_r2c0 : 256
matrix_r2c1 : 454
matrix_r2c2 : 0
input_bias_0 : 0
input_bias_1 : 128
input_bias_2 : 128
src_image_size_w: 128
src_image_size_h: 256
}

View File

@@ -0,0 +1,22 @@
aipp_op {
aipp_mode: static
input_format : YUV420SP_U8
csc_switch : true
rbuv_swap_switch : false
matrix_r0c0 : 256
matrix_r0c1 : 0
matrix_r0c2 : 359
matrix_r1c0 : 256
matrix_r1c1 : -88
matrix_r1c2 : -183
matrix_r2c0 : 256
matrix_r2c1 : 454
matrix_r2c2 : 0
input_bias_0 : 0
input_bias_1 : 128
input_bias_2 : 128
var_reci_chn_0 : 0.003921568627451
var_reci_chn_1 : 0.003921568627451
var_reci_chn_2 : 0.003921568627451
}

View File

@@ -0,0 +1,109 @@
{
"galleryProcess": {
"stream_config": {
"deviceId": "0"
},
"appsrc0": {
"props": {
"blocksize": "409600"
},
"factory": "appsrc",
"next": "mxpi_imagedecoder0"
},
"mxpi_imagedecoder0": {
"factory": "mxpi_imagedecoder",
"next": "mxpi_imageresize0"
},
"mxpi_imageresize0": {
"props": {
"resizeHeight": "416",
"resizeWidth": "416"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer0"
},
"mxpi_tensorinfer0": {
"props": {
"dataSource": "mxpi_imageresize0",
"modelPath": "models/YOLOv3/yolov3.om",
"waitingTime": "1"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_objectpostprocessor0"
},
"mxpi_objectpostprocessor0": {
"props": {
"dataSource": "mxpi_tensorinfer0",
"postProcessConfigPath": "config/yolov3_tf_bs1_fp16.cfg",
"labelPath": "config/coco.names",
"postProcessLibPath": "libyolov3postprocess.so"
},
"factory": "mxpi_objectpostprocessor",
"next": "mxpi_objectselector0"
},
"mxpi_objectselector0": {
"props": {
"dataSource": "mxpi_objectpostprocessor0",
"FirstDetectionFilter": {
"Type": "Area",
"TopN": 1,
"BottomN": 0,
"MinArea": 0,
"MaxArea": 0,
"ConfThresh": 0.1
}
},
"factory": "mxpi_objectselector",
"next": "mxpi_distributor0"
},
"mxpi_distributor0": {
"props": {
"dataSource": "mxpi_objectselector0",
"classIds": "0"
},
"factory": "mxpi_distributor",
"next": "queue2"
},
"queue2": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imagecrop0"
},
"mxpi_imagecrop0":{
"props":{
"dataSource": "mxpi_distributor0_0",
"dataSourceImage": "mxpi_imageresize0",
"resizeHeight":"256",
"resizeWidth":"128"
},
"factory":"mxpi_imagecrop",
"next":"mxpi_imageresize1"
},
"mxpi_imageresize1": {
"props": {
"resizeHeight": "256",
"resizeWidth": "128"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer1"
},
"mxpi_tensorinfer1": {
"props": {
"dataSource": "mxpi_imageresize1",
"dynamicStrategy": "Upper",
"modelPath": "models/OSNet/osnet.om",
"waitingTime": "1"
},
"factory": "mxpi_tensorinfer",
"next": "appsink0"
},
"appsink0": {
"props": {
"blocksize": "4096000"
},
"factory": "appsink"
}
}
}

View File

@@ -0,0 +1,185 @@
{
"queryImageProcess": {
"stream_config": {
"deviceId": "0"
},
"appsrc0": {
"props": {
"blocksize": "409600"
},
"factory": "appsrc",
"next": "mxpi_imagedecoder0"
},
"mxpi_imagedecoder0": {
"factory": "mxpi_imagedecoder",
"next": "tee0"
},
"tee0": {
"factory": "tee",
"next": [
"queue0",
"queue1"
]
},
"queue0": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imageresize0"
},
"mxpi_imageresize0": {
"props": {
"resizeHeight": "416",
"resizeWidth": "416"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer0"
},
"mxpi_tensorinfer0": {
"props": {
"dataSource": "mxpi_imageresize0",
"modelPath": "models/YOLOv3/yolov3.om",
"waitingTime": "1"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_objectpostprocessor0"
},
"queue1": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_opencvosd0:0"
},
"mxpi_objectpostprocessor0": {
"props": {
"dataSource": "mxpi_tensorinfer0",
"postProcessConfigPath": "config/yolov3_tf_bs1_fp16.cfg",
"labelPath": "config/coco.names",
"postProcessLibPath": "libyolov3postprocess.so"
},
"factory": "mxpi_objectpostprocessor",
"next": "mxpi_objectselector0"
},
"mxpi_objectselector0": {
"props": {
"FirstDetectionFilter": {
"Type": "Area",
"TopN": 0,
"BottomN": 0,
"MinArea": 5120,
"MaxArea": 173056,
"ConfThresh": 0.4
}
},
"factory": "mxpi_objectselector",
"next": "mxpi_distributor0"
},
"mxpi_distributor0": {
"props": {
"dataSource": "mxpi_objectselector0",
"classIds": "0"
},
"factory": "mxpi_distributor",
"next": "tee1"
},
"tee1": {
"factory": "tee",
"next": [
"queue2",
"queue3"
]
},
"queue2": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imagecrop0"
},
"queue3": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_featurematch0:1"
},
"mxpi_imagecrop0":{
"props":{
"dataSource": "mxpi_distributor0_0",
"dataSourceImage": "mxpi_imageresize0",
"resizeHeight":"256",
"resizeWidth":"128"
},
"factory":"mxpi_imagecrop",
"next":"mxpi_tensorinfer1"
},
"mxpi_tensorinfer1": {
"props": {
"dataSource": "mxpi_imagecrop0",
"dynamicStrategy": "Upper",
"modelPath": "models/OSNet/osnet.om",
"waitingTime": "1"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_featurematch0"
},
"mxpi_featurematch0": {
"props": {
"status": "1",
"querySource": "mxpi_tensorinfer1",
"objectSource": "mxpi_distributor0_0",
"galleryFeaturesPath": "output/gallery/gallery_features.bin",
"galleryIdsPath": "output/gallery/persons.txt",
"metric": "euclidean",
"threshold": "-1"
},
"factory": "mxpi_featurematch",
"next": "mxpi_object2osdinstances0"
},
"mxpi_object2osdinstances0": {
"props": {
"dataSource": "mxpi_featurematch0",
"colorMap":"255,100,100|100,255,100",
"fontFace": "1",
"fontScale": "0.8",
"fontThickness": "1",
"fontLineType": "8",
"rectThickness": "1",
"rectLineType": "8"
},
"factory": "mxpi_object2osdinstances",
"next": "queue4"
},
"queue4": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_opencvosd0:1"
},
"mxpi_opencvosd0":{
"factory":"mxpi_opencvosd",
"next":"mxpi_imageencoder0"
},
"mxpi_imageencoder0":{
"factory":"mxpi_imageencoder",
"next":"queue5"
},
"queue5": {
"props": {
"max-size-buffers": "100"
},
"factory": "queue",
"next":"appsink0"
},
"appsink0": {
"props": {
"blocksize": "4096000"
},
"factory": "appsink"
}
}
}

View File

@@ -0,0 +1,215 @@
{
"queryVideoProcess": {
"stream_config": {
"deviceId": "0"
},
"mxpi_rtspsrc0": {
"factory": "mxpi_rtspsrc",
"props": {
"rtspUrl":"rtsp://xxx.xxx.xxx.xxx:xxxx/xxx.264",
"channelId": "0",
"timeout": "30"
},
"next": "mxpi_videodecoder0"
},
"mxpi_videodecoder0": {
"factory": "mxpi_videodecoder",
"props": {
"inputVideoFormat": "H264",
"outputImageFormat": "YUV420SP_NV12"
},
"next": "tee0"
},
"tee0": {
"factory": "tee",
"next": [
"queue0",
"queue1"
]
},
"queue0": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imageresize0"
},
"mxpi_imageresize0": {
"props": {
"resizeHeight": "416",
"resizeWidth": "416"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer0"
},
"queue1": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_opencvosd0:0"
},
"mxpi_tensorinfer0": {
"props": {
"dataSource": "mxpi_imageresize0",
"modelPath": "models/YOLOv3/yolov3.om",
"waitingTime": "50"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_objectpostprocessor0"
},
"mxpi_objectpostprocessor0": {
"props": {
"dataSource": "mxpi_tensorinfer0",
"postProcessConfigPath": "config/yolov3_tf_bs1_fp16.cfg",
"labelPath": "config/coco.names",
"postProcessLibPath": "libyolov3postprocess.so"
},
"factory": "mxpi_objectpostprocessor",
"next": "mxpi_objectselector0"
},
"mxpi_objectselector0": {
"props": {
"dataSource": "mxpi_objectpostprocessor0",
"FirstDetectionFilter": {
"Type": "Area",
"TopN": 0,
"BottomN": 0,
"MinArea": 5120,
"MaxArea": 173056,
"ConfThresh": 0.3
}
},
"factory": "mxpi_objectselector",
"next": "mxpi_distributor0"
},
"mxpi_distributor0": {
"props": {
"dataSource": "mxpi_objectselector0",
"classIds": "0"
},
"factory": "mxpi_distributor",
"next": "tee1"
},
"tee1": {
"factory": "tee",
"next": [
"queue2",
"queue3"
]
},
"queue2": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imagecrop0"
},
"queue3": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_featurematch0:1"
},
"mxpi_imagecrop0":{
"props":{
"dataSource": "mxpi_distributor0_0",
"dataSourceImage": "mxpi_imageresize0",
"resizeHeight":"256",
"resizeWidth":"128"
},
"factory":"mxpi_imagecrop",
"next":"mxpi_imageresize1"
},
"mxpi_imageresize1": {
"props": {
"resizeHeight": "256",
"resizeWidth": "128"
},
"factory": "mxpi_imageresize",
"next": "mxpi_tensorinfer1"
},
"mxpi_tensorinfer1": {
"props": {
"dataSource": "mxpi_imageresize1",
"dynamicStrategy": "Upper",
"modelPath": "models/OSNet/osnet.om",
"waitingTime": "60"
},
"factory": "mxpi_tensorinfer",
"next": "mxpi_featurematch0"
},
"mxpi_featurematch0": {
"props": {
"status": "1",
"querySource": "mxpi_tensorinfer1",
"objectSource": "mxpi_distributor0_0",
"galleryFeaturesPath": "output/gallery/gallery_features.bin",
"galleryIdsPath": "output/gallery/persons.txt",
"metric": "euclidean",
"threshold": "-1"
},
"factory": "mxpi_featurematch",
"next": "mxpi_object2osdinstances0"
},
"mxpi_object2osdinstances0": {
"props": {
"dataSource": "mxpi_featurematch0",
"colorMap":"255,100,100|100,255,100",
"fontFace": "1",
"fontScale": "0.8",
"fontThickness": "1",
"fontLineType": "8",
"rectThickness": "1",
"rectLineType": "8"
},
"factory": "mxpi_object2osdinstances",
"next": "queue4"
},
"queue4": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_opencvosd0:1"
},
"mxpi_opencvosd0":{
"factory":"mxpi_opencvosd",
"next":"queue6"
},
"queue6": {
"props": {
"max-size-buffers": "200"
},
"factory": "queue",
"next": "mxpi_imageresize2"
},
"mxpi_imageresize2": {
"props": {
"dataSource": "mxpi_opencvosd0",
"resizeHeight": "416",
"resizeWidth": "416"
},
"factory": "mxpi_imageresize",
"next": "mxpi_videoencoder0"
},
"mxpi_videoencoder0":{
"props": {
"imageHeight": "416",
"imageWidth": "416",
"iFrameInterval": "200",
"fps": "1"
},
"factory":"mxpi_videoencoder",
"next":"filesink0"
},
"filesink0":{
"props":{
"blocksize":"40960000",
"location":"output/query/videos/reid.264"
},
"factory":"filesink"
}
}
}

View File

@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.10)
project(mxpi_featurematch)
set(CMAKE_CXX_STANDARD 11)
set(PLUGIN_NAME "mxpi_featurematch")
set(TARGET_LIBRARY ${PLUGIN_NAME})
add_compile_options(-fPIC -fstack-protector-all -g -Wl,-z,relro,-z,now,-z -pie -Wall)
add_compile_options(-std=c++11 -Wno-deprecated-declarations)
add_compile_options("-DPLUGIN_NAME=${PLUGIN_NAME}")
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0 -Dgoogle=mindxsdk_private)
add_definitions(-DENABLE_DVPP_INTERFACE)
set(MX_SDK_HOME "$ENV{MX_SDK_HOME}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${MX_SDK_HOME}/lib/plugins/)
include_directories(${MX_SDK_HOME}/include)
include_directories(${MX_SDK_HOME}/opensource/include)
include_directories(${MX_SDK_HOME}/opensource/include/gstreamer-1.0)
include_directories(${MX_SDK_HOME}/opensource/include/opencv4)
include_directories(${MX_SDK_HOME}/opensource/include/glib-2.0)
include_directories(${MX_SDK_HOME}/opensource/lib/glib-2.0/include)
link_directories(${MX_SDK_HOME}/lib)
link_directories(${MX_SDK_HOME}/opensource/lib)
file(GLOB PLUGIN_SRC ./*.cpp)
message(${PLUGIN_SRC})
add_library(${TARGET_LIBRARY} SHARED ${PLUGIN_SRC})
target_link_libraries(${TARGET_LIBRARY}
mxpidatatype
plugintoolkit
mxbase
streammanager
mindxsdk_protobuf
glib-2.0
gstreamer-1.0
gobject-2.0
gstbase-1.0
gmodule-2.0
)

View File

@@ -0,0 +1,335 @@
/*
* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MxBase/Log/Log.h"
#include "MxBase/Tensor/TensorBase/TensorBase.h"
#include "Plugin_FeatureMatch.h"
#include <algorithm>
#include <iostream>
#include <fstream>
using namespace MxPlugins;
using namespace MxTools;
using namespace MxBase;
using namespace cv;
APP_ERROR PluginFeatureMatch::Init(std::map<std::string, std::shared_ptr<void>> &configParamMap) {
LogInfo << "Begin to initialize PluginFeatureMatch(" << pluginName_ << ").";
// Get the property values by key
std::shared_ptr<std::string> querySource = std::static_pointer_cast<std::string>(configParamMap["querySource"]);
querySource_ = *querySource;
std::shared_ptr<std::string> objectSource = std::static_pointer_cast<std::string>(configParamMap["objectSource"]);
objectSource_ = *objectSource;
std::shared_ptr<std::string> galleryFeaturesPath = std::static_pointer_cast<std::string>(configParamMap["galleryFeaturesPath"]);
galleryFeaturesPath_ = *galleryFeaturesPath;
std::shared_ptr<std::string> galleryIdsPath = std::static_pointer_cast<std::string>(configParamMap["galleryIdsPath"]);
galleryIdsPath_ = *galleryIdsPath;
std::shared_ptr<std::string> metric = std::static_pointer_cast<std::string>(configParamMap["metric"]);
metric_ = *metric;
std::shared_ptr<std::float_t> threshold = std::static_pointer_cast<float_t>(configParamMap["threshold"]);
threshold_ = *threshold;
ReadGalleryFeatures(galleryFeaturesPath_, galleryIdsPath_, galleryFeatures, galleryIds);
LogInfo << "End to initialize PluginFeatureMatch(" << pluginName_ << ").";
return APP_ERR_OK;
}
APP_ERROR PluginFeatureMatch::DeInit() {
LogInfo << "Begin to deinitialize PluginFeatureMatch(" << pluginName_ << ").";
LogInfo << "End to deinitialize PluginFeatureMatch(" << pluginName_ << ").";
return APP_ERR_OK;
}
APP_ERROR PluginFeatureMatch::CheckDataSource(MxTools::MxpiMetadataManager &mxpiMetadataManager,
MxTools::MxpiMetadataManager &mxpiMetadataManager1) {
if (mxpiMetadataManager.GetMetadata(querySource_) == nullptr) {
LogDebug << GetError(APP_ERR_METADATA_IS_NULL, pluginName_)
<< "class metadata is null. please check"
<< "Your property querySource(" << querySource_ << ").";
return APP_ERR_METADATA_IS_NULL;
}
if (mxpiMetadataManager1.GetMetadata(objectSource_) == nullptr) {
LogDebug << GetError(APP_ERR_METADATA_IS_NULL, pluginName_)
<< "class metadata is null. please check"
<< "Your property objectSource(" << objectSource_ << ").";
return APP_ERR_METADATA_IS_NULL;
}
return APP_ERR_OK;
}
void EuclideanSquaredDistance(Mat queryFeatures, Mat galleryFeatures, Mat &distMat) {
int admm_beta = 1;
int admm_alpha = -2;
int square = 2;
Mat qdst1;
pow(queryFeatures, square, qdst1);
Mat qdst2;
reduce(qdst1, qdst2, 1, REDUCE_SUM);
Mat qdst3 = Mat(qdst2.rows, galleryFeatures.rows, CV_32FC1);
for (int i = 0; i < galleryFeatures.rows; i++) {
Mat dstTemp = qdst3.col(i);
qdst2.copyTo(dstTemp);
}
Mat gdst1;
pow(galleryFeatures, square, gdst1);
Mat gdst2;
reduce(gdst1, gdst2, 1, REDUCE_SUM);
Mat gdst3 = Mat(gdst2.rows, queryFeatures.rows, CV_32FC1);
for (int i = 0; i < queryFeatures.rows;i++) {
Mat dstTemp = gdst3.col(i);
gdst2.copyTo(dstTemp);
}
Mat gdst4;
transpose(gdst3, gdst4);
Mat dst1 = qdst3 +gdst4;
Mat gdst5;
transpose(galleryFeatures, gdst5);
Mat dst2 = queryFeatures * gdst5;
distMat = admm_beta * dst1 + admm_alpha * dst2;
}
void CosineDistance(Mat queryFeatures, Mat galleryFeatures, Mat &distMat) {
Mat gdst1;
transpose(galleryFeatures, gdst1);
distMat = queryFeatures * gdst1;
}
APP_ERROR PluginFeatureMatch::ComputeDistance(MxpiTensorPackageList queryTensors,
Mat galleryFeatures, int tensorSize, Mat &distMat) {
Mat queryFeatures = Mat::zeros(tensorSize, queryTensors.
tensorpackagevec(0).tensorvec(0).tensorshape(1), CV_32FC1);
for (int i = 0; i < tensorSize; i++) {
Mat dstTemp = queryFeatures.row(i);
auto vec = queryTensors.tensorpackagevec(i).tensorvec(0);
Mat feature = Mat(vec.tensorshape(0), vec.tensorshape(1), CV_32FC1,
(void *) vec.tensordataptr());
feature.copyTo(dstTemp);
}
if (!(metric_.compare("euclidean"))) {
EuclideanSquaredDistance(queryFeatures, galleryFeatures, distMat);
}
else if (!(metric_.compare("cosine"))) {
CosineDistance(queryFeatures, galleryFeatures, distMat);
}
return APP_ERR_OK;
}
void PluginFeatureMatch::GenerateOutput(MxpiObjectList srcObjectList, Mat distMat,
int tensorSize, std::vector<std::string> galleryIds,
MxpiObjectList &dstMxpiObjectList) {
std::vector<float> minValues = {};
for (int i = 0; i < tensorSize; i++) {
auto minValue = std::min_element(distMat.ptr<float>(i, 0), distMat.ptr<float>(i, 0+distMat.cols));
if (threshold_ == -1 || *minValue < threshold_) {
int minIndex = std::distance(distMat.ptr<float>(i, 0), minValue);
auto objvec = srcObjectList.objectvec(i);
MxpiObject* dstMxpiObject = dstMxpiObjectList.add_objectvec();
MxpiMetaHeader* dstMxpiMetaHeaderList = dstMxpiObject->add_headervec();
dstMxpiMetaHeaderList->set_datasource(objectSource_);
dstMxpiMetaHeaderList->set_memberid(0);
dstMxpiObject->set_x0(objvec.x0());
dstMxpiObject->set_y0(objvec.y0());
dstMxpiObject->set_x1(objvec.x1());
dstMxpiObject->set_y1(objvec.y1());
// Generate ClassList
MxpiClass* dstMxpiClass = dstMxpiObject->add_classvec();
MxpiMetaHeader* dstMxpiMetaHeaderList_c = dstMxpiClass->add_headervec();
dstMxpiMetaHeaderList_c->set_datasource(objectSource_);
dstMxpiMetaHeaderList_c->set_memberid(0);
dstMxpiClass->set_classid(i);
dstMxpiClass->set_confidence(objvec.classvec(0).confidence());
dstMxpiClass->set_classname(galleryIds[minIndex]);
}
}
}
void PluginFeatureMatch::ReadGalleryFeatures(std::string featuresPath,
std::string idsPath, Mat &galleryFeatures,
std::vector<std::string> &galleryIds) {
// 读取gallery的人名
std::ifstream ifile(idsPath);
std::string str1;
while (std::getline(ifile, str1)) {
galleryIds.push_back(str1);
}
ifile.close();
// 读取gallery特征库
int featureLen = 512;
const char* feaPath = featuresPath.c_str();
FILE* fp = fopen(feaPath, "rb");
galleryFeatures = Mat::zeros(int(galleryIds.size()), featureLen, CV_32FC1);
for (int i = 0; i < int(galleryIds.size()); i++)
{
for (int j = 0; j < featureLen; j++)
{
fread(&galleryFeatures.at<float>(i, j), 1, sizeof(float), fp);
}
}
fclose(fp);
fp = NULL;
}
APP_ERROR PluginFeatureMatch::Process(std::vector<MxpiBuffer *> &mxpiBuffer) {
LogInfo << "Begin to process PluginFeatureMatch.";
// Get MxpiClassList from MxpiBuffer
MxpiBuffer *inputMxpiBuffer = mxpiBuffer[0];
MxpiMetadataManager mxpiMetadataManager(*inputMxpiBuffer);
auto errorInfoPtr = mxpiMetadataManager.GetErrorInfo();
MxpiBuffer *inputMxpiBuffer1 = mxpiBuffer[1];
MxpiMetadataManager mxpiMetadataManager1(*inputMxpiBuffer1);
auto errorInfoPtr1 = mxpiMetadataManager1.GetErrorInfo();
MxpiErrorInfo mxpiErrorInfo;
if (errorInfoPtr != nullptr | errorInfoPtr1 != nullptr) {
ErrorInfo_ << GetError(APP_ERR_COMM_FAILURE, pluginName_)
<< "PluginFeatureMatch process is not implemented";
mxpiErrorInfo.ret = APP_ERR_COMM_FAILURE;
mxpiErrorInfo.errorInfo = ErrorInfo_.str();
LogError << "PluginFeatureMatch process is not implemented";
return APP_ERR_COMM_FAILURE;
}
// check data source
APP_ERROR ret = CheckDataSource(mxpiMetadataManager, mxpiMetadataManager1);
if (ret != APP_ERR_OK) {
SendData(0, *inputMxpiBuffer1);
return ret;
}
std::shared_ptr<void> queryListMetadata = mxpiMetadataManager.GetMetadata(querySource_);
std::shared_ptr<void> objectListMetadata = mxpiMetadataManager1.GetMetadata(objectSource_);
if (queryListMetadata == nullptr || objectListMetadata == nullptr) {
ErrorInfo_ << GetError(APP_ERR_METADATA_IS_NULL, pluginName_) << "Metadata is NULL, failed";
mxpiErrorInfo.ret = APP_ERR_METADATA_IS_NULL;
mxpiErrorInfo.errorInfo = ErrorInfo_.str();
return APP_ERR_METADATA_IS_NULL;
}
std::shared_ptr<MxpiTensorPackageList> srcQueryListPtr = std::static_pointer_cast<MxpiTensorPackageList>(queryListMetadata);
std::shared_ptr<MxpiObjectList> srcObjectListPtr = std::static_pointer_cast<MxpiObjectList>(objectListMetadata);
std::shared_ptr<MxpiObjectList> resultObjectListPtr = std::make_shared<MxpiObjectList>();
int objectSize = (*srcObjectListPtr).objectvec_size();
int tensorSize = (*srcQueryListPtr).tensorpackagevec_size();
Mat distMat = Mat(tensorSize, int(galleryIds.size()), CV_32FC1);
if (objectSize > 0 && objectSize == tensorSize) {
ret = ComputeDistance(*srcQueryListPtr, galleryFeatures, tensorSize, distMat);
}
GenerateOutput(*srcObjectListPtr, distMat, tensorSize, galleryIds, *resultObjectListPtr);
ret = mxpiMetadataManager1.AddProtoMetadata(pluginName_, std::static_pointer_cast<void>(resultObjectListPtr));
if (ret != APP_ERR_OK) {
LogError << ErrorInfo_.str();
SendMxpiErrorInfo(*inputMxpiBuffer1, pluginName_, ret, ErrorInfo_.str());
SendData(0, *inputMxpiBuffer1);
}
// Send the data to downstream plugin
SendData(0, *inputMxpiBuffer1);
LogInfo << "End to process PluginFeatureMatch(" << elementName_ << ").";
return APP_ERR_OK;
}
std::vector<std::shared_ptr<void>> PluginFeatureMatch::DefineProperties() {
std::vector<std::shared_ptr<void>> properties;
// Get the action category from previous plugin
auto querySource = std::make_shared<ElementProperty<std::string>>(ElementProperty<std::string> {
STRING,
"querySource",
"queryFeatureSource",
"query infer output ",
"default", "NULL", "NULL"
});
auto objectSource = std::make_shared<ElementProperty<std::string>>(ElementProperty<std::string> {
STRING,
"objectSource",
"queryobjectSource",
"detection infer postprocess output ",
"default", "NULL", "NULL"
});
// The action of interest file path
auto galleryFeaturesPath = std::make_shared<ElementProperty<std::string>>(ElementProperty<std::string> {
STRING,
"galleryFeaturesPath",
"features of gallery file path",
"the path of gallery images features file",
"NULL", "NULL", "NULL"
});
auto galleryIdsPath = std::make_shared<ElementProperty<std::string>>(ElementProperty<std::string> {
STRING,
"galleryIdsPath",
"id of gallery file path",
"the path of gallery person name file",
"NULL", "NULL", "NULL"
});
auto metric = std::make_shared<ElementProperty<std::string>>(ElementProperty<std::string> {
STRING,
"metric",
"metric",
"the method of compute distance, select from 'euclidean', 'cosine'.",
"euclidean", "NULL", "NULL"
});
auto threshold = std::make_shared<ElementProperty<float>>(ElementProperty<float> {
FLOAT,
"threshold",
"distanceThreshold",
"if distance is more than threshold,not matched gallery",
-1.0, -1.0, 1000.0
});
properties.push_back(querySource);
properties.push_back(objectSource);
properties.push_back(galleryFeaturesPath);
properties.push_back(galleryIdsPath);
properties.push_back(metric);
properties.push_back(threshold);
return properties;
}
MxpiPortInfo PluginFeatureMatch::DefineInputPorts() {
MxpiPortInfo inputPortInfo;
std::vector<std::vector<std::string>> value = {{"ANY"}, {"ANY"}};
GenerateStaticInputPortsInfo(value, inputPortInfo);
return inputPortInfo;
}
MxpiPortInfo PluginFeatureMatch::DefineOutputPorts() {
MxpiPortInfo outputPortInfo;
// Output: {{MxpiObjectList}}
std::vector<std::vector<std::string>> value = {{"ANY"}};
GenerateStaticOutputPortsInfo(value, outputPortInfo);
return outputPortInfo;
}
namespace {
MX_PLUGIN_GENERATE(PluginFeatureMatch)
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SDKMEMORY_PLUGINFEATUREMATCH_H
#define SDKMEMORY_PLUGINFEATUREMATCH_H
#include "opencv2/opencv.hpp"
#include "opencv2/core/mat.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "MxBase/ErrorCode/ErrorCode.h"
#include "MxTools/PluginToolkit/base/MxPluginGenerator.h"
#include "MxTools/PluginToolkit/base/MxPluginBase.h"
#include "MxTools/PluginToolkit/metadata/MxpiMetadataManager.h"
#include "MxTools/Proto/MxpiDataType.pb.h"
#include "MxBase/ErrorCode/ErrorCode.h"
/**
* This plug is to recognize whether the object's action is a Violent Action and alarm.
*/
namespace MxPlugins {
class PluginFeatureMatch : public MxTools::MxPluginBase {
public:
/**
* @description: Init configs.
* @param configParamMap: config.
* @return: Error code.
*/
APP_ERROR Init(std::map<std::string, std::shared_ptr<void>> &configParamMap) override;
/**
* @description: DeInit device.
* @return: Error code.
*/
APP_ERROR DeInit() override;
/**
* @description: Plugin_FeatureMatch plugin process.
* @param mxpiBuffer: data receive from the previous.
* @return: Error code.
*/
APP_ERROR Process(std::vector<MxTools::MxpiBuffer *> &mxpiBuffer) override;
/**
* @description: Plugin_FeatureMatch plugin define properties.
* @return: properties.
*/
static std::vector<std::shared_ptr<void>> DefineProperties();
/**
* @api
* @brief Define the number and data type of input ports.
* @return MxTools::MxpiPortInfo.
*/
static MxTools::MxpiPortInfo DefineInputPorts();
/**
* @api
* @brief Define the number and data type of output ports.
* @return MxTools::MxpiPortInfo.
*/
static MxTools::MxpiPortInfo DefineOutputPorts();
private:
/**
* @api
* @brief Check metadata.
* @param MxTools::MxpiMetadataManager.
* @return Error Code.
*/
APP_ERROR CheckDataSource(MxTools::MxpiMetadataManager &mxpiMetadataManager,
MxTools::MxpiMetadataManager &mxpiMetadataManager1);
APP_ERROR ComputeDistance(MxTools::MxpiTensorPackageList queryTensors,
cv::Mat galleryFeatures, int tensorSize, cv::Mat &distMat);
void GenerateOutput(MxTools::MxpiObjectList srcObjectList, cv::Mat distMat,
int tensorSize, std::vector<std::string> galleryIds,
MxTools::MxpiObjectList &dstMxpiObjectList);
void ReadGalleryFeatures(std::string featuresPath,
std::string idsPath, cv::Mat &galleryFeatures, std::vector<std::string> &galleryIds);
std::string querySource_ = ""; // previous plugin MxpiClassList
std::string objectSource_ = "";
std::string galleryFeaturesPath_ = "";
std::string galleryIdsPath_ = "";
std::string metric_ = "";
std::ostringstream ErrorInfo_; // Error Code
float threshold_ = 0.0;
cv::Mat galleryFeatures = cv::Mat();
std::vector<std::string> galleryIds = {};
};
}
#endif

View File

@@ -0,0 +1,36 @@
#!/bin/bash
# Copyright 2021 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.mitations under the License.
set -e
current_folder="$( cd "$(dirname "$0")" ;pwd -P )"
function build_plugin() {
build_path=$current_folder/build
if [ -d "$build_path" ]; then
rm -rf "$build_path"
else
echo "file $build_path is not exist."
fi
mkdir -p "$build_path"
cd "$build_path"
cmake ..
make -j
cd ..
exit 0
}
build_plugin
exit 0

30
Video-ReID/run.sh Normal file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# Copyright(C) 2021. Huawei Technologies Co.,Ltd. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
MODE=$1
DURATION=$2
if [ ${MODE} = "image" ]; then
python image.py
elif [ ${MODE} = "video" ]; then
python video.py --duration ${DURATION}
elif [ ${MODE} = "gallery" ]; then
python get_gallery_features.py
else
echo -e "The mode must be image or video or gallery"
fi
exit 0

83
Video-ReID/video.py Normal file
View File

@@ -0,0 +1,83 @@
#!/usr/bin/env python
# coding=utf-8
"""
Copyright(C) Huawei Technologies Co.,Ltd. 2012-2021 All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import argparse
import time
from StreamManagerApi import StreamManagerApi
def initialize_stream():
"""
Initialize stream for detecting and re-identifying persons in video
:arg:
None
:return:
Stream api
"""
stream_api = StreamManagerApi()
ret = stream_api.InitManager()
if ret != 0:
error_message = "Failed to init Stream manager, ret=%s" % str(ret)
print(error_message)
exit()
# creating stream based on json strings in the pipeline file: 'ReID.pipeline'
with open("pipeline/video.pipeline", 'rb') as f:
pipeline = f.read()
ret = stream_api.CreateMultipleStreams(pipeline)
if ret != 0:
error_message = "Failed to create Stream, ret=%s" % str(ret)
print(error_message)
exit()
return stream_api
def wait(duration):
"""
Wait for destroy streams
:arg:
duration: Duration of process video
:return:
None
"""
start = time.time()
while True:
now = time.time()
cost_time = now - start
if cost_time >= duration:
break
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--duration', type=float, default= 30, help="Duration Of Process Video")
opt = parser.parse_args()
stream_manager_api = initialize_stream()
wait(opt.duration)
stream_manager_api.DestroyAllStreams()