Download app.py from RomRam1/SLR: direct link, hf CLI and curl.
- Browser
- Download file 4.51 kB
-
https://huggingface.co/spaces/RomRam1/SLR/resolve/main/app.py
- Command line
-
hf download hf://spaces/RomRam1/SLR/app.py
-
curl -L -o app.py https://huggingface.co/spaces/RomRam1/SLR/resolve/main/app.py
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 ---------------- | |
| 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) | |