Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 1585

Python • Stitching Two Cameras Together for Sports

$
0
0
I am attempting to create a camera unit with the below hardware.

Raspberry Pi 5 (8GB)
2x Raspberry Pi Camera Module 3 (Wide) - mounted on a T-Bar with around 40mm spacing and 0 degrees of tilt (optimum spacing and angle to be determined once stitching is functional).
Eventually will add a SSD and an AI Processing Chip

First step for me is to stitch the two video feeds together for which I have put together the below code (with some help from the internet).

Code:

import subprocessimport numpy as npimport cv2# Frame size and overlapWIDTH, HEIGHT = 960, 540OVERLAP = 100  # pixels overlap for stitchingdef read_frame(pipe, width, height):    """Read one frame from pipe (libcamera-vid YUV420 output)."""    # YUV420 size: width * height * 1.5    size = int(width * height * 1.5)    raw = pipe.stdout.read(size)    if len(raw) < size:        return None    # Convert YUV420 to BGR for OpenCV    yuv = np.frombuffer(raw, dtype=np.uint8).reshape((int(height * 1.5), width))    bgr = cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR_I420)    return bgrdef stitch_images(img1, img2, overlap):    """Simple horizontal blend stitching with overlap."""    height, width, _ = img1.shape    blended_width = width * 2 - overlap    blended = np.zeros((height, blended_width, 3), dtype=np.uint8)    # Left part from img1 (excluding overlap)    blended[:, :width - overlap] = img1[:, :width - overlap]    # Right part from img2 (excluding overlap)    blended[:, width:] = img2[:, overlap:]    # Blend the overlap region    for x in range(overlap):        alpha = x / overlap        blended[:, width - overlap + x] = (            (1 - alpha) * img1[:, width - overlap + x] + alpha * img2[:, x]        ).astype(np.uint8)    return blendeddef main():    # libcamera-vid command for camera 0    cmd0 = [        "libcamera-vid", "--camera", "0",        "--width", str(WIDTH), "--height", str(HEIGHT),        "--codec", "yuv420",        "--nopreview",        "--timeout", "0",  # Keep streaming indefinitely        "-o", "-"    ]    # libcamera-vid command for camera 1    cmd1 = [        "libcamera-vid", "--camera", "1",        "--width", str(WIDTH), "--height", str(HEIGHT),        "--codec", "yuv420",        "--nopreview",        "--timeout", "0",  # Keep streaming indefinitely        "-o", "-"    ]    # Start both libcamera-vid subprocesses    pipe0 = subprocess.Popen(cmd0, stdout=subprocess.PIPE)    pipe1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)    try:        while True:            frame0 = read_frame(pipe0, WIDTH, HEIGHT)            frame1 = read_frame(pipe1, WIDTH, HEIGHT)            if frame0 is None or frame1 is None:                print("Frame read failed or stream ended")                break            stitched = stitch_images(frame0, frame1, OVERLAP)            cv2.imshow("Stitched", stitched)            if cv2.waitKey(1) & 0xFF == ord("q"):                break    finally:        pipe0.terminate()        pipe1.terminate()        cv2.destroyAllWindows()if __name__ == "__main__":    main()
The output though is highly unstable, with obvious ghosting of features in the background and any movement is chaotic/blurred/ghosted. It also comes out as a very low framerate (not sure on figure, but it's very jolty and not at all smooth).

Is there a better way to do this? I just want a single panoramic video feed with the two cameras side-by-side to cover the whole pitch.

Statistics: Posted by longtail — Mon May 19, 2025 12:14 pm



Viewing all articles
Browse latest Browse all 1585

Trending Articles