Skip to main content

Create Camera function (Windows)

Import necessary package.​

import imp
from pickle import NONE
import cv2
import mediapipe as mp
import sys
import os
import json
import time
import datetime
from mediapipe.framework.formats import landmark_pb2
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
import mqtt
from mqtt import *

Create a class​

To start custom function, first we need to create a class. This class will includes the initializetion, all the necessary function and the main run function.

class Camera:
def __init__(self):

Data logging​

Set the function to save the data in a json file.

def data_log(self,data):
# file name
now = self.log_dir.split("/")[-1]
name = now + '_hand_track.json'
key = str(datetime.datetime.now())

self.log[key] = data

path = os.path.join(self.log_dir, name)
with open(path, "w") as file:
json.dump(self.log, file, indent=4)

Video record​

To save the video, we need to use the cv2.VideoWriter() function.

def vedio_log(self, cap):
fourcc = cv2.VideoWriter_fourcc(*'XVID')
fps = 18
frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
now = self.log_dir.split("/")[-1]
name = now + '_hand_track.avi'
path = os.path.join(self.log_dir, name)
out = cv2.VideoWriter(path, fourcc, fps, frame_size)
return out

Add custom annotation​

To add custom annotation, we need to use the cv2.putText() function.

def custom_annotations(self,frame):
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.8
color = (255, 255, 255)
thickness = 2
x = 10
y = frame.shape[0] - 30
item_list = list(self.log.items())
if len(item_list) != 0:
id, value = item_list[-1]
key_text = f"{id}:{value}"
cv2.putText(frame, key_text, (x, y), font, font_scale, color, thickness)
return frame

Main run function​

To start the hand gesture recognition, we need to create a main run function and start camera feed.

First, we need to prepare the log directory.

def run(self):
current = os.path.abspath(__file__)
self.log_dir = current + "logs/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
if not os.path.exists(self.log_dir):
os.makedirs(self.log_dir)

Then, set the mqtt connection.

mqtt.setup(CLIENT,TOPIC,PREVIOUSCHECK)
mqtt.setup_emqx(CLIENT,TOPIC,PREVIOUSCHECK)

Load gesture recognition model.

with open(self.MODEL_PATH, 'rb') as f:
model = f.read()
base_options = python.BaseOptions(model_asset_buffer=model)
options = vision.GestureRecognizerOptions(
base_options=base_options,
running_mode=vision.RunningMode.LIVE_STREAM,
num_hands=self.NUM_HANDS,
min_hand_detection_confidence=self.MIN_HAND_DETECTION_CONFIDENCE,
min_hand_presence_confidence=self.MIN_HAND_PRESENCE_CONFIDENCE,
min_tracking_confidence=self.MIN_TRACKING_CONFIDENCE,
result_callback=self.save_result)
recognizer = vision.GestureRecognizer.create_from_options(options)

Set camera and video record.

cap = cv2.VideoCapture(self.CAMERA_ID)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.FRAME_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.FRAME_HEIGHT)
out = self.vedio_log(cap)

Turn on camera and start recognition.

 while cap.isOpened():
success, image = cap.read()
if not success:
sys.exit('ERROR: Unable to read from webcam. Please verify your webcam settings.')

image = cv2.flip(image, 1)
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb_image)
recognizer.recognize_async(mp_image, time.time_ns() // 1_000_000)

Show FPS and custom annotation.

fps_text = 'FPS = {:.1f}'.format(self.FPS)
text_location = (self.left_margin, self.row_size)
current_frame = image
cv2.putText(current_frame, fps_text, text_location, cv2.FONT_HERSHEY_DUPLEX,
self.font_size, self.text_color, self.font_thickness, cv2.LINE_AA)

current_frame = self.custom_annotations(current_frame)

Check recognition result, save the data, and add draw landmark on the hand.

if self.recognition_result_list:
#_______________________________
for hand_index, hand_landmarks in enumerate(
self.recognition_result_list[0].hand_landmarks):
# Calculate the bounding box of the hand
x_min = min([landmark.x for landmark in hand_landmarks])
y_min = min([landmark.y for landmark in hand_landmarks])
y_max = max([landmark.y for landmark in hand_landmarks])

# Convert normalized coordinates to pixel values
frame_height, frame_width = current_frame.shape[:2]
x_min_px = int(x_min * frame_width)
y_min_px = int(y_min * frame_height)
y_max_px = int(y_max * frame_height)

# Get gesture classification results
if self.recognition_result_list[0].gestures:
gesture = self.recognition_result_list[0].gestures[hand_index]
category_name = gesture[0].category_name
score = round(gesture[0].score, 2)
result_text = f'{category_name} ({score})'

#log values
self.data_log(category_name)

#MQTT Publish
#mqtt.publish(category_name,CLIENT,TOPIC)

# Compute text size
text_size = \
cv2.getTextSize(result_text, cv2.FONT_HERSHEY_DUPLEX, self.label_font_size,
self.label_thickness)[0]
text_width, text_height = text_size

# Calculate text position (above the hand)
text_x = x_min_px
text_y = y_min_px - 10 # Adjust this value as needed

# Make sure the text is within the frame boundaries
if text_y < 0:
text_y = y_max_px + text_height

# Draw the text
cv2.putText(current_frame, result_text, (text_x, text_y),
cv2.FONT_HERSHEY_DUPLEX, self.label_font_size,
self.label_text_color, self.label_thickness, cv2.LINE_AA)

# Draw hand landmarks on the frame
hand_landmarks_proto = landmark_pb2.NormalizedLandmarkList()
hand_landmarks_proto.landmark.extend([
landmark_pb2.NormalizedLandmark(x=landmark.x, y=landmark.y,
z=landmark.z) for landmark in
hand_landmarks
])
self.mp_drawing.draw_landmarks(
current_frame,
hand_landmarks_proto,
self.mp_hands.HAND_CONNECTIONS,
self.mp_drawing_styles.get_default_hand_landmarks_style(),
self.mp_drawing_styles.get_default_hand_connections_style())

Save the video and clear recognition result.

recognition_frame = current_frame
self.recognition_result_list.clear()
out.write(current_frame)

Encode the frame as JPEG for web page response

ret, buffer = cv2.imencode('.jpg', image)
frame_jpeg = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame_jpeg + b'\r\n')

Set keyboard break.

if cv2.waitKey(1) & 0xFF == 27:
break

Stop the camera and release.

recognizer.close()
self.CLIENT.disconnect()
cap.release()
cv2.destroyAllWindows()