├── .gitignore
├── README.md
├── common
├── FPS.py
├── bus_call.py
└── is_aarch_64.py
├── configs
└── config_infer_triton_yolov7.txt
├── demo.py
├── nvdsinfer_custom_impl_Yolo
├── Makefile
├── README
├── libnvds_infercustomparser.so
├── nvdsinfer_custombboxparser.cpp
└── nvdsinfer_customclassifierparser.cpp
├── run_docker.sh
├── triton_yolov7
└── yolov7
│ ├── classes.txt
│ └── config.pbtxt
└── weights
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | weights/yolov7-tiny.onnx
2 | triton_yolov7/yolov7/1/*
3 | __pycache__
4 | timing.cache
5 | timing.cache.lock
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # yolov7-triton-deepstream
2 |
3 | ## Perpare weight model and run docker
4 | Perpare weight:
5 | ```bash
6 | See README into folder weights to generate onnx
7 | or download onnx converted from link google drive
8 | ```
9 | Pepare docker:
10 | ```bash
11 | #Setup docker
12 | docker pull thanhlnbka/deepstream-python-app:3.0-triton
13 | #Run docker to inference yolov7 with triton deepstream
14 | bash run_docker.sh
15 | ```
16 | ##### NOTE: NEXT STEPS WORK INTO DOCKER
17 | ## Using deepstream-triton to convert engine
18 | Install package TensorRT:
19 | ```bash
20 | #install package tensorrt 8.*
21 | apt-get install libnvinfer8 libnvinfer-plugin8 libnvparsers8 libnvonnxparsers8 libnvinfer-bin libnvinfer-dev libnvinfer-plugin-dev libnvparsers-dev libnvonnxparsers-dev libnvinfer-samples libcudnn8-dev libcudnn8
22 | apt-mark hold libnvinfer* libnvparsers* libnvonnxparsers* libcudnn8* tensorrt
23 | ```
24 |
25 | Export Engine:
26 | ```bash
27 | #access folder weights
28 | cd /opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream/weights
29 | #export file engine with trtexec
30 | /usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx --minShapes=images:1x3x640x640 --optShapes=images:8x3x640x640 --maxShapes=images:8x3x640x640 --fp16 --workspace=4096 --saveEngine=yolov7-fp16-1x8x8.engine --timingCacheFile=timing.cache
31 | #move file engine to deploy triton
32 | mv yolov7-fp16-1x8x8.engine ../triton_yolov7/yolov7/1/model.plan
33 | ```
34 | ## Custom config parse box
35 | Run cmd:
36 | ```bash
37 | #access folder custom parse box yolo
38 | cd /opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream/nvdsinfer_custom_impl_Yolo
39 | #generate file .so
40 | CUDA_VER=11.7 make install
41 | ```
42 | ## Demo
43 | Run demo:
44 | ```bash
45 | #access folder demo
46 | cd /opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream
47 | #run demo with fake rtsp
48 | python3 demo.py -i rtsp://localhost:128/gst -g nvinferserver -c configs/config_infer_triton_yolov7.txt
49 | ```
50 | 
51 |
52 |
53 | ## Acknowledgements
54 |
55 | Expand
56 |
57 | * [https://github.com/WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7)
58 | * [https://github.com/NVIDIA-AI-IOT/deepstream_python_apps](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps)
59 |
60 |
61 |
--------------------------------------------------------------------------------
/common/FPS.py:
--------------------------------------------------------------------------------
1 |
2 | import time
3 | from threading import Lock
4 | start_time=time.time()
5 |
6 | fps_mutex = Lock()
7 |
8 | class GETFPS:
9 | def __init__(self,stream_id):
10 | global start_time
11 | self.start_time=start_time
12 | self.is_first=True
13 | self.frame_count=0
14 | self.stream_id=stream_id
15 |
16 | def update_fps(self):
17 | end_time = time.time()
18 | if self.is_first:
19 | self.start_time = end_time
20 | self.is_first = False
21 | else:
22 | global fps_mutex
23 | with fps_mutex:
24 | self.frame_count = self.frame_count + 1
25 |
26 | def get_fps(self):
27 | end_time = time.time()
28 | with fps_mutex:
29 | stream_fps = float(self.frame_count/(end_time - self.start_time))
30 | self.frame_count = 0
31 | self.start_time = end_time
32 | return round(stream_fps, 2)
33 |
34 | def print_data(self):
35 | print('frame_count=',self.frame_count)
36 | print('start_time=',self.start_time)
37 |
38 | class PERF_DATA:
39 | def __init__(self, num_streams=1):
40 | self.perf_dict = {}
41 | self.all_stream_fps = {}
42 | for i in range(num_streams):
43 | self.all_stream_fps["stream{0}".format(i)]=GETFPS(i)
44 |
45 | def perf_print_callback(self):
46 | self.perf_dict = {stream_index:stream.get_fps() for (stream_index, stream) in self.all_stream_fps.items()}
47 | print ("\n**PERF: ", self.perf_dict, "\n")
48 | return True
49 |
50 | def update_fps(self, stream_index):
51 | self.all_stream_fps[stream_index].update_fps()
--------------------------------------------------------------------------------
/common/bus_call.py:
--------------------------------------------------------------------------------
1 |
2 | import gi
3 | import sys
4 | gi.require_version('Gst', '1.0')
5 | from gi.repository import Gst
6 |
7 | def bus_call(bus, message, loop):
8 | t = message.type
9 | if t == Gst.MessageType.EOS:
10 | sys.stdout.write("End-of-stream\n")
11 | loop.quit()
12 | elif t==Gst.MessageType.WARNING:
13 | err, debug = message.parse_warning()
14 | sys.stderr.write("Warning: %s: %s\n" % (err, debug))
15 | elif t == Gst.MessageType.ERROR:
16 | err, debug = message.parse_error()
17 | sys.stderr.write("Error: %s: %s\n" % (err, debug))
18 | loop.quit()
19 | elif t == Gst.MessageType.ELEMENT:
20 | struct = message.get_structure()
21 | #Check for stream-eos message
22 | if struct is not None and struct.has_name("stream-eos"):
23 | parsed, stream_id = struct.get_uint("stream-id")
24 | if parsed:
25 | #Set eos status of stream to True, to be deleted in delete-sources
26 | print("Got EOS from stream %d" % stream_id)
27 | return True
28 |
--------------------------------------------------------------------------------
/common/is_aarch_64.py:
--------------------------------------------------------------------------------
1 | import platform
2 | import sys
3 |
4 |
5 | def is_aarch64():
6 | return platform.uname()[4] == 'aarch64'
7 |
8 | sys.path.append('/opt/nvidia/deepstream-6.1/deepstream/lib')
--------------------------------------------------------------------------------
/configs/config_infer_triton_yolov7.txt:
--------------------------------------------------------------------------------
1 | infer_config {
2 | unique_id: 1
3 | gpu_ids: [0]
4 | max_batch_size: 20
5 |
6 | backend {
7 | triton {
8 | model_name: "yolov7"
9 | version: -1
10 | model_repo {
11 | root: "/opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream/triton_yolov7"
12 | }
13 | }
14 | }
15 |
16 | preprocess {
17 | network_format: IMAGE_FORMAT_RGB
18 | tensor_order: TENSOR_ORDER_LINEAR
19 | maintain_aspect_ratio: 1
20 | frame_scaling_hw: FRAME_SCALING_HW_DEFAULT
21 | frame_scaling_filter: 1
22 | normalize {
23 | scale_factor: 0.0039215697906911373
24 | }
25 | }
26 |
27 | postprocess {
28 | labelfile_path: "/opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream/triton_yolov7/yolov7/classes.txt"
29 | detection {
30 | num_detected_classes: 80
31 | custom_parse_bbox_func: "NvDsInferParseCustomEfficientNMS"
32 | nms {
33 | confidence_threshold: 0.5
34 | topk: 300
35 | iou_threshold: 0.45
36 | }
37 | }
38 | }
39 |
40 | extra {
41 | copy_input_to_host_buffers: false
42 | }
43 | custom_lib {
44 | path : "/opt/nvidia/deepstream/deepstream-6.1/lib/libnvds_infercustomparser.so"
45 | }
46 | }
47 |
48 | input_control {
49 | process_mode : PROCESS_MODE_FULL_FRAME
50 | interval : 0
51 | }
--------------------------------------------------------------------------------
/demo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | ################################################################################
4 | # SPDX-FileCopyrightText: Copyright (c) 2019-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 | # SPDX-License-Identifier: Apache-2.0
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the "License");
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 | ################################################################################
19 |
20 | import sys
21 | sys.path.append('../')
22 | from pathlib import Path
23 | import gi
24 | import configparser
25 | import argparse
26 | gi.require_version('Gst', '1.0')
27 | from gi.repository import GLib, Gst
28 | from ctypes import *
29 | import time
30 | import sys
31 | import math
32 | import platform
33 | from common.is_aarch_64 import is_aarch64
34 | from common.bus_call import bus_call
35 | from common.FPS import PERF_DATA
36 |
37 | import pyds
38 |
39 | no_display = False
40 | silent = False
41 | file_loop = False
42 | perf_data = None
43 |
44 | MAX_DISPLAY_LEN=64
45 | MUXER_OUTPUT_WIDTH=1920
46 | MUXER_OUTPUT_HEIGHT=1080
47 | MUXER_BATCH_TIMEOUT_USEC=4000000
48 | TILED_OUTPUT_WIDTH=1280
49 | TILED_OUTPUT_HEIGHT=720
50 | GST_CAPS_FEATURES_NVMM="memory:NVMM"
51 | OSD_PROCESS_MODE= 0
52 | OSD_DISPLAY_TEXT= 1
53 |
54 |
55 |
56 | def cb_newpad(decodebin, decoder_src_pad,data):
57 | print("In cb_newpad\n")
58 | caps=decoder_src_pad.get_current_caps()
59 | if not caps:
60 | caps = decoder_src_pad.query_caps()
61 | gststruct=caps.get_structure(0)
62 | gstname=gststruct.get_name()
63 | source_bin=data
64 | features=caps.get_features(0)
65 |
66 | # Need to check if the pad created by the decodebin is for video and not
67 | # audio.
68 | print("gstname=",gstname)
69 | if(gstname.find("video")!=-1):
70 | # Link the decodebin pad only if decodebin has picked nvidia
71 | # decoder plugin nvdec_*. We do this by checking if the pad caps contain
72 | # NVMM memory features.
73 | print("features=",features)
74 | if features.contains("memory:NVMM"):
75 | # Get the source bin ghost pad
76 | bin_ghost_pad=source_bin.get_static_pad("src")
77 | if not bin_ghost_pad.set_target(decoder_src_pad):
78 | sys.stderr.write("Failed to link decoder src pad to source bin ghost pad\n")
79 | else:
80 | sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")
81 |
82 | def decodebin_child_added(child_proxy,Object,name,user_data):
83 | print("Decodebin child added:", name, "\n")
84 | if(name.find("decodebin") != -1):
85 | Object.connect("child-added",decodebin_child_added,user_data)
86 |
87 | if "source" in name:
88 | source_element = child_proxy.get_by_name("source")
89 | if source_element.find_property('drop-on-latency') != None:
90 | Object.set_property("drop-on-latency", True)
91 |
92 |
93 |
94 | def create_source_bin(index,uri):
95 | print("Creating source bin")
96 |
97 | # Create a source GstBin to abstract this bin's content from the rest of the
98 | # pipeline
99 | bin_name="source-bin-%02d" %index
100 | print(bin_name)
101 | nbin=Gst.Bin.new(bin_name)
102 | if not nbin:
103 | sys.stderr.write(" Unable to create source bin \n")
104 |
105 | # Source element for reading from the uri.
106 | # We will use decodebin and let it figure out the container format of the
107 | # stream and the codec and plug the appropriate demux and decode plugins.
108 | if file_loop:
109 | # use nvurisrcbin to enable file-loop
110 | uri_decode_bin=Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")
111 | uri_decode_bin.set_property("file-loop", 1)
112 | else:
113 | uri_decode_bin=Gst.ElementFactory.make("uridecodebin", "uri-decode-bin")
114 | if not uri_decode_bin:
115 | sys.stderr.write(" Unable to create uri decode bin \n")
116 | # We set the input uri to the source element
117 | uri_decode_bin.set_property("uri",uri)
118 | # Connect to the "pad-added" signal of the decodebin which generates a
119 | # callback once a new pad for raw data has beed created by the decodebin
120 | uri_decode_bin.connect("pad-added",cb_newpad,nbin)
121 | uri_decode_bin.connect("child-added",decodebin_child_added,nbin)
122 |
123 | # We need to create a ghost pad for the source bin which will act as a proxy
124 | # for the video decoder src pad. The ghost pad will not have a target right
125 | # now. Once the decode bin creates the video decoder and generates the
126 | # cb_newpad callback, we will set the ghost pad target to the video decoder
127 | # src pad.
128 | Gst.Bin.add(nbin,uri_decode_bin)
129 | bin_pad=nbin.add_pad(Gst.GhostPad.new_no_target("src",Gst.PadDirection.SRC))
130 | if not bin_pad:
131 | sys.stderr.write(" Failed to add ghost pad in source bin \n")
132 | return None
133 | return nbin
134 |
135 | def main(args, requested_pgie=None, config=None, disable_probe=False):
136 | global perf_data
137 | perf_data = PERF_DATA(len(args))
138 |
139 | number_sources=len(args)
140 |
141 | # Standard GStreamer initialization
142 | Gst.init(None)
143 |
144 | # Create gstreamer elements */
145 | # Create Pipeline element that will form a connection of other elements
146 | print("Creating Pipeline \n ")
147 | pipeline = Gst.Pipeline()
148 | is_live = False
149 |
150 | if not pipeline:
151 | sys.stderr.write(" Unable to create Pipeline \n")
152 | print("Creating streamux \n ")
153 |
154 | # Create nvstreammux instance to form batches from one or more sources.
155 | streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
156 | if not streammux:
157 | sys.stderr.write(" Unable to create NvStreamMux \n")
158 |
159 | pipeline.add(streammux)
160 | for i in range(number_sources):
161 | print("Creating source_bin ",i," \n ")
162 | uri_name=args[i]
163 | if uri_name.find("rtsp://") == 0 :
164 | is_live = True
165 | source_bin=create_source_bin(i, uri_name)
166 | if not source_bin:
167 | sys.stderr.write("Unable to create source bin \n")
168 | pipeline.add(source_bin)
169 | padname="sink_%u" %i
170 | sinkpad= streammux.get_request_pad(padname)
171 | if not sinkpad:
172 | sys.stderr.write("Unable to create sink pad bin \n")
173 | srcpad=source_bin.get_static_pad("src")
174 | if not srcpad:
175 | sys.stderr.write("Unable to create src pad bin \n")
176 | srcpad.link(sinkpad)
177 | queue1=Gst.ElementFactory.make("queue","queue1")
178 | queue2=Gst.ElementFactory.make("queue","queue2")
179 | queue3=Gst.ElementFactory.make("queue","queue3")
180 | queue4=Gst.ElementFactory.make("queue","queue4")
181 | queue5=Gst.ElementFactory.make("queue","queue5")
182 | pipeline.add(queue1)
183 | pipeline.add(queue2)
184 | pipeline.add(queue3)
185 | pipeline.add(queue4)
186 | pipeline.add(queue5)
187 |
188 | nvdslogger = None
189 | transform = None
190 |
191 | print("Creating Pgie \n ")
192 | if requested_pgie != None and (requested_pgie == 'nvinferserver' or requested_pgie == 'nvinferserver-grpc') :
193 | pgie = Gst.ElementFactory.make("nvinferserver", "primary-inference")
194 | elif requested_pgie != None and requested_pgie == 'nvinfer':
195 | pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
196 | else:
197 | pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
198 |
199 | if not pgie:
200 | sys.stderr.write(" Unable to create pgie : %s\n" % requested_pgie)
201 |
202 | if disable_probe:
203 | # Use nvdslogger for perf measurement instead of probe function
204 | print ("Creating nvdslogger \n")
205 | nvdslogger = Gst.ElementFactory.make("nvdslogger", "nvdslogger")
206 |
207 | print("Creating tiler \n ")
208 | tiler=Gst.ElementFactory.make("nvmultistreamtiler", "nvtiler")
209 | if not tiler:
210 | sys.stderr.write(" Unable to create tiler \n")
211 | print("Creating nvvidconv \n ")
212 | nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
213 | if not nvvidconv:
214 | sys.stderr.write(" Unable to create nvvidconv \n")
215 | print("Creating nvosd \n ")
216 | nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
217 | if not nvosd:
218 | sys.stderr.write(" Unable to create nvosd \n")
219 | nvosd.set_property('process-mode',OSD_PROCESS_MODE)
220 | nvosd.set_property('display-text',OSD_DISPLAY_TEXT)
221 |
222 |
223 | if no_display:
224 | print("Creating Fakesink \n")
225 | sink = Gst.ElementFactory.make("fakesink", "fakesink")
226 | sink.set_property('enable-last-sample', 0)
227 | sink.set_property('sync', 0)
228 | else:
229 | if(is_aarch64()):
230 | print("Creating transform \n ")
231 | transform=Gst.ElementFactory.make("nvegltransform", "nvegl-transform")
232 | if not transform:
233 | sys.stderr.write(" Unable to create transform \n")
234 | print("Creating EGLSink \n")
235 | sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
236 |
237 | if not sink:
238 | sys.stderr.write(" Unable to create sink element \n")
239 |
240 | if is_live:
241 | print("At least one of the sources is live")
242 | streammux.set_property('live-source', 1)
243 |
244 | streammux.set_property('width', 1920)
245 | streammux.set_property('height', 1080)
246 | streammux.set_property('batch-size', number_sources)
247 | streammux.set_property('batched-push-timeout', 40)
248 | if requested_pgie == "nvinferserver" and config != None:
249 | pgie.set_property('config-file-path', config)
250 | elif requested_pgie == "nvinferserver-grpc" and config != None:
251 | pgie.set_property('config-file-path', config)
252 | elif requested_pgie == "nvinfer" and config != None:
253 | pgie.set_property('config-file-path', config)
254 | else:
255 | pgie.set_property('config-file-path', "dstest3_pgie_config.txt")
256 | pgie_batch_size=pgie.get_property("batch-size")
257 | if(pgie_batch_size != number_sources):
258 | print("WARNING: Overriding infer-config batch-size",pgie_batch_size," with number of sources ", number_sources," \n")
259 | pgie.set_property("batch-size",number_sources)
260 | tiler_rows=int(math.sqrt(number_sources))
261 | tiler_columns=int(math.ceil((1.0*number_sources)/tiler_rows))
262 | tiler.set_property("rows",tiler_rows)
263 | tiler.set_property("columns",tiler_columns)
264 | tiler.set_property("width", TILED_OUTPUT_WIDTH)
265 | tiler.set_property("height", TILED_OUTPUT_HEIGHT)
266 | sink.set_property("qos",0)
267 | sink.set_property('sync', 0)
268 | print("Adding elements to Pipeline \n")
269 | pipeline.add(pgie)
270 | if nvdslogger:
271 | pipeline.add(nvdslogger)
272 | pipeline.add(tiler)
273 | pipeline.add(nvvidconv)
274 | pipeline.add(nvosd)
275 | if transform:
276 | pipeline.add(transform)
277 | pipeline.add(sink)
278 |
279 | print("Linking elements in the Pipeline \n")
280 | streammux.link(queue1)
281 | queue1.link(pgie)
282 | pgie.link(queue2)
283 | if nvdslogger:
284 | queue2.link(nvdslogger)
285 | nvdslogger.link(tiler)
286 | else:
287 | queue2.link(tiler)
288 | tiler.link(queue3)
289 | queue3.link(nvvidconv)
290 | nvvidconv.link(queue4)
291 | queue4.link(nvosd)
292 | if transform:
293 | nvosd.link(queue5)
294 | queue5.link(transform)
295 | transform.link(sink)
296 | else:
297 | nvosd.link(queue5)
298 | queue5.link(sink)
299 |
300 | # create an event loop and feed gstreamer bus mesages to it
301 | loop = GLib.MainLoop()
302 | bus = pipeline.get_bus()
303 | bus.add_signal_watch()
304 | bus.connect ("message", bus_call, loop)
305 | pgie_src_pad=pgie.get_static_pad("src")
306 |
307 | # List the sources
308 | print("Now playing...")
309 | for i, source in enumerate(args):
310 | print(i, ": ", source)
311 |
312 | print("Starting pipeline \n")
313 | # start play back and listed to events
314 | pipeline.set_state(Gst.State.PLAYING)
315 | try:
316 | loop.run()
317 | except:
318 | pass
319 | # cleanup
320 | print("Exiting app\n")
321 | pipeline.set_state(Gst.State.NULL)
322 |
323 | def parse_args():
324 |
325 | parser = argparse.ArgumentParser(prog="deepstream_test_3",
326 | description="deepstream-test3 multi stream, multi model inference reference app")
327 | parser.add_argument(
328 | "-i",
329 | "--input",
330 | help="Path to input streams",
331 | nargs="+",
332 | metavar="URIs",
333 | default=["a"],
334 | required=True,
335 | )
336 | parser.add_argument(
337 | "-c",
338 | "--configfile",
339 | metavar="config_location.txt",
340 | default=None,
341 | help="Choose the config-file to be used with specified pgie",
342 | )
343 | parser.add_argument(
344 | "-g",
345 | "--pgie",
346 | default=None,
347 | help="Choose Primary GPU Inference Engine",
348 | choices=["nvinfer", "nvinferserver", "nvinferserver-grpc"],
349 | )
350 | parser.add_argument(
351 | "--no-display",
352 | action="store_true",
353 | default=False,
354 | dest='no_display',
355 | help="Disable display of video output",
356 | )
357 | parser.add_argument(
358 | "--file-loop",
359 | action="store_true",
360 | default=False,
361 | dest='file_loop',
362 | help="Loop the input file sources after EOS",
363 | )
364 | parser.add_argument(
365 | "--disable-probe",
366 | action="store_true",
367 | default=False,
368 | dest='disable_probe',
369 | help="Disable the probe function and use nvdslogger for FPS",
370 | )
371 | parser.add_argument(
372 | "-s",
373 | "--silent",
374 | action="store_true",
375 | default=False,
376 | dest='silent',
377 | help="Disable verbose output",
378 | )
379 | # Check input arguments
380 | if len(sys.argv) == 1:
381 | parser.print_help(sys.stderr)
382 | sys.exit(1)
383 | args = parser.parse_args()
384 |
385 | stream_paths = args.input
386 | pgie = args.pgie
387 | config = args.configfile
388 | disable_probe = args.disable_probe
389 | global no_display
390 | global silent
391 | global file_loop
392 | no_display = args.no_display
393 | silent = args.silent
394 | file_loop = args.file_loop
395 |
396 | if config and not pgie or pgie and not config:
397 | sys.stderr.write ("\nEither pgie or configfile is missing. Please specify both! Exiting...\n\n\n\n")
398 | parser.print_help()
399 | sys.exit(1)
400 | if config:
401 | config_path = Path(config)
402 | if not config_path.is_file():
403 | sys.stderr.write ("Specified config-file: %s doesn't exist. Exiting...\n\n" % config)
404 | sys.exit(1)
405 |
406 | print(vars(args))
407 | return stream_paths, pgie, config, disable_probe
408 |
409 | if __name__ == '__main__':
410 | stream_paths, pgie, config, disable_probe = parse_args()
411 | sys.exit(main(stream_paths, pgie, config, disable_probe))
412 |
--------------------------------------------------------------------------------
/nvdsinfer_custom_impl_Yolo/Makefile:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a
5 | # copy of this software and associated documentation files (the "Software"),
6 | # to deal in the Software without restriction, including without limitation
7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | # and/or sell copies of the Software, and to permit persons to whom the
9 | # Software is furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | # DEALINGS IN THE SOFTWARE.
21 | ################################################################################
22 |
23 | CC:= g++
24 |
25 | CFLAGS:= -Wall -std=c++11
26 |
27 | CFLAGS+= -shared -fPIC
28 |
29 | CFLAGS+= -I../../includes \
30 | -I /usr/local/cuda-$(CUDA_VER)/include
31 |
32 | LIBS:= -lnvinfer -lnvparsers
33 | LFLAGS:= -Wl,--start-group $(LIBS) -Wl,--end-group
34 |
35 | SRCFILES:= nvdsinfer_custombboxparser.cpp nvdsinfer_customclassifierparser.cpp
36 | TARGET_LIB:= libnvds_infercustomparser.so
37 |
38 | all: $(TARGET_LIB)
39 |
40 | $(TARGET_LIB) : $(SRCFILES)
41 | $(CC) -o $@ $^ $(CFLAGS) $(LFLAGS)
42 |
43 | install: $(TARGET_LIB)
44 | cp $(TARGET_LIB) ../../../lib
45 |
46 | clean:
47 | rm -rf $(TARGET_LIB)
48 |
--------------------------------------------------------------------------------
/nvdsinfer_custom_impl_Yolo/README:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # NVIDIA Corporation and its licensors retain all intellectual property
5 | # and proprietary rights in and to this software, related documentation
6 | # and any modifications thereto. Any use, reproduction, disclosure or
7 | # distribution of this software and related documentation without an express
8 | # license agreement from NVIDIA Corporation is strictly prohibited.
9 | #
10 | ################################################################################
11 |
12 | Refer to the DeepStream SDK documentation for a description of the library.
13 |
14 | --------------------------------------------------------------------------------
15 | Compile the library :
16 |
17 | # Export correct CUDA version as per the platform
18 | For Jetson: $ export CUDA_VER=11.4
19 | For x86: $ export CUDA_VER=11.7
20 |
21 | $ sudo -E make install
22 |
23 | --------------------------------------------------------------------------------
24 | This source has been written to parse the output layers of the resnet10 detector
25 | and the resnet18 vehicle type classifier model provided with the SDK. To use this
26 | library for bounding box / classifier output parsing instead of the inbuilt
27 | parsing function, modify the following parameters in [property] section of
28 | primary/secondary vehicle type infer configuration file (config_infer_primary.txt/
29 | config_infer_secondary_vehicletypes.txt) provided with the SDK:
30 |
31 | # For resnet10 detector
32 | parse-bbox-func-name=NvDsInferParseCustomResnet
33 | custom-lib-path=/path/to/this/directory/libnvds_infercustomparser.so
34 |
35 | # For resnet18 vehicle type classifier
36 | parse-classifier-func-name=NvDsInferClassiferParseCustomSoftmax
37 | custom-lib-path=/path/to/this/directory/libnvds_infercustomparser.so
38 |
39 | # For Tensorflow/Onnx SSD detector within nvinferserver
40 | infer_config {
41 | postprocess { detection {
42 | custom_parse_bbox_func: "NvDsInferParseCustomTfSSD"
43 | ...
44 | } }
45 | ...
46 | custom_lib {
47 | path: "/path/to/this/directory/libnvds_infercustomparser.so"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/nvdsinfer_custom_impl_Yolo/libnvds_infercustomparser.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thanhlnbka/yolov7-triton-deepstream/3e35c77f0c7c2937746a3dcae7e200b3d043671d/nvdsinfer_custom_impl_Yolo/libnvds_infercustomparser.so
--------------------------------------------------------------------------------
/nvdsinfer_custom_impl_Yolo/nvdsinfer_custombboxparser.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #include
24 | #include
25 | #include "nvdsinfer_custom_impl.h"
26 | #include
27 | #include
28 |
29 | #define MIN(a,b) ((a) < (b) ? (a) : (b))
30 | #define MAX(a,b) ((a) > (b) ? (a) : (b))
31 | #define CLIP(a,min,max) (MAX(MIN(a, max), min))
32 | #define DIVIDE_AND_ROUND_UP(a, b) ((a + b - 1) / b)
33 |
34 | struct MrcnnRawDetection {
35 | float y1, x1, y2, x2, class_id, score;
36 | };
37 |
38 | /* This is a sample bounding box parsing function for the sample Resnet10
39 | * detector model provided with the SDK. */
40 |
41 | /* C-linkage to prevent name-mangling */
42 | extern "C"
43 | bool NvDsInferParseCustomResnet (std::vector const &outputLayersInfo,
44 | NvDsInferNetworkInfo const &networkInfo,
45 | NvDsInferParseDetectionParams const &detectionParams,
46 | std::vector &objectList);
47 |
48 | /* This is a sample bounding box parsing function for the tensorflow SSD models
49 | * detector model provided with the SDK. */
50 |
51 | /* C-linkage to prevent name-mangling */
52 | extern "C"
53 | bool NvDsInferParseCustomTfSSD (std::vector const &outputLayersInfo,
54 | NvDsInferNetworkInfo const &networkInfo,
55 | NvDsInferParseDetectionParams const &detectionParams,
56 | std::vector &objectList);
57 |
58 | extern "C"
59 | bool NvDsInferParseCustomNMSTLT (
60 | std::vector const &outputLayersInfo,
61 | NvDsInferNetworkInfo const &networkInfo,
62 | NvDsInferParseDetectionParams const &detectionParams,
63 | std::vector &objectList);
64 |
65 | extern "C"
66 | bool NvDsInferParseYoloV5CustomBatchedNMSTLT (
67 | std::vector const &outputLayersInfo,
68 | NvDsInferNetworkInfo const &networkInfo,
69 | NvDsInferParseDetectionParams const &detectionParams,
70 | std::vector &objectList);
71 |
72 | extern "C"
73 | bool NvDsInferParseCustomBatchedNMSTLT (
74 | std::vector const &outputLayersInfo,
75 | NvDsInferNetworkInfo const &networkInfo,
76 | NvDsInferParseDetectionParams const &detectionParams,
77 | std::vector &objectList);
78 |
79 | extern "C"
80 | bool NvDsInferParseCustomMrcnnTLT (std::vector const &outputLayersInfo,
81 | NvDsInferNetworkInfo const &networkInfo,
82 | NvDsInferParseDetectionParams const &detectionParams,
83 | std::vector &objectList);
84 |
85 | extern "C"
86 | bool NvDsInferParseCustomMrcnnTLTV2 (std::vector const &outputLayersInfo,
87 | NvDsInferNetworkInfo const &networkInfo,
88 | NvDsInferParseDetectionParams const &detectionParams,
89 | std::vector &objectList);
90 |
91 | extern "C"
92 | bool NvDsInferParseCustomEfficientDetTAO (
93 | std::vector const &outputLayersInfo,
94 | NvDsInferNetworkInfo const &networkInfo,
95 | NvDsInferParseDetectionParams const &detectionParams,
96 | std::vector &objectList);
97 |
98 | extern "C"
99 | bool NvDsInferParseCustomResnet (std::vector const &outputLayersInfo,
100 | NvDsInferNetworkInfo const &networkInfo,
101 | NvDsInferParseDetectionParams const &detectionParams,
102 | std::vector &objectList)
103 | {
104 | static NvDsInferDimsCHW covLayerDims;
105 | static NvDsInferDimsCHW bboxLayerDims;
106 | static int bboxLayerIndex = -1;
107 | static int covLayerIndex = -1;
108 | static bool classMismatchWarn = false;
109 | int numClassesToParse;
110 |
111 | /* Find the bbox layer */
112 | if (bboxLayerIndex == -1) {
113 | for (unsigned int i = 0; i < outputLayersInfo.size(); i++) {
114 | if (strcmp(outputLayersInfo[i].layerName, "conv2d_bbox") == 0) {
115 | bboxLayerIndex = i;
116 | getDimsCHWFromDims(bboxLayerDims, outputLayersInfo[i].inferDims);
117 | break;
118 | }
119 | }
120 | if (bboxLayerIndex == -1) {
121 | std::cerr << "Could not find bbox layer buffer while parsing" << std::endl;
122 | return false;
123 | }
124 | }
125 |
126 | /* Find the cov layer */
127 | if (covLayerIndex == -1) {
128 | for (unsigned int i = 0; i < outputLayersInfo.size(); i++) {
129 | if (strcmp(outputLayersInfo[i].layerName, "conv2d_cov/Sigmoid") == 0) {
130 | covLayerIndex = i;
131 | getDimsCHWFromDims(covLayerDims, outputLayersInfo[i].inferDims);
132 | break;
133 | }
134 | }
135 | if (covLayerIndex == -1) {
136 | std::cerr << "Could not find bbox layer buffer while parsing" << std::endl;
137 | return false;
138 | }
139 | }
140 |
141 | /* Warn in case of mismatch in number of classes */
142 | if (!classMismatchWarn) {
143 | if (covLayerDims.c != detectionParams.numClassesConfigured) {
144 | std::cerr << "WARNING: Num classes mismatch. Configured:" <<
145 | detectionParams.numClassesConfigured << ", detected by network: " <<
146 | covLayerDims.c << std::endl;
147 | }
148 | classMismatchWarn = true;
149 | }
150 |
151 | /* Calculate the number of classes to parse */
152 | numClassesToParse = MIN (covLayerDims.c, detectionParams.numClassesConfigured);
153 |
154 | int gridW = covLayerDims.w;
155 | int gridH = covLayerDims.h;
156 | int gridSize = gridW * gridH;
157 | float gcCentersX[gridW];
158 | float gcCentersY[gridH];
159 | float bboxNormX = 35.0;
160 | float bboxNormY = 35.0;
161 | float *outputCovBuf = (float *) outputLayersInfo[covLayerIndex].buffer;
162 | float *outputBboxBuf = (float *) outputLayersInfo[bboxLayerIndex].buffer;
163 | int strideX = DIVIDE_AND_ROUND_UP(networkInfo.width, bboxLayerDims.w);
164 | int strideY = DIVIDE_AND_ROUND_UP(networkInfo.height, bboxLayerDims.h);
165 |
166 | for (int i = 0; i < gridW; i++)
167 | {
168 | gcCentersX[i] = (float)(i * strideX + 0.5);
169 | gcCentersX[i] /= (float)bboxNormX;
170 |
171 | }
172 | for (int i = 0; i < gridH; i++)
173 | {
174 | gcCentersY[i] = (float)(i * strideY + 0.5);
175 | gcCentersY[i] /= (float)bboxNormY;
176 |
177 | }
178 |
179 | for (int c = 0; c < numClassesToParse; c++)
180 | {
181 | float *outputX1 = outputBboxBuf + (c * 4 * bboxLayerDims.h * bboxLayerDims.w);
182 |
183 | float *outputY1 = outputX1 + gridSize;
184 | float *outputX2 = outputY1 + gridSize;
185 | float *outputY2 = outputX2 + gridSize;
186 |
187 | float threshold = detectionParams.perClassPreclusterThreshold[c];
188 | for (int h = 0; h < gridH; h++)
189 | {
190 | for (int w = 0; w < gridW; w++)
191 | {
192 | int i = w + h * gridW;
193 | if (outputCovBuf[c * gridSize + i] >= threshold)
194 | {
195 | NvDsInferObjectDetectionInfo object;
196 | float rectX1f, rectY1f, rectX2f, rectY2f;
197 |
198 | rectX1f = (outputX1[w + h * gridW] - gcCentersX[w]) * -bboxNormX;
199 | rectY1f = (outputY1[w + h * gridW] - gcCentersY[h]) * -bboxNormY;
200 | rectX2f = (outputX2[w + h * gridW] + gcCentersX[w]) * bboxNormX;
201 | rectY2f = (outputY2[w + h * gridW] + gcCentersY[h]) * bboxNormY;
202 |
203 | object.classId = c;
204 | object.detectionConfidence = outputCovBuf[c * gridSize + i];
205 |
206 | /* Clip object box co-ordinates to network resolution */
207 | object.left = CLIP(rectX1f, 0, networkInfo.width - 1);
208 | object.top = CLIP(rectY1f, 0, networkInfo.height - 1);
209 | object.width = CLIP(rectX2f, 0, networkInfo.width - 1) -
210 | object.left + 1;
211 | object.height = CLIP(rectY2f, 0, networkInfo.height - 1) -
212 | object.top + 1;
213 |
214 | objectList.push_back(object);
215 | }
216 | }
217 | }
218 | }
219 | return true;
220 | }
221 |
222 | extern "C"
223 | bool NvDsInferParseCustomTfSSD (std::vector const &outputLayersInfo,
224 | NvDsInferNetworkInfo const &networkInfo,
225 | NvDsInferParseDetectionParams const &detectionParams,
226 | std::vector &objectList)
227 | {
228 | auto layerFinder = [&outputLayersInfo](const std::string &name)
229 | -> const NvDsInferLayerInfo *{
230 | for (auto &layer : outputLayersInfo) {
231 | if (layer.dataType == FLOAT &&
232 | (layer.layerName && name == layer.layerName)) {
233 | return &layer;
234 | }
235 | }
236 | return nullptr;
237 | };
238 |
239 | const NvDsInferLayerInfo *numDetectionLayer = layerFinder("num_detections");
240 | const NvDsInferLayerInfo *scoreLayer = layerFinder("detection_scores");
241 | const NvDsInferLayerInfo *classLayer = layerFinder("detection_classes");
242 | const NvDsInferLayerInfo *boxLayer = layerFinder("detection_boxes");
243 | if (!scoreLayer || !classLayer || !boxLayer) {
244 | std::cerr << "ERROR: some layers missing or unsupported data types "
245 | << "in output tensors" << std::endl;
246 | return false;
247 | }
248 |
249 | unsigned int numDetections = classLayer->inferDims.d[0];
250 | if (numDetectionLayer && numDetectionLayer->buffer) {
251 | numDetections = (int)((float*)numDetectionLayer->buffer)[0];
252 | }
253 | if (numDetections > classLayer->inferDims.d[0]) {
254 | numDetections = classLayer->inferDims.d[0];
255 | }
256 | numDetections = std::max(0, numDetections);
257 | for (unsigned int i = 0; i < numDetections; ++i) {
258 | NvDsInferObjectDetectionInfo res;
259 | res.detectionConfidence = ((float*)scoreLayer->buffer)[i];
260 | res.classId = ((float*)classLayer->buffer)[i];
261 | if (res.classId >= detectionParams.perClassPreclusterThreshold.size() ||
262 | res.detectionConfidence <
263 | detectionParams.perClassPreclusterThreshold[res.classId]) {
264 | continue;
265 | }
266 | enum {y1, x1, y2, x2};
267 | float rectX1f, rectY1f, rectX2f, rectY2f;
268 | rectX1f = ((float*)boxLayer->buffer)[i *4 + x1] * networkInfo.width;
269 | rectY1f = ((float*)boxLayer->buffer)[i *4 + y1] * networkInfo.height;
270 | rectX2f = ((float*)boxLayer->buffer)[i *4 + x2] * networkInfo.width;;
271 | rectY2f = ((float*)boxLayer->buffer)[i *4 + y2] * networkInfo.height;
272 | rectX1f = CLIP(rectX1f, 0.0f, networkInfo.width - 1);
273 | rectX2f = CLIP(rectX2f, 0.0f, networkInfo.width - 1);
274 | rectY1f = CLIP(rectY1f, 0.0f, networkInfo.height - 1);
275 | rectY2f = CLIP(rectY2f, 0.0f, networkInfo.height - 1);
276 | if (rectX2f <= rectX1f || rectY2f <= rectY1f) {
277 | continue;
278 | }
279 | res.left = rectX1f;
280 | res.top = rectY1f;
281 | res.width = rectX2f - rectX1f;
282 | res.height = rectY2f - rectY1f;
283 | if (res.width && res.height) {
284 | objectList.emplace_back(res);
285 | }
286 | }
287 |
288 | return true;
289 | }
290 |
291 | extern "C"
292 | bool NvDsInferParseCustomMrcnnTLT (std::vector const &outputLayersInfo,
293 | NvDsInferNetworkInfo const &networkInfo,
294 | NvDsInferParseDetectionParams const &detectionParams,
295 | std::vector &objectList) {
296 | auto layerFinder = [&outputLayersInfo](const std::string &name)
297 | -> const NvDsInferLayerInfo *{
298 | for (auto &layer : outputLayersInfo) {
299 | if (layer.dataType == FLOAT &&
300 | (layer.layerName && name == layer.layerName)) {
301 | return &layer;
302 | }
303 | }
304 | return nullptr;
305 | };
306 |
307 | const NvDsInferLayerInfo *detectionLayer = layerFinder("generate_detections");
308 | const NvDsInferLayerInfo *maskLayer = layerFinder("mask_head/mask_fcn_logits/BiasAdd");
309 |
310 | if (!detectionLayer || !maskLayer) {
311 | std::cerr << "ERROR: some layers missing or unsupported data types "
312 | << "in output tensors" << std::endl;
313 | return false;
314 | }
315 |
316 | if(maskLayer->inferDims.numDims != 4U) {
317 | std::cerr << "Network output number of dims is : " <<
318 | maskLayer->inferDims.numDims << " expect is 4"<< std::endl;
319 | return false;
320 | }
321 |
322 | const unsigned int det_max_instances = maskLayer->inferDims.d[0];
323 | const unsigned int num_classes = maskLayer->inferDims.d[1];
324 | if(num_classes != detectionParams.numClassesConfigured) {
325 | std::cerr << "WARNING: Num classes mismatch. Configured:" <<
326 | detectionParams.numClassesConfigured << ", detected by network: " <<
327 | num_classes << std::endl;
328 | }
329 | const unsigned int mask_instance_height= maskLayer->inferDims.d[2];
330 | const unsigned int mask_instance_width = maskLayer->inferDims.d[3];
331 |
332 | auto out_det = reinterpret_cast( detectionLayer->buffer);
333 | auto out_mask = reinterpret_cast(maskLayer->buffer);
335 |
336 | for(auto i = 0U; i < det_max_instances; i++) {
337 | MrcnnRawDetection &rawDec = out_det[i];
338 |
339 | if(rawDec.score < detectionParams.perClassPreclusterThreshold[0])
340 | continue;
341 |
342 | NvDsInferInstanceMaskInfo obj;
343 | obj.left = CLIP(rawDec.x1, 0, networkInfo.width - 1);
344 | obj.top = CLIP(rawDec.y1, 0, networkInfo.height - 1);
345 | obj.width = CLIP(rawDec.x2, 0, networkInfo.width - 1) - rawDec.x1;
346 | obj.height = CLIP(rawDec.y2, 0, networkInfo.height - 1) - rawDec.y1;
347 | if(obj.width <= 0 || obj.height <= 0)
348 | continue;
349 | obj.classId = static_cast(rawDec.class_id);
350 | obj.detectionConfidence = rawDec.score;
351 |
352 | obj.mask_size = sizeof(float)*mask_instance_width*mask_instance_height;
353 | obj.mask = new float[mask_instance_width*mask_instance_height];
354 | obj.mask_width = mask_instance_width;
355 | obj.mask_height = mask_instance_height;
356 |
357 | float *rawMask = reinterpret_cast(out_mask + i
358 | * detectionParams.numClassesConfigured + obj.classId);
359 | memcpy (obj.mask, rawMask, sizeof(float)*mask_instance_width*mask_instance_height);
360 |
361 | objectList.push_back(obj);
362 | }
363 |
364 | return true;
365 |
366 | }
367 |
368 | extern "C"
369 | bool NvDsInferParseCustomNMSTLT (std::vector const &outputLayersInfo,
370 | NvDsInferNetworkInfo const &networkInfo,
371 | NvDsInferParseDetectionParams const &detectionParams,
372 | std::vector &objectList) {
373 | if(outputLayersInfo.size() != 2)
374 | {
375 | std::cerr << "Mismatch in the number of output buffers."
376 | << "Expected 2 output buffers, detected in the network :"
377 | << outputLayersInfo.size() << std::endl;
378 | return false;
379 | }
380 |
381 | // Host memory for "nms" which has 2 output bindings:
382 | // the order is bboxes and keep_count
383 | float* out_nms = (float *) outputLayersInfo[0].buffer;
384 | int * p_keep_count = (int *) outputLayersInfo[1].buffer;
385 | const float threshold = detectionParams.perClassThreshold[0];
386 |
387 | float* det;
388 |
389 | for (int i = 0; i < p_keep_count[0]; i++) {
390 | det = out_nms + i * 7;
391 |
392 | // Output format for each detection is stored in the below order
393 | // [image_id, label, confidence, xmin, ymin, xmax, ymax]
394 | if ( det[2] < threshold) continue;
395 | assert((unsigned int) det[1] < detectionParams.numClassesConfigured);
396 |
397 | #if 0
398 | std::cout << "id/label/conf/ x/y x/y -- "
399 | << det[0] << " " << det[1] << " " << det[2] << " "
400 | << det[3] << " " << det[4] << " " << det[5] << " " << det[6] << std::endl;
401 | #endif
402 | NvDsInferObjectDetectionInfo object;
403 | object.classId = (int) det[1];
404 | object.detectionConfidence = det[2];
405 |
406 | /* Clip object box co-ordinates to network resolution */
407 | object.left = CLIP(det[3] * networkInfo.width, 0, networkInfo.width - 1);
408 | object.top = CLIP(det[4] * networkInfo.height, 0, networkInfo.height - 1);
409 | object.width = CLIP((det[5] - det[3]) * networkInfo.width, 0, networkInfo.width - 1);
410 | object.height = CLIP((det[6] - det[4]) * networkInfo.height, 0, networkInfo.height - 1);
411 |
412 | objectList.push_back(object);
413 | }
414 |
415 | return true;
416 | }
417 |
418 | extern "C"
419 | bool NvDsInferParseYoloV5CustomBatchedNMSTLT (
420 | std::vector const &outputLayersInfo,
421 | NvDsInferNetworkInfo const &networkInfo,
422 | NvDsInferParseDetectionParams const &detectionParams,
423 | std::vector &objectList) {
424 |
425 | if(outputLayersInfo.size() != 4)
426 | {
427 | std::cerr << "Mismatch in the number of output buffers."
428 | << "Expected 4 output buffers, detected in the network :"
429 | << outputLayersInfo.size() << std::endl;
430 | return false;
431 | }
432 |
433 | /* Host memory for "BatchedNMS"
434 | BatchedNMS has 4 output bindings, the order is:
435 | keepCount, bboxes, scores, classes
436 | */
437 | int* p_keep_count = (int *) outputLayersInfo[0].buffer;
438 | float* p_bboxes = (float *) outputLayersInfo[1].buffer;
439 | float* p_scores = (float *) outputLayersInfo[2].buffer;
440 | float* p_classes = (float *) outputLayersInfo[3].buffer;
441 |
442 | const float threshold = detectionParams.perClassThreshold[0];
443 |
444 | const int keep_top_k = 200;
445 | const char* log_enable = std::getenv("ENABLE_DEBUG");
446 |
447 | if(log_enable != NULL && std::stoi(log_enable)) {
448 | std::cout <<"keep cout"
449 | <= detectionParams.numClassesConfigured) continue;
463 | if(p_bboxes[4*i+2] < p_bboxes[4*i] || p_bboxes[4*i+3] < p_bboxes[4*i+1]) continue;
464 |
465 | NvDsInferObjectDetectionInfo object;
466 | object.classId = (int) p_classes[i];
467 | object.detectionConfidence = p_scores[i];
468 |
469 | object.left = CLIP(p_bboxes[4*i], 0, networkInfo.width - 1);
470 | object.top = CLIP(p_bboxes[4*i+1], 0, networkInfo.height - 1);
471 | object.width = CLIP(p_bboxes[4*i+2], 0, networkInfo.width - 1) - object.left;
472 | object.height = CLIP(p_bboxes[4*i+3], 0, networkInfo.height - 1) - object.top;
473 |
474 | if(object.height < 0 || object.width < 0)
475 | continue;
476 | objectList.push_back(object);
477 | }
478 | return true;
479 | }
480 |
481 | extern "C"
482 | bool NvDsInferParseCustomBatchedNMSTLT (
483 | std::vector const &outputLayersInfo,
484 | NvDsInferNetworkInfo const &networkInfo,
485 | NvDsInferParseDetectionParams const &detectionParams,
486 | std::vector &objectList) {
487 |
488 | if(outputLayersInfo.size() != 4)
489 | {
490 | std::cerr << "Mismatch in the number of output buffers."
491 | << "Expected 4 output buffers, detected in the network :"
492 | << outputLayersInfo.size() << std::endl;
493 | return false;
494 | }
495 |
496 | /* Host memory for "BatchedNMS"
497 | BatchedNMS has 4 output bindings, the order is:
498 | keepCount, bboxes, scores, classes
499 | */
500 | int* p_keep_count = (int *) outputLayersInfo[0].buffer;
501 | float* p_bboxes = (float *) outputLayersInfo[1].buffer;
502 | float* p_scores = (float *) outputLayersInfo[2].buffer;
503 | float* p_classes = (float *) outputLayersInfo[3].buffer;
504 |
505 | const float threshold = detectionParams.perClassThreshold[0];
506 |
507 | const int keep_top_k = 200;
508 | const char* log_enable = std::getenv("ENABLE_DEBUG");
509 |
510 | if(log_enable != NULL && std::stoi(log_enable)) {
511 | std::cout <<"keep cout"
512 | <= detectionParams.numClassesConfigured) continue;
526 | if(p_bboxes[4*i+2] < p_bboxes[4*i] || p_bboxes[4*i+3] < p_bboxes[4*i+1]) continue;
527 |
528 | NvDsInferObjectDetectionInfo object;
529 | object.classId = (int) p_classes[i];
530 | object.detectionConfidence = p_scores[i];
531 |
532 | /* Clip object box co-ordinates to network resolution */
533 | object.left = CLIP(p_bboxes[4*i] * networkInfo.width, 0, networkInfo.width - 1);
534 | object.top = CLIP(p_bboxes[4*i+1] * networkInfo.height, 0, networkInfo.height - 1);
535 | object.width = CLIP(p_bboxes[4*i+2] * networkInfo.width, 0, networkInfo.width - 1) - object.left;
536 | object.height = CLIP(p_bboxes[4*i+3] * networkInfo.height, 0, networkInfo.height - 1) - object.top;
537 |
538 | if(object.height < 0 || object.width < 0)
539 | continue;
540 | objectList.push_back(object);
541 | }
542 | return true;
543 | }
544 |
545 | extern "C"
546 | bool NvDsInferParseCustomMrcnnTLTV2 (std::vector const &outputLayersInfo,
547 | NvDsInferNetworkInfo const &networkInfo,
548 | NvDsInferParseDetectionParams const &detectionParams,
549 | std::vector &objectList) {
550 | auto layerFinder = [&outputLayersInfo](const std::string &name)
551 | -> const NvDsInferLayerInfo *{
552 | for (auto &layer : outputLayersInfo) {
553 | if (layer.dataType == FLOAT &&
554 | (layer.layerName && name == layer.layerName)) {
555 | return &layer;
556 | }
557 | }
558 | return nullptr;
559 | };
560 |
561 | const NvDsInferLayerInfo *detectionLayer = layerFinder("generate_detections");
562 | const NvDsInferLayerInfo *maskLayer = layerFinder("mask_fcn_logits/BiasAdd");
563 |
564 | if (!detectionLayer || !maskLayer) {
565 | std::cerr << "ERROR: some layers missing or unsupported data types "
566 | << "in output tensors" << std::endl;
567 | return false;
568 | }
569 |
570 | if(maskLayer->inferDims.numDims != 4U) {
571 | std::cerr << "Network output number of dims is : " <<
572 | maskLayer->inferDims.numDims << " expect is 4"<< std::endl;
573 | return false;
574 | }
575 |
576 | const unsigned int det_max_instances = maskLayer->inferDims.d[0];
577 | const unsigned int num_classes = maskLayer->inferDims.d[1];
578 | if(num_classes != detectionParams.numClassesConfigured) {
579 | std::cerr << "WARNING: Num classes mismatch. Configured:" <<
580 | detectionParams.numClassesConfigured << ", detected by network: " <<
581 | num_classes << std::endl;
582 | }
583 | const unsigned int mask_instance_height= maskLayer->inferDims.d[2];
584 | const unsigned int mask_instance_width = maskLayer->inferDims.d[3];
585 |
586 | auto out_det = reinterpret_cast( detectionLayer->buffer);
587 | auto out_mask = reinterpret_cast(maskLayer->buffer);
589 |
590 | for(auto i = 0U; i < det_max_instances; i++) {
591 | MrcnnRawDetection &rawDec = out_det[i];
592 |
593 | if(rawDec.score < detectionParams.perClassPreclusterThreshold[0])
594 | continue;
595 |
596 | NvDsInferInstanceMaskInfo obj;
597 | obj.left = CLIP(rawDec.x1, 0, networkInfo.width - 1);
598 | obj.top = CLIP(rawDec.y1, 0, networkInfo.height - 1);
599 | obj.width = CLIP(rawDec.x2, 0, networkInfo.width - 1) - rawDec.x1;
600 | obj.height = CLIP(rawDec.y2, 0, networkInfo.height - 1) - rawDec.y1;
601 | if(obj.width <= 0 || obj.height <= 0)
602 | continue;
603 | obj.classId = static_cast(rawDec.class_id);
604 | obj.detectionConfidence = rawDec.score;
605 |
606 | obj.mask_size = sizeof(float)*mask_instance_width*mask_instance_height;
607 | obj.mask = new float[mask_instance_width*mask_instance_height];
608 | obj.mask_width = mask_instance_width;
609 | obj.mask_height = mask_instance_height;
610 |
611 | float *rawMask = reinterpret_cast(out_mask + i
612 | * detectionParams.numClassesConfigured + obj.classId);
613 | memcpy (obj.mask, rawMask, sizeof(float)*mask_instance_width*mask_instance_height);
614 |
615 | objectList.push_back(obj);
616 | }
617 |
618 | return true;
619 |
620 | }
621 |
622 | extern "C"
623 | bool NvDsInferParseCustomEfficientDetTAO (std::vector const &outputLayersInfo,
624 | NvDsInferNetworkInfo const &networkInfo,
625 | NvDsInferParseDetectionParams const &detectionParams,
626 | std::vector &objectList) {
627 | if(outputLayersInfo.size() != 4)
628 | {
629 | std::cerr << "Mismatch in the number of output buffers."
630 | << "Expected 4 output buffers, detected in the network :"
631 | << outputLayersInfo.size() << std::endl;
632 | return false;
633 | }
634 |
635 | int* p_keep_count = (int *) outputLayersInfo[0].buffer;
636 |
637 | float* p_bboxes = (float *) outputLayersInfo[1].buffer;
638 | NvDsInferDims inferDims_p_bboxes = outputLayersInfo[1].inferDims;
639 | int numElements_p_bboxes=inferDims_p_bboxes.numElements;
640 |
641 | float* p_scores = (float *) outputLayersInfo[2].buffer;
642 | float* p_classes = (float *) outputLayersInfo[3].buffer;
643 |
644 | const float threshold = detectionParams.perClassThreshold[0];
645 |
646 | float max_bbox=0;
647 | for (int i=0; i < numElements_p_bboxes; i++)
648 | {
649 | // std::cout <<"p_bboxes: "
650 | // < 0)
656 | {
657 | assert (!(max_bbox < 2.0));
658 | for (int i = 0; i < p_keep_count[0]; i++) {
659 |
660 |
661 | if ( p_scores[i] < threshold) continue;
662 | assert((unsigned int) p_classes[i] < detectionParams.numClassesConfigured);
663 |
664 |
665 | // std::cout << "label/conf/ x/y x/y -- "
666 | // << (int)p_classes[i] << " " << p_scores[i] << " "
667 | // << p_bboxes[4*i] << " " << p_bboxes[4*i+1] << " " << p_bboxes[4*i+2] << " "<< p_bboxes[4*i+3] << " " << std::endl;
668 |
669 | if(p_bboxes[4*i+2] < p_bboxes[4*i] || p_bboxes[4*i+3] < p_bboxes[4*i+1])
670 | continue;
671 |
672 | NvDsInferObjectDetectionInfo object;
673 | object.classId = (int) p_classes[i];
674 | object.detectionConfidence = p_scores[i];
675 |
676 |
677 | object.left=p_bboxes[4*i+1];
678 | object.top=p_bboxes[4*i];
679 | object.width=( p_bboxes[4*i+3] - object.left);
680 | object.height= ( p_bboxes[4*i+2] - object.top);
681 |
682 | object.left=CLIP(object.left, 0, networkInfo.width - 1);
683 | object.top=CLIP(object.top, 0, networkInfo.height - 1);
684 | object.width=CLIP(object.width, 0, networkInfo.width - 1);
685 | object.height=CLIP(object.height, 0, networkInfo.height - 1);
686 |
687 | objectList.push_back(object);
688 | }
689 | }
690 | return true;
691 | }
692 |
693 |
694 | extern "C"
695 | bool NvDsInferParseCustomEfficientNMS (std::vector const &outputLayersInfo,
696 | NvDsInferNetworkInfo const &networkInfo,
697 | NvDsInferParseDetectionParams const &detectionParams,
698 | std::vector &objectList) {
699 | if(outputLayersInfo.size() != 4)
700 | {
701 | std::cerr << "Mismatch in the number of output buffers."
702 | << "Expected 4 output buffers, detected in the network :"
703 | << outputLayersInfo.size() << std::endl;
704 | return false;
705 | }
706 |
707 | int* p_keep_count = (int *) outputLayersInfo[0].buffer;
708 |
709 | float* p_bboxes = (float *) outputLayersInfo[1].buffer;
710 | NvDsInferDims inferDims_p_bboxes = outputLayersInfo[1].inferDims;
711 | int numElements_p_bboxes=inferDims_p_bboxes.numElements;
712 |
713 | float* p_scores = (float *) outputLayersInfo[2].buffer;
714 | unsigned int* p_classes = (unsigned int *) outputLayersInfo[3].buffer;
715 |
716 | const float threshold = detectionParams.perClassThreshold[0];
717 |
718 | float max_bbox=0;
719 | for (int i=0; i < numElements_p_bboxes; i++)
720 | {
721 | if ( max_bbox < p_bboxes[i] )
722 | max_bbox=p_bboxes[i];
723 | }
724 |
725 | if (p_keep_count[0] > 0)
726 | {
727 | assert (!(max_bbox < 2.0));
728 | for (int i = 0; i < p_keep_count[0]; i++) {
729 |
730 | if ( p_scores[i] < threshold) continue;
731 | assert((unsigned int) p_classes[i] < detectionParams.numClassesConfigured);
732 |
733 | NvDsInferObjectDetectionInfo object;
734 | object.classId = (int) p_classes[i];
735 | object.detectionConfidence = p_scores[i];
736 |
737 | object.left=p_bboxes[4*i];
738 | object.top=p_bboxes[4*i+1];
739 | object.width=(p_bboxes[4*i+2] - object.left);
740 | object.height= (p_bboxes[4*i+3] - object.top);
741 |
742 |
743 | object.left=CLIP(object.left, 0, networkInfo.width - 1);
744 | object.top=CLIP(object.top, 0, networkInfo.height - 1);
745 | object.width=CLIP(object.width, 0, networkInfo.width - 1);
746 | object.height=CLIP(object.height, 0, networkInfo.height - 1);
747 |
748 | objectList.push_back(object);
749 | }
750 | }
751 | return true;
752 | }
753 |
754 |
755 |
756 | /* Check that the custom function has been defined correctly */
757 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomResnet);
758 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomTfSSD);
759 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomNMSTLT);
760 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseYoloV5CustomBatchedNMSTLT);
761 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomBatchedNMSTLT);
762 | CHECK_CUSTOM_INSTANCE_MASK_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomMrcnnTLT);
763 | CHECK_CUSTOM_INSTANCE_MASK_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomMrcnnTLTV2);
764 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomEfficientDetTAO);
765 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomEfficientNMS);
766 |
--------------------------------------------------------------------------------
/nvdsinfer_custom_impl_Yolo/nvdsinfer_customclassifierparser.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #include
24 | #include
25 | #include "nvdsinfer_custom_impl.h"
26 |
27 | /* This is a sample classifier output parsing function from softmax layers for
28 | * the vehicle type classifier model provided with the SDK. */
29 |
30 | /* C-linkage to prevent name-mangling */
31 | extern "C"
32 | bool NvDsInferClassiferParseCustomSoftmax (std::vector const &outputLayersInfo,
33 | NvDsInferNetworkInfo const &networkInfo,
34 | float classifierThreshold,
35 | std::vector &attrList,
36 | std::string &descString);
37 |
38 | static std::vector < std::vector< std:: string > > labels { {
39 | "coupe1", "largevehicle1", "sedan1", "suv1", "truck1", "van1"} };
40 |
41 | extern "C"
42 | bool NvDsInferClassiferParseCustomSoftmax (std::vector const &outputLayersInfo,
43 | NvDsInferNetworkInfo const &networkInfo,
44 | float classifierThreshold,
45 | std::vector &attrList,
46 | std::string &descString)
47 | {
48 | /* Get the number of attributes supported by the classifier. */
49 | unsigned int numAttributes = outputLayersInfo.size();
50 |
51 | /* Iterate through all the output coverage layers of the classifier.
52 | */
53 | for (unsigned int l = 0; l < numAttributes; l++)
54 | {
55 | /* outputCoverageBuffer for classifiers is usually a softmax layer.
56 | * The layer is an array of probabilities of the object belonging
57 | * to each class with each probability being in the range [0,1] and
58 | * sum all probabilities will be 1.
59 | */
60 | NvDsInferDimsCHW dims;
61 |
62 | getDimsCHWFromDims(dims, outputLayersInfo[l].inferDims);
63 | unsigned int numClasses = dims.c;
64 | float *outputCoverageBuffer = (float *)outputLayersInfo[l].buffer;
65 | float maxProbability = 0;
66 | bool attrFound = false;
67 | NvDsInferAttribute attr;
68 |
69 | /* Iterate through all the probabilities that the object belongs to
70 | * each class. Find the maximum probability and the corresponding class
71 | * which meets the minimum threshold. */
72 | for (unsigned int c = 0; c < numClasses; c++)
73 | {
74 | float probability = outputCoverageBuffer[c];
75 | if (probability > classifierThreshold
76 | && probability > maxProbability)
77 | {
78 | maxProbability = probability;
79 | attrFound = true;
80 | attr.attributeIndex = l;
81 | attr.attributeValue = c;
82 | attr.attributeConfidence = probability;
83 | }
84 | }
85 | if (attrFound)
86 | {
87 | if (labels.size() > attr.attributeIndex &&
88 | attr.attributeValue < labels[attr.attributeIndex].size())
89 | attr.attributeLabel =
90 | strdup(labels[attr.attributeIndex][attr.attributeValue].c_str());
91 | else
92 | attr.attributeLabel = nullptr;
93 | attrList.push_back(attr);
94 | if (attr.attributeLabel)
95 | descString.append(attr.attributeLabel).append(" ");
96 | }
97 | }
98 |
99 | return true;
100 | }
101 |
102 | /* Check that the custom function has been defined correctly */
103 | CHECK_CUSTOM_CLASSIFIER_PARSE_FUNC_PROTOTYPE(NvDsInferClassiferParseCustomSoftmax);
104 |
--------------------------------------------------------------------------------
/run_docker.sh:
--------------------------------------------------------------------------------
1 | docker run -it --rm --gpus all --network host \
2 | -v $PWD:/opt/nvidia/deepstream/deepstream-6.1/sources/yolov7-triton-deepstream \
3 | -e DISPLAY=$DISPLAY -e XAUTHORITY=$XAUTHORITY -v $XAUTHORITY:$XAUTHORITY -v /tmp/.X11-unix:/tmp/.X11-unix thanhlnbka/deepstream-python-app:3.0-triton
--------------------------------------------------------------------------------
/triton_yolov7/yolov7/classes.txt:
--------------------------------------------------------------------------------
1 | person
2 | bicycle
3 | car
4 | motorcycle
5 | airplane
6 | bus
7 | train
8 | truck
9 | boat
10 | traffic light
11 | fire hydrant
12 | stop sign
13 | parking meter
14 | bench
15 | bird
16 | cat
17 | dog
18 | horse
19 | sheep
20 | cow
21 | elephant
22 | bear
23 | zebra
24 | giraffe
25 | backpack
26 | umbrella
27 | handbag
28 | tie
29 | suitcase
30 | frisbee
31 | skis
32 | snowboard
33 | sports ball
34 | kite
35 | baseball bat
36 | baseball glove
37 | skateboard
38 | surfboard
39 | tennis racket
40 | bottle
41 | wine glass
42 | cup
43 | fork
44 | knife
45 | spoon
46 | bowl
47 | banana
48 | apple
49 | sandwich
50 | orange
51 | broccoli
52 | carrot
53 | hot dog
54 | pizza
55 | donut
56 | cake
57 | chair
58 | couch
59 | potted plant
60 | bed
61 | dining table
62 | toilet
63 | tv
64 | laptop
65 | mouse
66 | remote
67 | keyboard
68 | cell phone
69 | microwave
70 | oven
71 | toaster
72 | sink
73 | refrigerator
74 | book
75 | clock
76 | vase
77 | scissors
78 | teddy bear
79 | hair drier
80 | toothbrush
81 |
--------------------------------------------------------------------------------
/triton_yolov7/yolov7/config.pbtxt:
--------------------------------------------------------------------------------
1 | name: "yolov7"
2 | platform: "tensorrt_plan"
3 | max_batch_size: 8
4 | input [
5 | {
6 | name: "images"
7 | data_type: TYPE_FP32
8 | dims: [3,640,640]
9 | }
10 | ]
11 | output [
12 | {
13 | name: "det_boxes"
14 | data_type: TYPE_FP32
15 | dims: [ 100, 4]
16 | reshape { shape: [100,4] }
17 | },
18 | {
19 | name: "det_classes"
20 | data_type: TYPE_INT32
21 | dims: [ 100 ]
22 | },
23 | {
24 | name: "det_scores"
25 | data_type: TYPE_FP32
26 | dims: [ 100 ]
27 | },
28 | {
29 | name: "num_dets"
30 | data_type: TYPE_INT32
31 | dims: [ 1]
32 | }
33 | ]
34 |
--------------------------------------------------------------------------------
/weights/README.md:
--------------------------------------------------------------------------------
1 | # Export model pt to onnx with yolov7
2 | See https://github.com/WongKinYiu/yolov7#export for more info.
3 |
4 | * python export.py --weights ./yolov7.pt --grid --end2end --dynamic-batch --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640
5 |
6 |
7 | download yolov7-tiny converted onnx: https://drive.google.com/file/d/1GZbGFUk8BtEO-ZyWL2aoa3g4YS40dKiF/view?usp=sharing
--------------------------------------------------------------------------------