jeffbass / imagezmq

A set of Python classes that transport OpenCV images from one computer to another using PyZMQ messaging.
MIT License
1.01k stars 160 forks source link

Utilizing ImageZMQ to stream video using a webcam and PiCamera from one device #65

Closed amerritt44 closed 2 years ago

amerritt44 commented 2 years ago

I am in the process of trying to stream two cameras from one Arduino device to a laptop, but the send_image and recv_image commands lock up on operation. I notice in the documentation for pub_sub_broadcast.py script, it states the following:

***Open input stream; comment out one of these capture = VideoStream() lines! ***You must use only one of Webcam OR PiCamera

If I am understanding that correctly, that means you cannot use both a webcam and PiCamera at the same time? Is this a limitation of the ImageZMQ library, or is there some sort of a workaround solution?

amerritt44 commented 2 years ago

Following this advice, I managed to successfully stream 2 cameras at the same time.

It is definitely possible to use multiple cameras to send images using the imageZMQ library. The example code you mentioned only shows a single camera, but if you instantiate multiple cameras with VideoStream, AND if you send the images with unique camera names, you can use multiple cameras. You can only use 1 PiCamera per Raspberry Pi because of its hardware, but I have used a PiCamera and a Webcam on the same RPI before. I have also used multiple Webcams on Raspberry Pi’s. I have not used an arduino, but it should allow multiple USB webcams if it has multiple USB ports.

It is important that you test your cameras using the VideoSteam() class, so that you know how your arduino assigns “src” numbers to the cameras when there are 2 or more. The VideoSteam() class is part of the imutils library here: https://github.com/jrosebr1/imutils/blob/master/imutils/video/videostream.py . VideoSteam uses a parameter “src” to assign camera numbers. Typically, OpenCV (and VideoStream, which uses OpenCV camera reader module) assigns USB Webcams in the order of 0,1,2… If you don’t specify a src (the most common use case), it defaults to src=0, which is usually the first USB webcam found by OpenCV.

Here is what pub_sub_broadcast.py would look like with 2 WebCams, assuming they are src=0 and src=1 (which is likely, but this must be tested specifically on your arduino):

"""pub_sub_broadcast.py -- broadcast OpenCV stream using PUB SUB."""

import sys

import traceback
from time import sleep
import cv2
from imutils.video import VideoStream
import imagezmq

if __name__ == "__main__":
    # Publish on port
    port = 5555
    sender = imagezmq.ImageSender("tcp://*:{}".format(port), REQ_REP=False

    # Open 2 unique input image stream cameras, with different instance names and different camera names
    # Each Webcam must have a unique VideoSteam instance AND a unique camera “text_name” 

    cam1 = VideoStream(src=0)  # Webcam #1
    cam1.start()
    cam1_name = “WebCam1”

    cam2 = VideoStream(src=1)  # Webcam #2
    cam2.start()
    cam2_name = “WebCam2”

    sleep(2.0)  # Warmup time; needed by most cameras before looping over images below

    # JPEG quality, 0 - 100
    jpeg_quality = 95

    try:
        while True:
            # capture frame and send it from Webcam #1
            frame = cam1.read()
            ret_code, jpg_buffer = cv2.imencode(
                ".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_quality])
            sender.send_jpg(cam1_name, jpg_buffer)

            # capture frame and send it from Webcam #2
            frame = cam2.read()
            ret_code, jpg_buffer = cv2.imencode(
                ".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_quality])
            sender.send_jpg(cam2_name, jpg_buffer)

    except (KeyboardInterrupt, SystemExit):
        print('Exit due to keyboard interrupt')
    except Exception as ex:
        print('Python error with no Exception handler:')
        print('Traceback error:', ex)
        traceback.print_exc()
    finally:
        cam1.stop()
        cam2.stop()
        sender.close()`

The receiving program pub_sub_receive.py would need the cv2.imshow()line to be changed so that the 2 camera streams would each show up in their own named cv2.imshow windows. The variable “msg” will contain the cam1_name or cam2_name sent. Nothing else would need to change for the receiving program to work with the above sending program.

        cv2.imshow(msg, image)  # msg will hold the unique camera name that was specified in the program above.
jeffbass commented 2 years ago

This "how to use 2 cameras" issue is a great candidate for an example program in the imageZMQ examples folder. I'll add it to my to-do list. Thanks for bringing up this issue!