SLR / app.py
RomRam1's picture
Update app.py
4fa21dc verified
Raw
History Blame Contribute Delete
4.51 kB
import os
# 🔥 HARD CPU ENFORCEMENT (BEFORE ANY IMPORTS)
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
os.environ["MEDIAPIPE_DISABLE_GPU"] = "1"
import cv2
import numpy as np
import math
from flask import Flask, request, jsonify
import mediapipe as mp
from cvzone.ClassificationModule import Classifier
# ---------------- CONFIG ----------------
MODEL_PATH = "Model_old/keras_model.h5"
LABELS_PATH = "Model_old/labels.txt"
IMG_SIZE = 300
OFFSET = 20
CONFIDENCE_THRESHOLD = 0.5
STABILITY_THRESHOLD = 3
# ---------------- APP ----------------
app = Flask(__name__)
# ---------------- MEDIAPIPE (CPU-ONLY, CLOUD SAFE) ----------------
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=True, # REQUIRED for server inference
max_num_hands=1,
model_complexity=0, # CPU graph only
min_detection_confidence=0.6,
min_tracking_confidence=0.6
)
# ---------------- CLASSIFIER ----------------
classifier = Classifier(MODEL_PATH, LABELS_PATH)
with open(LABELS_PATH, "r") as f:
labels = [line.strip() for line in f.readlines()]
stable_gesture = None
stable_count = 0
# ---------------- API ----------------
@app.route("/predict", methods=["POST"])
def predict():
global stable_gesture, stable_count
if "frame" not in request.files:
return jsonify({"gesture": None, "confidence": 0})
# ---- Decode image safely ----
try:
file = request.files["frame"]
img_bytes = np.frombuffer(file.read(), np.uint8)
frame = cv2.imdecode(img_bytes, cv2.IMREAD_COLOR)
if frame is None:
return jsonify({"gesture": None, "confidence": 0})
frame = cv2.flip(frame, 1)
except Exception:
return jsonify({"gesture": None, "confidence": 0})
# ---- MediaPipe inference ----
try:
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
result = hands.process(rgb)
except Exception:
stable_gesture = None
stable_count = 0
return jsonify({"gesture": None, "confidence": 0})
if not result.multi_hand_landmarks:
stable_gesture = None
stable_count = 0
return jsonify({"gesture": None, "confidence": 0})
# ---- Bounding box from landmarks ----
h, w, _ = frame.shape
lm = result.multi_hand_landmarks[0].landmark
x_vals = [int(p.x * w) for p in lm]
y_vals = [int(p.y * h) for p in lm]
x, y = min(x_vals), min(y_vals)
bw, bh = max(x_vals) - x, max(y_vals) - y
if bw <= 0 or bh <= 0:
return jsonify({"gesture": None, "confidence": 0})
# ---- Image preprocessing ----
imgWhite = np.ones((IMG_SIZE, IMG_SIZE, 3), np.uint8) * 255
imgCrop = frame[
max(0, y - OFFSET): y + bh + OFFSET,
max(0, x - OFFSET): x + bw + OFFSET
]
if imgCrop.size == 0:
return jsonify({"gesture": None, "confidence": 0})
aspectRatio = bh / bw
try:
if aspectRatio > 1:
k = IMG_SIZE / bh
wCal = max(1, math.ceil(k * bw))
imgResize = cv2.resize(imgCrop, (wCal, IMG_SIZE))
wGap = (IMG_SIZE - wCal) // 2
imgWhite[:, wGap:wGap + wCal] = imgResize
else:
k = IMG_SIZE / bw
hCal = max(1, math.ceil(k * bh))
imgResize = cv2.resize(imgCrop, (IMG_SIZE, hCal))
hGap = (IMG_SIZE - hCal) // 2
imgWhite[hGap:hGap + hCal, :] = imgResize
except Exception:
return jsonify({"gesture": None, "confidence": 0})
# ---- Classification ----
try:
prediction, index = classifier.getPrediction(imgWhite, draw=False)
index = int(index)
if index < 0 or index >= len(labels):
return jsonify({"gesture": None, "confidence": 0})
confidence = float(prediction[index])
confidence = max(0.0, min(confidence, 1.0))
gesture = labels[index]
except Exception:
return jsonify({"gesture": None, "confidence": 0})
# ---- Stability logic ----
if gesture == stable_gesture:
stable_count += 1
else:
stable_gesture = gesture
stable_count = 1
if stable_count >= STABILITY_THRESHOLD and confidence >= CONFIDENCE_THRESHOLD:
return jsonify({
"gesture": gesture,
"confidence": confidence
})
return jsonify({"gesture": None, "confidence": 0})
# ---------------- ENTRY ----------------
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)