Bidirectional Mode enables seamless support for Bidirectional data transmission between Client and Sender along with video-frames through its synchronous messaging patterns such as zmq.PAIR (ZMQ Pair Pattern) & zmq.REQ/zmq.REP (ZMQ Request/Reply Pattern) in NetGear_Async API.
In Bidirectional Mode, we utilizes the NetGear_Async API's transceive_data method for transmitting data (at Client's end) and receiving data (in Server's end) all while transferring frames in real-time.
This mode can be easily activated in NetGear_Async through bidirectional_mode attribute of its options dictionary parameter during initialization.
Important
In Bidirectional Mode, zmq.PAIR(ZMQ Pair) & zmq.REQ/zmq.REP(ZMQ Request/Reply) are ONLY Supported messaging patterns. Accessing this mode with any other messaging pattern, will result in ValueError.
Bidirectional Mode only works with User-defined Custom Source on Server end. Otherwise, NetGear_Async API will throw ValueError.
Bidirectional Mode enables you to send data of ANY1 Data-type along with frame bidirectionally.
NetGear_Async API will throw RuntimeError if Bidirectional Mode is disabled at Server end or Client end but not both.
Bidirectional Mode may lead to additional LATENCY depending upon the size of data being transfer bidirectionally. User discretion is advised!
To send data bidirectionally, NetGear_Async API provides following exclusive method and parameter:
transceive_data only works when Bidirectional Mode is enabled.
transceive_data: It's a bidirectional mode exclusive method to transmit data (in Receive mode) and receive data (in Send mode), all while transferring frames in real-time.
data: In transceive_data method, this parameter enables user to inputs data (of ANY1 datatype) for sending back to Server at Client's end.
# import libraryfromvidgear.gears.asyncioimportNetGear_Asyncimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# initialize Server without any sourceserver=NetGear_Async(source=None,logging=True,**options)# Create a async frame generator as custom sourceasyncdefmy_frame_generator():# !!! define your own video source here !!!# Open any valid video stream(for e.g `foo.mp4` file)stream=cv2.VideoCapture("foo.mp4")# loop over stream until its terminatedwhileTrue:# read frames(grabbed,frame)=stream.read()# check for empty frameifnotgrabbed:break# {do something with the frame to be sent here}# prepare data to be sent(a simple text in our case)target_data="Hello, I am a Server."# receive data from Clientrecv_data=awaitserver.transceive_data()# print data just received from Clientifnot(recv_dataisNone):print(recv_data)# send our frame & datayield(target_data,frame)# (1)# sleep for sometimeawaitasyncio.sleep(0)# safely close video streamstream.release()if__name__=="__main__":# set event loopasyncio.set_event_loop(server.loop)# Add your custom source generator to Server configurationserver.config["generator"]=my_frame_generator()# Launch the Serverserver.launch()try:# run your main function task until it is completeserver.loop.run_until_complete(server.task)except(KeyboardInterrupt,SystemExit):# wait for interruptspassfinally:# finally close the serverserver.close()
Everything except numpy.ndarray datatype data is accepted in target_data.
# import librariesfromvidgear.gears.asyncioimportNetGear_Asyncimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# define and launch Client with `receive_mode=True`client=NetGear_Async(receive_mode=True,logging=True,**options).launch()# Create a async function where you want to show/manipulate your received framesasyncdefmain():# loop over Client's Asynchronous Frame Generatorasyncfor(data,frame)inclient.recv_generator():# do something with receive data from serverifnot(dataisNone):# let's print itprint(data)# {do something with received frames here}# Show output window(comment these lines if not required)cv2.imshow("Output Frame",frame)cv2.waitKey(1)&0xFF# prepare data to be senttarget_data="Hi, I am a Client here."# send our data to serverawaitclient.transceive_data(data=target_data)# await before continuingawaitasyncio.sleep(0)if__name__=="__main__":# Set event loop to client'sasyncio.set_event_loop(client.loop)try:# run your main function task until it is completeclient.loop.run_until_complete(main())except(KeyboardInterrupt,SystemExit):# wait for interruptspass# close all output windowcv2.destroyAllWindows()# safely close clientclient.close()
Using Bidirectional Mode with Variable Parameters¶
Open a terminal on Client System (where you want to display the input frames received from the Server) and execute the following python code:
Note down the local IP-address of this system(required at Server's end) and also replace it in the following code. You can follow this FAQ for this purpose.
You can terminate client anytime by pressing Ctrl+C on your keyboard!
# import librariesfromvidgear.gears.asyncioimportNetGear_Asyncimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# Define NetGear_Async Client at given IP address and define parameters # !!! change following IP address '192.168.x.xxx' with yours !!!client=NetGear_Async(address="192.168.x.xxx",port="5454",protocol="tcp",pattern=1,receive_mode=True,logging=True,**options)# Create a async function where you want to show/manipulate your received framesasyncdefmain():# loop over Client's Asynchronous Frame Generatorasyncfor(data,frame)inclient.recv_generator():# do something with receive data from serverifnot(dataisNone):# let's print itprint(data)# {do something with received frames here}# Show output window(comment these lines if not required)cv2.imshow("Output Frame",frame)cv2.waitKey(1)&0xFF# prepare data to be senttarget_data="Hi, I am a Client here."# send our data to serverawaitclient.transceive_data(data=target_data)# await before continuingawaitasyncio.sleep(0)if__name__=="__main__":# Set event loop to client'sasyncio.set_event_loop(client.loop)try:# run your main function task until it is completeclient.loop.run_until_complete(main())except(KeyboardInterrupt,SystemExit):# wait for interruptspass# close all output windowcv2.destroyAllWindows()# safely close clientclient.close()
Now, Open the terminal on another Server System (a Raspberry Pi with Camera Module), and execute the following python code:
Replace the IP address in the following code with Client's IP address you noted earlier.
You can terminate stream on both side anytime by pressing Ctrl+C on your keyboard!
Backend PiGear API now fully supports the newer picamera2 python library under the hood for Raspberry Pi camera modules. Follow this guide ➶ for its installation.
# import libsfromvidgear.gears.asyncioimportNetGear_Asyncfromvidgear.gearsimportVideoGearfromlibcameraimportTransformimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# initialize Server without any source at given IP address and define parameters # !!! change following IP address '192.168.x.xxx' with client's IP address !!!server=NetGear_Async(source=None,address="192.168.x.xxx",port="5454",protocol="tcp",pattern=1,logging=True,**options)# Create a async frame generator as custom sourceasyncdefmy_frame_generator():# !!! define your own video source below !!!# define various Picamera2 tweak parametersoptions={"queue":True,"buffer_count":4,"controls":{"Brightness":0.5,"ExposureValue":2.0},"transform":Transform(hflip=1),"auto_align_output_config":True,# auto-align camera configuration}# open pi video stream with defined parametersstream=PiGear(resolution=(640,480),framerate=60,logging=True,**options).start()# loop over stream until its terminatedwhileTrue:# read framesframe=stream.read()# check for frame if NonetypeifframeisNone:break# {do something with the frame to be sent here}# prepare data to be sent(a simple text in our case)target_data="Hello, I am a Server."# receive data from Clientrecv_data=awaitserver.transceive_data()# print data just received from Clientifnot(recv_dataisNone):print(recv_data)# send our frame & datayield(target_data,frame)# (1)# sleep for sometimeawaitasyncio.sleep(0)# safely close video streamstream.stop()if__name__=="__main__":# set event loopasyncio.set_event_loop(server.loop)# Add your custom source generator to Server configurationserver.config["generator"]=my_frame_generator()# Launch the Serverserver.launch()try:# run your main function task until it is completeserver.loop.run_until_complete(server.task)except(KeyboardInterrupt,SystemExit):# wait for interruptspassfinally:# finally close the serverserver.close()
Everything except numpy.ndarray datatype data is accepted in target_data.
Under the hood, Backend PiGear API (version 0.3.3 onwards) prioritizes the new picamera2 API backend.
However, the API seamlessly switches to the legacy picamera backend, if the picamera2 library is unavailable or not installed.
It is advised to enable logging(logging=True) to see which backend is being used.
The picamera library is built on the legacy camera stack that is NOT (and never has been) supported on 64-bit OS builds.
You could also enforce the legacy picamera API backend in PiGear by using the enforce_legacy_picamera user-defined optional parameter boolean attribute.
# import libraryfromvidgear.gears.asyncioimportNetGear_Asyncfromvidgear.gearsimportVideoGearimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# initialize Server without any source at given IP address and define parameters # !!! change following IP address '192.168.x.xxx' with client's IP address !!!server=NetGear_Async(source=None,address="192.168.x.xxx",port="5454",protocol="tcp",pattern=1,logging=True,**options)# Create a async frame generator as custom sourceasyncdefmy_frame_generator():# !!! define your own video source below !!!# define various Picamera tweak parametersoptions={"hflip":True,"exposure_mode":"auto","iso":800,"exposure_compensation":15,"awb_mode":"horizon","sensor_mode":0,}# open pi video stream with defined parametersstream=PiGear(resolution=(640,480),framerate=60,logging=True,**options).start()# loop over stream until its terminatedwhileTrue:# read framesframe=stream.read()# check for frame if NonetypeifframeisNone:break# {do something with the frame to be sent here}# prepare data to be sent(a simple text in our case)target_data="Hello, I am a Server."# receive data from Clientrecv_data=awaitserver.transceive_data()# print data just received from Clientifnot(recv_dataisNone):print(recv_data)# send our frame & datayield(target_data,frame)# (1)# sleep for sometimeawaitasyncio.sleep(0)# safely close video streamstream.stop()if__name__=="__main__":# set event loopasyncio.set_event_loop(server.loop)# Add your custom source generator to Server configurationserver.config["generator"]=my_frame_generator()# Launch the Serverserver.launch()try:# run your main function task until it is completeserver.loop.run_until_complete(server.task)except(KeyboardInterrupt,SystemExit):# wait for interruptspassfinally:# finally close the serverserver.close()
Everything except numpy.ndarray datatype data is accepted in target_data.
Using Bidirectional Mode for Video-Frames Transfer¶
In this example we are going to implement a bare-minimum example, where we will be sending video-frames (3-Dimensional numpy arrays) of the same Video bidirectionally at the same time, for testing the real-time performance and synchronization between the Server and the Client using this(Bidirectional) Mode.
This feature is great for building applications like Real-Time Video Chat.
We're also using reducer() method for reducing frame-size on-the-go for additional performance.
Remember, Sending large HQ video-frames may required more network bandwidth and packet size which may lead to video latency!
# import libraryfromvidgear.gears.asyncioimportNetGear_Asyncfromvidgear.gears.asyncio.helperimportreducerimportcv2,asyncioimportnumpyasnp# activate Bidirectional modeoptions={"bidirectional_mode":True}# Define NetGear Server without any source and with defined parametersserver=NetGear_Async(source=None,pattern=1,logging=True,**options)# Create a async frame generator as custom sourceasyncdefmy_frame_generator():# !!! define your own video source here !!!# Open any valid video stream(for e.g `foo.mp4` file)stream=cv2.VideoCapture("foo.mp4")# loop over stream until its terminatedwhileTrue:# read frames(grabbed,frame)=stream.read()# check for empty frameifnotgrabbed:break# reducer frames size if you want more performance, otherwise comment this lineframe=awaitreducer(frame,percentage=30)# reduce frame by 30%# {do something with the frame to be sent here}# send frame & data and also receive data from Clientrecv_data=awaitserver.transceive_data()# receive data from Clientifnot(recv_dataisNone):# check data is a numpy frameifisinstance(recv_data,np.ndarray):# {do something with received numpy frame here}# Let's show it on output windowcv2.imshow("Received Frame",recv_data)cv2.waitKey(1)&0xFFelse:# otherwise just print dataprint(recv_data)# prepare data to be sent(a simple text in our case)target_data="Hello, I am a Server."# send our frame & data to clientyield(target_data,frame)# (1)# sleep for sometimeawaitasyncio.sleep(0)# safely close video streamstream.release()if__name__=="__main__":# set event loopasyncio.set_event_loop(server.loop)# Add your custom source generator to Server configurationserver.config["generator"]=my_frame_generator()# Launch the Serverserver.launch()try:# run your main function task until it is completeserver.loop.run_until_complete(server.task)except(KeyboardInterrupt,SystemExit):# wait for interruptspassfinally:# finally close the serverserver.close()
Everything except numpy.ndarray datatype data is accepted in target_data.
# import librariesfromvidgear.gears.asyncioimportNetGear_Asyncfromvidgear.gears.asyncio.helperimportreducerimportcv2,asyncio# activate Bidirectional modeoptions={"bidirectional_mode":True}# define and launch Client with `receive_mode=True`client=NetGear_Async(pattern=1,receive_mode=True,logging=True,**options).launch()# Create a async function where you want to show/manipulate your received framesasyncdefmain():# !!! define your own video source here !!!# again open the same video stream for comparisonstream=cv2.VideoCapture("foo.mp4")# loop over Client's Asynchronous Frame Generatorasyncfor(server_data,frame)inclient.recv_generator():# check for server dataifnot(server_dataisNone):# {do something with the server data here}# lets print extracted server dataprint(server_data)# {do something with received frames here}# Show output windowcv2.imshow("Output Frame",frame)key=cv2.waitKey(1)&0xFF# read frame target data from stream to be sent to server(grabbed,target_data)=stream.read()# check for frameifgrabbed:# reducer frames size if you want more performance, otherwise comment this linetarget_data=awaitreducer(target_data,percentage=30)# reduce frame by 30%# send our frame dataawaitclient.transceive_data(data=target_data)# await before continuingawaitasyncio.sleep(0)# safely close video streamstream.release()if__name__=="__main__":# Set event loop to client'sasyncio.set_event_loop(client.loop)try:# run your main function task until it is completeclient.loop.run_until_complete(main())except(KeyboardInterrupt,SystemExit):# wait for interruptspass# close all output windowcv2.destroyAllWindows()# safely close clientclient.close()
Additional data of numpy.ndarray datatype is ONLY SUPPORTED at Client's end with transceive_data method using its data parameter. Whereas Server end can only send numpy.ndarray datatype as frame but not as data.