In this exciting project, we combine a Raspberry Pi 5, the Fusion AI Lab Kit, a Pi Camera, and a remote IP camera to generate a stunning real-time composite video. Watch as a glowing, translucent MediaPipe face mesh of my face hovers magically over live video of the Mighty River Nice scenery captured by an IP camera. The effect looks futuristic and professional — perfect for creative video effects, interactive installations, or just blowing your mind with computer vision! Using Picamera2 for high-frame-rate local capture and OpenCV with an RTSP stream from the river camera, we process everything in real time. MediaPipe’s Face Mesh detects and tracks facial landmarks, which we draw as beautiful cyan/teal contours with glowing irises. Then we create a clean mask, separate the mesh foreground from the river background, and blend them seamlessly into one composite frame. You’ll see every debugging layer live on screen too — meshLayer, mask, inverted mask, riverBG, and meshFG — so you can understand exactly how the magic happens.This tutorial is beginner-to-intermediate friendly and packed with practical OpenCV + MediaPipe techniques you can adapt for your own augmented reality projects. Whether you’re a longtime follower of the Paul McWhorter channel or new to the Fusion AI Kit, you’ll walk away inspired and ready to build your own hovering effects, overlays, or interactive displays.Grab the full code from the video description, fire up your Pi 5, and start creating jaw-dropping computer vision projects today. Drop a comment and let me know what you’d like to overlay next — another face mesh, hand tracking, or something completely different? Let’s keep pushing the limits of what we can do with affordable AI hardware!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
import cv2 import time from picamera2 import Picamera2 import mediapipe as mp import numpy as np import secret W=1280 H=720 tStart = time.time() fps = 0 piCam = Picamera2(1) piCam.preview_configuration.main.size = (W, H) piCam.preview_configuration.main.format = "RGB888" piCam.preview_configuration.controls.FrameRate = 60 piCam.preview_configuration.align() piCam.configure("preview") piCam.start() cam = cv2.VideoCapture(secret.RTSP1,cv2.CAP_FFMPEG) cam.set(cv2.CAP_PROP_BUFFERSIZE,0) cam.set(cv2.CAP_PROP_POS_FRAMES,0) textLowerLeft = (int(W*.01),int(H*.05)) fontFace = cv2.FONT_HERSHEY_SIMPLEX fontThickness = int(W/425) fontScale = H*.0015 fontColor = (0,0,255) faceMesh = mp.solutions.face_mesh.FaceMesh( max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5) cv2.namedWindow('Composite',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('Composite',W,H) cv2.moveWindow('Composite',0,65) cv2.namedWindow('meshLayer',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('meshLayer',320,180) cv2.moveWindow('meshLayer',0,H+65) cv2.namedWindow('grayLayer',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('grayLayer',320,180) cv2.moveWindow('grayLayer',320,H+65) cv2.namedWindow('maskLayer',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('maskLayer',320,180) cv2.moveWindow('maskLayer',320*2,H+65) cv2.namedWindow('invLayer',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('invLayer',320,180) cv2.moveWindow('invLayer',320*3,H+65) cv2.namedWindow('riverBG',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('riverBG',320,180) cv2.moveWindow('riverBG',W,65) cv2.namedWindow('meshFG',cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('meshFG',320,180) cv2.moveWindow('meshFG',W,180+65+20) while True: deltaT = time.time() - tStart tStart=time.time() fps = fps*.95 + (1/deltaT)*.05 frame = piCam.capture_array() frame = cv2.flip(frame, -1) _, river = cam.read() river = cv2.resize(river,(W,H)) meshLayer = frame.copy() meshLayer[0:H-1,0:W-1] = [255,255,255] rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = faceMesh.process(rgb) if results.multi_face_landmarks: for faceLandmarks in results.multi_face_landmarks: # Optional second pass for glow effect mp.solutions.drawing_utils.draw_landmarks( image=meshLayer, landmark_list=faceLandmarks, connections=mp.solutions.face_mesh.FACEMESH_TESSELATION, landmark_drawing_spec=None, connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec( color=(0, 150, 200), thickness=2 ) ) # === Best looking translucent mesh === mp.solutions.drawing_utils.draw_landmarks( image=meshLayer, landmark_list=faceLandmarks, connections=mp.solutions.face_mesh.FACEMESH_CONTOURS, landmark_drawing_spec=None, # Hide dots connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec( color=(0, 0, 180), # Bright cyan/teal thickness=7 ) ) # Optional: Extra detail on irises (makes eyes look better) mp.solutions.drawing_utils.draw_landmarks( image=meshLayer, landmark_list=faceLandmarks, connections=mp.solutions.face_mesh.FACEMESH_IRISES, landmark_drawing_spec=None, connection_drawing_spec=mp.solutions.drawing_utils.DrawingSpec( color=(255, 0, 0), thickness=7 ) ) gray = cv2.cvtColor(meshLayer,cv2.COLOR_BGR2GRAY) _ , mask =cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY) invLayer = cv2.bitwise_not(mask) riverBG =cv2.bitwise_and(river, river, mask = mask) meshFG = cv2.bitwise_and(meshLayer,meshLayer, mask=invLayer) composite = cv2.add(riverBG,meshFG) myText = "FPS: "+str(round(fps,1)) cv2.putText(composite,myText,textLowerLeft,fontFace,fontScale,fontColor,fontThickness) cv2.imshow("Composite", composite) cv2.imshow("meshLayer",meshLayer) cv2.imshow("grayLayer",gray) cv2.imshow("maskLayer",mask) cv2.imshow("invLayer",invLayer) cv2.imshow("riverBG",riverBG) cv2.imshow("meshFG",meshFG) if cv2.waitKey(1) == ord('q'): break cv2.destroyAllWindows() piCam.stop() |