DetectNet load multiple networks - how to parse multiple inputs to different nets

I am attempting to implement a system whereby I use a model I have trained as well as mobilenet itself as I have a custom object detector but also need to detect objects that the mobilenet can already detect.

Anyway I wrote the below code but I cannot quite figure out the command line syntax for using two networks on two different inputs.

On a side note my ultimate goal will be to have a gui and two background workers one detecting on one data set, and another detecting on another data set being pulled from cameras…so first step is to work out how to run two nets in the same program :)

Thanks in advance, code below:

import jetson.inference
import jetson.utils
import argparse
import sys

parser = argparse.ArgumentParser(description="Run a primary as well as a secondary detection network.", 
                                 formatter_class=argparse.RawTextHelpFormatter, epilog=jetson.inference.detectNet.Usage() +
                                 jetson.utils.videoSource.Usage() + jetson.utils.videoOutput.Usage() + jetson.utils.logUsage())

parser.add_argument("primary_input_URI", type=str, default="", nargs='?', help="URI of the primary input stream")
parser.add_argument("primary_output_URI", type=str, default="", nargs='?', help="URI of the primary output stream")
parser.add_argument("--primarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--primarythreshold", type=float, default=0.3, help="minimum detection threshold to use")
parser.add_argument("--primarylabels", type=str, default="primary_labels.txt", help=" primary labels txt file")

parser.add_argument("secondary_input_URI", type=str, default="", nargs='?', help="URI of the secondary input stream")
parser.add_argument("secondary_output_URI", type=str, default="", nargs='?', help="URI of the secondary output stream")
parser.add_argument("--secondarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--secondarythreshold", type=float, default=0.8, help="minimum detection threshold to use")
parser.add_argument("--secondarylabels", type=str, default="secondary_labels.txt", help=" secondary labels txt file")

parser.add_argument("--overlay", type=str, default="box,labels,conf", help="detection overlay flags (e.g. --overlay=box,labels,conf)\nvalid combinations are:  'box', 'labels', 'conf', 'none'")

# argument options:


try:
	opt = parser.parse_known_args()[0]
except:
	print("")
	parser.print_help()
	sys.exit(0)

# load the object detection networks
primary_net = jetson.inference.detectNet(opt.primarynet, sys.argv)
secondary_net = jetson.inference.detectNet(opt.secondarynet, sys.argv)

# create video sources & outputs
primary_input = jetson.utils.videoSource(opt.primary_input_URI, argv=sys.argv)
primary_output = jetson.utils.videoOutput(opt.primary_output_URI, argv=sys.argv)
secondary_input = jetson.utils.videoSource(opt.secondary_input_URI, argv=sys.argv)
secondary_output = jetson.utils.videoOutput(opt.secondary_output_URI, argv=sys.argv)

def detect_primary():
    # capture the next image
	img = primary_input.Capture()

	# detect objects in the image (with overlay)
	primary_detections = primary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d}  primary objects in image".format(len(detections)))

	for detection in primary_detections:
		print(detection)

	# render the image
	output.Render(img)

	# update the title bar
	output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.network, primary_net.GetNetworkFPS()))

	# print out performance info
	primary_net.PrintProfilerTimes()


def detect_secondary():
    
    # capture the next image
	img = secondary_input.Capture()

	# detect objects in the image (with overlay)
	secondary_detections = secondary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d} cars in image".format(len(detections)))

	for detection in secondary_detections:
		print(detection)

	# render the image
	output.Render(img)

	# update the title bar
	output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.network, secondary_net.GetNetworkFPS()))

	# print out performance info
	secondary_net.PrintProfilerTimes()        

# process frames until the user exits
while True:
	
    detect_primary()
    detect_secondary()

	# exit on input/output EOS
    if not primary_input.IsStreaming() or not primary_output.IsStreaming() and not secondary_input.IsStreaming() or not secondary_output.IsStreaming():
        break

Command line I am attempting to run:

python3 main.py --firenet=models/detection/Primary_Custom.onnx --primary_labels=models/detection/primary_labels.txt --primary_input_URI="file://primary_test_images/image*.jpg" --primary_output_URI="file://primary_test_images/results/result_%i.jpg" --secondary_input_URI="file://secondary_test_images/*.jpg" --secodnary_output_URI="file://secondary_test_images/results/result_%i.jpg" --secondary_labels=secondary_labels.txt --input-blob=input_0 --output-cvg=scores --output-bbox=boxes

Output (which tells me my command is not correct):

[gstreamer] gstreamer message stream-start ==> pipeline0
Error generated. /dvs/git/dirty/git-master_linux/multimedia/nvgstreamer/gst-nvarguscamera/gstnvarguscamerasrc.cpp, execute:645 No cameras available
[gstreamer] gstCamera -- end of stream (EOS)

(python3:11829): GStreamer-CRITICAL **: 22:46:27.014: gst_mini_object_set_qdata: assertion 'object != NULL' failed

I had a look at the c files and I am wondering if what I am attempting is even possible?

Update:

I changed:

# load the object detection networks
primary_net = jetson.inference.detectNet(opt.primarynet, sys.argv)
secondary_net = jetson.inference.detectNet(opt.secondarynet, sys.argv)

to:

primary_net = jetson.inference.detectNet('ssd-mobilenet-v2', [f"--model={opt.primarynet}", f"labels={opt.primarylabels}"])
secondary_net = jetson.inference.detectNet('ssd-mobilenet-v2', [f"--model={opt.secondarynet}", f"labels={opt.secondarylabels}"])

then ran command:

python3 main.py --primarynet=models/detection/Primary_Custom.onnx --primary_labels=models/detection/primary_labels.txt --primary_input_URI="primary_test_images/image*.jpg" --primary_output_URI="primary_test_images/results/result_%i.jpg" --secondary_input_URI="secondary_test_images/*.jpg" --secodnary_output_URI="secondary_test_images/results/result_%i.jpg" --secondary_labels=models/detection/secondary_labels.txt --input-blob=input_0 --output-cvg=scores --output-bbox=boxes

Output:

detectNet -- loading detection network model from:
          -- prototxt     NULL
          -- model        models/detection/Primary_Custom.onnx
          -- input_blob   'data'
          -- output_cvg   'coverage'
          -- output_bbox  'bboxes'
          -- mean_pixel   0.000000
          -- mean_binary  NULL
          -- class_labels NULL
          -- threshold    0.500000
          -- batch_size   1

[TRT]    TensorRT version 7.1.3
[TRT]    loading NVIDIA plugins...
[TRT]    Registered plugin creator - ::GridAnchor_TRT version 1
[TRT]    Registered plugin creator - ::NMS_TRT version 1
[TRT]    Registered plugin creator - ::Reorg_TRT version 1
[TRT]    Registered plugin creator - ::Region_TRT version 1
[TRT]    Registered plugin creator - ::Clip_TRT version 1
[TRT]    Registered plugin creator - ::LReLU_TRT version 1
[TRT]    Registered plugin creator - ::PriorBox_TRT version 1
[TRT]    Registered plugin creator - ::Normalize_TRT version 1
[TRT]    Registered plugin creator - ::RPROI_TRT version 1
[TRT]    Registered plugin creator - ::BatchedNMS_TRT version 1
[TRT]    Could not register plugin creator -  ::FlattenConcat_TRT version 1
[TRT]    Registered plugin creator - ::CropAndResize version 1
[TRT]    Registered plugin creator - ::DetectionLayer_TRT version 1
[TRT]    Registered plugin creator - ::Proposal version 1
[TRT]    Registered plugin creator - ::ProposalLayer_TRT version 1
[TRT]    Registered plugin creator - ::PyramidROIAlign_TRT version 1
[TRT]    Registered plugin creator - ::ResizeNearest_TRT version 1
[TRT]    Registered plugin creator - ::Split version 1
[TRT]    Registered plugin creator - ::SpecialSlice_TRT version 1
[TRT]    Registered plugin creator - ::InstanceNormalization_TRT version 1
[TRT]    detected model format - ONNX  (extension '.onnx')
[TRT]    desired precision specified for GPU: FASTEST
[TRT]    requested fasted precision for device GPU without providing valid calibrator, disabling INT8
[TRT]    native precisions detected for GPU:  FP32, FP16
[TRT]    selecting fastest native precision for GPU:  FP16
[TRT]    attempting to open engine cache file models/detection/Primary_Custom.onnx.1.1.7103.GPU.FP16.engine
[TRT]    loading network plan from engine cache... models/detection/Primary_Custom.onnx.1.1.7103.GPU.FP16.engine
[TRT]    device GPU, loaded models/detection/Primary_Custom.onnx
[TRT]    Deserialize required 3152052 microseconds.
[TRT]    
[TRT]    CUDA engine context initialized on device GPU:
[TRT]       -- layers       104
[TRT]       -- maxBatchSize 1
[TRT]       -- workspace    0
[TRT]       -- deviceMemory 19780608
[TRT]       -- bindings     3
[TRT]       binding 0
                -- index   0
                -- name    'input_0'
                -- type    FP32
                -- in/out  INPUT
                -- # dims  4
                -- dim #0  1 (SPATIAL)
                -- dim #1  3 (SPATIAL)
                -- dim #2  300 (SPATIAL)
                -- dim #3  300 (SPATIAL)
[TRT]       binding 1
                -- index   1
                -- name    'scores'
                -- type    FP32
                -- in/out  OUTPUT
                -- # dims  3
                -- dim #0  1 (SPATIAL)
                -- dim #1  3000 (SPATIAL)
                -- dim #2  3 (SPATIAL)
[TRT]       binding 2
                -- index   2
                -- name    'boxes'
                -- type    FP32
                -- in/out  OUTPUT
                -- # dims  3
                -- dim #0  1 (SPATIAL)
                -- dim #1  3000 (SPATIAL)
                -- dim #2  4 (SPATIAL)
[TRT]    
[TRT]    INVALID_ARGUMENT: Cannot find binding of given name: data
[TRT]    failed to find requested input layer data in network
[TRT]    device GPU, failed to create resources for CUDA engine
[TRT]    failed to create TensorRT engine for models/detection/Primary_Custom.onnx, device GPU
[TRT]    detectNet -- failed to initialize.
jetson.inference -- detectNet failed to load network

Hi @tom.parkerete4t, can you try it like this instead?

primary_net = jetson.inference.detectNet(argv=[f"--model={opt.primarynet}", f"--labels={opt.primarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])
secondary_net = jetson.inference.detectNet(argv=[f"--model={opt.secondarynet}", f"--labels={opt.secondarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])

Note the -- in front of --labels and also the layer names that needed specified.

Thank You, hehe, I woke up this morning thinking “why didn’t I hardcode the input output blobs?”

This worked in allowing the networks to finally load properly but my command for feeding the image directories is still not correct:

python3 main.py --primarynet=models/detection/Primary_Custom.onnx --primary_labels=models/detection/primary_labels.txt secondary_labels=models/detection/secondary_labels.txt --primary_input_URI="primary_test_images/image*.jpg" --primary_output_URI=primary_test_images/results/result_%i.jpg --secondary_input_URI="secondary_test_images/*.jpg" --secodnary_output_URI=secondary_test_images/results/result_%i.jpg

returns:

[gstreamer] gstreamer stream status ENTER ==> src
[gstreamer] gstreamer message stream-start ==> pipeline0
Error generated. /dvs/git/dirty/git-master_linux/multimedia/nvgstreamer/gst-nvarguscamera/gstnvarguscamerasrc.cpp, execute:645 No cameras available

(python3:10170): GStreamer-CRITICAL **: 10:00:24.398: gst_mini_object_set_qdata: assertion 'object != NULL' failed
[gstreamer] gstCamera -- end of stream (EOS)

***EDIT:

Update:

I hardcoded the paths and it worked now I have a new error lol

code:

input_primary = f'primary_test_images/image*.jpg'
output_primary = f'primary_test_images/results/%i.jpg'
input_secondary = f'secondary_test_images/*.jpg'
output_secondary = f'secondary_test_images/results/%i.jpg'
primary_input = jetson.utils.videoSource(input_primary, argv=sys.argv)
primary_output = jetson.utils.videoOutput(output_primary, argv=sys.argv)
secondary_input = jetson.utils.videoSource(input_secondary, argv=sys.argv)
secondary_output = jetson.utils.videoOutput(output_secondary, argv=sys.argv)

command line:

python3 main.py --primarynet=models/detection/Primary_Custom.onnx --primary_labels=models/detection/primary_labels.txt secondary_labels=models/detection/secondary_labels.txt

Now I get the following error (bare in mind this model works fine with these images if I run it with your detectnet.py, so I know it works):

------------------------------------------------
[OpenGL] glDisplay -- X screen 0 resolution:  1920x1080
[OpenGL] glDisplay -- X window resolution:    1920x1080
[OpenGL] glDisplay -- display device initialized (1920x1080)
[video]  created glDisplay from display://0
------------------------------------------------
glDisplay video options:
------------------------------------------------
  -- URI: display://0
     - protocol:  display
     - location:  0
  -- deviceType: display
  -- ioType:     output
  -- codec:      raw
  -- width:      1920
  -- height:     1080
  -- frameRate:  0.000000
  -- bitRate:    0
  -- numBuffers: 4
  -- zeroCopy:   true
  -- flipMethod: none
  -- loop:       0
  -- rtspLatency 2000
------------------------------------------------
[image] loaded 'primary_test_images/image1.jpg'  (640x640, 3 channels)
Segmentation fault (core dumped)

Thanks

EDIT:

Just thought I would include up to date code as it stands:

import jetson.inference
import jetson.utils
import argparse
import sys

parser = argparse.ArgumentParser(description="Run a primary as well as a secondary detection network.", 
                                 formatter_class=argparse.RawTextHelpFormatter, epilog=jetson.inference.detectNet.Usage() +
                                 jetson.utils.videoSource.Usage() + jetson.utils.videoOutput.Usage() + jetson.utils.logUsage())

parser.add_argument("primary_input_URI", type=str, default="", nargs='?', help="URI of the primary input stream")
parser.add_argument("primary_output_URI", type=str, default="", nargs='?', help="URI of the primary output stream")
parser.add_argument("--primarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--primarythreshold", type=float, default=0.3, help="minimum detection threshold to use")
parser.add_argument("--primarylabels", type=str, default="primary_labels.txt", help=" primary labels txt file")

parser.add_argument("secondary_input_URI", type=str, default="", nargs='?', help="URI of the secondary input stream")
parser.add_argument("secondary_output_URI", type=str, default="", nargs='?', help="URI of the secondary output stream")
parser.add_argument("--secondarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--secondarythreshold", type=float, default=0.8, help="minimum detection threshold to use")
parser.add_argument("--secondarylabels", type=str, default="secondary_labels.txt", help=" secondary labels txt file")

parser.add_argument("--overlay", type=str, default="box,labels,conf", help="detection overlay flags (e.g. --overlay=box,labels,conf)\nvalid combinations are:  'box', 'labels', 'conf', 'none'")

# argument options:


try:
	opt = parser.parse_known_args()[0]
except:
	print("")
	parser.print_help()
	sys.exit(0)

# load the object detection networks
primary_net = jetson.inference.detectNet(argv=[f"--model={opt.primarynet}", f"--labels={opt.primarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])
secondary_net = jetson.inference.detectNet(argv=[f"--model={opt.secondarynet}", f"--labels={opt.secondarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])

# create video sources & outputs
input_primary = f'primary_test_images/image*.jpg'
output_primary = f'primary_test_images/results/%i.jpg'
input_secondary = f'secondary_test_images/*.jpg'
output_secondary = f'secondary_test_images/results/%i.jpg'

primary_input = jetson.utils.videoSource(input_primary, argv=sys.argv)
primary_output = jetson.utils.videoOutput(output_primary, argv=sys.argv)
secondary_input = jetson.utils.videoSource(input_secondary, argv=sys.argv)
secondary_output = jetson.utils.videoOutput(output_secondary, argv=sys.argv)

def detect_primary():
    # capture the next image
	img = primary_input.Capture()

	# detect objects in the image (with overlay)
	primary_detections = primary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d}  primary objects in image".format(len(detections)))

	for detection in primary_detections:
		print(detection)

	# render the image
	primary_output.Render(img)

	# update the title bar
	primary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.primarynet, primary_net.GetNetworkFPS()))

	# print out performance info
	primary_net.PrintProfilerTimes()


def detect_secondary():
    
    # capture the next image
	img = secondary_input.Capture()

	# detect objects in the image (with overlay)
	secondary_detections = secondary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d} cars in image".format(len(detections)))

	for detection in secondary_detections:
		print(detection)

	# render the image
	seconday_output.Render(img)

	# update the title bar
	secondary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.secondarynet, secondary_net.GetNetworkFPS()))

	# print out performance info
	secondary_net.PrintProfilerTimes()        

# process frames until the user exits
while True:
	
    detect_primary()
    detect_secondary()

	# exit on input/output EOS
    if not primary_input.IsStreaming() or not primary_output.IsStreaming() and not secondary_input.IsStreaming() or not secondary_output.IsStreaming():
        break

I solved it for now, I had not typed the labels file name correctly in the command line!

It now works perfectly, although I found some errors in the code that I have corrected below full working code and command line for anyone who stumbles on this in future:

main.py:

import jetson.inference
import jetson.utils
import argparse
import sys

parser = argparse.ArgumentParser(description="Run a primary as well as a secondary detection network.", 
                                 formatter_class=argparse.RawTextHelpFormatter, epilog=jetson.inference.detectNet.Usage() +
                                 jetson.utils.videoSource.Usage() + jetson.utils.videoOutput.Usage() + jetson.utils.logUsage())

parser.add_argument("primary_input_URI", type=str, default="", nargs='?', help="URI of the primary input stream")
parser.add_argument("primary_output_URI", type=str, default="", nargs='?', help="URI of the primary output stream")
parser.add_argument("--primarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--primarythreshold", type=float, default=0.3, help="minimum detection threshold to use")
parser.add_argument("--primarylabels", type=str, default="primary_labels.txt", help=" primary labels txt file")

parser.add_argument("secondary_input_URI", type=str, default="", nargs='?', help="URI of the secondary input stream")
parser.add_argument("secondary_output_URI", type=str, default="", nargs='?', help="URI of the secondary output stream")
parser.add_argument("--secondarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--secondarythreshold", type=float, default=0.8, help="minimum detection threshold to use")
parser.add_argument("--secondarylabels", type=str, default="secondary_labels.txt", help=" secondary labels txt file")

parser.add_argument("--overlay", type=str, default="box,labels,conf", help="detection overlay flags (e.g. --overlay=box,labels,conf)\nvalid combinations are:  'box', 'labels', 'conf', 'none'")

# argument options:


try:
	opt = parser.parse_known_args()[0]
except:
	print("")
	parser.print_help()
	sys.exit(0)

# load the object detection networks
primary_net = jetson.inference.detectNet(argv=[f"--model={opt.primarynet}", f"--labels={opt.primarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])
secondary_net = jetson.inference.detectNet(argv=[f"--model={opt.secondarynet}", f"--labels={opt.secondarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])

# create video sources & outputs
input_primary = f'primary_test_images/image*.jpg'
output_primary = f'primary_test_images/results/%i.jpg'
input_secondary = f'secondary_test_images/*.jpg'
output_secondary = f'secondary_test_images/results/%i.jpg'

primary_input = jetson.utils.videoSource(input_primary, argv=sys.argv)
primary_output = jetson.utils.videoOutput(output_primary, argv=sys.argv)
secondary_input = jetson.utils.videoSource(input_secondary, argv=sys.argv)
secondary_output = jetson.utils.videoOutput(output_secondary, argv=sys.argv)

def detect_primary():
    # capture the next image
	img = primary_input.Capture()

	# detect objects in the image (with overlay)
	primary_detections = primary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d}  primary objects in image".format(len(primary_detections)))

	for detection in primary_detections:
		print(detection)

	# render the image
	primary_output.Render(img)

	# update the title bar
	primary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.primarynet, primary_net.GetNetworkFPS()))

	# print out performance info
	primary_net.PrintProfilerTimes()


def detect_secondary():
    
    # capture the next image
	img = secondary_input.Capture()

	# detect objects in the image (with overlay)
	secondary_detections = secondary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d} cars in image".format(len(secondary_detections)))

	for detection in secondary_detections:
		print(detection)

	# render the image
	seconday_output.Render(img)

	# update the title bar
	secondary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.secondarynet, secondary_net.GetNetworkFPS()))

	# print out performance info
	secondary_net.PrintProfilerTimes()        

# process frames until the user exits
while True:
	
    detect_primary()
    detect_secondary()

	# exit on input/output EOS
    if not primary_input.IsStreaming() or not primary_output.IsStreaming() and not secondary_input.IsStreaming() or not secondary_output.IsStreaming():
        break

command line:

python3 main.py --primarynet=models/detection/Primary_Custom.onnx --primarylabels=models/detection/primary_labels.txt --secondarylabels=models/detection/secondary_labels.txt

Still would be nice to work out how to feed the input/output URI of two nets in the commandline properly. It clearly has something to do with the way python processes the strings as videsource.h requires it to be in quotes. Hence why the f string hard coding worked.

Not a major issue as can hard code in deployment anyway.

Thanks for the help!

Ah ok, cool - I had just tested it here and got it working on two ssd-mobilenet.onnx models too.

I was able to get it working using named arguments as opposed to positional ones.

python3 multi_detection.py --primarynet ssd-mobilenet-fruit/ssd-mobilenet.onnx \
                           --primarylabels ssd-mobilenet-fruit/labels.txt \
                           --secondarynet tractors/ssd-mobilenet.onnx \
                           --secondarylabels tractors/labels.txt \
                           --primary_input "/media/datasets/open_images/fruit/test/*.jpg" \
                           --primary_output /media/datasets/test_output/fruit%i.jpg \
                           --secondary_input "/media/datasets/tractors/JPEGImages/*.jpg" \
                           --secondary_output /media/datasets/test_output/tractors/%i.jpg

This was the code:

import jetson.inference
import jetson.utils
import argparse
import sys

parser = argparse.ArgumentParser(description="Run a primary as well as a secondary detection network.", 
                                 formatter_class=argparse.RawTextHelpFormatter, epilog=jetson.inference.detectNet.Usage() +
                                 jetson.utils.videoSource.Usage() + jetson.utils.videoOutput.Usage() + jetson.utils.logUsage())

parser.add_argument("--primary_input", type=str, default="", help="URI of the primary input stream")
parser.add_argument("--primary_output", type=str, default="", help="URI of the primary output stream")
parser.add_argument("--primarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--primarythreshold", type=float, default=0.3, help="minimum detection threshold to use")
parser.add_argument("--primarylabels", type=str, default="primary_labels.txt", help=" primary labels txt file")

parser.add_argument("--secondary_input", type=str, default="", help="URI of the secondary input stream")
parser.add_argument("--secondary_output", type=str, default="", help="URI of the secondary output stream")
parser.add_argument("--secondarynet", type=str, default="ssd-mobilenet-v2", help="pre-trained model to load (see below for options)")
parser.add_argument("--secondarythreshold", type=float, default=0.8, help="minimum detection threshold to use")
parser.add_argument("--secondarylabels", type=str, default="secondary_labels.txt", help=" secondary labels txt file")

parser.add_argument("--overlay", type=str, default="box,labels,conf", help="detection overlay flags (e.g. --overlay=box,labels,conf)\nvalid combinations are:  'box', 'labels', 'conf', 'none'")

# argument options:


try:
	opt = parser.parse_known_args()[0]
except:
	print("")
	parser.print_help()
	sys.exit(0)

# load the object detection networks
primary_net = jetson.inference.detectNet(argv=[f"--model={opt.primarynet}", f"--labels={opt.primarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])
secondary_net = jetson.inference.detectNet(argv=[f"--model={opt.secondarynet}", f"--labels={opt.secondarylabels}", "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"])

# create video sources & outputs
primary_input = jetson.utils.videoSource(opt.primary_input, argv=sys.argv)
primary_output = jetson.utils.videoOutput(opt.primary_output, argv=sys.argv)
secondary_input = jetson.utils.videoSource(opt.secondary_input, argv=sys.argv)
secondary_output = jetson.utils.videoOutput(opt.secondary_output, argv=sys.argv)

def detect_primary():
    # capture the next image
	img = primary_input.Capture()

	# detect objects in the image (with overlay)
	primary_detections = primary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d}  primary objects in image".format(len(primary_detections)))

	for detection in primary_detections:
		print(detection)

	# render the image
	primary_output.Render(img)

	# update the title bar
	primary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.primarynet, primary_net.GetNetworkFPS()))

	# print out performance info
	primary_net.PrintProfilerTimes()


def detect_secondary():
    
    # capture the next image
	img = secondary_input.Capture()

	# detect objects in the image (with overlay)
	secondary_detections = secondary_net.Detect(img, overlay=opt.overlay)

	# print the detections
	print("detected {:d} cars in image".format(len(secondary_detections)))

	for detection in secondary_detections:
		print(detection)

	# render the image
	secondary_output.Render(img)

	# update the title bar
	secondary_output.SetStatus("{:s} | Network {:.0f} FPS".format(opt.secondarynet, secondary_net.GetNetworkFPS()))

	# print out performance info
	secondary_net.PrintProfilerTimes()        

# process frames until the user exits
while True:
	
    detect_primary()
    detect_secondary()

	# exit on input/output EOS
    if not primary_input.IsStreaming() or not primary_output.IsStreaming() and not secondary_input.IsStreaming() or not secondary_output.IsStreaming():
        break

awesome thank you!