Versions used: OpenCV v4.4.0, Python v3.8.5

Camera used: TP-Link Tapo C200

The code below shows how you can capture and display video frames from multiple IP cameras.

import cv2

print("Connecting to cameras ...")

cam1 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam2 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam3 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam4 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")

while True:
    ret, frame1 = cam1.read()
    ret, frame2 = cam2.read()
    ret, frame3 = cam3.read()
    ret, frame4 = cam4.read()

    if (ret):
        cv2.imshow('west', frame1)
        cv2.imshow('north', frame2)
        cv2.imshow('south', frame3)
        cv2.imshow('south-west', frame4)

    # keyboard shortcut to kill the application
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break

print("Quitting, cleaning up ...")
cv2.destroyAllWindows()

The method shown above is a straightforward approach, especially for OpenCV beginners. The code is to the point, short, and easy to understand.

Still, let us break down what is going on.

Using VideoCapture() we connect to our cameras with RTSP (Real Time Streaming Protocol), and store the connection in objects cam1-cam4.

Inside an indefinite loop, while True:, we continously read the frames from VideoCapture instances (cam1-cam4). The read() method returns the next video frame, it returns false if it receives no frames.

If read() returned True, we use imshow() to draw the video frames.

Stacking the camera frames

If you have multiple cameras (or images/other video sources), as in this example, you will have noticed that each camera gets its own window. This is rarely what we would like, as it makes for cumbersome window management. We can fix this by stacking our frames, in a single window, using Numpy and simple matrix operations.

Add the import statement to the top of your source.

import numpy as np

Be sure to install Numpy: pip install numpy.

Resize our frames so they can all fit inside a single window.

frame1_resized = cv2.resize(frame1, (0, 0), None, 0.5, 0.5)
frame2_resized = cv2.resize(frame2, (0, 0), None, 0.5, 0.5)
frame3_resized = cv2.resize(frame3, (0, 0), None, 0.5, 0.5)
frame4_resized = cv2.resize(frame4, (0, 0), None, 0.5, 0.5)

Using Numpy’s hstack() we create two horizontal stacks, then feed vstack() those two horizontal stacks, creating a new vertical one. Now we have a 2x2 grid of cameras.

horizontal_stack1 = np.hstack((frame1_resized, frame2_resized))
horizontal_stack2 = np.hstack((frame3_resized, frame4_resized))

vertical_stack = np.vstack((horizontal_stack1, horizontal_stack2))

cv2.imshow('Camera Monitor', vertical_stack)

The full code:

import cv2
import numpy as np

print("Connecting to cameras ...")

cam1 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam2 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam3 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")
cam4 = cv2.VideoCapture("rtsp://<username>:<password>@<ip>:<port>/stream1")

cameras = cam1, cam2, cam3, cam4

while True:
    ret, frame1 = cam1.read()
    ret, frame2 = cam2.read()
    ret, frame3 = cam3.read()
    ret, frame4 = cam4.read()

    # resize frames to fit in a single window
    frame1_resized = cv2.resize(frame1, (0, 0), None, 0.5, 0.5)
    frame2_resized = cv2.resize(frame2, (0, 0), None, 0.5, 0.5)
    frame3_resized = cv2.resize(frame3, (0, 0), None, 0.5, 0.5)
    frame4_resized = cv2.resize(frame4, (0, 0), None, 0.5, 0.5)

    if (ret):
        horizontal_stack1 = np.hstack((frame1_resized, frame2_resized))
        horizontal_stack2 = np.hstack((frame3_resized, frame4_resized))

        # stack the two horizontal stacks vertically, thereby creating a 2x2 grid
        vertical_stack = np.vstack((horizontal_stack1, horizontal_stack2))

        cv2.imshow('Camera Monitor', vertical_stack)

    # keyboard shortcut to kill the application
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break

print("Quitting, cleaning up ...")
cv2.destroyAllWindows()