| import os |
|
|
| |
| 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 |
|
|
| |
|
|
| 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 = Flask(__name__) |
|
|
| |
|
|
| mp_hands = mp.solutions.hands |
|
|
| hands = mp_hands.Hands( |
| static_image_mode=True, |
| max_num_hands=1, |
| model_complexity=0, |
| min_detection_confidence=0.6, |
| min_tracking_confidence=0.6 |
| ) |
|
|
| |
|
|
| 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 |
|
|
| |
|
|
| @app.route("/predict", methods=["POST"]) |
| def predict(): |
| global stable_gesture, stable_count |
|
|
| if "frame" not in request.files: |
| return jsonify({"gesture": None, "confidence": 0}) |
|
|
| |
| 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}) |
|
|
| |
| 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}) |
|
|
| |
| 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}) |
|
|
| |
| 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}) |
|
|
| |
| 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}) |
|
|
| |
| 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}) |
|
|
|
|
| |
|
|
| if __name__ == "__main__": |
| app.run(host="0.0.0.0", port=7860) |
|
|