I have researched various ways to achieve basic requirements such as having a preview with On Screen Display (OSD) that is streamed through FPV transmitter and recording unmodified video at the same time.
From my research I found a few ways of overlaying text and additional information while using the Raspberry Pi camera:
- By using UV4L library (www.linux-projects.org/uv4l/tutorials/text-overlay/ and chrome-extension://klbibkeccnjlkjkiokjodocebajanakg/suspended.html#uri=http://www.linux-projects.org/home/wp-content/uploads/2016/06/uv4l-overlay.cpp)
- By using OpenGL libraries, some of which are outdated and are hard to get started with
- By using PiCamera Python library and its feature of overlaying an arbitrary array of pixels in Preview mode (picamera.readthedocs.io/en/release-1.12/recipes1.html#overlaying-images-on-the-preview)
I settled on the third option as it is easy to program and has great documentation. I then searched Github for existing projects that use the preview overlay feature in PiCamera and found a single example that pointed me in the right direction. PiCamera library allows overlaying an array of pixels which means you can either create your own drawing library to create that array or use existing Python libraries that will do that for you based on your requirements. The project at https://github.com/darkcrash21/raspberry_pi uses Image and ImageDraw Python libraries to create a great looking HUD. Another example in the same project had a basic implementation that proved to be useful for my FPV OSD project.
Based on that find, here's the code I wrote that displays a crosshair and current timestamps in multiple positions on the preview screen while recording unmodified HD video:
import picamera import time import numpy from PIL import Image, ImageDraw, ImageFont # Video Resolution VIDEO_HEIGHT = 720 VIDEO_WIDTH = 1280 # Cross Hair Image crossHair = Image.new("RGB", (VIDEO_WIDTH, VIDEO_HEIGHT)) crossHairPixels = crossHair.load() for x in range (0, VIDEO_WIDTH): crossHairPixels[x, 360] = (255, 255, 0) for x in range(0, VIDEO_HEIGHT): crossHairPixels[640, x] = (255, 255, 0) with picamera.PiCamera() as camera: camera.resolution = (VIDEO_WIDTH, VIDEO_HEIGHT) camera.framerate = 30 camera.led = False camera.start_preview() camera.start_recording('timestamped.h264') img = crossHair.copy() overlay = camera.add_overlay(img.tostring(), layer = 3, alpha = 100) time.sleep(1) try: while True: text = time.strftime('%H:%M:%S', time.gmtime()) img = crossHair.copy() draw = ImageDraw.Draw(img) draw.font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 20) draw.text((10, 10), text, (255, 255, 255)) draw.text((10, 100), text, (0, 255, 255)) draw.text((10, 200), text, (255, 0, 255)) draw.text((10, 300), text, (255, 255, 0)) draw.text((200, 10), text, (255, 255, 255)) draw.text((300, 100), text, (0, 255, 255)) draw.text((400, 200), text, (255, 0, 255)) draw.text((500, 300), text, (255, 255, 0)) overlay.update(img.tostring()) camera.wait_recording(0.9) finally: camera.remove_overlay(overlay) camera.stop_recording()
I have tested this code on the Pi Zero and was able to get consistent 30FPS recording while overlaying the text in multiple places on the preview screen. The CPU load was at about 23% because the overlay is updated once a second or so.
Now with this basic functionality working I can get to plugging in the sensors and reading the data from the flight controller that has serial interface.
My next steps are:
- Get GPS data and display it on the screen
- Get positional/rotational data from the Flight controller and display it on the preview screen
- Get battery status and display it on the screen