蚕茧视频识别AI程序关键代码(不包含资源、模型、转换库)
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
Prerequisites:
|
||||
- DeepStreamSDK 8.0
|
||||
- NVIDIA Triton Inference Server (optional)
|
||||
- Python 3.12
|
||||
- Gst-python
|
||||
|
||||
To set up Triton Inference Server: (optional)
|
||||
For x86_64 and Jetson Docker:
|
||||
1. Use the provided docker container and follow directions for
|
||||
Triton Inference Server in the SDK README --
|
||||
be sure to prepare the detector models.
|
||||
2. Run the docker with this Python Bindings directory mapped
|
||||
3. Install required Python packages inside the container:
|
||||
$ apt update
|
||||
$ apt install python3-gi python3-dev python3-gst-1.0 -y
|
||||
$ pip3 install pathlib
|
||||
4. Build and install pyds bindings:
|
||||
Follow the instructions in bindings README in this repo to build and install
|
||||
pyds wheel for Ubuntu 24.04
|
||||
5. For Triton gRPC setup, please follow the instructions at below location:
|
||||
/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app-triton-grpc/README
|
||||
|
||||
For Jetson without Docker:
|
||||
1. Follow instructions in the DeepStream SDK README to set up
|
||||
Triton Inference Server:
|
||||
2.1 Compile and install the nvdsinfer_customparser
|
||||
2.2 Prepare at least the Triton detector models
|
||||
2. Build and install pyds bindings:
|
||||
Follow the instructions in bindings README in this repo to build and install
|
||||
pyds wheel for Ubuntu 24.04
|
||||
3. Clear the GStreamer cache if pipeline creation fails:
|
||||
rm ~/.cache/gstreamer-1.0/*
|
||||
4. For Triton gRPC setup, please follow the instructions at below location:
|
||||
/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app-triton-grpc/README
|
||||
|
||||
To setup peoplenet model and configs (optional):
|
||||
Download Peoplenet model:
|
||||
$ mkdir -p /opt/nvidia/deepstream/deepstream/samples/models/peoplenet
|
||||
$ cd /opt/nvidia/deepstream/deepstream/samples/models/peoplenet
|
||||
$ wget --content-disposition 'https://api.ngc.nvidia.com/v2/models/org/nvidia/team/tao/peoplenet/pruned_quantized_decrypted_v2.3.4/files?redirect=true&path=resnet34_peoplenet_int8.onnx' -O resnet34_peoplenet_int8.onnx
|
||||
$ wget --content-disposition 'https://api.ngc.nvidia.com/v2/models/org/nvidia/team/tao/peoplenet/pruned_quantized_decrypted_v2.3.4/files?redirect=true&path=resnet34_peoplenet_int8.txt' -O resnet34_peoplenet_int8.txt
|
||||
$ wget --content-disposition 'https://api.ngc.nvidia.com/v2/models/org/nvidia/team/tao/peoplenet/pruned_quantized_decrypted_v2.3.4/files?redirect=true&path=labels.txt' -O labels.txt
|
||||
|
||||
Additionally, for Triton and Triton gRPC
|
||||
$ cp config.pbtxt /opt/nvidia/deepstream/deepstream/samples/models/peoplenet
|
||||
$ mkdir -p /opt/nvidia/deepstream/deepstream/samples/models/peoplenet/1
|
||||
$ /usr/src/tensorrt/bin/trtexec --onnx=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/resnet34_peoplenet_int8.onnx --fp16 \
|
||||
--saveEngine=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/1/resnet34_peoplenet_int8.onnx_b2_gpu0_fp16.engine \
|
||||
--minShapes="input_1:0":1x3x544x960 \
|
||||
--optShapes="input_1:0":2x3x544x960 \
|
||||
--maxShapes="input_1:0":2x3x544x960
|
||||
|
||||
To run:
|
||||
$ python3 deepstream_test_3.py -i <uri1> [uri2] ... [uriN] [--no-display] [--silent]
|
||||
e.g.
|
||||
$ python3 deepstream_test_3.py -i file:///home/ubuntu/video1.mp4 file:///home/ubuntu/video2.mp4
|
||||
$ python3 deepstream_test_3.py -i rtsp://127.0.0.1/video1 rtsp://127.0.0.1/video2 -s
|
||||
|
||||
To run peoplenet, test3 now supports 3 modes:
|
||||
|
||||
1. nvinfer + peoplenet: this mode still uses TRT for inferencing.
|
||||
|
||||
$ python3 deepstream_test_3.py -i <uri1> [uri2] ... [uriN] --pgie nvinfer -c config_infer_primary_peoplenet.txt [--no-display] [--silent]
|
||||
|
||||
2. nvinferserver + peoplenet : this mode uses Triton for inferencing.
|
||||
|
||||
$ python3 deepstream_test_3.py -i <uri1> [uri2] ... [uriN] --pgie nvinferserver -c config_triton_infer_primary_peoplenet.txt [--no-display] [-s]
|
||||
|
||||
3. nvinferserver (gRPC) + peoplenet : this mode uses Triton gRPC for inferencing.
|
||||
|
||||
$ mkdir -p /opt/nvidia/deepstream/deepstream/samples/models/peoplenet-grpc/peoplenet
|
||||
$ cp /opt/nvidia/deepstream/deepstream/samples/models/peoplenet/config.pbtxt /opt/nvidia/deepstream/deepstream/samples/models/peoplenet-grpc/peoplenet/config.pbtxt
|
||||
$ cp -a /opt/nvidia/deepstream/deepstream/samples/models/peoplenet/1 /opt/nvidia/deepstream/deepstream/samples/models/peoplenet-grpc/peoplenet/1
|
||||
$ tritonserver --model-repository=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet-grpc
|
||||
$ python3 deepstream_test_3.py -i <uri1> [uri2] ... [uriN] --pgie nvinferserver-grpc -c config_triton_grpc_infer_primary_peoplenet.txt [--no-display] [--silent]
|
||||
|
||||
e.g.
|
||||
$ python3 deepstream_test_3.py -i file:///home/ubuntu/video1.mp4 file:///home/ubuntu/video2.mp4 --pgie nvinfer -c config_infer_primary_peoplenet.txt --no-display --silent
|
||||
$ python3 deepstream_test_3.py -i rtsp://127.0.0.1/video1 rtsp://127.0.0.1/video2 --pgie nvinferserver -c config_triton_infer_primary_peoplenet.txt -s
|
||||
$ python3 deepstream_test_3.py -i rtsp://127.0.0.1/video1 rtsp://127.0.0.1/video2 --pgie nvinferserver-grpc -c config_triton_grpc_infer_primary_peoplenet.txt --no-display --silent
|
||||
|
||||
Note:
|
||||
1) if --pgie is not specified, test3 uses nvinfer and default model, not peoplenet.
|
||||
2) Both --pgie and -c need to be provided for custom models.
|
||||
3) Configs other than peoplenet can also be provided using the above approach.
|
||||
4) --no-display option disables on-screen video display.
|
||||
5) -s/--silent option can be used to suppress verbose output.
|
||||
6) --file-loop option can be used to loop input files after EOS.
|
||||
7) --disable-probe option can be used to disable the probe function and to use nvdslogger for perf measurements.
|
||||
8) To enable Pipeline Latency Measurement, set environment variable : NVDS_ENABLE_LATENCY_MEASUREMENT=1
|
||||
9) To enable Component Level Latency Measurement, set environment variable : NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 in addition to NVDS_ENABLE_LATENCY_MEASUREMENT=1
|
||||
|
||||
This document describes the sample deepstream-test3 application.
|
||||
|
||||
|
||||
* Use multiple sources in the pipeline.
|
||||
* Use a uridecodebin so that any type of input (e.g. RTSP/File), any GStreamer
|
||||
supported container format, and any codec can be used as input.
|
||||
* Configure the stream-muxer to generate a batch of frames and infer on the
|
||||
batch for better resource utilization.
|
||||
* Extract the stream metadata, which contains useful information about the
|
||||
frames in the batched buffer.
|
||||
* Showcases how to enable latency measurement using probe function
|
||||
|
||||
Refer to the deepstream-test1 sample documentation for an example of simple
|
||||
single-stream inference, bounding-box overlay, and rendering.
|
||||
|
||||
This sample accepts one or more H.264/H.265 video streams as input. It creates
|
||||
a source bin for each input and connects the bins to an instance of the
|
||||
"nvstreammux" element, which forms the batch of frames. The batch of
|
||||
frames is fed to "nvinfer" for batched inferencing. The batched buffer is
|
||||
composited into a 2D tile array using "nvmultistreamtiler." The rest of the
|
||||
pipeline is similar to the deepstream-test1 sample.
|
||||
|
||||
The "width" and "height" properties must be set on the stream-muxer to set the
|
||||
output resolution. If the input frame resolution is different from
|
||||
stream-muxer's "width" and "height", the input frame will be scaled to muxer's
|
||||
output resolution.
|
||||
|
||||
The stream-muxer waits for a user-defined timeout before forming the batch. The
|
||||
timeout is set using the "batched-push-timeout" property. If the complete batch
|
||||
is formed before the timeout is reached, the batch is pushed to the downstream
|
||||
element. If the timeout is reached before the complete batch can be formed
|
||||
(which can happen in case of rtsp sources), the batch is formed from the
|
||||
available input buffers and pushed. Ideally, the timeout of the stream-muxer
|
||||
should be set based on the framerate of the fastest source. It can also be set
|
||||
to -1 to make the stream-muxer wait infinitely.
|
||||
|
||||
The "nvmultistreamtiler" composite streams based on their stream-ids in
|
||||
row-major order (starting from stream 0, left to right across the top row, then
|
||||
across the next row, etc.).
|
||||
@@ -0,0 +1,97 @@
|
||||
import pika
|
||||
import uuid
|
||||
import time
|
||||
import os
|
||||
import urllib.parse
|
||||
from minio import Minio
|
||||
import os
|
||||
from sca import sca
|
||||
|
||||
|
||||
# MinIO 配置
|
||||
MINIO_HOST = "ai.ronsunny.cn:9000"
|
||||
MINIO_ACCESS_KEY = "minioadmin"
|
||||
MINIO_SECRET_KEY = "minioadmin"
|
||||
MINIO_SECURE = True
|
||||
BUCKET_NAME = "video-sca"
|
||||
TEMP_DIR = "tmp/"
|
||||
|
||||
# RabbitMQ 配置
|
||||
RABBITMQ_HOST = "10.10.12.101"
|
||||
RABBITMQ_PORT = 5672
|
||||
RABBITMQ_VHOST = "bbit_ai"
|
||||
RABBITMQ_USER = "ai_lab_iva_sca"
|
||||
RABBITMQ_PASS = "123456"
|
||||
RABBITMQ_QUEUE = "/sca_queue"
|
||||
|
||||
# 初始化 MinIO 客户端
|
||||
minio_client = Minio(
|
||||
MINIO_HOST,
|
||||
access_key=MINIO_ACCESS_KEY,
|
||||
secret_key=MINIO_SECRET_KEY,
|
||||
secure=MINIO_SECURE
|
||||
)
|
||||
|
||||
# ------------------- 消息处理 -------------------
|
||||
def callback(ch, method, properties, body):
|
||||
import json
|
||||
data = json.loads(body)
|
||||
|
||||
for record in data.get("Records", []):
|
||||
# 解析桶名和文件 Key
|
||||
bucket = record["s3"]["bucket"]["name"]
|
||||
key_encoded = record["s3"]["object"]["key"]
|
||||
key = urllib.parse.unquote(key_encoded)
|
||||
filename = os.path.basename(key)
|
||||
temp_file_path = os.path.join(TEMP_DIR, filename)
|
||||
|
||||
print(f"[1] 下载文件 {key} 到临时目录 {temp_file_path}")
|
||||
minio_client.fget_object(bucket, key, temp_file_path)
|
||||
|
||||
# AI 分析耗时
|
||||
print(f"[2] AI 分析...")
|
||||
# 生成分析后文件 UUID 名
|
||||
new_uuid = str(uuid.uuid4())
|
||||
new_filename = f"{new_uuid}"
|
||||
new_file_path = os.path.join(TEMP_DIR, new_filename)
|
||||
|
||||
# 转为绝对路径
|
||||
temp_file_path = os.path.abspath(temp_file_path)
|
||||
new_file_path = os.path.abspath(new_file_path)
|
||||
|
||||
# 这里暂时复制同一个视频模拟分析结果
|
||||
sca(["file://" + temp_file_path], new_file_path)
|
||||
print(f"[3] AI 分析完成,生成文件 {new_file_path}")
|
||||
|
||||
# 上传分析后视频到 video-sca/ai/
|
||||
dest_key = f"ai/{new_filename}"
|
||||
minio_client.fput_object(bucket, dest_key, new_file_path)
|
||||
print(f"[4] 上传分析视频到 MinIO {dest_key}")
|
||||
|
||||
# 删除临时文件
|
||||
os.remove(temp_file_path)
|
||||
os.remove(new_file_path)
|
||||
print("[5] 临时文件已删除\n")
|
||||
print("[6] 本次任务完成\n")
|
||||
|
||||
# 确认消息
|
||||
ch.basic_ack(delivery_tag=method.delivery_tag)
|
||||
# 连接 RabbitMQ
|
||||
# 设置凭证
|
||||
credentials = pika.PlainCredentials(RABBITMQ_USER, RABBITMQ_PASS)
|
||||
connection = pika.BlockingConnection(
|
||||
pika.ConnectionParameters(
|
||||
host=RABBITMQ_HOST,
|
||||
port=RABBITMQ_PORT,
|
||||
virtual_host=RABBITMQ_VHOST,
|
||||
credentials=credentials
|
||||
)
|
||||
)
|
||||
channel = connection.channel()
|
||||
channel.queue_declare(queue=RABBITMQ_QUEUE, durable=True)
|
||||
|
||||
channel.basic_qos(prefetch_count=1) # 一次只处理一个文件
|
||||
channel.basic_consume(queue=RABBITMQ_QUEUE, on_message_callback=callback)
|
||||
|
||||
print("Waiting for messages...")
|
||||
channel.start_consuming()
|
||||
@@ -0,0 +1,67 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 time
|
||||
from threading import Lock
|
||||
start_time=time.time()
|
||||
|
||||
fps_mutex = Lock()
|
||||
|
||||
class GETFPS:
|
||||
def __init__(self,stream_id):
|
||||
global start_time
|
||||
self.start_time=start_time
|
||||
self.is_first=True
|
||||
self.frame_count=0
|
||||
self.stream_id=stream_id
|
||||
|
||||
def update_fps(self):
|
||||
end_time = time.time()
|
||||
if self.is_first:
|
||||
self.start_time = end_time
|
||||
self.is_first = False
|
||||
else:
|
||||
global fps_mutex
|
||||
with fps_mutex:
|
||||
self.frame_count = self.frame_count + 1
|
||||
|
||||
def get_fps(self):
|
||||
end_time = time.time()
|
||||
with fps_mutex:
|
||||
stream_fps = float(self.frame_count/(end_time - self.start_time))
|
||||
self.frame_count = 0
|
||||
self.start_time = end_time
|
||||
return round(stream_fps, 2)
|
||||
|
||||
def print_data(self):
|
||||
print('frame_count=',self.frame_count)
|
||||
print('start_time=',self.start_time)
|
||||
|
||||
class PERF_DATA:
|
||||
def __init__(self, num_streams=1):
|
||||
self.perf_dict = {}
|
||||
self.all_stream_fps = {}
|
||||
for i in range(num_streams):
|
||||
self.all_stream_fps["stream{0}".format(i)]=GETFPS(i)
|
||||
|
||||
def perf_print_callback(self):
|
||||
self.perf_dict = {stream_index:stream.get_fps() for (stream_index, stream) in self.all_stream_fps.items()}
|
||||
print ("\n**PERF: ", self.perf_dict, "\n")
|
||||
return True
|
||||
|
||||
def update_fps(self, stream_index):
|
||||
self.all_stream_fps[stream_index].update_fps()
|
||||
@@ -0,0 +1,34 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 gi
|
||||
import sys
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst
|
||||
def bus_call(bus, message, loop):
|
||||
t = message.type
|
||||
if t == Gst.MessageType.EOS:
|
||||
sys.stdout.write("End-of-stream\n")
|
||||
loop.quit()
|
||||
elif t==Gst.MessageType.WARNING:
|
||||
err, debug = message.parse_warning()
|
||||
sys.stderr.write("Warning: %s: %s\n" % (err, debug))
|
||||
elif t == Gst.MessageType.ERROR:
|
||||
err, debug = message.parse_error()
|
||||
sys.stderr.write("Error: %s: %s\n" % (err, debug))
|
||||
loop.quit()
|
||||
return True
|
||||
@@ -0,0 +1,94 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 sys
|
||||
import platform
|
||||
from threading import Lock
|
||||
from cuda.bindings import runtime
|
||||
from cuda.bindings import driver
|
||||
|
||||
guard_platform_info = Lock()
|
||||
|
||||
class PlatformInfo:
|
||||
def __init__(self):
|
||||
self.is_wsl_system = False
|
||||
self.wsl_verified = False
|
||||
self.is_integrated_gpu_system = False
|
||||
self.is_integrated_gpu_verified = False
|
||||
self.is_aarch64_platform = False
|
||||
self.is_aarch64_verified = False
|
||||
|
||||
def is_wsl(self):
|
||||
with guard_platform_info:
|
||||
# Check if its already verified as WSL system or not.
|
||||
if not self.wsl_verified:
|
||||
try:
|
||||
# Open /proc/version file
|
||||
with open("/proc/version", "r") as version_file:
|
||||
# Read the content
|
||||
version_info = version_file.readline()
|
||||
version_info = version_info.lower()
|
||||
self.wsl_verified = True
|
||||
|
||||
# Check if "microsoft" is present in the version information
|
||||
if "microsoft" in version_info:
|
||||
self.is_wsl_system = True
|
||||
except Exception as e:
|
||||
print(f"ERROR: Opening /proc/version failed: {e}")
|
||||
|
||||
return self.is_wsl_system
|
||||
|
||||
def is_integrated_gpu(self):
|
||||
#Using cuda apis to identify whether integrated/discreet
|
||||
#This is required to distinguish Tegra and ARM_SBSA devices
|
||||
with guard_platform_info:
|
||||
#Cuda initialize
|
||||
if not self.is_integrated_gpu_verified:
|
||||
cuda_init_result, = driver.cuInit(0)
|
||||
if cuda_init_result == driver.CUresult.CUDA_SUCCESS:
|
||||
#Get cuda devices count
|
||||
device_count_result, num_devices = driver.cuDeviceGetCount()
|
||||
if device_count_result == driver.CUresult.CUDA_SUCCESS:
|
||||
#If atleast one device is found, we can use the property from
|
||||
#the first device
|
||||
if num_devices >= 1:
|
||||
#Get properties from first device
|
||||
property_result, properties = runtime.cudaGetDeviceProperties(0)
|
||||
if property_result == runtime.cudaError_t.cudaSuccess:
|
||||
print("Is it Integrated GPU? :", properties.integrated)
|
||||
self.is_integrated_gpu_system = properties.integrated
|
||||
self.is_integrated_gpu_verified = True
|
||||
else:
|
||||
print("ERROR: Getting cuda device property failed: {}".format(property_result))
|
||||
else:
|
||||
print("ERROR: No cuda devices found to check whether iGPU/dGPU")
|
||||
else:
|
||||
print("ERROR: Getting cuda device count failed: {}".format(device_count_result))
|
||||
else:
|
||||
print("ERROR: Cuda init failed: {}".format(cuda_init_result))
|
||||
|
||||
return self.is_integrated_gpu_system
|
||||
|
||||
def is_platform_aarch64(self):
|
||||
#Check if platform is aarch64 using uname
|
||||
if not self.is_aarch64_verified:
|
||||
if platform.uname()[4] == 'aarch64':
|
||||
self.is_aarch64_platform = True
|
||||
self.is_aarch64_verified = True
|
||||
return self.is_aarch64_platform
|
||||
|
||||
sys.path.append('/opt/nvidia/deepstream/deepstream/lib')
|
||||
@@ -0,0 +1,24 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 ctypes
|
||||
import sys
|
||||
sys.path.append('/opt/nvidia/deepstream/deepstream/lib')
|
||||
|
||||
def long_to_uint64(l):
|
||||
value = ctypes.c_uint64(l & 0xffffffffffffffff).value
|
||||
return value
|
||||
@@ -0,0 +1,43 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: LicenseRef-NvidiaProprietary
|
||||
#
|
||||
# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
|
||||
# property and proprietary rights in and to this material, related
|
||||
# documentation and any modifications thereto. Any use, reproduction,
|
||||
# disclosure or distribution of this material and related documentation
|
||||
# without an express license agreement from NVIDIA CORPORATION or
|
||||
# its affiliates is strictly prohibited.
|
||||
################################################################################
|
||||
|
||||
name: "peoplenet"
|
||||
platform: "tensorrt_plan"
|
||||
max_batch_size: 2
|
||||
default_model_filename: "resnet34_peoplenet_int8.onnx_b2_gpu0_fp16.engine"
|
||||
input [
|
||||
{
|
||||
name: "input_1:0"
|
||||
data_type: TYPE_FP32
|
||||
dims: [ 3, 544, 960 ]
|
||||
}
|
||||
]
|
||||
output [
|
||||
{
|
||||
name: "output_bbox/BiasAdd:0"
|
||||
data_type: TYPE_FP32
|
||||
dims: [ 12, 34, 60 ]
|
||||
},
|
||||
{
|
||||
name: "output_cov/Sigmoid:0"
|
||||
data_type: TYPE_FP32
|
||||
dims: [ 3, 34, 60 ]
|
||||
}
|
||||
]
|
||||
|
||||
instance_group [
|
||||
{
|
||||
kind: KIND_GPU
|
||||
count: 1
|
||||
gpus: 0
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,58 @@
|
||||
%YAML:1.0
|
||||
|
||||
BaseConfig:
|
||||
minDetectorConfidence: 0.6 # If the confidence of a detector bbox is lower than this, then it won't be considered for tracking
|
||||
|
||||
TargetManagement:
|
||||
enableBboxUnClipping: 1 # In case the bbox is likely to be clipped by image border, unclip bbox
|
||||
maxTargetsPerStream: 150 # Max number of targets to track per stream. Recommended to set >10. Note: this value should account for the targets being tracked in shadow mode as well. Max value depends on the GPU memory capacity
|
||||
|
||||
# [Creation & Termination Policy]
|
||||
minIouDiff4NewTarget: 0.55 # If the IOU between the newly detected object and any of the existing targets is higher than this threshold, this newly detected object will be discarded.
|
||||
minTrackerConfidence: 0.2 # If the confidence of an object tracker is lower than this on the fly, then it will be tracked in shadow mode. Valid Range: [0.0, 1.0]
|
||||
probationAge: 5 # If the target's age exceeds this, the target will be considered to be valid.
|
||||
maxShadowTrackingAge: 30 # Max length of shadow tracking. If the shadowTrackingAge exceeds this limit, the tracker will be terminated.
|
||||
earlyTerminationAge: 1 # If the shadowTrackingAge reaches this threshold while in TENTATIVE period, the target will be terminated prematurely.
|
||||
|
||||
TrajectoryManagement:
|
||||
useUniqueID: 0 # Use 64-bit long Unique ID when assignining tracker ID. Default is [true]
|
||||
|
||||
DataAssociator:
|
||||
dataAssociatorType: 0 # the type of data associator among { DEFAULT= 0 }
|
||||
associationMatcherType: 0 # the type of matching algorithm among { GREEDY=0, GLOBAL=1 }
|
||||
checkClassMatch: 1 # If checked, only the same-class objects are associated with each other. Default: true
|
||||
|
||||
# [Association Metric: Thresholds for valid candidates]
|
||||
minMatchingScore4Overall: 0.0 # Min total score
|
||||
minMatchingScore4SizeSimilarity: 0.6 # Min bbox size similarity score
|
||||
minMatchingScore4Iou: 0.0 # Min IOU score
|
||||
minMatchingScore4VisualSimilarity: 0.7 # Min visual similarity score
|
||||
|
||||
# [Association Metric: Weights]
|
||||
matchingScoreWeight4VisualSimilarity: 0.6 # Weight for the visual similarity (in terms of correlation response ratio)
|
||||
matchingScoreWeight4SizeSimilarity: 0.0 # Weight for the Size-similarity score
|
||||
matchingScoreWeight4Iou: 0.4 # Weight for the IOU score
|
||||
|
||||
StateEstimator:
|
||||
stateEstimatorType: 1 # the type of state estimator among { DUMMY=0, SIMPLE=1, REGULAR=2 }
|
||||
|
||||
# [Dynamics Modeling]
|
||||
processNoiseVar4Loc: 2.0 # Process noise variance for bbox center
|
||||
processNoiseVar4Size: 1.0 # Process noise variance for bbox size
|
||||
processNoiseVar4Vel: 0.1 # Process noise variance for velocity
|
||||
measurementNoiseVar4Detector: 4.0 # Measurement noise variance for detector's detection
|
||||
measurementNoiseVar4Tracker: 16.0 # Measurement noise variance for tracker's localization
|
||||
|
||||
VisualTracker:
|
||||
visualTrackerType: 1 # the type of visual tracker among { DUMMY=0, NvDCF=1 }
|
||||
|
||||
# [NvDCF: Feature Extraction]
|
||||
useColorNames: 1 # Use ColorNames feature
|
||||
useHog: 0 # Use Histogram-of-Oriented-Gradient (HOG) feature
|
||||
featureImgSizeLevel: 2 # Size of a feature image. Valid range: {1, 2, 3, 4, 5}, from the smallest to the largest
|
||||
featureFocusOffsetFactor_y: -0.2 # The offset for the center of hanning window relative to the feature height. The center of hanning window would move by (featureFocusOffsetFactor_y*featureMatSize.height) in vertical direction
|
||||
|
||||
# [NvDCF: Correlation Filter]
|
||||
filterLr: 0.075 # learning rate for DCF filter in exponential moving average. Valid Range: [0.0, 1.0]
|
||||
filterChannelWeightsLr: 0.1 # learning rate for the channel weights among feature channels. Valid Range: [0.0, 1.0]
|
||||
gaussianSigma: 0.75 # Standard deviation for Gaussian for desired response when creating DCF filter [pixels]
|
||||
@@ -0,0 +1,81 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
# Following properties are mandatory when engine files are not specified:
|
||||
# int8-calib-file(Only in INT8)
|
||||
# Caffemodel mandatory properties: model-file, proto-file, output-blob-names
|
||||
# UFF: uff-file, input-dims, uff-input-blob-name, output-blob-names
|
||||
# ONNX: onnx-file
|
||||
#
|
||||
# Mandatory properties for detectors:
|
||||
# num-detected-classes
|
||||
#
|
||||
# Optional properties for detectors:
|
||||
# cluster-mode(Default=Group Rectangles), interval(Primary mode only, Default=0)
|
||||
# custom-lib-path
|
||||
# parse-bbox-func-name
|
||||
#
|
||||
# Mandatory properties for classifiers:
|
||||
# classifier-threshold, is-classifier
|
||||
#
|
||||
# Optional properties for classifiers:
|
||||
# classifier-async-mode(Secondary mode only, Default=false)
|
||||
#
|
||||
# Optional properties in secondary mode:
|
||||
# operate-on-gie-id(Default=0), operate-on-class-ids(Defaults to all classes),
|
||||
# input-object-min-width, input-object-min-height, input-object-max-width,
|
||||
# input-object-max-height
|
||||
#
|
||||
# Following properties are always recommended:
|
||||
# batch-size(Default=1)
|
||||
#
|
||||
# Other optional properties:
|
||||
# net-scale-factor(Default=1), network-mode(Default=0 i.e FP32),
|
||||
# model-color-format(Default=0 i.e. RGB) model-engine-file, labelfile-path,
|
||||
# mean-file, gie-unique-id(Default=0), offsets, process-mode (Default=1 i.e. primary),
|
||||
# custom-lib-path, network-mode(Default=0 i.e FP32)
|
||||
#
|
||||
# The values in the config file are overridden by values set through GObject
|
||||
# properties.
|
||||
|
||||
[property]
|
||||
gpu-id=0
|
||||
net-scale-factor=0.00392156862745098
|
||||
onnx-file=../../models/best.pt.onnx
|
||||
model-engine-file=../../models/best.pt.onnx_b1_gpu0_fp16.engine
|
||||
labelfile-path=../../models/labels.txt
|
||||
batch-size=1
|
||||
process-mode=1
|
||||
model-color-format=0
|
||||
## 0=FP32, 1=INT8, 2=FP16 mode
|
||||
network-mode=2
|
||||
num-detected-classes=5
|
||||
interval=0
|
||||
gie-unique-id=1
|
||||
## 1=DBSCAN, 2=NMS, 3= DBSCAN+NMS Hybrid, 4 = None(No clustering)
|
||||
cluster-mode=2
|
||||
custom-lib-path=../lib/libnvdsinfer_custom_impl_Yolo.so
|
||||
parse-bbox-func-name=NvDsInferParseYolo
|
||||
engine-create-func-name=NvDsInferYoloCudaEngineGet
|
||||
network-type=0
|
||||
maintain-aspect-ratio=1
|
||||
symmetric-padding=1
|
||||
|
||||
[class-attrs-all]
|
||||
topk=20
|
||||
nms-iou-threshold=0.45
|
||||
pre-cluster-threshold=0.2
|
||||
@@ -0,0 +1,60 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
[property]
|
||||
gpu-id=0
|
||||
net-scale-factor=0.0039215697906911373
|
||||
onnx-file=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/resnet34_peoplenet_int8.onnx
|
||||
labelfile-path=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/labels.txt
|
||||
model-engine-file=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/resnet34_peoplenet_int8.onnx_b1_gpu0_fp16.engine
|
||||
int8-calib-file=/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/resnet34_peoplenet_int8.txt
|
||||
infer-dims=3;544;960
|
||||
uff-input-blob-name=input_1
|
||||
batch-size=1
|
||||
process-mode=1
|
||||
model-color-format=0
|
||||
## 0=FP32, 1=INT8, 2=FP16 mode
|
||||
network-mode=2
|
||||
num-detected-classes=3
|
||||
cluster-mode=2
|
||||
interval=0
|
||||
gie-unique-id=1
|
||||
output-blob-names=output_bbox/BiasAdd:0;output_cov/Sigmoid:0
|
||||
|
||||
#Use the config params below for dbscan clustering mode
|
||||
#[class-attrs-all]
|
||||
#detected-min-w=4
|
||||
#detected-min-h=4
|
||||
#minBoxes=3
|
||||
#eps=0.7
|
||||
|
||||
#Use the config params below for NMS clustering mode
|
||||
[class-attrs-all]
|
||||
topk=20
|
||||
nms-iou-threshold=0.5
|
||||
pre-cluster-threshold=0.2
|
||||
|
||||
## Per class configurations
|
||||
[class-attrs-0]
|
||||
topk=20
|
||||
nms-iou-threshold=0.5
|
||||
pre-cluster-threshold=0.4
|
||||
|
||||
#[class-attrs-1]
|
||||
#pre-cluster-threshold=0.05
|
||||
#eps=0.7
|
||||
#dbscan-min-score=0.5
|
||||
@@ -0,0 +1,79 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
infer_config {
|
||||
unique_id: 1
|
||||
gpu_ids: [0]
|
||||
max_batch_size: 1
|
||||
backend {
|
||||
inputs: [ {
|
||||
name: "input_1:0"
|
||||
}]
|
||||
outputs: [
|
||||
{name: "output_bbox/BiasAdd:0"},
|
||||
{name: "output_cov/Sigmoid:0"}
|
||||
]
|
||||
triton {
|
||||
model_name: "peoplenet"
|
||||
version: -1
|
||||
grpc {
|
||||
url: "0.0.0.0:8001"
|
||||
enable_cuda_buffer_sharing: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocess {
|
||||
network_format: IMAGE_FORMAT_RGB
|
||||
tensor_order: TENSOR_ORDER_LINEAR
|
||||
tensor_name: "input_1:0"
|
||||
maintain_aspect_ratio: 0
|
||||
frame_scaling_hw: FRAME_SCALING_HW_DEFAULT
|
||||
frame_scaling_filter: 1
|
||||
normalize {
|
||||
scale_factor: 0.0039215697906911373
|
||||
channel_offsets: [0, 0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
postprocess {
|
||||
labelfile_path: "/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/labels.txt"
|
||||
detection {
|
||||
num_detected_classes: 4
|
||||
per_class_params {
|
||||
key: 0
|
||||
value { pre_threshold: 0.4 }
|
||||
}
|
||||
nms {
|
||||
confidence_threshold:0.2
|
||||
topk:20
|
||||
iou_threshold:0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extra {
|
||||
copy_input_to_host_buffers: false
|
||||
output_buffer_pool_size: 2
|
||||
}
|
||||
}
|
||||
input_control {
|
||||
process_mode: PROCESS_MODE_FULL_FRAME
|
||||
operate_on_gie_id: -1
|
||||
interval: 0
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
infer_config {
|
||||
unique_id: 1
|
||||
gpu_ids: [0]
|
||||
max_batch_size: 1
|
||||
backend {
|
||||
inputs: [ {
|
||||
name: "input_1:0"
|
||||
}]
|
||||
outputs: [
|
||||
{name: "output_bbox/BiasAdd:0"},
|
||||
{name: "output_cov/Sigmoid:0"}
|
||||
]
|
||||
triton {
|
||||
model_name: "peoplenet"
|
||||
version: -1
|
||||
model_repo {
|
||||
root: "/opt/nvidia/deepstream/deepstream/samples/models/"
|
||||
strict_model_config: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preprocess {
|
||||
network_format: IMAGE_FORMAT_RGB
|
||||
tensor_order: TENSOR_ORDER_LINEAR
|
||||
tensor_name: "input_1:0"
|
||||
maintain_aspect_ratio: 0
|
||||
frame_scaling_hw: FRAME_SCALING_HW_DEFAULT
|
||||
frame_scaling_filter: 1
|
||||
normalize {
|
||||
scale_factor: 0.0039215697906911373
|
||||
channel_offsets: [0, 0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
postprocess {
|
||||
labelfile_path: "/opt/nvidia/deepstream/deepstream/samples/models/peoplenet/labels.txt"
|
||||
detection {
|
||||
num_detected_classes: 4
|
||||
per_class_params {
|
||||
key: 0
|
||||
value { pre_threshold: 0.4 }
|
||||
}
|
||||
nms {
|
||||
confidence_threshold:0.2
|
||||
topk:20
|
||||
iou_threshold:0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extra {
|
||||
copy_input_to_host_buffers: false
|
||||
output_buffer_pool_size: 2
|
||||
}
|
||||
}
|
||||
input_control {
|
||||
process_mode: PROCESS_MODE_FULL_FRAME
|
||||
operate_on_gie_id: -1
|
||||
interval: 0
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,635 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent # apps/mine
|
||||
DEEPSTREAM_PY_DIR = SCRIPT_DIR.parent # deepstream_python_apps
|
||||
|
||||
sys.path.insert(0, str(DEEPSTREAM_PY_DIR)) # common/ 在这里
|
||||
sys.path.insert(0, str(DEEPSTREAM_PY_DIR / "pyds")) # pyds 模块
|
||||
|
||||
from pathlib import Path
|
||||
from os import environ
|
||||
import gi
|
||||
import configparser
|
||||
import argparse
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import GLib, Gst
|
||||
from ctypes import *
|
||||
import time
|
||||
import sys
|
||||
import math
|
||||
import platform
|
||||
from common.platform_info import PlatformInfo
|
||||
from common.bus_call import bus_call
|
||||
from common.FPS import PERF_DATA
|
||||
|
||||
import pyds
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from sources.dev.app.utils.my_utils import *
|
||||
|
||||
display_type = 0
|
||||
silent = False
|
||||
file_loop = False
|
||||
perf_data = None
|
||||
measure_latency = False
|
||||
|
||||
MAX_DISPLAY_LEN=64
|
||||
PGIE_CLASS_ID_NORMAL = 0
|
||||
PGIE_CLASS_ID_DOUBLE_PUPA = 1
|
||||
PGIE_CLASS_ID_SPOT = 2
|
||||
PGIE_CLASS_ID_HAIRY = 3
|
||||
PGIE_CLASS_ID_MAGGOT_SHELL = 4
|
||||
MUXER_OUTPUT_WIDTH=1920
|
||||
MUXER_OUTPUT_HEIGHT=1080
|
||||
MUXER_BATCH_TIMEOUT_USEC = 33000
|
||||
TILED_OUTPUT_WIDTH=1280
|
||||
TILED_OUTPUT_HEIGHT=720
|
||||
YOLO_SIZE=768
|
||||
GST_CAPS_FEATURES_NVMM="memory:NVMM"
|
||||
OSD_PROCESS_MODE= 0
|
||||
OSD_DISPLAY_TEXT= 1
|
||||
pgie_classes_str= ["Vehicle", "TwoWheeler", "Person","RoadSign"]
|
||||
|
||||
count_sc = 0
|
||||
od_labels = []
|
||||
|
||||
with open("../models/labels.txt", "r") as f:
|
||||
od_labels = [line.strip() for line in f.readlines()]
|
||||
|
||||
def add_fps_display_meta(lineCount, batch_meta, frame_meta, display_text):
|
||||
display_meta=pyds.nvds_acquire_display_meta_from_pool(batch_meta)
|
||||
display_meta.num_labels = 1
|
||||
py_nvosd_text_params = display_meta.text_params[0]
|
||||
py_nvosd_text_params.display_text = display_text
|
||||
py_nvosd_text_params.x_offset = 12
|
||||
py_nvosd_text_params.y_offset = 12 + lineCount * 24
|
||||
py_nvosd_text_params.font_params.font_name = "Serif"
|
||||
py_nvosd_text_params.font_params.font_size = 10
|
||||
py_nvosd_text_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0)
|
||||
py_nvosd_text_params.set_bg_clr = 1
|
||||
py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
|
||||
# print(pyds.get_string(py_nvosd_text_params.display_text))
|
||||
pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
|
||||
|
||||
# pgie_src_pad_buffer_probe will extract metadata received on tiler sink pad
|
||||
# and update params for drawing rectangle, object information etc.
|
||||
def pgie_src_pad_buffer_probe(pad,info,u_data):
|
||||
frame_number=0
|
||||
num_rects=0
|
||||
got_fps = False
|
||||
gst_buffer = info.get_buffer()
|
||||
if not gst_buffer:
|
||||
print("Unable to get GstBuffer ")
|
||||
return
|
||||
# Retrieve batch metadata from the gst_buffer
|
||||
# Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
|
||||
# C address of gst_buffer as input, which is obtained with hash(gst_buffer)
|
||||
|
||||
# Enable latency measurement via probe if environment variable NVDS_ENABLE_LATENCY_MEASUREMENT=1 is set.
|
||||
# To enable component level latency measurement, please set environment variable
|
||||
# NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 in addition to the above.
|
||||
global measure_latency
|
||||
if measure_latency:
|
||||
num_sources_in_batch = pyds.nvds_measure_buffer_latency(hash(gst_buffer))
|
||||
if num_sources_in_batch == 0:
|
||||
print("Unable to get number of sources in GstBuffer for latency measurement")
|
||||
|
||||
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
|
||||
l_frame = batch_meta.frame_meta_list
|
||||
global count_sc
|
||||
while l_frame is not None:
|
||||
try:
|
||||
# Note that l_frame.data needs a cast to pyds.NvDsFrameMeta
|
||||
# The casting is done by pyds.NvDsFrameMeta.cast()
|
||||
# The casting also keeps ownership of the underlying memory
|
||||
# in the C code, so the Python garbage collector will leave
|
||||
# it alone.
|
||||
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
frame_number=frame_meta.frame_num
|
||||
l_obj=frame_meta.obj_meta_list
|
||||
num_rects = frame_meta.num_obj_meta
|
||||
obj_counter = {
|
||||
PGIE_CLASS_ID_NORMAL:0,
|
||||
PGIE_CLASS_ID_SPOT:0,
|
||||
PGIE_CLASS_ID_DOUBLE_PUPA:0,
|
||||
PGIE_CLASS_ID_HAIRY:0,
|
||||
PGIE_CLASS_ID_MAGGOT_SHELL:0
|
||||
}
|
||||
while l_obj is not None:
|
||||
try:
|
||||
# Casting l_obj.data to pyds.NvDsObjectMeta
|
||||
obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
|
||||
except StopIteration:
|
||||
break
|
||||
obj_counter[obj_meta.class_id] += 1
|
||||
try:
|
||||
l_obj=l_obj.next
|
||||
except StopIteration:
|
||||
break
|
||||
# if not silent:
|
||||
# print("帧ID=", frame_number, "总蚕茧数=",num_rects,"正茧数=",obj_counter[PGIE_CLASS_ID_NORMAL],"黄斑茧数=",obj_counter[PGIE_CLASS_ID_SPOT])
|
||||
|
||||
# Update frame rate through this probe
|
||||
stream_index = "stream{0}".format(frame_meta.pad_index)
|
||||
global perf_data
|
||||
perf_data.update_fps(stream_index)
|
||||
|
||||
text = f"Object Count: {num_rects}\n"
|
||||
for i in range(len(od_labels)):
|
||||
if obj_counter[i] > 0:
|
||||
text += f"\t{od_labels[i]}: {obj_counter[i]}\n"
|
||||
add_fps_display_meta(0,batch_meta,frame_meta, text)
|
||||
try:
|
||||
l_frame=l_frame.next
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
# Multi Object Tracker----------------------------------------------------
|
||||
l_obj=batch_meta.batch_user_meta_list
|
||||
while l_obj is not None:
|
||||
try:
|
||||
user_meta=pyds.NvDsUserMeta.cast(l_obj.data)
|
||||
except StopIteration:
|
||||
break
|
||||
# 遍历user_meta.base_meta.meta_type
|
||||
if user_meta and user_meta.base_meta.meta_type==pyds.NvDsMetaType.NVDS_TRACKER_PAST_FRAME_META :
|
||||
try:
|
||||
pPastDataBatch = pyds.NvDsTargetMiscDataBatch.cast(user_meta.user_meta_data)
|
||||
except StopIteration:
|
||||
break
|
||||
for miscDataStream in pyds.NvDsTargetMiscDataBatch.list(pPastDataBatch):
|
||||
for miscDataObj in pyds.NvDsTargetMiscDataStream.list(miscDataStream):
|
||||
unique_id = miscDataObj.uniqueId
|
||||
if unique_id > count_sc:
|
||||
count_sc = unique_id
|
||||
try:
|
||||
l_obj=l_obj.next
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
return Gst.PadProbeReturn.OK
|
||||
|
||||
def cb_newpad(decodebin, decoder_src_pad,data):
|
||||
print("In cb_newpad\n")
|
||||
caps=decoder_src_pad.get_current_caps()
|
||||
if not caps:
|
||||
caps = decoder_src_pad.query_caps()
|
||||
gststruct=caps.get_structure(0)
|
||||
gstname=gststruct.get_name()
|
||||
source_bin=data
|
||||
features=caps.get_features(0)
|
||||
|
||||
# Need to check if the pad created by the decodebin is for video and not
|
||||
# audio.
|
||||
print("gstname=",gstname)
|
||||
if(gstname.find("video")!=-1):
|
||||
# Link the decodebin pad only if decodebin has picked nvidia
|
||||
# decoder plugin nvdec_*. We do this by checking if the pad caps contain
|
||||
# NVMM memory features.
|
||||
print("features=",features)
|
||||
if features.contains("memory:NVMM"):
|
||||
# Get the source bin ghost pad
|
||||
bin_ghost_pad=source_bin.get_static_pad("src")
|
||||
if not bin_ghost_pad.set_target(decoder_src_pad):
|
||||
sys.stderr.write("Failed to link decoder src pad to source bin ghost pad\n")
|
||||
else:
|
||||
sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")
|
||||
|
||||
def decodebin_child_added(child_proxy,Object,name,user_data):
|
||||
print("Decodebin child added:", name, "\n")
|
||||
if(name.find("decodebin") != -1):
|
||||
Object.connect("child-added",decodebin_child_added,user_data)
|
||||
|
||||
if "source" in name:
|
||||
source_element = child_proxy.get_by_name("source")
|
||||
if source_element.find_property('drop-on-latency') != None:
|
||||
Object.set_property("drop-on-latency", True)
|
||||
|
||||
def create_source_bin(index,uri):
|
||||
print("Creating source bin")
|
||||
|
||||
# Create a source GstBin to abstract this bin's content from the rest of the
|
||||
# pipeline
|
||||
bin_name="source-bin-%02d" %index
|
||||
print(bin_name)
|
||||
nbin=Gst.Bin.new(bin_name)
|
||||
if not nbin:
|
||||
sys.stderr.write(" Unable to create source bin \n")
|
||||
|
||||
# Source element for reading from the uri.
|
||||
# We will use decodebin and let it figure out the container format of the
|
||||
# stream and the codec and plug the appropriate demux and decode plugins.
|
||||
if file_loop:
|
||||
# use nvurisrcbin to enable file-loop
|
||||
uri_decode_bin=Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")
|
||||
uri_decode_bin.set_property("file-loop", 1)
|
||||
uri_decode_bin.set_property("cudadec-memtype", 0)
|
||||
else:
|
||||
uri_decode_bin=Gst.ElementFactory.make("uridecodebin", "uri-decode-bin")
|
||||
if not uri_decode_bin:
|
||||
sys.stderr.write(" Unable to create uri decode bin \n")
|
||||
# We set the input uri to the source element
|
||||
uri_decode_bin.set_property("uri",uri)
|
||||
# Connect to the "pad-added" signal of the decodebin which generates a
|
||||
# callback once a new pad for raw data has beed created by the decodebin
|
||||
uri_decode_bin.connect("pad-added",cb_newpad,nbin)
|
||||
uri_decode_bin.connect("child-added",decodebin_child_added,nbin)
|
||||
|
||||
# We need to create a ghost pad for the source bin which will act as a proxy
|
||||
# for the video decoder src pad. The ghost pad will not have a target right
|
||||
# now. Once the decode bin creates the video decoder and generates the
|
||||
# cb_newpad callback, we will set the ghost pad target to the video decoder
|
||||
# src pad.
|
||||
Gst.Bin.add(nbin,uri_decode_bin)
|
||||
bin_pad=nbin.add_pad(Gst.GhostPad.new_no_target("src",Gst.PadDirection.SRC))
|
||||
if not bin_pad:
|
||||
sys.stderr.write(" Failed to add ghost pad in source bin \n")
|
||||
return None
|
||||
return nbin
|
||||
|
||||
def main(args, requested_pgie=None, config=None, disable_probe=False):
|
||||
global perf_data,TILED_OUTPUT_WIDTH,TILED_OUTPUT_HEIGHT,YOLO_SIZE,count_sc
|
||||
perf_data = PERF_DATA(len(args))
|
||||
|
||||
number_sources=len(args)
|
||||
if number_sources == 1:
|
||||
print("重置输出分辨率至:{} x {}".format(TILED_OUTPUT_WIDTH, TILED_OUTPUT_HEIGHT))
|
||||
TILED_OUTPUT_WIDTH, TILED_OUTPUT_HEIGHT = getVideoResolution(stream_paths[0])
|
||||
|
||||
platform_info = PlatformInfo()
|
||||
# Standard GStreamer initialization
|
||||
Gst.init(None)
|
||||
|
||||
# Create gstreamer elements */
|
||||
# Create Pipeline element that will form a connection of other elements
|
||||
print("Creating Pipeline \n ")
|
||||
pipeline = Gst.Pipeline()
|
||||
is_live = False
|
||||
|
||||
if not pipeline:
|
||||
sys.stderr.write(" Unable to create Pipeline \n")
|
||||
print("Creating streamux \n ")
|
||||
|
||||
# Create nvstreammux instance to form batches from one or more sources.
|
||||
nvstreammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
|
||||
if not nvstreammux:
|
||||
sys.stderr.write(" Unable to create NvStreamMux \n")
|
||||
|
||||
pipeline.add(nvstreammux)
|
||||
for i in range(number_sources):
|
||||
print("Creating source_bin ",i," \n ")
|
||||
uri_name=args[i]
|
||||
if uri_name.find("rtsp://") == 0 :
|
||||
is_live = True
|
||||
source_bin=create_source_bin(i, uri_name)
|
||||
if not source_bin:
|
||||
sys.stderr.write("Unable to create source bin \n")
|
||||
pipeline.add(source_bin)
|
||||
padname="sink_%u" %i
|
||||
sinkpad= nvstreammux.request_pad_simple(padname)
|
||||
if not sinkpad:
|
||||
sys.stderr.write("Unable to create sink pad bin \n")
|
||||
srcpad=source_bin.get_static_pad("src")
|
||||
if not srcpad:
|
||||
sys.stderr.write("Unable to create src pad bin \n")
|
||||
srcpad.link(sinkpad)
|
||||
queue1=Gst.ElementFactory.make("queue","queue1")
|
||||
queue2=Gst.ElementFactory.make("queue","queue2")
|
||||
queue3=Gst.ElementFactory.make("queue","queue3")
|
||||
queue4=Gst.ElementFactory.make("queue","queue4")
|
||||
queue5=Gst.ElementFactory.make("queue","queue5")
|
||||
pipeline.add(queue1)
|
||||
pipeline.add(queue2)
|
||||
pipeline.add(queue3)
|
||||
pipeline.add(queue4)
|
||||
pipeline.add(queue5)
|
||||
|
||||
nvdslogger = None
|
||||
|
||||
print("Creating Pgie \n ")
|
||||
if requested_pgie != None and (requested_pgie == 'nvinferserver' or requested_pgie == 'nvinferserver-grpc') :
|
||||
pgie = Gst.ElementFactory.make("nvinferserver", "primary-inference")
|
||||
elif requested_pgie != None and requested_pgie == 'nvinfer':
|
||||
# local tensorrt infer
|
||||
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
|
||||
else:
|
||||
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
|
||||
|
||||
if not pgie:
|
||||
sys.stderr.write(" Unable to create pgie : %s\n" % requested_pgie)
|
||||
|
||||
if disable_probe:
|
||||
# Use nvdslogger for perf measurement instead of probe function
|
||||
print ("Creating nvdslogger \n")
|
||||
nvdslogger = Gst.ElementFactory.make("nvdslogger", "nvdslogger")
|
||||
|
||||
print("Creating tiler \n ")
|
||||
nvmultistreamtiler=Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
|
||||
if not nvmultistreamtiler:
|
||||
sys.stderr.write(" Unable to create tiler \n")
|
||||
print("Creating nvvidconv \n ")
|
||||
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
|
||||
if not nvvidconv:
|
||||
sys.stderr.write(" Unable to create nvvidconv \n")
|
||||
print("Creating nvosd \n ")
|
||||
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
|
||||
if not nvosd:
|
||||
sys.stderr.write(" Unable to create nvosd \n")
|
||||
nvosd.set_property('process-mode',OSD_PROCESS_MODE)
|
||||
nvosd.set_property('display-text',OSD_DISPLAY_TEXT)
|
||||
|
||||
if file_loop:
|
||||
if platform_info.is_integrated_gpu():
|
||||
# Set nvbuf-memory-type=4 for integrated gpu for file-loop (nvurisrcbin case)
|
||||
nvstreammux.set_property('nvbuf-memory-type', 4)
|
||||
else:
|
||||
# Set nvbuf-memory-type=2 for x86 for file-loop (nvurisrcbin case)
|
||||
nvstreammux.set_property('nvbuf-memory-type', 2)
|
||||
|
||||
if display_type == 0:
|
||||
sink = Gst.ElementFactory.make("filesink", "file-sink")
|
||||
sink.set_property("location", "./out.mp4")
|
||||
sink.set_property("sync", 0)
|
||||
elif display_type == 1:
|
||||
print("Creating Fakesink \n")
|
||||
sink = Gst.ElementFactory.make("fakesink", "fakesink")
|
||||
sink.set_property('enable-last-sample', 0)
|
||||
sink.set_property('sync', 0)
|
||||
else:
|
||||
if platform_info.is_integrated_gpu():
|
||||
print("Creating nv3dsink \n")
|
||||
sink = Gst.ElementFactory.make("nv3dsink", "nv3d-sink")
|
||||
if not sink:
|
||||
sys.stderr.write(" Unable to create nv3dsink \n")
|
||||
else:
|
||||
if platform_info.is_platform_aarch64():
|
||||
print("Creating nv3dsink \n")
|
||||
sink = Gst.ElementFactory.make("nv3dsink", "nv3d-sink")
|
||||
else:
|
||||
print("Creating EGLSink \n")
|
||||
sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
|
||||
if not sink:
|
||||
sys.stderr.write(" Unable to create egl sink \n")
|
||||
|
||||
if not sink:
|
||||
sys.stderr.write(" Unable to create sink element \n")
|
||||
|
||||
if is_live:
|
||||
print("At least one of the sources is live")
|
||||
nvstreammux.set_property('live-source', 1)
|
||||
|
||||
|
||||
nvvidconv2 = Gst.ElementFactory.make("nvvideoconvert", "convertor2")
|
||||
if not nvvidconv2:
|
||||
sys.stderr.write(" Unable to create nvvidconv2 \n")
|
||||
|
||||
encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder") # for h264
|
||||
# encoder = Gst.ElementFactory.make("avenc_mpeg4", "encoder") # for mpeg4
|
||||
if not encoder:
|
||||
sys.stderr.write(" Unable to create encoder \n")
|
||||
encoder.set_property("bitrate", 2000000)
|
||||
|
||||
h264parse = Gst.ElementFactory.make("h264parse", "parser")
|
||||
# codeparser = Gst.ElementFactory.make("mpeg4videoparse", "mpeg4-parser")
|
||||
if not h264parse:
|
||||
sys.stderr.write(" Unable to create code parser \n")
|
||||
|
||||
qtmux = Gst.ElementFactory.make("qtmux", "qtmux")
|
||||
if not qtmux:
|
||||
sys.stderr.write(" Unable to create code parser \n")
|
||||
|
||||
nvstreammux.set_property('width', YOLO_SIZE)
|
||||
nvstreammux.set_property('height', YOLO_SIZE)
|
||||
nvstreammux.set_property('batch-size', number_sources)
|
||||
nvstreammux.set_property('batched-push-timeout', MUXER_BATCH_TIMEOUT_USEC)
|
||||
if requested_pgie == "nvinferserver" and config != None:
|
||||
pgie.set_property('config-file-path', config)
|
||||
elif requested_pgie == "nvinferserver-grpc" and config != None:
|
||||
pgie.set_property('config-file-path', config)
|
||||
elif requested_pgie == "nvinfer" and config != None:
|
||||
pgie.set_property('config-file-path', config)
|
||||
else:
|
||||
pgie.set_property('config-file-path', "config/pgie_config.txt")
|
||||
pgie_batch_size=pgie.get_property("batch-size")
|
||||
if(pgie_batch_size != number_sources):
|
||||
print("WARNING: Overriding infer-config batch-size",pgie_batch_size," with number of sources ", number_sources," \n")
|
||||
pgie.set_property("batch-size",number_sources)
|
||||
tiler_rows=int(math.sqrt(number_sources))
|
||||
tiler_columns=int(math.ceil((1.0*number_sources)/tiler_rows))
|
||||
nvmultistreamtiler.set_property("rows",tiler_rows)
|
||||
nvmultistreamtiler.set_property("columns",tiler_columns)
|
||||
nvmultistreamtiler.set_property("width", TILED_OUTPUT_WIDTH)
|
||||
nvmultistreamtiler.set_property("height", TILED_OUTPUT_HEIGHT)
|
||||
if platform_info.is_integrated_gpu():
|
||||
nvmultistreamtiler.set_property("compute-hw", 2)
|
||||
else:
|
||||
nvmultistreamtiler.set_property("compute-hw", 1)
|
||||
sink.set_property("qos",0)
|
||||
|
||||
tracker = Gst.ElementFactory.make("nvtracker", "tracker")
|
||||
if not tracker:
|
||||
sys.stderr.write(" Unable to create tracker \n")
|
||||
tracker.set_property('tracker-width', YOLO_SIZE)
|
||||
tracker.set_property('tracker-height', YOLO_SIZE)
|
||||
# To fix 'gstnvtracker: Unable to acquire a user meta buffer. Try increasing user-meta-pool-size'
|
||||
tracker.set_property('user-meta-pool-size', 128)
|
||||
tracker.set_property('gpu_id', 0)
|
||||
tracker.set_property('ll-lib-file', 'lib/libnvds_nvmultiobjecttracker.so')
|
||||
tracker.set_property('ll-config-file', 'config/config_tracker_NvDCF_perf.yml')
|
||||
|
||||
print("Adding elements to Pipeline \n")
|
||||
pipeline.add(pgie)
|
||||
if nvdslogger:
|
||||
pipeline.add(nvdslogger)
|
||||
pipeline.add(nvmultistreamtiler)
|
||||
pipeline.add(tracker)
|
||||
pipeline.add(nvvidconv)
|
||||
pipeline.add(nvosd)
|
||||
pipeline.add(sink)
|
||||
|
||||
print("Linking elements in the Pipeline \n")
|
||||
nvstreammux.link(queue1)
|
||||
queue1.link(pgie)
|
||||
pgie.link(tracker)
|
||||
tracker.link(queue2)
|
||||
if nvdslogger:
|
||||
queue2.link(nvdslogger)
|
||||
nvdslogger.link(nvmultistreamtiler)
|
||||
else:
|
||||
queue2.link(nvmultistreamtiler)
|
||||
nvmultistreamtiler.link(queue3)
|
||||
queue3.link(nvvidconv)
|
||||
nvvidconv.link(queue4)
|
||||
queue4.link(nvosd)
|
||||
nvosd.link(queue5)
|
||||
|
||||
if display_type == 0:
|
||||
pipeline.add(nvvidconv2)
|
||||
pipeline.add(encoder)
|
||||
pipeline.add(h264parse)
|
||||
pipeline.add(qtmux)
|
||||
|
||||
queue5.link(nvvidconv2)
|
||||
nvvidconv2.link(encoder)
|
||||
encoder.link(h264parse)
|
||||
h264parse.link(qtmux)
|
||||
qtmux.link(sink)
|
||||
else:
|
||||
queue5.link(sink)
|
||||
|
||||
# create an event loop and feed gstreamer bus mesages to it
|
||||
loop = GLib.MainLoop()
|
||||
bus = pipeline.get_bus()
|
||||
bus.add_signal_watch()
|
||||
bus.connect ("message", bus_call, loop)
|
||||
pgie_src_pad=nvosd.get_static_pad("sink")
|
||||
if not pgie_src_pad:
|
||||
sys.stderr.write(" Unable to get src pad \n")
|
||||
else:
|
||||
if not disable_probe:
|
||||
pgie_src_pad.add_probe(Gst.PadProbeType.BUFFER, pgie_src_pad_buffer_probe, 0)
|
||||
# perf callback function to print fps every 5 sec
|
||||
GLib.timeout_add(5000, perf_data.perf_print_callback)
|
||||
|
||||
# Enable latency measurement via probe if environment variable NVDS_ENABLE_LATENCY_MEASUREMENT=1 is set.
|
||||
# To enable component level latency measurement, please set environment variable
|
||||
# NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 in addition to the above.
|
||||
if environ.get('NVDS_ENABLE_LATENCY_MEASUREMENT') == '1':
|
||||
print ("Pipeline Latency Measurement enabled!\nPlease set env var NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 for Component Latency Measurement")
|
||||
global measure_latency
|
||||
measure_latency = True
|
||||
|
||||
# List the sources
|
||||
print("Now playing...")
|
||||
for i, source in enumerate(args):
|
||||
print(i, ": ", source)
|
||||
|
||||
print("Starting pipeline \n")
|
||||
# start play back and listed to events
|
||||
pipeline.set_state(Gst.State.PLAYING)
|
||||
try:
|
||||
loop.run()
|
||||
except:
|
||||
pass
|
||||
|
||||
print("总蚕茧数:",count_sc-1)
|
||||
# cleanup
|
||||
print("Exiting app\n")
|
||||
pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
def parse_args():
|
||||
|
||||
parser = argparse.ArgumentParser(prog="deepstream_test_3",
|
||||
description="deepstream-test3 multi stream, multi model inference reference app")
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--input",
|
||||
help="Path to input streams",
|
||||
nargs="+",
|
||||
metavar="URIs",
|
||||
default=["a"],
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--configfile",
|
||||
metavar="config_location.txt",
|
||||
default=None,
|
||||
help="Choose the config-file to be used with specified pgie",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--pgie",
|
||||
default=None,
|
||||
help="Choose Primary GPU Inference Engine",
|
||||
choices=["nvinfer", "nvinferserver", "nvinferserver-grpc"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"--display_type",
|
||||
type=int,
|
||||
default=0,
|
||||
help="Display type: 0=file, 1=fake"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--file-loop",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest='file_loop',
|
||||
help="Loop the input file sources after EOS",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-probe",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest='disable_probe',
|
||||
help="Disable the probe function and use nvdslogger for FPS",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--silent",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest='silent',
|
||||
help="Disable verbose output",
|
||||
)
|
||||
# Check input arguments
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help(sys.stderr)
|
||||
sys.exit(1)
|
||||
args = parser.parse_args()
|
||||
|
||||
stream_paths = args.input
|
||||
pgie = args.pgie
|
||||
config = args.configfile
|
||||
disable_probe = args.disable_probe
|
||||
global display_type
|
||||
global silent
|
||||
global file_loop
|
||||
display_type = args.display_type
|
||||
silent = args.silent
|
||||
file_loop = args.file_loop
|
||||
|
||||
if config and not pgie or pgie and not config:
|
||||
sys.stderr.write ("\nEither pgie or configfile is missing. Please specify both! Exiting...\n\n\n\n")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
if config:
|
||||
config_path = Path(config)
|
||||
if not config_path.is_file():
|
||||
sys.stderr.write ("Specified config-file: %s doesn't exist. Exiting...\n\n" % config)
|
||||
sys.exit(1)
|
||||
|
||||
print(vars(args))
|
||||
return stream_paths, pgie, config, disable_probe
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 此程序支持多个视频流输入,但只为单视频流输入开发
|
||||
|
||||
# 外部输入参数
|
||||
stream_paths, pgie, config, disable_probe = parse_args()
|
||||
|
||||
sys.exit(main(stream_paths, pgie, config, disable_probe))
|
||||
@@ -0,0 +1,515 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
################################################################################
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 sys
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent # apps/mine
|
||||
DEEPSTREAM_PY_DIR = SCRIPT_DIR.parent # deepstream_python_apps
|
||||
|
||||
sys.path.insert(0, str(DEEPSTREAM_PY_DIR)) # common/ 在这里
|
||||
sys.path.insert(0, str(DEEPSTREAM_PY_DIR / "pyds")) # pyds 模块
|
||||
|
||||
from pathlib import Path
|
||||
from os import environ
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import GLib, Gst
|
||||
from ctypes import *
|
||||
import time
|
||||
import sys
|
||||
import math
|
||||
from common.platform_info import PlatformInfo
|
||||
from common.bus_call import bus_call
|
||||
from common.FPS import PERF_DATA
|
||||
import pyds
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from utils.my_utils import *
|
||||
|
||||
display_type = 0
|
||||
silent = False
|
||||
file_loop = False
|
||||
perf_data = None
|
||||
measure_latency = False
|
||||
|
||||
MAX_DISPLAY_LEN=64
|
||||
PGIE_CLASS_ID_NORMAL = 0
|
||||
PGIE_CLASS_ID_DOUBLE_PUPA = 1
|
||||
PGIE_CLASS_ID_SPOT = 2
|
||||
PGIE_CLASS_ID_HAIRY = 3
|
||||
PGIE_CLASS_ID_MAGGOT_SHELL = 4
|
||||
|
||||
MUXER_OUTPUT_WIDTH=1920
|
||||
MUXER_OUTPUT_HEIGHT=1080
|
||||
MUXER_BATCH_TIMEOUT_USEC = 33000
|
||||
TILED_OUTPUT_WIDTH=1280
|
||||
TILED_OUTPUT_HEIGHT=720
|
||||
YOLO_SIZE=768
|
||||
GST_CAPS_FEATURES_NVMM="memory:NVMM"
|
||||
OSD_PROCESS_MODE= 0
|
||||
OSD_DISPLAY_TEXT= 1
|
||||
tracker_dict = {}
|
||||
|
||||
obj_counter = {
|
||||
PGIE_CLASS_ID_NORMAL:0,
|
||||
PGIE_CLASS_ID_SPOT:0,
|
||||
PGIE_CLASS_ID_DOUBLE_PUPA:0,
|
||||
PGIE_CLASS_ID_HAIRY:0,
|
||||
PGIE_CLASS_ID_MAGGOT_SHELL:0
|
||||
}
|
||||
|
||||
od_labels = []
|
||||
statics={}
|
||||
timestamp_in_video = 0
|
||||
|
||||
with open("../models/labels.txt", "r") as f:
|
||||
od_labels = [line.strip() for line in f.readlines()]
|
||||
|
||||
def stats_callback():
|
||||
# 这里统计你需要的数据,例如:
|
||||
global timestamp_in_video,statics
|
||||
timestamp_in_video += 1 # 假设每秒钟调用一次
|
||||
temp_json = {}
|
||||
|
||||
for class_id, count in obj_counter.items():
|
||||
class_name = get_type_name_by_id(class_id)
|
||||
temp_json[class_name] = count
|
||||
statics[timestamp_in_video] = temp_json
|
||||
return True # 返回 True 才会继续循环调用
|
||||
|
||||
|
||||
def add_fps_display_meta(lineCount, batch_meta, frame_meta, display_text):
|
||||
display_meta=pyds.nvds_acquire_display_meta_from_pool(batch_meta)
|
||||
display_meta.num_labels = 1
|
||||
py_nvosd_text_params = display_meta.text_params[0]
|
||||
py_nvosd_text_params.display_text = display_text
|
||||
py_nvosd_text_params.x_offset = 12
|
||||
py_nvosd_text_params.y_offset = 12 + lineCount * 24
|
||||
py_nvosd_text_params.font_params.font_name = "Serif"
|
||||
py_nvosd_text_params.font_params.font_size = 10
|
||||
py_nvosd_text_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0)
|
||||
py_nvosd_text_params.set_bg_clr = 1
|
||||
py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
|
||||
# print(pyds.get_string(py_nvosd_text_params.display_text))
|
||||
pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
|
||||
|
||||
# pgie_src_pad_buffer_probe will extract metadata received on tiler sink pad
|
||||
# and update params for drawing rectangle, object information etc.
|
||||
def pgie_src_pad_buffer_probe(pad,info,u_data):
|
||||
global measure_latency, obj_counter
|
||||
frame_number=0
|
||||
num_rects=0
|
||||
got_fps = False
|
||||
gst_buffer = info.get_buffer()
|
||||
if not gst_buffer:
|
||||
print("Unable to get GstBuffer ")
|
||||
return
|
||||
# Retrieve batch metadata from the gst_buffer
|
||||
# Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
|
||||
# C address of gst_buffer as input, which is obtained with hash(gst_buffer)
|
||||
|
||||
# Enable latency measurement via probe if environment variable NVDS_ENABLE_LATENCY_MEASUREMENT=1 is set.
|
||||
# To enable component level latency measurement, please set environment variable
|
||||
# NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 in addition to the above.
|
||||
if measure_latency:
|
||||
num_sources_in_batch = pyds.nvds_measure_buffer_latency(hash(gst_buffer))
|
||||
if num_sources_in_batch == 0:
|
||||
print("Unable to get number of sources in GstBuffer for latency measurement")
|
||||
|
||||
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
|
||||
l_frame = batch_meta.frame_meta_list
|
||||
while l_frame is not None:
|
||||
try:
|
||||
# Note that l_frame.data needs a cast to pyds.NvDsFrameMeta
|
||||
# The casting is done by pyds.NvDsFrameMeta.cast()
|
||||
# The casting also keeps ownership of the underlying memory
|
||||
# in the C code, so the Python garbage collector will leave
|
||||
# it alone.
|
||||
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
frame_number=frame_meta.frame_num
|
||||
l_obj=frame_meta.obj_meta_list
|
||||
num_rects = frame_meta.num_obj_meta
|
||||
|
||||
# 初始化
|
||||
obj_counter = {
|
||||
PGIE_CLASS_ID_NORMAL:0,
|
||||
PGIE_CLASS_ID_SPOT:0,
|
||||
PGIE_CLASS_ID_DOUBLE_PUPA:0,
|
||||
PGIE_CLASS_ID_HAIRY:0,
|
||||
PGIE_CLASS_ID_MAGGOT_SHELL:0
|
||||
}
|
||||
while l_obj is not None:
|
||||
try:
|
||||
# Casting l_obj.data to pyds.NvDsObjectMeta
|
||||
obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
|
||||
except StopIteration:
|
||||
break
|
||||
obj_counter[obj_meta.class_id] += 1
|
||||
try:
|
||||
l_obj=l_obj.next
|
||||
except StopIteration:
|
||||
break
|
||||
# if not silent:
|
||||
# print("帧ID=", frame_number, "总蚕茧数=",num_rects,"正茧数=",obj_counter[PGIE_CLASS_ID_NORMAL],"黄斑茧数=",obj_counter[PGIE_CLASS_ID_SPOT])
|
||||
|
||||
# Update frame rate through this probe
|
||||
stream_index = "stream{0}".format(frame_meta.pad_index)
|
||||
global perf_data
|
||||
perf_data.update_fps(stream_index)
|
||||
|
||||
text = f"Object Count: {num_rects}\n"
|
||||
for i in range(len(od_labels)):
|
||||
if obj_counter[i] > 0:
|
||||
text += f"\t{od_labels[i]}: {obj_counter[i]}\n"
|
||||
add_fps_display_meta(0,batch_meta,frame_meta, text)
|
||||
try:
|
||||
l_frame=l_frame.next
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
# Multi Object Tracker----------------------------------------------------
|
||||
l_obj=batch_meta.batch_user_meta_list
|
||||
while l_obj is not None:
|
||||
try:
|
||||
user_meta=pyds.NvDsUserMeta.cast(l_obj.data)
|
||||
except StopIteration:
|
||||
break
|
||||
# 遍历user_meta.base_meta.meta_type
|
||||
if user_meta and user_meta.base_meta.meta_type==pyds.NvDsMetaType.NVDS_TRACKER_PAST_FRAME_META :
|
||||
try:
|
||||
pPastDataBatch = pyds.NvDsTargetMiscDataBatch.cast(user_meta.user_meta_data)
|
||||
except StopIteration:
|
||||
break
|
||||
for miscDataStream in pyds.NvDsTargetMiscDataBatch.list(pPastDataBatch):
|
||||
for miscDataObj in pyds.NvDsTargetMiscDataStream.list(miscDataStream):
|
||||
unique_id = miscDataObj.uniqueId
|
||||
tracker_dict[unique_id] = miscDataObj.classId
|
||||
try:
|
||||
l_obj=l_obj.next
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
return Gst.PadProbeReturn.OK
|
||||
|
||||
def cb_newpad(decodebin, decoder_src_pad,data):
|
||||
print("In cb_newpad\n")
|
||||
caps=decoder_src_pad.get_current_caps()
|
||||
if not caps:
|
||||
caps = decoder_src_pad.query_caps()
|
||||
gststruct=caps.get_structure(0)
|
||||
gstname=gststruct.get_name()
|
||||
source_bin=data
|
||||
features=caps.get_features(0)
|
||||
|
||||
# Need to check if the pad created by the decodebin is for video and not
|
||||
# audio.
|
||||
print("gstname=",gstname)
|
||||
if(gstname.find("video")!=-1):
|
||||
# Link the decodebin pad only if decodebin has picked nvidia
|
||||
# decoder plugin nvdec_*. We do this by checking if the pad caps contain
|
||||
# NVMM memory features.
|
||||
print("features=",features)
|
||||
if features.contains("memory:NVMM"):
|
||||
# Get the source bin ghost pad
|
||||
bin_ghost_pad=source_bin.get_static_pad("src")
|
||||
if not bin_ghost_pad.set_target(decoder_src_pad):
|
||||
sys.stderr.write("Failed to link decoder src pad to source bin ghost pad\n")
|
||||
else:
|
||||
sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")
|
||||
|
||||
def decodebin_child_added(child_proxy,Object,name,user_data):
|
||||
print("Decodebin child added:", name, "\n")
|
||||
if(name.find("decodebin") != -1):
|
||||
Object.connect("child-added",decodebin_child_added,user_data)
|
||||
|
||||
if "source" in name:
|
||||
source_element = child_proxy.get_by_name("source")
|
||||
if source_element.find_property('drop-on-latency') != None:
|
||||
Object.set_property("drop-on-latency", True)
|
||||
|
||||
def create_source_bin(index,uri):
|
||||
print("Creating source bin")
|
||||
|
||||
# Create a source GstBin to abstract this bin's content from the rest of the
|
||||
# pipeline
|
||||
bin_name="source-bin-%02d" %index
|
||||
print(bin_name)
|
||||
nbin=Gst.Bin.new(bin_name)
|
||||
if not nbin:
|
||||
sys.stderr.write(" Unable to create source bin \n")
|
||||
|
||||
# Source element for reading from the uri.
|
||||
# We will use decodebin and let it figure out the container format of the
|
||||
# stream and the codec and plug the appropriate demux and decode plugins.
|
||||
if file_loop:
|
||||
# use nvurisrcbin to enable file-loop
|
||||
uri_decode_bin=Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")
|
||||
uri_decode_bin.set_property("file-loop", 1)
|
||||
uri_decode_bin.set_property("cudadec-memtype", 0)
|
||||
else:
|
||||
uri_decode_bin=Gst.ElementFactory.make("uridecodebin", "uri-decode-bin")
|
||||
if not uri_decode_bin:
|
||||
sys.stderr.write(" Unable to create uri decode bin \n")
|
||||
# We set the input uri to the source element
|
||||
uri_decode_bin.set_property("uri",uri)
|
||||
# Connect to the "pad-added" signal of the decodebin which generates a
|
||||
# callback once a new pad for raw data has beed created by the decodebin
|
||||
uri_decode_bin.connect("pad-added",cb_newpad,nbin)
|
||||
uri_decode_bin.connect("child-added",decodebin_child_added,nbin)
|
||||
|
||||
# We need to create a ghost pad for the source bin which will act as a proxy
|
||||
# for the video decoder src pad. The ghost pad will not have a target right
|
||||
# now. Once the decode bin creates the video decoder and generates the
|
||||
# cb_newpad callback, we will set the ghost pad target to the video decoder
|
||||
# src pad.
|
||||
Gst.Bin.add(nbin,uri_decode_bin)
|
||||
bin_pad=nbin.add_pad(Gst.GhostPad.new_no_target("src",Gst.PadDirection.SRC))
|
||||
if not bin_pad:
|
||||
sys.stderr.write(" Failed to add ghost pad in source bin \n")
|
||||
return None
|
||||
return nbin
|
||||
|
||||
def sca(stream_paths, out_put_file_name:str):
|
||||
disable_probe=False
|
||||
global perf_data,TILED_OUTPUT_WIDTH,TILED_OUTPUT_HEIGHT,YOLO_SIZE
|
||||
perf_data = PERF_DATA(len(stream_paths))
|
||||
|
||||
number_sources=len(stream_paths)
|
||||
if number_sources == 1:
|
||||
print("重置输出分辨率至:{} x {}".format(TILED_OUTPUT_WIDTH, TILED_OUTPUT_HEIGHT))
|
||||
TILED_OUTPUT_WIDTH, TILED_OUTPUT_HEIGHT = getVideoResolution(stream_paths[0])
|
||||
|
||||
platform_info = PlatformInfo()
|
||||
Gst.init(None)
|
||||
|
||||
pipeline = Gst.Pipeline()
|
||||
is_live = False
|
||||
|
||||
if not pipeline:
|
||||
sys.stderr.write(" Unable to create Pipeline \n")
|
||||
|
||||
nvstreammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
|
||||
if not nvstreammux:
|
||||
sys.stderr.write(" Unable to create NvStreamMux \n")
|
||||
|
||||
pipeline.add(nvstreammux)
|
||||
for i in range(number_sources):
|
||||
uri_name=stream_paths[i]
|
||||
source_bin=create_source_bin(i, uri_name)
|
||||
if not source_bin:
|
||||
sys.stderr.write("Unable to create source bin \n")
|
||||
pipeline.add(source_bin)
|
||||
padname="sink_%u" %i
|
||||
sinkpad= nvstreammux.request_pad_simple(padname)
|
||||
if not sinkpad:
|
||||
sys.stderr.write("Unable to create sink pad bin \n")
|
||||
srcpad=source_bin.get_static_pad("src")
|
||||
if not srcpad:
|
||||
sys.stderr.write("Unable to create src pad bin \n")
|
||||
srcpad.link(sinkpad)
|
||||
queue1=Gst.ElementFactory.make("queue","queue1")
|
||||
queue2=Gst.ElementFactory.make("queue","queue2")
|
||||
queue3=Gst.ElementFactory.make("queue","queue3")
|
||||
queue4=Gst.ElementFactory.make("queue","queue4")
|
||||
queue5=Gst.ElementFactory.make("queue","queue5")
|
||||
pipeline.add(queue1)
|
||||
pipeline.add(queue2)
|
||||
pipeline.add(queue3)
|
||||
pipeline.add(queue4)
|
||||
pipeline.add(queue5)
|
||||
|
||||
nvdslogger = None
|
||||
|
||||
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
|
||||
|
||||
if not pgie:
|
||||
sys.stderr.write(" Unable to create pgie")
|
||||
pgie.set_property('config-file-path', "config/pgie_config.txt")
|
||||
|
||||
if disable_probe:
|
||||
# Use nvdslogger for perf measurement instead of probe function
|
||||
nvdslogger = Gst.ElementFactory.make("nvdslogger", "nvdslogger")
|
||||
|
||||
nvmultistreamtiler=Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
|
||||
if not nvmultistreamtiler:
|
||||
sys.stderr.write(" Unable to create tiler \n")
|
||||
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
|
||||
if not nvvidconv:
|
||||
sys.stderr.write(" Unable to create nvvidconv \n")
|
||||
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
|
||||
if not nvosd:
|
||||
sys.stderr.write(" Unable to create nvosd \n")
|
||||
nvosd.set_property('process-mode',OSD_PROCESS_MODE)
|
||||
nvosd.set_property('display-text',OSD_DISPLAY_TEXT)
|
||||
if file_loop:
|
||||
if platform_info.is_integrated_gpu():
|
||||
# Set nvbuf-memory-type=4 for integrated gpu for file-loop (nvurisrcbin case)
|
||||
nvstreammux.set_property('nvbuf-memory-type', 4)
|
||||
else:
|
||||
# Set nvbuf-memory-type=2 for x86 for file-loop (nvurisrcbin case)
|
||||
nvstreammux.set_property('nvbuf-memory-type', 2)
|
||||
|
||||
sink = Gst.ElementFactory.make("filesink", "file-sink")
|
||||
sink.set_property("location", out_put_file_name)
|
||||
sink.set_property("sync", 0)
|
||||
|
||||
if not sink:
|
||||
sys.stderr.write(" Unable to create sink element \n")
|
||||
if is_live:
|
||||
nvstreammux.set_property('live-source', 1)
|
||||
nvvidconv2 = Gst.ElementFactory.make("nvvideoconvert", "convertor2")
|
||||
if not nvvidconv2:
|
||||
sys.stderr.write(" Unable to create nvvidconv2 \n")
|
||||
encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder") # for h264
|
||||
# encoder = Gst.ElementFactory.make("avenc_mpeg4", "encoder") # for mpeg4
|
||||
if not encoder:
|
||||
sys.stderr.write(" Unable to create encoder \n")
|
||||
encoder.set_property("bitrate", 2000000)
|
||||
|
||||
h264parse = Gst.ElementFactory.make("h264parse", "parser")
|
||||
# codeparser = Gst.ElementFactory.make("mpeg4videoparse", "mpeg4-parser")
|
||||
if not h264parse:
|
||||
sys.stderr.write(" Unable to create code parser \n")
|
||||
|
||||
qtmux = Gst.ElementFactory.make("qtmux", "qtmux")
|
||||
if not qtmux:
|
||||
sys.stderr.write(" Unable to create code parser \n")
|
||||
|
||||
nvstreammux.set_property('width', YOLO_SIZE)
|
||||
nvstreammux.set_property('height', YOLO_SIZE)
|
||||
nvstreammux.set_property('batch-size', number_sources)
|
||||
nvstreammux.set_property('batched-push-timeout', MUXER_BATCH_TIMEOUT_USEC)
|
||||
|
||||
pgie_batch_size=pgie.get_property("batch-size")
|
||||
if(pgie_batch_size != number_sources):
|
||||
pgie.set_property("batch-size",number_sources)
|
||||
tiler_rows=int(math.sqrt(number_sources))
|
||||
tiler_columns=int(math.ceil((1.0*number_sources)/tiler_rows))
|
||||
nvmultistreamtiler.set_property("rows",tiler_rows)
|
||||
nvmultistreamtiler.set_property("columns",tiler_columns)
|
||||
nvmultistreamtiler.set_property("width", TILED_OUTPUT_WIDTH)
|
||||
nvmultistreamtiler.set_property("height", TILED_OUTPUT_HEIGHT)
|
||||
if platform_info.is_integrated_gpu():
|
||||
nvmultistreamtiler.set_property("compute-hw", 2)
|
||||
else:
|
||||
nvmultistreamtiler.set_property("compute-hw", 1)
|
||||
sink.set_property("qos",0)
|
||||
|
||||
tracker = Gst.ElementFactory.make("nvtracker", "tracker")
|
||||
if not tracker:
|
||||
sys.stderr.write(" Unable to create tracker \n")
|
||||
tracker.set_property('tracker-width', YOLO_SIZE)
|
||||
tracker.set_property('tracker-height', YOLO_SIZE)
|
||||
# To fix 'gstnvtracker: Unable to acquire a user meta buffer. Try increasing user-meta-pool-size'
|
||||
tracker.set_property('user-meta-pool-size', 128)
|
||||
tracker.set_property('gpu_id', 0)
|
||||
tracker.set_property('ll-lib-file', 'lib/libnvds_nvmultiobjecttracker.so')
|
||||
tracker.set_property('ll-config-file', 'config/config_tracker_NvDCF_perf.yml')
|
||||
|
||||
pipeline.add(pgie)
|
||||
if nvdslogger:
|
||||
pipeline.add(nvdslogger)
|
||||
pipeline.add(nvmultistreamtiler)
|
||||
pipeline.add(tracker)
|
||||
pipeline.add(nvvidconv)
|
||||
pipeline.add(nvosd)
|
||||
pipeline.add(sink)
|
||||
|
||||
nvstreammux.link(queue1)
|
||||
queue1.link(pgie)
|
||||
pgie.link(tracker)
|
||||
tracker.link(queue2)
|
||||
if nvdslogger:
|
||||
queue2.link(nvdslogger)
|
||||
nvdslogger.link(nvmultistreamtiler)
|
||||
else:
|
||||
queue2.link(nvmultistreamtiler)
|
||||
nvmultistreamtiler.link(queue3)
|
||||
queue3.link(nvvidconv)
|
||||
nvvidconv.link(queue4)
|
||||
queue4.link(nvosd)
|
||||
nvosd.link(queue5)
|
||||
|
||||
if display_type == 0:
|
||||
pipeline.add(nvvidconv2)
|
||||
pipeline.add(encoder)
|
||||
pipeline.add(h264parse)
|
||||
pipeline.add(qtmux)
|
||||
|
||||
queue5.link(nvvidconv2)
|
||||
nvvidconv2.link(encoder)
|
||||
encoder.link(h264parse)
|
||||
h264parse.link(qtmux)
|
||||
qtmux.link(sink)
|
||||
else:
|
||||
queue5.link(sink)
|
||||
|
||||
# create an event loop and feed gstreamer bus mesages to it
|
||||
loop = GLib.MainLoop()
|
||||
bus = pipeline.get_bus()
|
||||
bus.add_signal_watch()
|
||||
# 关闭日志
|
||||
bus.connect ("message", bus_call, loop)
|
||||
pgie_src_pad=nvosd.get_static_pad("sink")
|
||||
if not pgie_src_pad:
|
||||
sys.stderr.write(" Unable to get src pad \n")
|
||||
else:
|
||||
if not disable_probe:
|
||||
pgie_src_pad.add_probe(Gst.PadProbeType.BUFFER, pgie_src_pad_buffer_probe, 0)
|
||||
# perf callback function to print fps every 5 sec
|
||||
GLib.timeout_add(5000, perf_data.perf_print_callback)
|
||||
GLib.timeout_add(500, stats_callback)
|
||||
|
||||
|
||||
# Enable latency measurement via probe if environment variable NVDS_ENABLE_LATENCY_MEASUREMENT=1 is set.
|
||||
# To enable component level latency measurement, please set environment variable
|
||||
# NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 in addition to the above.
|
||||
if environ.get('NVDS_ENABLE_LATENCY_MEASUREMENT') == '1':
|
||||
print ("Pipeline Latency Measurement enabled!\nPlease set env var NVDS_ENABLE_COMPONENT_LATENCY_MEASUREMENT=1 for Component Latency Measurement")
|
||||
global measure_latency
|
||||
measure_latency = True
|
||||
|
||||
# List the sources
|
||||
for i, source in enumerate(stream_paths):
|
||||
print(i, ": ", source)
|
||||
|
||||
print("Starting pipeline \n")
|
||||
# start play back and listed to events
|
||||
pipeline.set_state(Gst.State.PLAYING)
|
||||
# 开始时间
|
||||
start_time = time.time()
|
||||
try:
|
||||
loop.run()
|
||||
except:
|
||||
pass
|
||||
|
||||
pipeline.set_state(Gst.State.NULL)
|
||||
print("总蚕茧数:",len(tracker_dict))
|
||||
print("Exiting app\n")
|
||||
# 结束时间
|
||||
end_time = time.time()
|
||||
analysis_time = end_time - start_time
|
||||
print(f"处理时间: {analysis_time} 秒")
|
||||
|
||||
global statics
|
||||
save_sca_results(stream_paths[0], out_put_file_name,tracker_dict,analysis_time,statics)
|
||||
@@ -0,0 +1 @@
|
||||
LOCAL_IP = "10.10.12.101"
|
||||
@@ -0,0 +1,78 @@
|
||||
from utils.pgDb import pg_pool
|
||||
import json
|
||||
|
||||
def insert_sca_video(
|
||||
name,
|
||||
raw_object_name,
|
||||
ai_object_name,
|
||||
duration,
|
||||
size,
|
||||
video_codec,
|
||||
audio_codec,
|
||||
overall_bit_rate,
|
||||
resolution,
|
||||
sc_analysis_time,
|
||||
sc_analysis_total_count,
|
||||
sc_analysis_max_count,
|
||||
sc_analysis_primary_type,
|
||||
sc_analysis_secondary_type,
|
||||
other_info,
|
||||
):
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
other_info = json.dumps(other_info, ensure_ascii=False)
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO sca_videos (
|
||||
name, raw_object_name, ai_object_name, duration, size, video_codec, audio_codec,
|
||||
overall_bit_rate, resolution, sc_analysis_time, sc_analysis_total_count, sc_analysis_max_count,
|
||||
sc_analysis_primary_type, sc_analysis_secondary_type, other_info, created_at
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW())
|
||||
RETURNING id
|
||||
""",
|
||||
(
|
||||
name,
|
||||
raw_object_name,
|
||||
ai_object_name,
|
||||
duration,
|
||||
size,
|
||||
video_codec,
|
||||
audio_codec,
|
||||
overall_bit_rate,
|
||||
resolution,
|
||||
sc_analysis_time,
|
||||
sc_analysis_total_count,
|
||||
sc_analysis_max_count,
|
||||
sc_analysis_primary_type,
|
||||
sc_analysis_secondary_type,
|
||||
other_info,
|
||||
),
|
||||
)
|
||||
|
||||
new_id = cursor.fetchone()[0]
|
||||
conn.commit()
|
||||
return new_id
|
||||
|
||||
def insert_sca_video_details(
|
||||
v_id,
|
||||
time_stamp,
|
||||
other_info,
|
||||
):
|
||||
with pg_pool.getConn() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
other_info = json.dumps(other_info, ensure_ascii=False)
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO sca_video_details (
|
||||
v_id, time_stamp, other_info
|
||||
)
|
||||
VALUES (%s, %s, %s)
|
||||
""",
|
||||
(
|
||||
v_id,
|
||||
time_stamp,
|
||||
other_info,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
from pymediainfo import MediaInfo
|
||||
from datetime import datetime
|
||||
import json
|
||||
from utils.db import insert_sca_video,insert_sca_video_details
|
||||
from collections import Counter
|
||||
|
||||
def getVideoResolution(file_path):
|
||||
media_info = MediaInfo.parse(file_path) # 解析视频文件
|
||||
|
||||
for track in media_info.tracks:
|
||||
if track.track_type == 'Video':
|
||||
return track.width,track.height
|
||||
|
||||
def get_type_name_by_id(id):
|
||||
type_dict = {
|
||||
0: "正茧",
|
||||
1: "双宫茧",
|
||||
2: "黄斑茧",
|
||||
3: "毛茧",
|
||||
4: "蛆壳茧",
|
||||
}
|
||||
return type_dict.get(id, "未知类型")
|
||||
|
||||
# 将视频信息保存至数据库
|
||||
def save_sca_results(file_path, out_put_file_name,tracker_dict,
|
||||
analysis_time,statics =[]):
|
||||
media_info = MediaInfo.parse(file_path) # 解析视频文件
|
||||
video_data = {} # 用于存储视频信息
|
||||
|
||||
for track in media_info.tracks:
|
||||
if track.track_type == 'General':
|
||||
video_data.update({
|
||||
"v_file_name": track.file_name_extension,
|
||||
"v_duration": round(track.duration / 1000),
|
||||
"v_size": round(track.file_size/1024,2),
|
||||
"v_video_codec": track.video_format_list,
|
||||
"v_audio_codec": track.audio_codecs,
|
||||
"v_overall_bit_rate": track.overall_bit_rate,
|
||||
})
|
||||
if track.track_type == 'Video':
|
||||
video_data.update({
|
||||
"v_resolution": f"{track.width}x{track.height}"
|
||||
})
|
||||
|
||||
# 分析 得出数量最多的种类 以及 出现次多的种类
|
||||
counter = Counter(tracker_dict.values()) # 统计所有 value 的出现次数
|
||||
most_common = counter.most_common(2) # 取出现次数前两名(返回列表)
|
||||
primary_type = get_type_name_by_id(most_common[0][0]) if most_common else ""
|
||||
secondary_type = get_type_name_by_id(most_common[1][0]) if len(most_common) > 1 else ""
|
||||
|
||||
# 1️⃣ 统计 { value: count }
|
||||
count_map = {}
|
||||
for k, v in tracker_dict.items():
|
||||
v_int = int(v)
|
||||
count_map[v_int] = count_map.get(v_int, 0) + 1
|
||||
|
||||
# 2️⃣ 替换成类型名称
|
||||
result = {}
|
||||
for type_value, count in count_map.items():
|
||||
type_name = get_type_name_by_id(type_value)
|
||||
result[type_name] = count
|
||||
|
||||
other_info = result
|
||||
|
||||
# 分析结果进行统计 得出 单帧最大蚕茧数量
|
||||
max_count_in_same_frame = 0
|
||||
for timestamp_in_video,json in statics.items():
|
||||
count_in_frame = sum(json.values())
|
||||
if count_in_frame > max_count_in_same_frame:
|
||||
max_count_in_same_frame = count_in_frame
|
||||
|
||||
# 保存至数据库
|
||||
video_id = insert_sca_video(
|
||||
# 取后八位
|
||||
name=out_put_file_name.split('/')[-1].split('.')[0].replace('-','')[-8:],
|
||||
raw_object_name=video_data.get("v_file_name", "").split('/')[-1],
|
||||
ai_object_name=out_put_file_name.split('/')[-1],
|
||||
duration=video_data.get("v_duration", 0),
|
||||
size=video_data.get("v_size", 0),
|
||||
video_codec=video_data.get("v_video_codec", ""),
|
||||
audio_codec=video_data.get("v_audio_codec", ""),
|
||||
overall_bit_rate=video_data.get("v_overall_bit_rate", 0),
|
||||
resolution=video_data.get("v_resolution", ""),
|
||||
sc_analysis_time= analysis_time,
|
||||
sc_analysis_total_count=len(tracker_dict),
|
||||
sc_analysis_max_count=max_count_in_same_frame,
|
||||
sc_analysis_primary_type= primary_type,
|
||||
sc_analysis_secondary_type= secondary_type,
|
||||
other_info= other_info,
|
||||
)
|
||||
|
||||
# 将细则存入数据库
|
||||
counter = Counter()
|
||||
|
||||
for timestamp_in_video, json in statics.items():
|
||||
insert_sca_video_details(video_id,timestamp_in_video, json)
|
||||
@@ -0,0 +1,72 @@
|
||||
import logging
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
|
||||
import psycopg
|
||||
from psycopg_pool import ConnectionPool
|
||||
from utils.GlobalVariable import LOCAL_IP
|
||||
|
||||
logger = logging.getLogger("PGPool")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
class PGPool:
|
||||
"""
|
||||
PostgreSQL 连接池封装
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
uri: str,
|
||||
min_size: int = 1,
|
||||
max_size: int = 20,
|
||||
max_idle: int = 30,
|
||||
max_lifetime: int = 300,
|
||||
timeout: int = 10,
|
||||
check: bool = False,
|
||||
):
|
||||
"""
|
||||
:param uri: PostgreSQL 连接 URI
|
||||
"""
|
||||
self.uri = uri
|
||||
self.pool = ConnectionPool(
|
||||
self.uri,
|
||||
min_size=min_size,
|
||||
max_size=max_size,
|
||||
max_idle=max_idle,
|
||||
max_lifetime=max_lifetime,
|
||||
timeout=timeout,
|
||||
check=check,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def getConn(self, retries: int = 2, delay: float = 1.0):
|
||||
"""
|
||||
获取数据库连接,带重试机制,自动健康检查。
|
||||
使用方式:
|
||||
with pg_pool.get_conn() as conn:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(...)
|
||||
"""
|
||||
attempt = 0
|
||||
while attempt <= retries:
|
||||
try:
|
||||
with self.pool.connection() as conn:
|
||||
conn.autocommit = True
|
||||
yield conn
|
||||
return
|
||||
except psycopg.OperationalError as e:
|
||||
logger.warning(f"数据库连接异常: {e}. 尝试重试 ({attempt+1}/{retries})")
|
||||
self.pool.check() # 丢掉坏连接,重新建
|
||||
attempt += 1
|
||||
time.sleep(delay)
|
||||
except Exception as e:
|
||||
logger.error(f"SQL执行异常: {e}")
|
||||
raise
|
||||
raise psycopg.OperationalError("无法获取数据库连接,多次重试失败")
|
||||
|
||||
|
||||
pg_pool = PGPool(
|
||||
uri="postgresql://postgres:123456@" + LOCAL_IP + "/ktor2",
|
||||
min_size=1,
|
||||
max_size=20,
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
from utils.GlobalVariable import LOCAL_IP
|
||||
|
||||
RABBIT_HOST = LOCAL_IP
|
||||
RABBIT_VHOST = "bbit_ai"
|
||||
RABBIT_USER = "ai_lab"
|
||||
RABBIT_PASSWORD = "123456"
|
||||
QUEUE_NAME = "analysis_queue"
|
||||
Reference in New Issue
Block a user