deepstream-osd-queue.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. # Import required libraries
  2. import sys
  3. sys.path.append('../')
  4. sys.path.append('../source_code')
  5. import gi
  6. import configparser
  7. gi.require_version('Gst', '1.0')
  8. from gi.repository import GObject, Gst
  9. from gi.repository import GLib
  10. from ctypes import *
  11. import time
  12. import sys
  13. import math
  14. import platform
  15. from common.bus_call import bus_call
  16. from common.FPS import GETFPS
  17. import pyds
  18. # Define variables to be used later
  19. fps_streams={}
  20. PGIE_CLASS_ID_VEHICLE = 0
  21. PGIE_CLASS_ID_BICYCLE = 1
  22. PGIE_CLASS_ID_PERSON = 2
  23. PGIE_CLASS_ID_ROADSIGN = 3
  24. MUXER_OUTPUT_WIDTH=1920
  25. MUXER_OUTPUT_HEIGHT=1080
  26. TILED_OUTPUT_WIDTH=1920
  27. TILED_OUTPUT_HEIGHT=1080
  28. OSD_PROCESS_MODE= 0
  29. OSD_DISPLAY_TEXT= 0
  30. pgie_classes_str= ["Vehicle", "TwoWheeler", "Person","RoadSign"]
  31. ################ Three Stream Pipeline ###########
  32. # Define Input and output Stream information
  33. num_sources = 3
  34. INPUT_VIDEO_1 = '/opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_720p.h264'
  35. INPUT_VIDEO_2 = '/opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_720p.h264'
  36. INPUT_VIDEO_3 = '/opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_720p.h264'
  37. OUTPUT_VIDEO_NAME = "../source_code/N1/ds_out2.mp4"
  38. ## Make Element or Print Error and any other detail
  39. def make_elm_or_print_err(factoryname, name, printedname, detail=""):
  40. print("Creating", printedname)
  41. elm = Gst.ElementFactory.make(factoryname, name)
  42. if not elm:
  43. sys.stderr.write("Unable to create " + printedname + " \n")
  44. if detail:
  45. sys.stderr.write(detail)
  46. return elm
  47. # #### Initialise GStreamer and Create an Empty Pipeline
  48. for i in range(0,num_sources):
  49. fps_streams["stream{0}".format(i)]=GETFPS(i)
  50. # Standard GStreamer initialization
  51. Gst.init(None)
  52. # Create gstreamer elements */
  53. # Create Pipeline element that will form a connection of other elements
  54. print("Creating Pipeline \n ")
  55. pipeline = Gst.Pipeline()
  56. if not pipeline:
  57. sys.stderr.write(" Unable to create Pipeline \n")
  58. # #### Create Elements that are required for our pipeline
  59. ########### Create Elements required for the Pipeline ###########
  60. ######### Defining Stream 1
  61. # Source element for reading from the file
  62. source1 = make_elm_or_print_err("filesrc", "file-source-1",'file-source-1')
  63. # Since the data format in the input file is elementary h264 stream,we need a h264parser
  64. h264parser1 = make_elm_or_print_err("h264parse", "h264-parser-1","h264-parser-1")
  65. # Use nvdec_h264 for hardware accelerated decode on GPU
  66. decoder1 = make_elm_or_print_err("nvv4l2decoder", "nvv4l2-decoder-1","nvv4l2-decoder-1")
  67. ##########
  68. ########## Defining Stream 2
  69. # Source element for reading from the file
  70. source2 = make_elm_or_print_err("filesrc", "file-source-2","file-source-2")
  71. # Since the data format in the input file is elementary h264 stream, we need a h264parser
  72. h264parser2 = make_elm_or_print_err("h264parse", "h264-parser-2", "h264-parser-2")
  73. # Use nvdec_h264 for hardware accelerated decode on GPU
  74. decoder2 = make_elm_or_print_err("nvv4l2decoder", "nvv4l2-decoder-2","nvv4l2-decoder-2")
  75. ###########
  76. ########## Defining Stream 3
  77. # Source element for reading from the file
  78. source3 = make_elm_or_print_err("filesrc", "file-source-3","file-source-3")
  79. # Since the data format in the input file is elementary h264 stream, we need a h264parser
  80. h264parser3 = make_elm_or_print_err("h264parse", "h264-parser-3", "h264-parser-3")
  81. # Use nvdec_h264 for hardware accelerated decode on GPU
  82. decoder3 = make_elm_or_print_err("nvv4l2decoder", "nvv4l2-decoder-3","nvv4l2-decoder-3")
  83. ###########
  84. # Create nvstreammux instance to form batches from one or more sources.
  85. streammux = make_elm_or_print_err("nvstreammux", "Stream-muxer","Stream-muxer")
  86. # Use nvinfer to run inferencing on decoder's output, behaviour of inferencing is set through config file
  87. pgie = make_elm_or_print_err("nvinfer", "primary-inference" ,"pgie")
  88. # Use nvtracker to give objects unique-ids
  89. tracker = make_elm_or_print_err("nvtracker", "tracker",'tracker')
  90. # Seconday inference for Finding Car Color
  91. sgie1 = make_elm_or_print_err("nvinfer", "secondary1-nvinference-engine",'sgie1')
  92. # Seconday inference for Finding Car Make
  93. sgie2 = make_elm_or_print_err("nvinfer", "secondary2-nvinference-engine",'sgie2')
  94. # Seconday inference for Finding Car Type
  95. sgie3 = make_elm_or_print_err("nvinfer", "secondary3-nvinference-engine",'sgie3')
  96. # Creating Tiler to present more than one streams
  97. tiler=make_elm_or_print_err("nvmultistreamtiler", "nvtiler","nvtiler")
  98. # Use convertor to convert from NV12 to RGBA as required by nvosd
  99. nvvidconv = make_elm_or_print_err("nvvideoconvert", "convertor","nvvidconv")
  100. # Create OSD to draw on the converted RGBA buffer
  101. nvosd = make_elm_or_print_err("nvdsosd", "onscreendisplay","nvosd")
  102. # Creating queue's to buffer incoming data from pgie
  103. queue1=make_elm_or_print_err("queue","queue1","queue1")
  104. # Creating queue's to buffer incoming data from tiler
  105. queue2=make_elm_or_print_err("queue","queue2","queue2")
  106. # Creating queue's to buffer incoming data from nvvidconv
  107. queue3=make_elm_or_print_err("queue","queue3","queue3")
  108. # Creating queue's to buffer incoming data from nvosd
  109. queue4=make_elm_or_print_err("queue","queue4","queue4")
  110. # Creating queue's to buffer incoming data from nvvidconv2
  111. queue5=make_elm_or_print_err("queue","queue5","queue5")
  112. # Creating queue's to buffer incoming data from nvtracker
  113. queue6=make_elm_or_print_err("queue","queue6","queue6")
  114. # Creating queue's to buffer incoming data from sgie1
  115. queue7=make_elm_or_print_err("queue","queue7","queue7")
  116. # Creating queue's to buffer incoming data from sgie2
  117. queue8=make_elm_or_print_err("queue","queue8","queue8")
  118. # Creating queue's to buffer incoming data from sgie3
  119. queue9=make_elm_or_print_err("queue","queue9","queue9")
  120. # Use convertor to convert from NV12 to RGBA as required by nvosd
  121. nvvidconv2 = make_elm_or_print_err("nvvideoconvert", "convertor2","nvvidconv2")
  122. # Place an encoder instead of OSD to save as video file
  123. encoder = make_elm_or_print_err("avenc_mpeg4", "encoder", "Encoder")
  124. # Parse output from Encoder
  125. codeparser = make_elm_or_print_err("mpeg4videoparse", "mpeg4-parser", 'Code Parser')
  126. # Create a container
  127. container = make_elm_or_print_err("qtmux", "qtmux", "Container")
  128. # Create Sink for storing the output
  129. sink = make_elm_or_print_err("filesink", "filesink", "Sink")
  130. ############ Set properties for the Elements ############
  131. # Set Input Video files
  132. source1.set_property('location', INPUT_VIDEO_1)
  133. source2.set_property('location', INPUT_VIDEO_2)
  134. source3.set_property('location', INPUT_VIDEO_2)
  135. # Set Input Width , Height and Batch Size
  136. streammux.set_property('width', 1920)
  137. streammux.set_property('height', 1080)
  138. streammux.set_property('batch-size', num_sources)
  139. # Timeout in microseconds to wait after the first buffer is available
  140. # to push the batch even if a complete batch is not formed.
  141. streammux.set_property('batched-push-timeout', 4000000)
  142. # Set configuration file for nvinfer
  143. # Set Congifuration file for nvinfer
  144. pgie.set_property('config-file-path', "../source_code/N1/dstest4_pgie_config.txt")
  145. sgie1.set_property('config-file-path', "../source_code/N1/dstest4_sgie1_config.txt")
  146. sgie2.set_property('config-file-path', "../source_code/N1/dstest4_sgie2_config.txt")
  147. sgie3.set_property('config-file-path', "../source_code/N1/dstest4_sgie3_config.txt")
  148. #Set properties of tracker from tracker_config
  149. config = configparser.ConfigParser()
  150. config.read('../source_code/N1/dstest4_tracker_config.txt')
  151. config.sections()
  152. for key in config['tracker']:
  153. if key == 'tracker-width' :
  154. tracker_width = config.getint('tracker', key)
  155. tracker.set_property('tracker-width', tracker_width)
  156. if key == 'tracker-height' :
  157. tracker_height = config.getint('tracker', key)
  158. tracker.set_property('tracker-height', tracker_height)
  159. if key == 'gpu-id' :
  160. tracker_gpu_id = config.getint('tracker', key)
  161. tracker.set_property('gpu_id', tracker_gpu_id)
  162. if key == 'll-lib-file' :
  163. tracker_ll_lib_file = config.get('tracker', key)
  164. tracker.set_property('ll-lib-file', tracker_ll_lib_file)
  165. if key == 'll-config-file' :
  166. tracker_ll_config_file = config.get('tracker', key)
  167. tracker.set_property('ll-config-file', tracker_ll_config_file)
  168. if key == 'enable-batch-process' :
  169. tracker_enable_batch_process = config.getint('tracker', key)
  170. tracker.set_property('enable_batch_process', tracker_enable_batch_process)
  171. # Set display configurations for nvmultistreamtiler
  172. tiler_rows=int(2)
  173. tiler_columns=int(2)
  174. tiler.set_property("rows",tiler_rows)
  175. tiler.set_property("columns",tiler_columns)
  176. tiler.set_property("width", TILED_OUTPUT_WIDTH)
  177. tiler.set_property("height", TILED_OUTPUT_HEIGHT)
  178. # Set encoding properties and Sink configs
  179. encoder.set_property("bitrate", 2000000)
  180. sink.set_property("location", OUTPUT_VIDEO_NAME)
  181. sink.set_property("sync", 0)
  182. sink.set_property("async", 0)
  183. # We now link all the elements in the order we prefer and create Gstreamer bus to feed all messages through it.
  184. ########## Add and Link ELements in the Pipeline ##########
  185. print("Adding elements to Pipeline \n")
  186. pipeline.add(source1)
  187. pipeline.add(h264parser1)
  188. pipeline.add(decoder1)
  189. pipeline.add(source2)
  190. pipeline.add(h264parser2)
  191. pipeline.add(decoder2)
  192. pipeline.add(source3)
  193. pipeline.add(h264parser3)
  194. pipeline.add(decoder3)
  195. pipeline.add(streammux)
  196. pipeline.add(pgie)
  197. pipeline.add(tracker)
  198. pipeline.add(sgie1)
  199. pipeline.add(sgie2)
  200. pipeline.add(sgie3)
  201. pipeline.add(tiler)
  202. pipeline.add(nvvidconv)
  203. pipeline.add(nvosd)
  204. pipeline.add(queue1)
  205. pipeline.add(queue2)
  206. pipeline.add(queue3)
  207. pipeline.add(queue4)
  208. pipeline.add(queue5)
  209. pipeline.add(queue6)
  210. pipeline.add(queue7)
  211. pipeline.add(queue8)
  212. pipeline.add(queue9)
  213. pipeline.add(nvvidconv2)
  214. pipeline.add(encoder)
  215. pipeline.add(codeparser)
  216. pipeline.add(container)
  217. pipeline.add(sink)
  218. print("Linking elements in the Pipeline \n")
  219. source1.link(h264parser1)
  220. h264parser1.link(decoder1)
  221. ###### Create Sink pad and connect to decoder's source pad
  222. sinkpad1 = streammux.get_request_pad("sink_0")
  223. if not sinkpad1:
  224. sys.stderr.write(" Unable to get the sink pad of streammux \n")
  225. srcpad1 = decoder1.get_static_pad("src")
  226. if not srcpad1:
  227. sys.stderr.write(" Unable to get source pad of decoder \n")
  228. srcpad1.link(sinkpad1)
  229. ######
  230. ###### Create Sink pad and connect to decoder's source pad
  231. source2.link(h264parser2)
  232. h264parser2.link(decoder2)
  233. sinkpad2 = streammux.get_request_pad("sink_1")
  234. if not sinkpad2:
  235. sys.stderr.write(" Unable to get the sink pad of streammux \n")
  236. srcpad2 = decoder2.get_static_pad("src")
  237. if not srcpad2:
  238. sys.stderr.write(" Unable to get source pad of decoder \n")
  239. srcpad2.link(sinkpad2)
  240. ######
  241. ###### Create Sink pad and connect to decoder's source pad
  242. source3.link(h264parser3)
  243. h264parser3.link(decoder3)
  244. sinkpad3 = streammux.get_request_pad("sink_2")
  245. if not sinkpad2:
  246. sys.stderr.write(" Unable to get the sink pad of streammux \n")
  247. srcpad3 = decoder3.get_static_pad("src")
  248. if not srcpad3:
  249. sys.stderr.write(" Unable to get source pad of decoder \n")
  250. srcpad3.link(sinkpad3)
  251. ######
  252. streammux.link(queue1)
  253. queue1.link(pgie)
  254. pgie.link(queue2)
  255. queue2.link(tracker)
  256. tracker.link(queue3)
  257. queue3.link(sgie1)
  258. sgie1.link(queue4)
  259. queue4.link(sgie2)
  260. sgie2.link(queue5)
  261. queue5.link(sgie3)
  262. sgie3.link(queue6)
  263. queue6.link(tiler)
  264. tiler.link(queue7)
  265. queue7.link(nvvidconv)
  266. nvvidconv.link(queue8)
  267. queue8.link(nvosd)
  268. nvosd.link(queue9)
  269. queue9.link(nvvidconv2)
  270. nvvidconv2.link(encoder)
  271. encoder.link(codeparser)
  272. codeparser.link(container)
  273. container.link(sink)
  274. # create an event loop and feed gstreamer bus mesages to it
  275. loop = GObject.MainLoop()
  276. bus = pipeline.get_bus()
  277. bus.add_signal_watch()
  278. bus.connect ("message", bus_call, loop)
  279. # tiler_sink_pad_buffer_probe will extract metadata received on OSD sink pad
  280. # and update params for drawing rectangle, object information etc.
  281. def tiler_src_pad_buffer_probe(pad,info,u_data):
  282. #Intiallizing object counter with 0.
  283. obj_counter = {
  284. PGIE_CLASS_ID_VEHICLE:0,
  285. PGIE_CLASS_ID_PERSON:0,
  286. PGIE_CLASS_ID_BICYCLE:0,
  287. PGIE_CLASS_ID_ROADSIGN:0
  288. }
  289. # Set frame_number & rectangles to draw as 0
  290. frame_number=0
  291. num_rects=0
  292. gst_buffer = info.get_buffer()
  293. if not gst_buffer:
  294. print("Unable to get GstBuffer ")
  295. return
  296. # Retrieve batch metadata from the gst_buffer
  297. # Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
  298. # C address of gst_buffer as input, which is obtained with hash(gst_buffer)
  299. batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
  300. l_frame = batch_meta.frame_meta_list
  301. while l_frame is not None:
  302. try:
  303. # Note that l_frame.data needs a cast to pyds.NvDsFrameMeta
  304. frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
  305. except StopIteration:
  306. break
  307. # Get frame number , number of rectables to draw and object metadata
  308. frame_number=frame_meta.frame_num
  309. num_rects = frame_meta.num_obj_meta
  310. l_obj=frame_meta.obj_meta_list
  311. while l_obj is not None:
  312. try:
  313. # Casting l_obj.data to pyds.NvDsObjectMeta
  314. obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
  315. except StopIteration:
  316. break
  317. # Increment Object class by 1 and Set Box border to Red color
  318. obj_counter[obj_meta.class_id] += 1
  319. obj_meta.rect_params.border_color.set(0.0, 0.0, 1.0, 0.0)
  320. try:
  321. l_obj=l_obj.next
  322. except StopIteration:
  323. break
  324. ################## Setting Metadata Display configruation ###############
  325. # Acquiring a display meta object.
  326. display_meta=pyds.nvds_acquire_display_meta_from_pool(batch_meta)
  327. display_meta.num_labels = 1
  328. py_nvosd_text_params = display_meta.text_params[0]
  329. # Setting display text to be shown on screen
  330. py_nvosd_text_params.display_text = "Frame Number={} Number of Objects={} Vehicle_count={} Person_count={}".format(frame_number, num_rects, obj_counter[PGIE_CLASS_ID_VEHICLE], obj_counter[PGIE_CLASS_ID_PERSON])
  331. # Now set the offsets where the string should appear
  332. py_nvosd_text_params.x_offset = 10
  333. py_nvosd_text_params.y_offset = 12
  334. # Font , font-color and font-size
  335. py_nvosd_text_params.font_params.font_name = "Serif"
  336. py_nvosd_text_params.font_params.font_size = 10
  337. # Set(red, green, blue, alpha); Set to White
  338. py_nvosd_text_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0)
  339. # Text background color
  340. py_nvosd_text_params.set_bg_clr = 1
  341. # Set(red, green, blue, alpha); set to Black
  342. py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
  343. # Using pyds.get_string() to get display_text as string to print in notebook
  344. print(pyds.get_string(py_nvosd_text_params.display_text))
  345. pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
  346. ############################################################################
  347. # Get frame rate through this probe
  348. fps_streams["stream{0}".format(frame_meta.pad_index)].get_fps()
  349. try:
  350. l_frame=l_frame.next
  351. except StopIteration:
  352. break
  353. return Gst.PadProbeReturn.OK
  354. tiler_src_pad=sgie3.get_static_pad("src")
  355. if not tiler_src_pad:
  356. sys.stderr.write(" Unable to get src pad \n")
  357. else:
  358. tiler_src_pad.add_probe(Gst.PadProbeType.BUFFER, tiler_src_pad_buffer_probe, 0)
  359. # Now with everything defined , we can start the playback and listen the events.
  360. # List the sources
  361. print("Now playing...")
  362. print("Starting pipeline \n")
  363. # start play back and listed to events
  364. pipeline.set_state(Gst.State.PLAYING)
  365. start_time = time.time()
  366. try:
  367. loop.run()
  368. except:
  369. pass
  370. print("Exiting app\n")
  371. pipeline.set_state(Gst.State.NULL)
  372. print("--- %s seconds ---" % (time.time() - start_time))