Spaces:
Sleeping
Sleeping
| import os | |
| import subprocess | |
| import sys | |
| import pkg_resources | |
| import warnings | |
| warnings.filterwarnings("ignore") | |
| def install_package(package, version=None): | |
| package_spec = f"{package}=={version}" if version else package | |
| print(f"Installing {package_spec}...") | |
| try: | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", "--no-cache-dir", package_spec]) | |
| except subprocess.CalledProcessError as e: | |
| print(f"Failed to install {package_spec}: {e}") | |
| raise | |
| # Required packages | |
| required_packages = { | |
| "mediapipe": None, | |
| "tensorflow": None, | |
| "opencv-python-headless": None, | |
| "gradio": None, | |
| "Pillow": None, | |
| "numpy": None | |
| } | |
| installed_packages = {pkg.key for pkg in pkg_resources.working_set} | |
| for package, version in required_packages.items(): | |
| if package not in installed_packages: | |
| install_package(package, version) | |
| import numpy as np | |
| import tensorflow as tf | |
| import cv2 | |
| import mediapipe as mp | |
| import gradio as gr | |
| from PIL import Image | |
| # Hand Tracker class - using the provided implementation | |
| class handTracker(): | |
| def __init__(self, mode=False, maxHands=2, modelComplexity=1, | |
| detectionConfidence=0.5, trackConfidence=0.5): | |
| self.mode = mode | |
| self.maxHands = maxHands | |
| self.modelComplexity = modelComplexity | |
| self.detectionConfidence = detectionConfidence | |
| self.trackConfidence = trackConfidence | |
| self.mpHands = mp.solutions.hands | |
| self.hands = self.mpHands.Hands( | |
| static_image_mode=self.mode, | |
| max_num_hands=self.maxHands, | |
| model_complexity=self.modelComplexity, | |
| min_detection_confidence=self.detectionConfidence, | |
| min_tracking_confidence=self.trackConfidence) | |
| self.mpDraw = mp.solutions.drawing_utils | |
| self.mpDrawStyles = mp.solutions.drawing_styles | |
| def findAndDrawHands(self, frame): | |
| RGBimage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
| self.results = self.hands.process(RGBimage) | |
| if self.results.multi_hand_landmarks: | |
| for handLms in self.results.multi_hand_landmarks: | |
| self.mpDraw.draw_landmarks( | |
| frame, | |
| handLms, | |
| self.mpHands.HAND_CONNECTIONS, | |
| self.mpDrawStyles.get_default_hand_landmarks_style(), | |
| self.mpDrawStyles.get_default_hand_connections_style()) | |
| return frame | |
| def findLandmarks(self, frame, handNo=0): | |
| landmarkList = [] | |
| x_list = [] | |
| y_list = [] | |
| bbox = [] | |
| if self.results.multi_hand_landmarks: | |
| if handNo < len(self.results.multi_hand_landmarks): | |
| myHand = self.results.multi_hand_landmarks[handNo] | |
| for id, lm in enumerate(myHand.landmark): | |
| h, w, c = frame.shape | |
| cx, cy = int(lm.x * w), int(lm.y * h) | |
| x_list.append(cx) | |
| y_list.append(cy) | |
| landmarkList.append([id, cx, cy]) | |
| if x_list and y_list: | |
| xmin, xmax = min(x_list), max(x_list) | |
| ymin, ymax = min(y_list), max(y_list) | |
| padding = 20 | |
| xmin = max(0, xmin - padding) | |
| ymin = max(0, ymin - padding) | |
| boxW = min(w - xmin, xmax - xmin + 2*padding) | |
| boxH = min(h - ymin, ymax - ymin + 2*padding) | |
| if boxW > boxH: | |
| diff = boxW - boxH | |
| ymin = max(0, ymin - diff//2) | |
| boxH = min(h - ymin, boxW) | |
| elif boxH > boxW: | |
| diff = boxH - boxW | |
| xmin = max(0, xmin - diff//2) | |
| boxW = min(w - xmin, boxH) | |
| bbox = [xmin, ymin, boxW, boxH] | |
| return landmarkList, bbox | |
| # Model loading with compatibility handling | |
| def load_model_with_compatibility(model_path): | |
| try: | |
| model = tf.keras.models.load_model(model_path) | |
| print("✓ Model loaded successfully") | |
| return model | |
| except Exception as e: | |
| print(f"Standard loading failed: {str(e)}") | |
| try: | |
| class CustomDepthwiseConv2D(tf.keras.layers.DepthwiseConv2D): | |
| def __init__(self, **kwargs): | |
| if 'groups' in kwargs: | |
| del kwargs['groups'] | |
| super(CustomDepthwiseConv2D, self).__init__(**kwargs) | |
| custom_objects = {'DepthwiseConv2D': CustomDepthwiseConv2D} | |
| model = tf.keras.models.load_model( | |
| model_path, | |
| custom_objects=custom_objects, | |
| compile=False | |
| ) | |
| print("✓ Model loaded in compatibility mode") | |
| return model | |
| except Exception as e2: | |
| print(f"Compatibility loading failed: {str(e2)}") | |
| return create_simple_asl_model() | |
| def create_simple_asl_model(): | |
| labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', | |
| 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', | |
| 'T', 'U', 'V', 'W', 'X', 'Y'] | |
| print("Creating a new compatible model...") | |
| model = tf.keras.Sequential([ | |
| tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)), | |
| tf.keras.layers.MaxPooling2D((2, 2)), | |
| tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), | |
| tf.keras.layers.MaxPooling2D((2, 2)), | |
| tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), | |
| tf.keras.layers.Flatten(), | |
| tf.keras.layers.Dense(128, activation='relu'), | |
| tf.keras.layers.Dropout(0.5), | |
| tf.keras.layers.Dense(len(labels), activation='softmax') | |
| ]) | |
| model.compile(optimizer='adam', | |
| loss='sparse_categorical_crossentropy', | |
| metrics=['accuracy']) | |
| return model | |
| model_path = "keras_model.h5" | |
| model = load_model_with_compatibility(model_path) | |
| model_input_shape = (224, 224, 3) | |
| labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', | |
| 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', | |
| 'T', 'U', 'V', 'W', 'X', 'Y'] | |
| def preprocess_hand_roi(hand_roi, target_shape): | |
| if target_shape[2] == 3: | |
| if len(hand_roi.shape) == 2 or hand_roi.shape[2] == 1: | |
| hand_roi_rgb = cv2.cvtColor(hand_roi, cv2.COLOR_GRAY2RGB) | |
| else: | |
| hand_roi_rgb = hand_roi.copy() | |
| resized = cv2.resize(hand_roi_rgb, (target_shape[0], target_shape[1])) | |
| normalized = resized.astype('float32') / 255.0 | |
| else: | |
| if len(hand_roi.shape) > 2 and hand_roi.shape[2] > 1: | |
| hand_roi_gray = cv2.cvtColor(hand_roi, cv2.COLOR_BGR2GRAY) | |
| else: | |
| hand_roi_gray = hand_roi | |
| resized = cv2.resize(hand_roi_gray, (target_shape[0], target_shape[1])) | |
| normalized = resized.astype('float32') / 255.0 | |
| if len(normalized.shape) == 2: | |
| normalized = normalized[..., np.newaxis] | |
| return np.expand_dims(normalized, axis=0), resized | |
| def process_image(input_image): | |
| frame = cv2.cvtColor(np.array(input_image), cv2.COLOR_RGB2BGR) | |
| tracker = handTracker(detectionConfidence=0.7) | |
| frame_with_hands = tracker.findAndDrawHands(frame.copy()) | |
| landmarks, bbox = tracker.findLandmarks(frame) | |
| if not bbox: | |
| return "No hand detected", None | |
| x, y, w, h = bbox | |
| hand_roi = frame[y:y+h, x:x+w] | |
| cv2.rectangle(frame_with_hands, (x, y), (x+w, y+h), (0, 255, 0), 2) | |
| model_input, _ = preprocess_hand_roi(hand_roi, model_input_shape) | |
| try: | |
| prediction = model.predict(model_input, verbose=0)[0] | |
| predicted_class = np.argmax(prediction) | |
| confidence = np.max(prediction) | |
| letter = labels[predicted_class] if predicted_class < len(labels) else "Unknown" | |
| except: | |
| return "Prediction error", None | |
| result_text = f"Prediction: {letter} (Confidence: {confidence:.2f})" | |
| cv2.putText(frame_with_hands, result_text, (10, 30), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) | |
| output_image = cv2.cvtColor(frame_with_hands, cv2.COLOR_BGR2RGB) | |
| return result_text, Image.fromarray(output_image) | |
| # Gradio interface | |
| interface = gr.Interface( | |
| fn=process_image, | |
| inputs=gr.Image(label="Upload Hand Sign Image", type="pil"), | |
| outputs=[ | |
| gr.Text(label="Prediction Result"), | |
| gr.Image(label="Processed Image") | |
| ], | |
| title="ASL Sign Language Recognition", | |
| description="Upload an image of a hand sign to recognize the ASL letter." | |
| ) | |
| if __name__ == "__main__": | |
| interface.launch(share=True) | |