Mediapipe Gesture Recognition – Gesture Control in Zoom Call

In this post, we will use MediaPipe to track hand gestures and convert them to appropriate actions like zooming in, blurring the background, and switching the camera on or off.
Gesture Control in Zoom Meeting-MediaPipe
Gesture Control in Zoom Meeting-MediaPipe

As you all know, Zoom has become the veritable king of online-meeting platforms and is widely used by corporates and universities.

Online meetings, however, come with their own set of problems. For example, every time you want to change anything in video, you have to go to zoom settings first. But isn’t that impractical, especially when you are presenting something and immediately want to zoom-in or blur the background. What if you need to turn the camera off right away because your roommate has unknowingly entered the camera frame. Today we will solve all these problems using Python and computer vision – Mediapipe Gesture Recognition.

  1. Overview
    1. Understanding the Requirements
    2. Finding the Solutions
  2. MediaPipe 
    1. MediaPipe Hands
    2. MediaPipe Face Detection
    3. MediaPipe Selfie Segmentation
  3. Setting Up the Development Environment
  4. Code
  5. Output
  6. Conclusion

1. Overview

1.1 Here’s a list of the project requirements:

  • Virtual camera
  • Detection of hand gestures
    • Detect if the hand is present
    • Detect gestures made by the user
  • Detection of more than one person
    • Stop the camera if more than one person is detected 
    • Stop the camera if no one is detected
  • Selfie segmentation
    • Separate the person from the background
    • Blur the background

1.2 Finding the Solutions

Start by finding a way to connect the camera’s output with Zoom. For this, you’ll need a virtual camera. If you have ever used any video-calling software, you must have seen an option for selecting camera input. 

Zoom Camera selection

The Integrated Webcam output takes input directly from the webcam,  its frames therefore cannot be edited or changed without a mediator. You’ll thus need a system that not only takes input from the webcam, but also lets you manipulate and then publish the edited frames. 

This flowchart shows exactly what a virtual camera lets you do:

How does a virtual camera work

After zeroing onto a virtual camera, the next step is to automate the process of manipulation and publishing with the help of Python. We will use pyvirtualcam to send the edited frames to the virtual camera.

Now that you have a frame that can be edited, start planning a pipeline that can make appropriate changes to the frame, before it is sent to the webcam.

It’s a good idea to begin with processes that you’ll be running forever, like:

  • Hand Gestures
  • Face Detection 

When we look for something that can detect our hand gestures and identify them correctly, MediaPipe Hands wins hands down. That’s because MediaPipe Hands can detect the points on hands and effectively track them. It also tells us which gesture we made. So, our pipeline has MediaPipe Hands running all through, right from the beginning, trying to detect hands and recognize any gestures they make.

Similarly, you can use MediaPipe Face Detection to find the number of faces in a frame. 

Next, try blurring the background, using MediaPipe Selfie Segmentation.

Check out this final pipeline for it performs all our requirements!

Pipeline of the project

2. MediaPipe

 A good part of this project relies on MediaPipe and its modules, so it is important you understand exactly how we use them. To learn what else MediaPipe can do,  visit our Introduction to MediaPipe.

2.1 MediaPipe Hands

First, learn how the Coordinate system works, only then use MediaPipe Hands. 

Hand coordinate system

Before you start coding, it’s important you know that the y axis is inverted.

Mediapipe hand landmarks - mediapipe gesture recognition

The MediaPipe Hands module will return coordinates of 20 points on fingers.

Note: It’s easy to detect gestures using a SVC or a DL model. As  MediaPipe Hands already does the heavy work of getting the coordinates, simply use these coordinates for gesture detection.

  1. Our first gesture is a pinch to zoom and check if the fist is closed.
pinch detection mediapipe
  • Basically, we want to know the distance between these two points: the INDEX_FINGER_TIP and the THUMB_TIP. 
  • Also, ensure that the following points: MIDDLE_FINGER_TIP, RING_FINGER_TIP and PINKY_TIP are below the MIDDLE_FINGER_MCP, RING_FINGER_MCP and the PINKY_MCP respectively.
  1. The next gesture is Hand up. 
hand up detection mediapipe

A simple check that tells whether the WRIST is below the MIDDLE_FINGER_TIP or not is enough for this.

  1. For Hand down, this check is reversed.
hand down detection mediapipe

You check if the WRIST is truly above the MIDDLE_FINGER_TIP.

  1. For this three-finger gesture, do the following checks:
three detection mediapipe - gesture recognition mediapipe
  • Are the INDEX_FINGER_TIP, MIDDLE_FINGER_TIP and the RING_FINGER_TIP  above the  INDEX_FINGER_MCP, MIDDLE_FINGER_MCP and the RING_FINGER_MCP respectively?
  •  Is the PINKY_TIP below the PINKY_MCP?
  1. Finally, for the two-finger gesture, check if:
two detection mediapipe - mediapipe hand gesture
  • the INDEX_FINGER_TIP and the MIDDLE_FINGER_TIP are actually above the 

INDEX_FINGER_MCP and the MIDDLE_FINGER_MCP respectively

  • the PINKY_TIP and RING_FINGER_TIP are below the PINKY_MCP and RING_FINGER_MCP respectively.

2.2 MediaPipe Face Detection

face detection mediapipe - mediapipe gesture recognition python

Here, MediaPipe detects the faces and you simply fetch its findings.

2.3 MediaPipe Selfie Segmentation

MediaPipe Selfie Segmentation allows us to separate a person from his or her background.

Selfie view - mediapipe hand gesture recognition python

MediaPipe Selfie Segmentation creates a mask around the person and gives us a matrix sized the same as the image. However, you still need to convert the 2-D matrix into 3-D to match the images.

selfie segmentation mediapipe - hand gesture recognition mediapipe

Consider the above image: The white part is the foreground, whereas the black area forms its background. Simply use this information to change the background.

selfie segmentation output mediapipe - Mediapipe Gesture Recognition

3. Setting Up the Development Environment

Create a new folder and in it a file called requirements.txt.

Add the following contents to this file:

 absl-py==1.0.0 attrs==21.4.0 cycler==0.11.0 fonttools==4.33.3 kiwisolver==1.4.2 matplotlib==3.5.1 mediapipe==0.8.9.1 numpy==1.22.3 opencv-contrib-python==4.5.5.64 packaging==21.3 Pillow==9.1.0 protobuf==3.20.1 pyparsing==3.0.8 python-dateutil==2.8.2 pyvirtualcam==0.9.1 six==1.16.0 

Now, run these commands:

 python3 -m venv zoom-gestures source zoom-gestures/bin/activate pip3 install -r requirements.txt 

Virtual Camera Setup

For linux:- Install v4l2loopback

Note: Follow the documentation of pyvirtualcam, whether you are on windows or mac.

 sudo modprobe v4l2loopback 

Folder Structure

Create empty Python files, in line with the structure given below.

 . ├── main.py ├── requirements.txt ├── utils │ ├── face_detection.py │ ├── faceutils.py │ ├── fingerutils.py │ ├── handsutils.py │ ├── __init__.py │ └── zoomutils.py 

You are now ready for coding!

4. Code

Note:- For in-depth information on the code functions, do refer to the comments given supporting them.

We begin here with fingerutils.py. 

To make things easy, go define all the finger points we discussed in the MediaPipe Hands section.

Download Code To easily follow along this tutorial, please download code by clicking on the button below. It's FREE!
 WRIST = 0 INDEX_FINGER_PIP = 6 INDEX_FINGER_TIP = 8 MIDDLE_FINGER_MCP = 9 MIDDLE_FINGER_PIP = 10 MIDDLE_FINGER_TIP = 12 RING_FINGER_MCP = 13 RING_FINGER_PIP = 14 RING_FINGER_TIP = 16 PINKY_MCP = 17 PINKY_PIP = 18 PINKY_TIP = 20 

Once the fingers are defined, create a function for each gesture. Each of these functions intakes a landmark array, which it fetches from MediaPipe Hands.

 def is_fist_closed(points): """ Args: points: landmarks from mediapipe Returns: boolean check if fist is closed """ return points[MIDDLE_FINGER_MCP].y < points[MIDDLE_FINGER_TIP].y and points[PINKY_MCP].y < points[PINKY_TIP].y and \ points[RING_FINGER_MCP].y < points[RING_FINGER_TIP].y def hand_down(points): """ Args: points: landmarks from mediapipe Returns: boolean check if hand is down i.e. inverted """ return points[MIDDLE_FINGER_TIP].y > points[WRIST].y def hand_up(points): """ Args: points: landmarks from mediapipe Returns: boolean check if hand is up """ return points[MIDDLE_FINGER_TIP].y < points[WRIST].y def two_signal(points): """ Args: points: landmarks from mediapipe Returns: boolean check if fingers show two """ return points[INDEX_FINGER_TIP].y < points[INDEX_FINGER_PIP].y and points[MIDDLE_FINGER_TIP].y < points[ MIDDLE_FINGER_PIP].y and points[RING_FINGER_PIP].y < points[ RING_FINGER_TIP].y and \ points[PINKY_PIP].y < \ points[PINKY_TIP].y def three_signal(points): """ Args: points: landmarks from mediapipe Returns: boolean check if fingers show three """ return points[INDEX_FINGER_TIP].y < points[INDEX_FINGER_PIP].y and points[MIDDLE_FINGER_TIP].y < points[ MIDDLE_FINGER_PIP].y and points[RING_FINGER_PIP].y > points[ RING_FINGER_TIP].y and \ points[PINKY_PIP].y < \ points[PINKY_TIP].y 

Now, it’s time for face detection.

You just have to get the total number of detections. 

So, go to file face_detection.py.

 import cv2 import mediapipe as mp def face_detect(image): """ Args: image: frame captured by camera Returns: The number of faces """ # Use Mediapipe face detection mp_face_detection = mp.solutions.face_detection # choose face detection criteria with mp_face_detection.FaceDetection( model_selection=0, min_detection_confidence=0.5) as face_detection: # Make the image non-writable since the detection needs no write access # Doing so also improves performance image.flags.writeable = False # Convert image from RGB to BGR image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # process the image results = face_detection.process(image) # If any face is detected return the number of faces if results.detections: return len(results.detections) 

Next, go to faceutils.py.

 import cv2import mediapipe as mpimport numpy as np def background_blur(image): """ Args: image: frame captured by camera Returns: The image with a blurred background """ mp_selfie_segmentation = mp.solutions.selfie_segmentation with mp_selfie_segmentation.SelfieSegmentation( model_selection=1) as selfie_segmentation: # Convert Image to RGB from BGR image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB) # Make image readable before processing to increase the performance image.flags.writeable = False results = selfie_segmentation.process(image) image.flags.writeable = True # Convert Image to BGR from RGB image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # Add a bilateral filter mask = cv2.ximgproc.jointBilateralFilter(np.uint8(results.segmentation_mask), image, 15, 5, 5) # Create a condition for blurring the background condition = np.stack( (results.segmentation_mask,) * 3, axis=-1) > 0.1 # Remove map the image on blurred background output_image = np.where(condition, image, mask) # Flip the output output_image = cv2.flip(output_image, 1) return output_image 

Here, you create a condition to check if: 

  • the coordinates given in the segmentation mask are greater than 1.0, which indicates the foreground
  • or these coordinates are less than 1.0, indicating they belong to the background 

We can blur the background and apply this filter to the image and then return it.

To bring this all together, let us go to handutils.py.

Import all the necessary modules and initialize the global variables. Also, initialize the MediaPipe Hands module and use it to detect the presence of hands.

 # Imports import math import cv2 import numpy as np import mediapipe as mp from . import faceutils from . import face_detection from . import fingerutils from . import zoomutils # Set all status to false video_status = False blur_status = False detect_face_status = False # Set default Zoom Factor zoom_factor = 1 # Initialization of mediapipe Hands mp_hands = mp.solutions.hands hands = mp_hands.Hands() """MediaPipe Hands() """ 

Create a function called mediapipe_gestures, which takes two inputs: 

  1. Firstly, the image 
  2. Secondly, a cropped frame, which helps reduce the processing load. (More on this when we discuss the main function.)
 def mediapipe_gestures(img, cropped_img): """ Args: img: current frame cropped_img: cropped image for hands detection Returns: frame with applied effects """ # Crop the image for area specific detection cropped_img_rgb = cv2.cvtColor(cropped_img, cv2.COLOR_BGR2RGB) # Fetch the results on cropped image results = hands.process(cropped_img_rgb) # Set global variable values global video_status global zoom_factor global blur_status global detect_face_status # Detect faces detect_face = face_detection.face_detect(img) if detect_face is None or detect_face != 1: detect_face_status = True if detect_face == 1: detect_face_status = False # Create frame with a black img stopped_img = np.zeros([100, 100, 3], dtype=np.uint8) 

Detect the finger gestures and perform actions based on it.

 if results.multi_hand_landmarks: for handLms in results.multi_hand_landmarks: zoom_arr = [] # coordinates of points on index finger and thumb top h, w, c = img.shape landmarks = handLms.landmark for lm_id, lm in enumerate(landmarks): # Convert landmark coordinates to actual image coordinates cx, cy = int(lm.x * w), int(lm.y * h) # Append the coordinates if lm_id == 4 or lm_id == 8: zoom_arr.append((cx, cy)) # Check if hand is inverted or down if fingerutils.hand_down(landmarks): video_status = True # Check if three signal is given if fingerutils.three_signal(landmarks): blur_status = True # Check if two signal is given if fingerutils.two_signal(landmarks): blur_status = False # Check if hand is up and continue the capture if fingerutils.hand_up(landmarks): video_status = False # Check if fingers are detected fists are closed and hand is up so video is on if len(zoom_arr) > 1 and fingerutils.is_fist_closed(landmarks) and fingerutils.hand_up(landmarks): p1 = zoom_arr[0] p2 = zoom_arr[1] # Calculate the distance between two fingertips dist = math.sqrt(pow(p1[0] - p2[0], 2) + pow(p1[1] - p2[1], 2)) # Zoom in or out if 150 <= dist <= 300: zoom_factor = zoomutils.fetch_zoom_factor(dist) # If the hand was down or there is more than one person in frame if video_status is True or detect_face_status is True: img = stopped_img # If blur is on blur the image if blur_status: img = faceutils.background_blur(img) # Zoom the image according to the needs img = zoomutils.zoom_center(img, zoom_factor) return img 

Finally, get main.py.

 # Imports import cv2 import pyvirtualcam from pyvirtualcam import PixelFormat from utils import handsutils import platform def main(): # Start video capture and set defaults device_val = None cap = cv2.VideoCapture(0) pref_width = 1280 pref_height = 720 pref_fps = 30 cap.set(cv2.CAP_PROP_FRAME_WIDTH, pref_width) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, pref_height) cap.set(cv2.CAP_PROP_FPS, pref_fps) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) fps = cap.get(cv2.CAP_PROP_FPS) os = platform.system() if os == "Linux": device_val = "/dev/video2" with pyvirtualcam.Camera(width, height, fps, device=device_val, fmt=PixelFormat.BGR) as cam: print('Virtual camera device: ' + cam.device) while True: success, img = cap.read() cropped_img = img[0:720, 0:400] img = handsutils.mediapipe_gestures(img, cropped_img) img = cv2.resize(img, (1280, 720)) cam.send(img) cam.sleep_until_next_frame() if __name__ == '__main__': """ Main Function """ main() 

Once you capture the frame, get the processed image using medipipe_gestures. It is important to pass a cropped image for hand detection because you don’t want the program to detect gestures outside your particular region.

5. Output

6. Conclusion

You now have a clear understanding of the basic concepts of MediaPipe. Also, you know how multiple modules of MediaPipe can be used together. 

One additional fact: Besides Zoom, this program works on any other software that can detect and use virtual cameras.

Also, Check Out: Center Stage for Zoom Calls using MediaPipe

More on Mediapipe

Hang on, the journey doesn’t end here. We have some more exciting blog posts for you to explore!!!

1. Building a Poor Body Posture Detection and Alert System using MediaPipe
2. Creating Snapchat/Instagram filters using Mediapipe
3. Center Stage for Zoom Calls using MediaPipe
4. Drowsy Driver Detection using Mediapipe
5. Comparing Yolov7 and Mediapipe Pose Estimation models

Never Stop Learning!!!


Read Next

VideoRAG: Redefining Long-Context Video Comprehension

VideoRAG: Redefining Long-Context Video Comprehension

Discover VideoRAG, a framework that fuses graph-based reasoning and multi-modal retrieval to enhance LLMs' ability to understand multi-hour videos efficiently.

AI Agent in Action: Automating Desktop Tasks with VLMs

AI Agent in Action: Automating Desktop Tasks with VLMs

Learn how to build AI agent from scratch using Moondream3 and Gemini. It is a generic task based agent free from…

The Ultimate Guide To VLM Evaluation Metrics, Datasets, And Benchmarks

The Ultimate Guide To VLM Evaluation Metrics, Datasets, And Benchmarks

Get a comprehensive overview of VLM Evaluation Metrics, Benchmarks and various datasets for tasks like VQA, OCR and Image Captioning.

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.

Subscribe to receive the download link, receive updates, and be notified of bug fixes

Which email should I send you the download link?

🎃 Halloween Sale: Exclusive Offer – 30% Off on All Courses.
D
H
M
S
Expired
 

Get Started with OpenCV

Subscribe To Receive

We hate SPAM and promise to keep your email address safe.​