Overlay Text On Raspberry Pi Video Stream & Save

by Marco 49 views

Introduction

Hey guys! Are you looking for a way to overlay text on a video stream from your Raspberry Pi and save it to the file system? You've come to the right place! This article will guide you through the process, providing a comprehensive solution for embedding text into your video recordings. We'll be focusing on using a Raspberry Pi, specifically the Raspberry Pi Zero, and the Raspicam, but the principles can be applied to other similar setups as well. We'll cover everything from setting up your Raspberry Pi to the Python code needed to capture video, read sensor data, overlay it as text, and save the final product to your file system. Let's dive in!

Problem Statement: Embedding Text into Video Streams

The challenge we're addressing here is how to integrate real-time data, such as sensor readings, directly into a video recording. Imagine you have a Raspberry Pi connected to various sensors, like temperature, pressure, or even custom sensors connected via UART. You want to record a video stream from the Raspicam and, at the same time, display the sensor values as text overlaid on the video. This is super useful for a variety of applications, such as:

  • Scientific experiments: Recording sensor data alongside visual observations.
  • Surveillance systems: Adding timestamps or other relevant information to video footage.
  • DIY projects: Creating informative videos with sensor data overlays.
  • Data logging: Creating a visual data log for easy analysis. It's important to have clear and concise information available at a glance, and overlaying sensor data onto video can help with that. Having the data embedded directly into the video stream can make analysis and interpretation much simpler. Furthermore, this approach eliminates the need to synchronize separate data logs with video recordings, thus saving time and reducing the potential for errors.

This article focuses on achieving this by using Python, which is a powerful and versatile language for the Raspberry Pi. We will utilize libraries that allow us to interact with the Raspicam, read data from the UART interface (for sensor readings), and manipulate the video stream to add the text overlay. The final output will be a video file saved on the Raspberry Pi's file system, with the text information permanently embedded within the video frames.

Prerequisites: Setting Up Your Raspberry Pi

Before we start coding, let's ensure your Raspberry Pi is ready to go. Here's what you'll need:

  1. Raspberry Pi: A Raspberry Pi Zero (or any other Raspberry Pi model) will work.
  2. Raspicam: The official Raspberry Pi camera module.
  3. MicroSD card: For installing the operating system.
  4. Power supply: To power the Raspberry Pi.
  5. UART connection (optional): If you're using external sensors connected via UART.

Here's a step-by-step guide to setting up your Raspberry Pi:

  1. Install the Operating System: Download the latest version of Raspberry Pi OS (formerly Raspbian) from the official Raspberry Pi website. Follow the instructions to flash the OS image onto your microSD card using a tool like Raspberry Pi Imager or Etcher.

  2. Enable the Camera: Boot up your Raspberry Pi and open the Raspberry Pi Configuration tool (either through the GUI or using sudo raspi-config in the terminal). Navigate to the Interfaces tab and enable the camera interface.

  3. Enable UART (if needed): If you are using UART communication, you may need to enable it. You can do this in raspi-config under Interface Options. Be aware that enabling UART might disable the serial console, so you'll need to use SSH or a monitor to access the Pi. You might need to also disable the serial console to free up the UART for your sensors. This can be done by editing /boot/cmdline.txt and removing console=serial0,115200.

  4. Install Necessary Packages: We'll need a few Python libraries for this project. Open a terminal on your Raspberry Pi and install them using pip:

    • sudo apt update
    • sudo apt install python3-pip
    • pip3 install picamera
    • pip3 install Pillow
    • pip3 install pyserial (if you're using UART)

    Explanation of packages:

    • picamera: This library provides a Python interface for interacting with the Raspicam.
    • Pillow: Pillow is an image processing library that allows us to draw text onto the video frames.
    • pyserial: This library enables serial communication with external devices via the UART interface.
  5. Connect Your Sensors (if using UART): Connect your sensors to the appropriate UART pins on the Raspberry Pi. You'll need to identify the TX (transmit) and RX (receive) pins, as well as the ground (GND) and power (VCC) pins. Refer to the documentation for your specific sensors and the Raspberry Pi's pinout diagram.

With these prerequisites out of the way, we can now focus on the core of the project: the Python code!

Python Code: Capturing Video and Overlaying Text

Now comes the fun part: writing the Python code! We'll break down the code into smaller, manageable chunks and explain each section in detail. Here's the overall structure of the script:

  1. Import Libraries: Import the necessary Python libraries.
  2. Initialize Camera and UART (if needed): Set up the Raspicam and the UART interface.
  3. Define Text Overlay Function: Create a function to draw text onto the video frames.
  4. Start Video Recording: Begin capturing video from the Raspicam.
  5. Read Sensor Data and Overlay Text: In a loop, read sensor data, overlay it on the current frame, and write the frame to the video file.
  6. Stop Recording and Cleanup: Stop the video recording and clean up resources.

Here's the Python code, with explanations:

import picamera
from PIL import Image, ImageDraw, ImageFont
import serial
import time

# 1. Import Libraries
# We import the necessary libraries: picamera for camera control,
# PIL (Pillow) for image manipulation, serial for UART communication,
# and time for pausing execution.

try:
    # 2. Initialize Camera and UART (if needed)
    # Set up the camera resolution and frame rate.
    camera = picamera.PiCamera()
    camera.resolution = (640, 480)
    camera.framerate = 30

    # Initialize UART (if using serial communication with sensors)
    # Replace '/dev/ttyS0' with the correct serial port for your setup.
    # The baud rate (9600) should match your sensor's configuration.
    ser = None
    try:
        ser = serial.Serial('/dev/ttyS0', 9600)
    except serial.SerialException:
        print("Serial port not available. Continuing without serial data.")


    # 3. Define Text Overlay Function
    # This function takes the camera object, text to overlay, and
    # position as input. It draws the text onto the video frame.
    def overlay_text(camera, text, position=(10, 10), font_size=20):
        # Create an image the same size as the camera preview.
        img = Image.new('RGBA', camera.resolution, (0, 0, 0, 0))
        draw = ImageDraw.Draw(img)

        # Load a font (you may need to adjust the path).
        font_path = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
        try:
            font = ImageFont.truetype(font_path, font_size)
        except IOError:
            print(f"Font file not found: {font_path}. Using default font.")
            font = ImageFont.load_default()

        # Draw the text onto the image.
        draw.text(position, text, font=font, fill='white')

        # Add the image as an overlay to the camera preview.
        overlay = camera.add_overlay(img.tobytes(), size=img.size, layer=3, alpha=128)
        return overlay


    # 4. Start Video Recording
    # Define the output video file name.
    output_file = 'video_with_overlay.h264'
    camera.start_recording(output_file)

    overlay = None # Initialize overlay variable

    try:
        # 5. Read Sensor Data and Overlay Text
        # This loop continuously reads sensor data (if UART is used),
        # formats the data as text, overlays it on the video frame,
        # and waits for a short period.
        start_time = time.time()
        while time.time() - start_time < 10:  # Record for 10 seconds
            sensor_data = ""
            if ser:
                try:
                    # Read data from UART (replace with your sensor reading logic).
                    sensor_data = ser.readline().decode('utf-8').strip()
                except Exception as e:
                    sensor_data = f"Error reading serial: {e}"
            text = f"Time: {time.strftime('%H:%M:%S')} Sensor: {sensor_data}"
            if overlay:
                camera.remove_overlay(overlay)  # Remove the old overlay
            overlay = overlay_text(camera, text)  # Create a new overlay
            camera.wait_recording(0.1)  # Wait a short time

    finally:
        # 6. Stop Recording and Cleanup
        # This block ensures that the camera and UART resources are
        # properly released, even if an error occurs.
        camera.stop_recording()
        if overlay:
            camera.remove_overlay(overlay)
        if ser:
            ser.close()
        print(f"Video saved to {output_file}")

except Exception as e:
    print(f"An error occurred: {e}")


Code Breakdown

  1. Import Libraries:
    • We import picamera to control the Raspicam.
    • PIL (Pillow) is used for image manipulation, specifically to draw text.
    • serial is used for UART communication with external sensors (if applicable).
    • time is used for time-related functions like pausing execution and getting the current time.
  2. Initialize Camera and UART:
    • We create a picamera.PiCamera() object to represent the camera.
    • camera.resolution = (640, 480) sets the video resolution.
    • camera.framerate = 30 sets the frame rate to 30 frames per second.
    • We attempt to initialize a serial connection using serial.Serial('/dev/ttyS0', 9600). Replace /dev/ttyS0 with the correct serial port for your setup. The baud rate (9600 in this example) should match your sensor's configuration. We handle potential serial.SerialException errors in case the serial port is not available, allowing the script to continue without serial data if necessary.
  3. Define Text Overlay Function (overlay_text):
    • This function takes the camera object, the text to overlay, the position of the text, and the font_size as input.
    • It creates a new Image object with the same dimensions as the camera preview, using an RGBA color mode (to support transparency).
    • It creates an ImageDraw object to draw on the image.
    • It loads a font using ImageFont.truetype(). You may need to adjust the font_path to the location of your desired font file. A try-except block is used to handle IOError exceptions in case the font file is not found, falling back to the default font if necessary.
    • It draws the text onto the image using draw.text(), specifying the position, text, font, and color.
    • It adds the image as an overlay to the camera preview using camera.add_overlay(). This function takes the image data as bytes (img.tobytes()), the size of the image, the layer number (3 in this case), and the alpha value (128 for semi-transparency). The overlay is added on top of the video stream.
    • The function returns the overlay object, which is needed to remove the overlay later.
  4. Start Video Recording:
    • We define the output video file name: output_file = 'video_with_overlay.h264'. You can change this to your desired file name and location.
    • We start the video recording using camera.start_recording(output_file). This tells the camera to start capturing video and save it to the specified file.
    • We initialize an overlay variable to None to keep track of the overlay object.
  5. Read Sensor Data and Overlay Text (Main Loop):
    • This is the core of the script, where we continuously read sensor data, format it as text, overlay it on the video frame, and wait for a short period.
    • We use a try...finally block to ensure that the camera and UART resources are properly released, even if an error occurs within the loop.
    • We record video for a set duration (10 seconds in this example). A start_time variable is used to track the recording duration.
    • Inside the while loop:
      • We initialize sensor_data to an empty string.
      • If ser is not None (meaning UART was successfully initialized), we attempt to read data from the UART interface using ser.readline().decode('utf-8').strip(). This reads a line of data from the serial port, decodes it as UTF-8, and removes leading/trailing whitespace. A try-except block is used to handle potential exceptions during serial communication, displaying an error message if necessary.
      • We construct the text string to be displayed on the video. This string includes the current time (using time.strftime('%H:%M:%S')) and the sensor data.
      • We manage the video overlays:
        • If an overlay already exists (if overlay:), we remove it using camera.remove_overlay(overlay). This is important to avoid stacking multiple overlays on top of each other.
        • We create a new overlay by calling the overlay_text() function with the camera object, the text to overlay, and the desired position. The returned overlay object is assigned to the overlay variable.
      • We pause execution for a short time using camera.wait_recording(0.1) (0.1 seconds). This allows the camera to process the frame and avoids overwhelming the system.
  6. Stop Recording and Cleanup:
    • The finally block is executed regardless of whether an exception occurred in the try block. This is crucial for releasing resources.
    • We stop the video recording using camera.stop_recording(). This saves the recorded video to the output file.
    • We remove the overlay using camera.remove_overlay(overlay) if one exists.
    • We close the serial connection using ser.close() if it was opened.
    • We print a message indicating the location of the saved video file.
  7. Error Handling:
    • The outer try...except block catches any exceptions that might occur during the script's execution and prints an error message.

Running the Code

  1. Save the code as a Python file (e.g., video_overlay.py).
  2. Open a terminal on your Raspberry Pi.
  3. Navigate to the directory where you saved the file.
  4. Run the script using python3 video_overlay.py.

Post-Processing: Converting the H.264 Video

The Raspberry Pi camera records video in the H.264 format, which may not be compatible with all video players. You can convert the video to a more widely supported format, such as MP4, using the MP4Box tool.

  1. Install MP4Box:

    sudo apt update
    sudo apt install gpac
    
  2. Convert the video:

    MP4Box -add video_with_overlay.h264 video_with_overlay.mp4
    

    Replace video_with_overlay.h264 with the name of your H.264 video file.

Troubleshooting

  • No video output:
    • Ensure the camera is properly connected and enabled in raspi-config.
    • Check the camera ribbon cable for any damage.
    • Verify that the picamera library is installed correctly.
  • Text overlay not visible:
    • Make sure the font path is correct.
    • Adjust the text position and font size as needed.
    • Check the alpha value of the overlay (a lower value makes the overlay more transparent).
  • Serial communication issues:
    • Double-check the serial port and baud rate settings.
    • Ensure the sensor is properly connected to the UART pins.
    • Verify that the sensor is sending data correctly.
  • Errors during conversion:
    • Make sure MP4Box is installed correctly.
    • Check the file names and paths.

Conclusion

Alright guys, you've made it to the end! We've covered how to overlay text on a video stream from your Raspberry Pi using the Raspicam and Python. This technique is super versatile and can be used in a wide range of applications, from scientific experiments to surveillance systems. By using the picamera, Pillow, and pyserial libraries, we've created a robust solution for embedding real-time data into video recordings.

Remember, the key takeaways are:

  • Setting up the Raspberry Pi: Ensure the camera and UART are enabled, and the necessary Python libraries are installed.
  • The overlay_text function: This function is the heart of the text overlay process, creating an image with the text and adding it as an overlay to the camera preview.
  • The main loop: This loop reads sensor data (if applicable), formats it, and overlays it on the video frames.
  • Error handling: Using try...except and try...finally blocks is crucial for ensuring that resources are properly released and errors are handled gracefully.

Now you can go ahead and adapt this code to your specific needs, such as using different sensors, displaying different data, or customizing the text appearance. Don't hesitate to experiment and explore the possibilities! Happy video recording and text overlaying!