#!/usr/bin/env python3
################################################################################
#
################################################################################
import sys
sys.path.append('../')
import gi
gi.require_version('Gst', '1.0')
from gi.repository import GObject, Gst
from gi.repository import GLib
import sys
import platform
from optparse import OptionParser
from common.is_aarch_64 import is_aarch64
from common.bus_call import bus_call
from common.utils import long_to_int
import pyds
MAX_DISPLAY_LEN=64
MAX_TIME_STAMP_LEN=32
PGIE_CLASS_ID_VEHICLE = 0
PGIE_CLASS_ID_BICYCLE = 1
PGIE_CLASS_ID_PERSON = 2
PGIE_CLASS_ID_ROADSIGN = 3
MUXER_OUTPUT_WIDTH=1920
MUXER_OUTPUT_HEIGHT=1080
MUXER_BATCH_TIMEOUT_USEC=4000000
input_file = None
schema_type = 0
frame_number = 0
proto_lib = None
conn_str="localhost;2181;testTopic"
cfg_file = None
topic = None
no_display = False
PGIE_CONFIG_FILE="dstest4_pgie_config.txt"
MSCONV_CONFIG_FILE="dstest4_msgconv_config.txt"
pgie_classes_str=["Vehicle", "TwoWheeler", "Person","Roadsign"]
# NvDsEventMsgMeta结构深拷贝copy的回调函数
def meta_copy_func(data,user_data):
# Cast data to pyds.NvDsUserMeta
user_meta=pyds.NvDsUserMeta.cast(data)
src_meta_data=user_meta.user_meta_data
# Cast src_meta_data to pyds.NvDsEventMsgMeta
srcmeta=pyds.NvDsEventMsgMeta.cast(src_meta_data)
# Duplicate the memory contents of srcmeta to dstmeta
# First use pyds.get_ptr() to get the C address of srcmeta, then
# use pyds.memdup() to allocate dstmeta and copy srcmeta into it.
# pyds.memdup returns C address of the allocated duplicate.
dstmeta_ptr=pyds.memdup(pyds.get_ptr(srcmeta), sys.getsizeof(pyds.NvDsEventMsgMeta))
# Cast the duplicated memory to pyds.NvDsEventMsgMeta
dstmeta=pyds.NvDsEventMsgMeta.cast(dstmeta_ptr)
# Duplicate contents of ts field. Note that reading srcmeat.ts
# returns its C address. This allows to memory operations to be
# performed on it.
dstmeta.ts=pyds.memdup(srcmeta.ts, MAX_TIME_STAMP_LEN+1)
# Copy the sensorStr. This field is a string property.
# The getter (read) returns its C address. The setter (write)
# takes string as input, allocates a string buffer and copies
# the input string into it.
# pyds.get_string() takes C address of a string and returns
# the reference to a string object and the assignment inside the binder copies content.
dstmeta.sensorStr=pyds.get_string(srcmeta.sensorStr)
if(srcmeta.objSignature.size>0):
dstmeta.objSignature.signature=pyds.memdup(srcmeta.objSignature.signature,srcMeta.objSignature.size)
dstmeta.objSignature.size = srcmeta.objSignature.size;
if(srcmeta.extMsgSize>0):
if(srcmeta.objType==pyds.NvDsObjectType.NVDS_OBJECT_TYPE_VEHICLE):
srcobj = pyds.NvDsVehicleObject.cast(srcmeta.extMsg);
obj = pyds.alloc_nvds_vehicle_object();
obj.type=pyds.get_string(srcobj.type)
obj.make=pyds.get_string(srcobj.make)
obj.model=pyds.get_string(srcobj.model)
obj.color=pyds.get_string(srcobj.color)
obj.license = pyds.get_string(srcobj.license)
obj.region = pyds.get_string(srcobj.region)
dstmeta.extMsg = obj;
dstmeta.extMsgSize = sys.getsizeof(pyds.NvDsVehicleObject)
if(srcmeta.objType==pyds.NvDsObjectType.NVDS_OBJECT_TYPE_PERSON):
srcobj = pyds.NvDsPersonObject.cast(srcmeta.extMsg);
obj = pyds.alloc_nvds_person_object()
obj.age = srcobj.age
obj.gender = pyds.get_string(srcobj.gender);
obj.cap = pyds.get_string(srcobj.cap)
obj.hair = pyds.get_string(srcobj.hair)
obj.apparel = pyds.get_string(srcobj.apparel);
dstmeta.extMsg = obj;
dstmeta.extMsgSize = sys.getsizeof(pyds.NvDsVehicleObject);
return dstmeta
# NvDsEventMsgMeta结构free实例的回调函数
def meta_free_func(data,user_data):
user_meta=pyds.NvDsUserMeta.cast(data)
srcmeta=pyds.NvDsEventMsgMeta.cast(user_meta.user_meta_data)
# pyds.free_buffer takes C address of a buffer and frees the memory
# It's a NOP if the address is NULL
pyds.free_buffer(srcmeta.ts)
pyds.free_buffer(srcmeta.sensorStr)
if(srcmeta.objSignature.size > 0):
pyds.free_buffer(srcmeta.objSignature.signature);
srcmeta.objSignature.size = 0
if(srcmeta.extMsgSize > 0):
if(srcmeta.objType == pyds.NvDsObjectType.NVDS_OBJECT_TYPE_VEHICLE):
obj =pyds.NvDsVehicleObject.cast(srcmeta.extMsg)
pyds.free_buffer(obj.type);
pyds.free_buffer(obj.color);
pyds.free_buffer(obj.make);
pyds.free_buffer(obj.model);
pyds.free_buffer(obj.license);
pyds.free_buffer(obj.region);
if(srcmeta.objType == pyds.NvDsObjectType.NVDS_OBJECT_TYPE_PERSON):
obj = pyds.NvDsPersonObject.cast(srcmeta.extMsg);
pyds.free_buffer(obj.gender);
pyds.free_buffer(obj.cap);
pyds.free_buffer(obj.hair);
pyds.free_buffer(obj.apparel);
pyds.free_gbuffer(srcmeta.extMsg);
srcmeta.extMsgSize = 0;
# 车辆数据
def generate_vehicle_meta(data):
obj = pyds.NvDsVehicleObject.cast(data);
obj.type ="sedan"
obj.color="blue"
obj.make ="Bugatti"
obj.model = "M"
obj.license ="XX1234"
obj.region ="CA"
return obj
# 行人数据
def generate_person_meta(data):
obj = pyds.NvDsPersonObject.cast(data)
obj.age = 45
obj.cap = "none"
obj.hair = "black"
obj.gender = "male"
obj.apparel= "formal"
return obj
# 生成事件数据
def generate_event_msg_meta(data, class_id):
meta =pyds.NvDsEventMsgMeta.cast(data)
meta.sensorId = 0
meta.placeId = 0
meta.moduleId = 0
meta.sensorStr = "sensor-0"
meta.ts = pyds.alloc_buffer(MAX_TIME_STAMP_LEN + 1)
pyds.generate_ts_rfc3339(meta.ts, MAX_TIME_STAMP_LEN)
# 这里展示的时如何关联普通的对象。
# 其他任何像NvDsVehicleObject 或 NvDsPersonObject 的对象都可以生成和关联。
# 然后这个对象就可以在诸如nvmsgconv.cpp库中生成发送的字符串。
if(class_id==PGIE_CLASS_ID_VEHICLE):
meta.type = pyds.NvDsEventType.NVDS_EVENT_MOVING
meta.objType = pyds.NvDsObjectType.NVDS_OBJECT_TYPE_VEHICLE
meta.objClassId = PGIE_CLASS_ID_VEHICLE
obj = pyds.alloc_nvds_vehicle_object()
obj = generate_vehicle_meta(obj)
meta.extMsg = obj
meta.extMsgSize = sys.getsizeof(pyds.NvDsVehicleObject)
if(class_id == PGIE_CLASS_ID_PERSON):
meta.type =pyds.NvDsEventType.NVDS_EVENT_ENTRY
meta.objType = pyds.NvDsObjectType.NVDS_OBJECT_TYPE_PERSON
meta.objClassId = PGIE_CLASS_ID_PERSON
obj = pyds.alloc_nvds_person_object()
obj=generate_person_meta(obj)
meta.extMsg = obj
meta.extMsgSize = sys.getsizeof(pyds.NvDsPersonObject)
return meta
# osd_sink_pad_buffer_probe 将提炼从OSD sink pad获取的元数据
# 更新画框的参数,obj的信息
# IMPORTANT NOTE:
# a) probe()回调函数是同步的,所以需要保留buffer(info.get_buffer())直到结束
# b) probe()回调中的循环在python里面是很耗资源的,所以需要用户优化
def osd_sink_pad_buffer_probe(pad,info,u_data):
frame_number=0
#初始化每个对象的计数都是0
obj_counter = {
PGIE_CLASS_ID_VEHICLE:0,
PGIE_CLASS_ID_PERSON:0,
PGIE_CLASS_ID_BICYCLE:0,
PGIE_CLASS_ID_ROADSIGN:0
}
is_first_object=True
gst_buffer = info.get_buffer()
if not gst_buffer:
print("Unable to get GstBuffer ")
return
# 从gst_buffer取回一个batch的元数据
# Note:pyds.gst_buffer_get_nvds_batch_meta() 输入数据是输入gst_buffer的c地址
# 这个c地址可由hash(gst_buffer)拿到
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
if not batch_meta:
return Gst.PadProbeReturn.OK
l_frame = batch_meta.frame_meta_list
while l_frame is not None:
try:
# 注意:l_frame.data需要cast到pyds.NvDsFrameMeta中
# 用的方法是pyds.NvDsFrameMeta.cast()
# cast函数会在c代码中保留内存的所有者,所以python回收会直接不再处理
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
except StopIteration:
continue
is_first_object = True
'''
print("Frame Number is ", frame_meta.frame_num)
print("Source id is ", frame_meta.source_id)
print("Batch id is ", frame_meta.batch_id)
print("Source Frame Width ", frame_meta.source_frame_width)
print("Source Frame Height ", frame_meta.source_frame_height)
print("Num object meta ", frame_meta.num_obj_meta)
'''
frame_number=frame_meta.frame_num
l_obj=frame_meta.obj_meta_list
while l_obj is not None:
try:
obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data)
except StopIteration:
continue
# 更新obj的文字显示信息
txt_params=obj_meta.text_params
# 配置obj的文字信息,绑定模块将释放任何现有的 display_text 字符串
txt_params.display_text = pgie_classes_str[obj_meta.class_id]
obj_counter[obj_meta.class_id] += 1
# 字体,颜色和大小
txt_params.font_params.font_name = "Serif"
txt_params.font_params.font_size = 10
# (red, green, blue, alpha); 此处为White
txt_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0);
# 文字背景颜色
txt_params.set_bg_clr = 1
# (red, green, blue, alpha); 此处为Black
txt_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0);
# 理想的NVDS_EVENT_MSG_META应该被实现检测/识别的逻辑组件关联到buffer。
# 这里展示的是怎样使用/关联到元数据
if(is_first_object and not (frame_number%30)):
# 消息发送的频率是基于应用的
# 这里的频率是有obj第一次出现后,每30帧发送一次
# 分配一个NvDsEventMsgMeta实例并获取其引用
# 相应的内存其实不是python管理的,所以downstream插件可以访问它
# 否则当probe推出时python garbage collector会释放内存。
msg_meta = pyds.alloc_nvds_event_msg_meta()
msg_meta.bbox.top = obj_meta.rect_params.top
msg_meta.bbox.left = obj_meta.rect_params.left
msg_meta.bbox.width = obj_meta.rect_params.width
msg_meta.bbox.height = obj_meta.rect_params.height
msg_meta.frameId = frame_number
msg_meta.trackingId = long_to_int(obj_meta.object_id)
msg_meta.confidence = obj_meta.confidence
# 生成事件元数据
msg_meta = generate_event_msg_meta(msg_meta, obj_meta.class_id)
# event 事件元数据
user_event_meta = pyds.nvds_acquire_user_meta_from_pool(batch_meta)
if(user_event_meta):
user_event_meta.user_meta_data = msg_meta;
user_event_meta.base_meta.meta_type = pyds.NvDsMetaType.NVDS_EVENT_MSG_META
# 在event元数据中设置回调函数
# 绑定层会将这些可调用对象封装在c函数中
# 当前仅支持一个系列的回调函数
pyds.user_copyfunc(user_event_meta, meta_copy_func)
pyds.user_releasefunc(user_event_meta, meta_free_func)
pyds.nvds_add_user_meta_to_frame(frame_meta, user_event_meta)
else:
print("Error in attaching event meta to buffer\n")
is_first_object = False
try:
l_obj=l_obj.next
except StopIteration:
break
try:
l_frame=l_frame.next
except StopIteration:
break
print("Frame Number =",frame_number,"Vehicle Count =",obj_counter[PGIE_CLASS_ID_VEHICLE],"Person Count =",obj_counter[PGIE_CLASS_ID_PERSON])
return Gst.PadProbeReturn.OK
def main(args):
# https://developer.gnome.org/gobject/unstable/gobject-The-Base-Object-Type.html#GObject-struct
GObject.threads_init()
Gst.init(None)
# 注册过程的回调函数
pyds.register_user_copyfunc(meta_copy_func)
pyds.register_user_releasefunc(meta_free_func)
print("Creating Pipeline \n ")
pipeline = Gst.Pipeline()
if not pipeline:
sys.stderr.write(" Unable to create Pipeline \n")
print("Creating Source \n ")
source = Gst.ElementFactory.make("filesrc", "file-source")
if not source:
sys.stderr.write(" Unable to create Source \n")
print("Creating H264Parser \n")
h264parser = Gst.ElementFactory.make("h264parse", "h264-parser")
if not h264parser:
sys.stderr.write(" Unable to create h264 parser \n")
print("Creating Decoder \n")
decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2-decoder")
if not decoder:
sys.stderr.write(" Unable to create Nvv4l2 Decoder \n")
streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
if not streammux:
sys.stderr.write(" Unable to create NvStreamMux \n")
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
if not pgie:
sys.stderr.write(" Unable to create pgie \n")
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
if not nvvidconv:
sys.stderr.write(" Unable to create nvvidconv \n")
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay")
if not nvosd:
sys.stderr.write(" Unable to create nvosd \n")
msgconv=Gst.ElementFactory.make("nvmsgconv", "nvmsg-converter")
if not msgconv:
sys.stderr.write(" Unable to create msgconv \n")
msgbroker=Gst.ElementFactory.make("nvmsgbroker", "nvmsg-broker")
if not msgbroker:
sys.stderr.write(" Unable to create msgbroker \n")
tee=Gst.ElementFactory.make("tee", "nvsink-tee")
if not tee:
sys.stderr.write(" Unable to create tee \n")
queue1=Gst.ElementFactory.make("queue", "nvtee-que1")
if not queue1:
sys.stderr.write(" Unable to create queue1 \n")
queue2=Gst.ElementFactory.make("queue", "nvtee-que2")
if not queue2:
sys.stderr.write(" Unable to create queue2 \n")
if (no_display) :
print("Creating FakeSink \n")
sink = Gst.ElementFactory.make("fakesink", "fakesink")
if not sink:
sys.stderr.write(" Unable to create fakesink \n")
else:
if is_aarch64():
transform = Gst.ElementFactory.make("nvegltransform", "nvegl-transform")
print("Creating EGLSink \n")
sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
if not sink:
sys.stderr.write(" Unable to create egl sink \n")
print("Playing file %s " %input_file)
source.set_property('location', input_file)
streammux.set_property('width', 1920)
streammux.set_property('height', 1080)
streammux.set_property('batch-size', 1)
streammux.set_property('batched-push-timeout', 4000000)
pgie.set_property('config-file-path', PGIE_CONFIG_FILE)
msgconv.set_property('config',MSCONV_CONFIG_FILE)
msgconv.set_property('payload-type', schema_type)
msgbroker.set_property('proto-lib', proto_lib)
msgbroker.set_property('conn-str', conn_str)
if cfg_file is not None:
msgbroker.set_property('config', cfg_file)
if topic is not None:
msgbroker.set_property('topic', topic)
msgbroker.set_property('sync', False) # 同步方式
print("Adding elements to Pipeline \n")
pipeline.add(source)
pipeline.add(h264parser)
pipeline.add(decoder)
pipeline.add(streammux)
pipeline.add(pgie)
pipeline.add(nvvidconv)
pipeline.add(nvosd)
pipeline.add(tee)
pipeline.add(queue1)
pipeline.add(queue2)
pipeline.add(msgconv)
pipeline.add(msgbroker)
pipeline.add(sink)
if is_aarch64() and not no_display:
pipeline.add(transform) # TODO? transform是干嘛的
print("Linking elements in the Pipeline \n")
source.link(h264parser)
h264parser.link(decoder)
sinkpad = streammux.get_request_pad("sink_0")
if not sinkpad:
sys.stderr.write(" Unable to get the sink pad of streammux \n")
srcpad = decoder.get_static_pad("src")
if not srcpad:
sys.stderr.write(" Unable to get source pad of decoder \n")
srcpad.link(sinkpad)
streammux.link(pgie)
pgie.link(nvvidconv)
nvvidconv.link(nvosd)
nvosd.link(tee)
queue1.link(msgconv)
msgconv.link(msgbroker)
if is_aarch64() and not no_display:
queue2.link(transform)
transform.link(sink)
else:
queue2.link(sink)
sink_pad=queue1.get_static_pad("sink")
tee_msg_pad=tee.get_request_pad('src_%u')
tee_render_pad=tee.get_request_pad("src_%u")
if not tee_msg_pad or not tee_render_pad:
sys.stderr.write("Unable to get request pads\n")
tee_msg_pad.link(sink_pad)
sink_pad=queue2.get_static_pad("sink")
tee_render_pad.link(sink_pad)
# 创建事件loop,填入gstreamer bus 消息到其中
loop = GObject.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect ("message", bus_call, loop)
osdsinkpad = nvosd.get_static_pad("sink")
if not osdsinkpad:
sys.stderr.write(" Unable to get sink pad of nvosd \n")
osdsinkpad.add_probe(Gst.PadProbeType.BUFFER, osd_sink_pad_buffer_probe, 0)
print("Starting pipeline \n")
# 开始运行,事件监听
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except:
pass
# 清理资源
pyds.unset_callback_funcs()
pipeline.set_state(Gst.State.NULL)
# 解析输入参数
def parse_args():
parser = OptionParser()
parser.add_option("-c", "--cfg-file", dest="cfg_file",
help="adaptor配置文件. conn-str信息充足时不用此参数.", metavar="FILE")
parser.add_option("-i", "--input-file", dest="input_file",
help="输入H264文件", metavar="FILE")
parser.add_option("-p", "--proto-lib", dest="proto_lib",
help="adaptor库的绝对路径", metavar="PATH")
parser.add_option("", "--conn-str", dest="conn_str",
help="消息服务器的连接参数. 若配置文件有连接参数则不需要.", metavar="STR")
parser.add_option("-s", "--schema-type", dest="schema_type", default="0",
help="消息策略类型 (0=Full, 1=minimal), default=0", metavar="<0|1>")
parser.add_option("-t", "--topic", dest="topic",
help="消息topic名称. 如果在配置文件已有参数则不需要.", metavar="TOPIC")
parser.add_option("", "--no-display", action="store_true", dest="no_display", default=False,
help="是否需要显示")
(options, args) = parser.parse_args()
global cfg_file
global input_file
global proto_lib
global conn_str
global topic
global schema_type
global no_display
cfg_file = options.cfg_file
input_file = options.input_file
proto_lib = options.proto_lib
conn_str = options.conn_str
topic = options.topic
no_display = options.no_display
if proto_lib is None or input_file is None:
print("Usage: python3 deepstream_test_4.py -i <H264 filename> -p <Proto adaptor library> --conn-str=<Connection string>")
return 1
if (options.schema_type == "0"):
schema_type = 0
else:
schema_type = 1
return 0
if __name__ == '__main__':
ret = parse_args()
# 返回非零则解析失败
if ret == 1:
sys.exit(1)
sys.exit(main(sys.argv))
deepstream_python_apps_test4之deepstream_test_4.py
最新推荐文章于 2024-05-21 23:23:24 发布