File size: 3,026 Bytes
e0e981c
04fcb4d
e0e981c
 
 
 
 
314b8df
 
 
e0e981c
04fcb4d
 
 
 
 
 
e0e981c
 
 
314b8df
 
 
e0e981c
 
 
 
 
 
 
 
314b8df
e0e981c
 
 
 
 
04fcb4d
e0e981c
 
092fbf5
e0e981c
 
314b8df
 
 
e16933e
 
314b8df
 
 
e16933e
314b8df
 
 
 
092fbf5
 
314b8df
e0e981c
e16933e
 
092fbf5
e0e981c
04fcb4d
e0e981c
 
 
314b8df
e0e981c
 
 
 
314b8df
092fbf5
 
 
 
314b8df
 
 
092fbf5
 
314b8df
e0e981c
 
314b8df
 
 
e0e981c
 
 
 
 
314b8df
e0e981c
092fbf5
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
import cv2
import mediapipe as mp
import numpy as np
import torch
import torch.nn as nn
import gradio as gr

# ----------------------------
# Labels
# ----------------------------
GESTURE_LABELS = {
    0: "A", 1: "B", 2: "L", 3: "U", 4: "V", 5: "W",
    6: "Z", 7: "F", 8: "five", 9: "one", 10: "three",
    11: "two", 12: "six", 13: "seven", 14: "eight",
    15: "nine", 16: "ten", 17: "E", 18: "four",
    19: "i", 20: "k", 21: "r", 22: "zero",
    23: "m", 24: "s"
}
CONF_THRESHOLD = 0.6

# ----------------------------
# Model
# ----------------------------
class GestureNet(nn.Module):
    def __init__(self, input_size=126, num_classes=len(GESTURE_LABELS)):
        super().__init__()
        self.fc1 = nn.Linear(input_size, 256)
        self.fc2 = nn.Linear(256, 128)
        self.fc3 = nn.Linear(128, num_classes)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(0.3)

    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.dropout(x)
        x = self.relu(self.fc2(x))
        x = self.dropout(x)
        return self.fc3(x)

model = GestureNet()
model.load_state_dict(torch.load("gesture_model1.pth", map_location="cpu"))
model.eval()

# ----------------------------
# MediaPipe
# ----------------------------
mp_hands = mp.solutions.hands

# ----------------------------
# Predict
# ----------------------------
def predict(image):
    # Gradio sends RGB, convert to BGR for OpenCV then back to RGB for MediaPipe
    image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)

    with mp_hands.Hands(static_image_mode=True, max_num_hands=2) as hands:
        results = hands.process(image_rgb)

    coords = []
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            hand_coords = np.array([[lm.x, lm.y, lm.z] for lm in hand_landmarks.landmark])
            hand_coords -= hand_coords[0]
            max_val = np.max(np.linalg.norm(hand_coords, axis=1))
            if max_val > 0:
                hand_coords /= max_val
            coords.extend(hand_coords.flatten())

    if len(coords) < 126:
        coords.extend([0.0] * (126 - len(coords)))
    elif len(coords) > 126:
        coords = coords[:126]

    if len(coords) == 126:
        input_tensor = torch.tensor(coords, dtype=torch.float32).unsqueeze(0)
        with torch.no_grad():
            outputs = model(input_tensor)
            probs = torch.softmax(outputs, dim=1)
            pred_class = torch.argmax(probs, dim=1).item()
            confidence = probs[0][pred_class].item()
        if confidence >= CONF_THRESHOLD:
            return f"{GESTURE_LABELS[pred_class]} ({confidence*100:.2f}%)"

    return "Unknown"

# ----------------------------
# Gradio UI
# ----------------------------
app = gr.Interface(
    fn=predict,
    inputs=gr.Image(type="numpy"),
    outputs="text",
    title="Hand Gesture Recognition",
    description="Upload an image of a hand gesture to recognize it."
)
app.launch()