deepstream-covid-19.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. # Import required libraries
  2. import argparse
  3. import sys
  4. import os
  5. sys.path.append('../')
  6. sys.path.append('../source_code')
  7. sys.path.append('../source_code/distancing/')
  8. import gi
  9. import configparser
  10. gi.require_version('Gst', '1.0')
  11. from gi.repository import GObject, Gst
  12. from gi.repository import GLib
  13. from ctypes import *
  14. import time
  15. import sys
  16. import math
  17. import platform
  18. from common.bus_call import bus_call
  19. from common.FPS import GETFPS
  20. import pyds
  21. PGIE_CLASS_ID_VEHICLE = 0
  22. PGIE_CLASS_ID_BICYCLE = 1
  23. PGIE_CLASS_ID_PERSON = 2
  24. PGIE_CLASS_ID_ROADSIGN = 3
  25. g_args=None
  26. # Define variables to be used later
  27. fps_streams={}
  28. pgie_classes_str= ["Vehicle", "TwoWheeler", "Person","RoadSign"]
  29. def parse_args():
  30. parser = argparse.ArgumentParser()
  31. parser.add_argument("--num-sources", type=int, default=1, help="Number of sources, it replicates inputs if its is greater than length of inputs")
  32. parser.add_argument("--prof", type=bool, default=False, help="Profiling Mode , Profiles with a shorter video clip.")
  33. args = parser.parse_args()
  34. return args
  35. def cb_newpad(decodebin, decoder_src_pad,data):
  36. print("In cb_newpad\n")
  37. caps=decoder_src_pad.get_current_caps()
  38. gststruct=caps.get_structure(0)
  39. gstname=gststruct.get_name()
  40. source_bin=data
  41. features=caps.get_features(0)
  42. # Need to check if the pad created by the decodebin is for video and not
  43. # audio.
  44. print("gstname=",gstname)
  45. if(gstname.find("video")!=-1):
  46. # Link the decodebin pad only if decodebin has picked nvidia
  47. # decoder plugin nvdec_*. We do this by checking if the pad caps contain
  48. # NVMM memory features.
  49. print("features=",features)
  50. if features.contains("memory:NVMM"):
  51. # Get the source bin ghost pad
  52. bin_ghost_pad=source_bin.get_static_pad("src")
  53. if not bin_ghost_pad.set_target(decoder_src_pad):
  54. sys.stderr.write("Failed to link decoder src pad to source bin ghost pad\n")
  55. else:
  56. sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")
  57. def decodebin_child_added(child_proxy,Object,name,user_data):
  58. print("Decodebin child added:", name, "\n")
  59. if(name.find("decodebin") != -1):
  60. Object.connect("child-added",decodebin_child_added,user_data)
  61. def create_source_bin(args, index,uri):
  62. print("Creating source bin")
  63. # Create a source GstBin to abstract this bin's content from the rest of the
  64. # pipeline
  65. bin_name="source-bin-%02d" %index
  66. print(bin_name)
  67. nbin=Gst.Bin.new(bin_name)
  68. if not nbin:
  69. sys.stderr.write(" Unable to create source bin \n")
  70. # Source element for reading from the uri.
  71. # We will use decodebin and let it figure out the container format of the
  72. # stream and the codec and plug the appropriate demux and decode plugins.
  73. uri_decode_bin=Gst.ElementFactory.make("uridecodebin", "uri-decode-bin")
  74. if not uri_decode_bin:
  75. sys.stderr.write(" Unable to create uri decode bin \n")
  76. # We set the input uri to the source element
  77. uri_decode_bin.set_property("uri",uri)
  78. # Connect to the "pad-added" signal of the decodebin which generates a
  79. # callback once a new pad for raw data has beed created by the decodebin
  80. uri_decode_bin.connect("pad-added",cb_newpad,nbin)
  81. uri_decode_bin.connect("child-added",decodebin_child_added,nbin)
  82. # We need to create a ghost pad for the source bin which will act as a proxy
  83. # for the video decoder src pad. The ghost pad will not have a target right
  84. # now. Once the decode bin creates the video decoder and generates the
  85. # cb_newpad callback, we will set the ghost pad target to the video decoder
  86. # src pad.
  87. Gst.Bin.add(nbin,uri_decode_bin)
  88. bin_pad=nbin.add_pad(Gst.GhostPad.new_no_target("src",Gst.PadDirection.SRC))
  89. if not bin_pad:
  90. sys.stderr.write("Failed to add ghost pad in source bin \n")
  91. return None
  92. return nbin
  93. ## Make Element or Print Error and any other detail
  94. def make_elm_or_print_err(factoryname, name, printedname, detail=""):
  95. print("Creating", printedname)
  96. elm = Gst.ElementFactory.make(factoryname, name)
  97. if not elm:
  98. sys.stderr.write("Unable to create " + printedname + " \n")
  99. if detail:
  100. sys.stderr.write(detail)
  101. return elm
  102. ############# Define Computation required for our pipeline #################
  103. def compute_dist(p1, p2):
  104. (x1, y1, h1) = p1;
  105. (x2, y2, h2) = p2;
  106. dx = x2 - x1;
  107. dy = y2 - y1;
  108. lx = dx * 170 * (1/h1 + 1/h2) / 2;
  109. ly = dy * 170 * (1/h1 + 1/h2) / 2;
  110. l = math.sqrt(lx*lx + ly*ly);
  111. return l
  112. def get_min_distances(centroids):
  113. mini=[]
  114. for i in range(len(centroids)):
  115. distance=[]
  116. for j in range(len(centroids)):
  117. distance.append(compute_dist(centroids[i],centroids[j]))
  118. distance[i]=10000000
  119. mini.append(min(distance))
  120. return mini
  121. def visualize(objs):
  122. violations = 0
  123. dist_threshold = 160 # Distance in cms
  124. for obj in objs:
  125. min_dist = obj["min_dist"]
  126. redness_factor = 1.5
  127. obj["violated"] = (min_dist < dist_threshold)
  128. violations = violations + int(min_dist < dist_threshold)
  129. return violations
  130. def get_centroid(rect):
  131. xmin = rect.left
  132. xmax = rect.left + rect.width
  133. ymin = rect.top
  134. ymax = rect.top + rect.height
  135. centroid_x = (xmin + xmax) / 2
  136. centroid_y = (ymin + ymax) / 2
  137. return (centroid_x, centroid_y, rect.height)
  138. def compute_min_distances_cpp(objs):
  139. centroids = [o["centroid"] for o in objs]
  140. min_distances = get_min_distances(centroids)
  141. for o in range(len(objs)):
  142. objs[o]["min_dist"] = min_distances[o]
  143. ############## Working with the Metadata ################
  144. def src_pad_buffer_probe(pad,info,u_data):
  145. # Set frame_number & rectangles to draw as 0
  146. frame_number=0
  147. num_rects=0
  148. gst_buffer = info.get_buffer()
  149. if not gst_buffer:
  150. print("Unable to get GstBuffer ")
  151. return
  152. # Retrieve batch metadata from the gst_buffer
  153. # Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
  154. # C address of gst_buffer as input, which is obtained with hash(gst_buffer)
  155. batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
  156. l_frame = batch_meta.frame_meta_list
  157. while l_frame is not None:
  158. try:
  159. # Note that l_frame.data needs a cast to pyds.NvDsFrameMeta
  160. frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
  161. except StopIteration:
  162. break
  163. objects=[]
  164. # Get frame number , number of rectables to draw and object metadata
  165. frame_number=frame_meta.frame_num
  166. num_rects = frame_meta.num_obj_meta
  167. l_obj=frame_meta.obj_meta_list
  168. #Intiallizing object counter with 0.
  169. obj_counter = {
  170. PGIE_CLASS_ID_VEHICLE:0,
  171. PGIE_CLASS_ID_PERSON:0,
  172. PGIE_CLASS_ID_BICYCLE:0,
  173. PGIE_CLASS_ID_ROADSIGN:0
  174. }
  175. while l_obj is not None:
  176. try:
  177. # Casting l_obj.data to pyds.NvDsObjectMeta
  178. obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
  179. except StopIteration:
  180. break
  181. # Increment Object class by 1 and Set Box border to Red color
  182. obj_counter[obj_meta.class_id] +=1
  183. obj_meta.rect_params.border_color.set(0.0, 0.0, 1.0, 0.0)
  184. if (obj_meta.class_id == PGIE_CLASS_ID_PERSON):
  185. obj = {}
  186. obj["tracker_id"] = obj_meta.object_id
  187. obj["unique_id"] = obj_meta.unique_component_id
  188. obj["centroid"] = get_centroid(obj_meta.rect_params)
  189. obj["obj_meta"] = obj_meta
  190. objects.append(obj)
  191. else:
  192. obj_meta.rect_params.border_width = 0
  193. try:
  194. l_obj=l_obj.next
  195. except StopIteration:
  196. break
  197. # Get the number of violations
  198. compute_min_distances_cpp(objects)
  199. violations = visualize(objects)
  200. print("Frame Number={} Number of Objects={} Vehicle_count={} Person_count={} Violations={}".format(frame_number, num_rects, obj_counter[PGIE_CLASS_ID_VEHICLE], obj_counter[PGIE_CLASS_ID_PERSON],violations))
  201. # Get frame rate through this probe
  202. fps_streams["stream{0}".format(frame_meta.pad_index)].get_fps()
  203. try:
  204. l_frame=l_frame.next
  205. except StopIteration:
  206. break
  207. return Gst.PadProbeReturn.OK
  208. def main():
  209. args = parse_args()
  210. global g_args
  211. g_args = args
  212. num_sources = args.num_sources
  213. path = os.path.abspath(os.getcwd())
  214. if (args.prof):
  215. INPUT_VIDEO = 'file://' + path +'/../source_code/dataset/wt_prof.mp4'
  216. else :
  217. INPUT_VIDEO = 'file://' + path +'/../source_code/dataset/wt.mp4'
  218. print("Creating pipeline with "+str(num_sources)+" streams")
  219. # Initialise FPS
  220. for i in range(0,num_sources):
  221. fps_streams["stream{0}".format(i)]=GETFPS(i)
  222. # Standard GStreamer initialization
  223. Gst.init(None)
  224. # Create gstreamer elements */
  225. # Create Pipeline element that will form a connection of other elements
  226. print("Creating Pipeline \n ")
  227. pipeline = Gst.Pipeline()
  228. if not pipeline:
  229. sys.stderr.write(" Unable to create Pipeline \n")
  230. ########### Create Elements required for the Pipeline ###########
  231. # Create nvstreammux instance to form batches from one or more sources.
  232. streammux = make_elm_or_print_err("nvstreammux", "Stream-muxer","Stream-muxer")
  233. pipeline.add(streammux)
  234. for i in range(num_sources):
  235. print("Creating source_bin ",i," \n ")
  236. uri_name=INPUT_VIDEO
  237. if uri_name.find("rtsp://") == 0 :
  238. is_live = True
  239. source_bin=create_source_bin(args, i, uri_name)
  240. if not source_bin:
  241. sys.stderr.write("Unable to create source bin \n")
  242. pipeline.add(source_bin)
  243. padname="sink_%u" %i
  244. sinkpad = streammux.get_request_pad(padname)
  245. if not sinkpad:
  246. sys.stderr.write("Unable to create sink pad bin \n")
  247. srcpad = source_bin.get_static_pad("src")
  248. if not srcpad:
  249. sys.stderr.write("Unable to create src pad bin \n")
  250. srcpad.link(sinkpad)
  251. # Use nvinfer to run inferencing on decoder's output, behaviour of inferencing is set through config file
  252. pgie = make_elm_or_print_err("nvinfer", "primary-inference" ,"pgie")
  253. # Create Sink for storing the output
  254. fakesink = make_elm_or_print_err("fakesink", "fakesink", "Sink")
  255. # Queues to enable buffering
  256. queue1=make_elm_or_print_err("queue","queue1","queue1")
  257. queue2=make_elm_or_print_err("queue","queue2","queue2")
  258. queue3=make_elm_or_print_err("queue","queue3","queue3")
  259. ############ Set properties for the Elements ############
  260. # Set Input Width , Height and Batch Size
  261. streammux.set_property('width', 1920)
  262. streammux.set_property('height', 1080)
  263. streammux.set_property('batch-size', num_sources)
  264. # Timeout in microseconds to wait after the first buffer is available
  265. # to push the batch even if a complete batch is not formed.
  266. streammux.set_property('batched-push-timeout', 4000000)
  267. # Set configuration file for nvinfer
  268. pgie.set_property('config-file-path', "../source_code/N3/dstest1_pgie_config.txt")
  269. # Setting batch_size for pgie
  270. pgie_batch_size=pgie.get_property("batch-size")
  271. if(pgie_batch_size != num_sources):
  272. print("WARNING: Overriding infer-config batch-size",pgie_batch_size," with number of sources ", num_sources," \n")
  273. pgie.set_property("batch-size",num_sources)
  274. # Fake sink properties
  275. fakesink.set_property("sync", 0)
  276. fakesink.set_property("async", 0)
  277. ########## Add and Link ELements in the Pipeline ##########
  278. print("Adding elements to Pipeline \n")
  279. pipeline.add(queue1)
  280. pipeline.add(pgie)
  281. pipeline.add(queue2)
  282. pipeline.add(queue3)
  283. pipeline.add(fakesink)
  284. print("Linking elements in the Pipeline \n")
  285. streammux.link(queue1)
  286. queue1.link(pgie)
  287. pgie.link(queue2)
  288. queue2.link(queue3)
  289. queue3.link(fakesink)
  290. # create an event loop and feed gstreamer bus mesages to it
  291. loop = GLib.MainLoop()
  292. bus = pipeline.get_bus()
  293. bus.add_signal_watch()
  294. bus.connect ("message", bus_call, loop)
  295. print("Added and Linked elements to pipeline")
  296. src_pad=queue3.get_static_pad("src")
  297. if not src_pad:
  298. sys.stderr.write(" Unable to get src pad \n")
  299. else:
  300. src_pad.add_probe(Gst.PadProbeType.BUFFER, src_pad_buffer_probe, 0)
  301. # List the sources
  302. print("Now playing...")
  303. print("Starting pipeline \n")
  304. # start play back and listed to events
  305. pipeline.set_state(Gst.State.PLAYING)
  306. start_time = time.time()
  307. try:
  308. loop.run()
  309. except:
  310. pass
  311. # cleanup
  312. print("Exiting app\n")
  313. pipeline.set_state(Gst.State.NULL)
  314. print("--- %s seconds ---" % (time.time() - start_time))
  315. if __name__ == '__main__':
  316. sys.exit(main())