thienphuc12339 commited on
Commit
efffbbc
·
verified ·
1 Parent(s): 67a75e2

Upload 5 files

Browse files
Files changed (5) hide show
  1. Lipnet.h5 +3 -0
  2. app.py +119 -0
  3. inference.py +98 -0
  4. preprocessing.py +96 -0
  5. requirements.txt +9 -0
Lipnet.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3dc9c92165acdfa5fffde1e002235d611407a132967cb1865545541a6479db46
3
+ size 98068312
app.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tempfile
3
+ import os
4
+ import cv2
5
+ import numpy as np
6
+ from inference3 import predict_from_video, LipReadingModel
7
+ import logging
8
+
9
+ # Configure Logging
10
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # Load the model once
14
+ def load_model():
15
+ logger.info("Loading Lip Reading Model...")
16
+ return LipReadingModel()
17
+
18
+ model = load_model()
19
+
20
+ # Prediction function with enhancements
21
+ def run_prediction(video_path):
22
+ """
23
+ Takes a video file path, processes it, and returns the predicted text.
24
+ Includes error handling.
25
+ """
26
+ MAX_SIZE_MB = 1000 # Maximum allowed video size in megabytes
27
+
28
+ if not video_path:
29
+ return "❌ No video provided. Please upload or record a video."
30
+
31
+ # Check video size
32
+ try:
33
+ video_size_mb = os.path.getsize(video_path) / (1024 * 1024)
34
+ logger.info(f"Uploaded video size: {video_size_mb:.2f} MB")
35
+ except Exception as e:
36
+ logger.error(f"Error accessing video file: {e}")
37
+ return f"❌ Error accessing video file: {e}"
38
+
39
+ if video_size_mb > MAX_SIZE_MB:
40
+ return f"❌ Video size exceeds {MAX_SIZE_MB} MB limit. Please upload a smaller video."
41
+
42
+ try:
43
+ # Run prediction
44
+ logger.info("Running prediction...")
45
+ prediction = predict_from_video(video_path=video_path, model=model)
46
+ logger.info("Prediction completed.")
47
+ except Exception as e:
48
+ logger.error(f"Prediction error: {e}")
49
+ prediction = f"❌ An error occurred during prediction: {e}"
50
+
51
+ return prediction
52
+
53
+ # Define Gradio interface
54
+ def create_interface():
55
+ with gr.Blocks(css="#title {font-size: 2em; color: #4CAF50}") as demo:
56
+ gr.Markdown("# 🧠 Lip Reading App")
57
+ gr.Markdown("""This application allows you to perform lip reading by either uploading a video or recording directly using your webcam.""")
58
+
59
+ with gr.TabItem("Upload Video"):
60
+ with gr.Column():
61
+ video_input = gr.Video(
62
+ label="📂 Upload Your Video",
63
+ sources="upload" # Specify source as upload
64
+ )
65
+ predict_button = gr.Button("🔍 Run Prediction")
66
+ prediction_output = gr.Textbox(
67
+ label="📝 Predicted Text",
68
+ interactive=False,
69
+ lines=4,
70
+ placeholder="Prediction will appear here."
71
+ )
72
+
73
+ with gr.TabItem("Record Video"):
74
+ with gr.Column():
75
+ video_recorder = gr.Video(
76
+ label="🎥 Record Your Video",
77
+ sources="webcam" # Specify source as webcam
78
+ )
79
+ predict_button_rec = gr.Button("🔍 Run Prediction on Recorded Video")
80
+ prediction_output_rec = gr.Textbox(
81
+ label="📝 Predicted Text",
82
+ interactive=False,
83
+ lines=4,
84
+ placeholder="Prediction will appear here."
85
+ )
86
+
87
+ # Add user instructions and feedback
88
+ with gr.Accordion("ℹ️ How to Use", open=False):
89
+ gr.Markdown("""
90
+ **Upload Video:**
91
+ - Click on the "Upload Your Video" button to select a video file from your device.
92
+ - Supported formats: MP4, AVI, MOV, MPG.
93
+ - After uploading, click "Run Prediction" to get the lip reading result.
94
+
95
+ **Record Video:**
96
+ - Click on the "Record Your Video" button to access your webcam.
97
+ - Grant the necessary permissions if prompted.
98
+ - Record your video and click "Stop Recording" (or equivalent) once done.
99
+ - Wait for 10 seconds until the recorded video appear on screen.
100
+ - Click "Run Prediction on Recorded Video" to get the lip reading result.
101
+ """)
102
+
103
+ # Define button actions
104
+ predict_button.click(fn=run_prediction, inputs=video_input, outputs=prediction_output)
105
+ predict_button_rec.click(fn=run_prediction, inputs=video_recorder, outputs=prediction_output_rec)
106
+
107
+ # Add footer or additional information if needed
108
+ gr.Markdown("""--- © 2024 Lip Reading App. All rights reserved.""")
109
+
110
+ return demo
111
+
112
+ # Launch the interface
113
+ if __name__ == "__main__":
114
+ demo = create_interface()
115
+ demo.launch(
116
+ server_name="0.0.0.0",
117
+ server_port=7860,
118
+ share=True # Set to False if not sharing publicly
119
+ )
inference.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # inference3.py
2
+
3
+ import tensorflow as tf
4
+ from preprocessing import VideoPreprocessor
5
+
6
+ class LipReadingModel:
7
+ def __init__(self, model_path='Lipnet.h5'):
8
+ # Initialize character mappings before loading the model
9
+ vocab = [x for x in "abcdefghijklmnopqrstuvwxyz'?!123456789 "]
10
+ self.char_to_num = tf.keras.layers.StringLookup(vocabulary=vocab, oov_token="")
11
+ self.num_to_char = tf.keras.layers.StringLookup(
12
+ vocabulary=self.char_to_num.get_vocabulary(), oov_token="", invert=True)
13
+
14
+ self.model = self.load_model()
15
+ try:
16
+ self.model.load_weights(model_path)
17
+ print("Model loaded successfully.")
18
+ except Exception as e:
19
+ print(f"Error loading model weights: {e}")
20
+
21
+ def load_model(self):
22
+ model = tf.keras.Sequential()
23
+ model.add(tf.keras.layers.Conv3D(128, (3, 3, 3), input_shape=(75, 75, 75, 1), padding='same'))
24
+ model.add(tf.keras.layers.Activation('relu'))
25
+ model.add(tf.keras.layers.MaxPooling3D((1, 2, 2)))
26
+
27
+ model.add(tf.keras.layers.Conv3D(256, (3, 3, 3), padding='same'))
28
+ model.add(tf.keras.layers.Activation('relu'))
29
+ model.add(tf.keras.layers.MaxPooling3D((1, 2, 2)))
30
+
31
+ model.add(tf.keras.layers.Conv3D(75, (3, 3, 3), padding='same'))
32
+ model.add(tf.keras.layers.Activation('relu'))
33
+ model.add(tf.keras.layers.MaxPooling3D((1, 2, 2)))
34
+
35
+ # Flatten the output for the RNN
36
+ model.add(tf.keras.layers.TimeDistributed(tf.keras.layers.Flatten()))
37
+
38
+ # LSTM layers
39
+ model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, return_sequences=True, kernel_initializer='Orthogonal')))
40
+ model.add(tf.keras.layers.Dropout(0.5))
41
+
42
+ model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(128, return_sequences=True, kernel_initializer='Orthogonal')))
43
+ model.add(tf.keras.layers.Dropout(0.5))
44
+
45
+ # Output layer
46
+ model.add(tf.keras.layers.Dense(self.char_to_num.vocabulary_size() + 1, activation='softmax'))
47
+
48
+ return model
49
+
50
+ def predict(self, normalized_frames):
51
+ if normalized_frames is None or normalized_frames.shape[0] == 0:
52
+ return "No frames extracted from the video. Please ensure the video contains a clear view of the face and lips."
53
+
54
+ frames = tf.expand_dims(normalized_frames, axis=0) # Add batch dimension
55
+ yhat = self.model.predict(frames, verbose=0)
56
+
57
+ input_length = [yhat.shape[1]] # batch size of 1
58
+ decoded = tf.keras.backend.ctc_decode(yhat, input_length=input_length, greedy=True)[0][0].numpy()[0]
59
+
60
+ # Convert numerical predictions to characters
61
+ prediction = ''.join(
62
+ [
63
+ self.num_to_char(num).numpy().decode('utf-8')
64
+ for num in decoded
65
+ if num != -1
66
+ ]
67
+ )
68
+
69
+ return prediction
70
+
71
+ def predict_from_video(video_path=None, frames=None, model=None):
72
+ """
73
+ Predicts the text from a video file or webcam frames using the provided model.
74
+
75
+ Args:
76
+ video_path (str, optional): Path to the video file. Defaults to None.
77
+ frames (List[np.ndarray], optional): List of frames from webcam. Defaults to None.
78
+ model (LipReadingModel, optional): An instance of the LipReadingModel. Defaults to None.
79
+
80
+ Returns:
81
+ str: Predicted text.
82
+ """
83
+ if model is None:
84
+ model = LipReadingModel()
85
+
86
+ preprocessor = VideoPreprocessor()
87
+
88
+ if video_path:
89
+ # Preprocess video from file
90
+ normalized_frames = preprocessor.preprocess_video(video_path)
91
+ elif frames is not None:
92
+ # Preprocess frames from webcam
93
+ normalized_frames = preprocessor.preprocess_frames(frames)
94
+ else:
95
+ return "No video or frames provided for prediction."
96
+
97
+ prediction = model.predict(normalized_frames)
98
+ return prediction
preprocessing.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # preprocessing.py
2
+
3
+ import cv2
4
+ import mediapipe as mp
5
+ import tensorflow as tf
6
+
7
+ class VideoPreprocessor:
8
+ def __init__(self):
9
+ self.mp_face_mesh = mp.solutions.face_mesh
10
+ # Indices for lip landmarks
11
+ self.UPPER_LIP_INDICES = [61, 185, 40, 39, 37, 0, 267, 269, 270, 409, 291]
12
+ self.LOWER_LIP_INDICES = [146, 91, 181, 84, 17, 314, 405, 321, 375, 291]
13
+ self.LIP_INDICES = self.UPPER_LIP_INDICES + self.LOWER_LIP_INDICES
14
+
15
+ def preprocess_video(self, video_path):
16
+ cap = cv2.VideoCapture(video_path)
17
+ frames = []
18
+
19
+ # Utilize mediapipe's GPU acceleration if available
20
+ with self.mp_face_mesh.FaceMesh(
21
+ static_image_mode=False,
22
+ max_num_faces=1,
23
+ refine_landmarks=True,
24
+ min_detection_confidence=0.5,
25
+ min_tracking_confidence=0.5
26
+ ) as face_mesh:
27
+ while cap.isOpened():
28
+ ret, frame = cap.read()
29
+ if not ret:
30
+ break
31
+
32
+ # Convert the BGR image to RGB
33
+ rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
34
+
35
+ # Process the frame and get the facial landmarks
36
+ results = face_mesh.process(rgb_frame)
37
+
38
+ if results.multi_face_landmarks:
39
+ # Get the landmarks for the first face
40
+ face_landmarks = results.multi_face_landmarks[0]
41
+
42
+ try:
43
+ # Extract lip landmarks
44
+ lip_landmarks = [face_landmarks.landmark[i] for i in self.LIP_INDICES]
45
+
46
+ # Extract bounding box around the lips
47
+ h, w, _ = frame.shape
48
+ x_coords = [int(landmark.x * w) for landmark in lip_landmarks]
49
+ y_coords = [int(landmark.y * h) for landmark in lip_landmarks]
50
+
51
+ x_min, x_max = max(0, min(x_coords)), min(w, max(x_coords))
52
+ y_min, y_max = max(0, min(y_coords)), min(h, max(y_coords))
53
+
54
+ if x_max > x_min and y_max > y_min:
55
+ # Crop the lip region
56
+ lip_frame = frame[y_min:y_max, x_min:x_max]
57
+
58
+ # Resize to 160x160
59
+ lip_frame_resized = cv2.resize(lip_frame, (75, 75))
60
+
61
+ # Convert to grayscale using TensorFlow
62
+ lip_frame_gray = tf.image.rgb_to_grayscale(lip_frame_resized)
63
+
64
+ frames.append(lip_frame_gray)
65
+ except Exception as e:
66
+ print(f"Error processing frame: {e}")
67
+ continue # Skip this frame
68
+ else:
69
+ print("No face landmarks detected in frame.")
70
+
71
+ cap.release()
72
+
73
+ if not frames:
74
+ print("No frames extracted during preprocessing.")
75
+ return None # Return None to indicate failure
76
+
77
+ # Stack frames into a tensor
78
+ frames = tf.stack(frames)
79
+
80
+ # Adjust frames to match expected input length
81
+ desired_num_frames = 75
82
+ num_frames = frames.shape[0]
83
+ if num_frames < desired_num_frames:
84
+ # Pad frames with zeros
85
+ padding = tf.zeros((desired_num_frames - num_frames, 75, 75, 1), dtype=tf.float32)
86
+ frames = tf.concat([frames, padding], axis=0)
87
+ elif num_frames > desired_num_frames:
88
+ # Truncate frames to desired_num_frames
89
+ frames = frames[:desired_num_frames]
90
+
91
+ # Normalize the frames
92
+ mean = tf.math.reduce_mean(frames)
93
+ std = tf.math.reduce_std(tf.cast(frames, tf.float32))
94
+ normalized_frames = tf.cast((frames - mean), tf.float32) / std
95
+
96
+ return normalized_frames # Return TensorFlow tensor
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ tensorflow
2
+ numpy
3
+ imageio
4
+ moviepy
5
+ mediapipe
6
+ opencv-python
7
+ keras
8
+ matplotlib
9
+ gradio