Natwar commited on
Commit
887b239
·
verified ·
1 Parent(s): 5133a2d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +328 -98
app.py CHANGED
@@ -15,125 +15,355 @@ def install_package(package, version=None):
15
  print(f"Failed to install {package_spec}: {e}")
16
  raise
17
 
18
- def ensure_package(package, version=None):
19
- try:
20
- if version:
21
- pkg_resources.require(f"{package}=={version}")
22
- else:
23
- importlib.import_module(package)
24
- print(f"{package} is already installed with the correct version.")
25
- except (ImportError, pkg_resources.VersionConflict, pkg_resources.DistributionNotFound) as e:
26
- print(f"Package requirement failed: {e}")
27
- install_package(package, version)
28
 
29
- if not os.path.exists("/.dockerenv") and not os.path.exists("/kaggle"):
30
- print("Setting up environment...")
31
- ensure_package("numpy", "1.23.5")
32
- ensure_package("protobuf", "3.20.3")
33
- ensure_package("tensorflow", "2.10.0")
34
- for pkg in ["gradio", "opencv-python-headless", "pillow"]:
35
- ensure_package(pkg)
36
 
37
- # Main App
38
- import gradio as gr
39
  import cv2
40
  import numpy as np
41
- from tensorflow.keras.models import load_model
 
 
42
  from tensorflow.keras.preprocessing.image import img_to_array
43
  from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
 
 
 
44
  import tempfile
45
 
46
- faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_alt2.xml")
47
- model = load_model("mask_recog.h5")
48
-
49
- def detect_blur(image):
50
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
51
- variance = cv2.Laplacian(gray, cv2.CV_64F).var()
52
- return "Blurry" if variance < 100 else "Clear"
53
-
54
- def detect_beard(image):
55
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
56
- h = gray.shape[0]
57
- lower_face = gray[h//2:]
58
- variance = np.var(lower_face)
59
- return "Beard" if variance > 400 else "No Beard"
60
 
61
- def analyze_frame(frame):
62
- results = []
63
- gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
64
- faces = faceCascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(60, 60))
65
 
66
- for (x, y, w, h) in faces:
67
- face = frame[y:y+h, x:x+w]
68
 
69
- face_input = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
70
- face_input = cv2.resize(face_input, (224, 224))
71
- face_input = img_to_array(face_input)
72
- face_input = np.expand_dims(face_input, axis=0)
73
- face_input = preprocess_input(face_input)
74
- (mask, withoutMask) = model.predict(face_input)[0]
75
- label = "Mask" if mask > withoutMask else "No Mask"
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- beard_status = detect_beard(face)
78
- blur_status = detect_blur(face)
 
79
 
80
- color = (0, 255, 0) if label == "Mask" else (0, 0, 255)
81
- cv2.rectangle(frame, (x, y), (x+w, y+h), color, 2)
82
- cv2.putText(frame, f"{label}, {beard_status}, {blur_status}", (x, y - 10),
83
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
 
84
 
85
- results.append(f"Face at ({x}, {y}): {label}, {beard_status}, {blur_status}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- return frame, "\n".join(results) if results else "No faces detected."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
- def analyze_image(image):
90
- image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
91
- annotated, report = analyze_frame(image)
92
- return cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), report
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- def analyze_video(video_path):
 
 
 
 
 
 
 
 
 
95
  cap = cv2.VideoCapture(video_path)
96
- frame_width = int(cap.get(3))
97
- frame_height = int(cap.get(4))
98
- temp_output = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
99
- out = cv2.VideoWriter(temp_output.name, cv2.VideoWriter_fourcc(*'mp4v'), 10, (frame_width, frame_height))
100
-
101
- full_report = []
102
-
103
- while True:
104
- ret, frame = cap.read()
105
- if not ret:
106
- break
107
- annotated, report = analyze_frame(frame)
108
- out.write(annotated)
109
- full_report.extend(report.split('\n'))
110
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  cap.release()
112
  out.release()
 
 
 
 
 
 
113
 
114
- report_text = "\n".join(full_report)
115
- return temp_output.name, report_text
116
-
117
- def handle_input(file):
118
- if file.name.endswith(".mp4") or file.name.endswith(".avi"):
119
- video_path, report = analyze_video(file.name)
120
- return gr.update(value=None), gr.update(value=video_path), gr.update(value=report)
121
- else:
122
- img = cv2.imread(file.name)
123
- annotated, report = analyze_frame(img)
124
- return cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), gr.update(value=None), gr.update(value=report)
125
-
126
- def launch_app():
127
- with gr.Blocks() as demo:
128
- gr.Markdown("## Mask, Beard, and Blur Detector")
129
- file_input = gr.File(label="Upload Image or Video", file_types=[".jpg", ".png", ".jpeg", ".mp4", ".avi"])
130
- output_video = gr.Video(label="Annotated Video Output")
131
- output_image = gr.Image(label="Annotated Image Output")
132
- report_box = gr.Textbox(label="Analysis Report", lines=10)
133
-
134
- file_input.change(fn=handle_input, inputs=[file_input], outputs=[output_image, output_video, report_box])
135
 
136
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  if __name__ == "__main__":
139
- launch_app()
 
15
  print(f"Failed to install {package_spec}: {e}")
16
  raise
17
 
18
+ # Check and install required packages
19
+ required_packages = {
20
+ "opencv-python": None,
21
+ "numpy": None,
22
+ "gradio": None,
23
+ "mediapipe": None,
24
+ "tensorflow": None
25
+ }
 
 
26
 
27
+ installed_packages = {pkg.key for pkg in pkg_resources.working_set}
28
+ for package, version in required_packages.items():
29
+ if package not in installed_packages:
30
+ install_package(package, version)
 
 
 
31
 
32
+ # Now import all necessary packages
 
33
  import cv2
34
  import numpy as np
35
+ import gradio as gr
36
+ import mediapipe as mp
37
+ import tensorflow as tf
38
  from tensorflow.keras.preprocessing.image import img_to_array
39
  from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
40
+ import time
41
+ import os
42
+ from pathlib import Path
43
  import tempfile
44
 
45
+ # Set TensorFlow to use memory growth to avoid consuming all GPU memory
46
+ physical_devices = tf.config.list_physical_devices('GPU')
47
+ if physical_devices:
48
+ try:
49
+ for device in physical_devices:
50
+ tf.config.experimental.set_memory_growth(device, True)
51
+ except:
52
+ print("Memory growth setting failed")
 
 
 
 
 
 
53
 
54
+ # Load face detection from MediaPipe (much faster than Haar cascades)
55
+ mp_face_detection = mp.solutions.face_detection
56
+ mp_drawing = mp.solutions.drawing_utils
 
57
 
58
+ # Global variable for model
59
+ mask_model = None
60
 
61
+ def load_mask_model(model_path="mask_recog.h5"):
62
+ """Load the mask detection model once and cache it"""
63
+ global mask_model
64
+ if mask_model is None:
65
+ try:
66
+ # Use TensorFlow Lite if available for better performance
67
+ if os.path.exists("mask_recog.tflite"):
68
+ interpreter = tf.lite.Interpreter(model_path="mask_recog.tflite")
69
+ interpreter.allocate_tensors()
70
+ mask_model = interpreter
71
+ print("Loaded TFLite model")
72
+ return True
73
+ # Otherwise use standard TF model
74
+ mask_model = tf.keras.models.load_model(model_path)
75
+ print(f"Loaded {model_path} successfully")
76
+ return True
77
+ except Exception as e:
78
+ print(f"Error loading model: {e}")
79
+ return False
80
+ return True
81
 
82
+ def variance_of_laplacian(image):
83
+ """Compute the variance of the Laplacian of the image (a measure of blur)."""
84
+ return cv2.Laplacian(image, cv2.CV_64F).var()
85
 
86
+ def is_image_blurry(image, threshold=100.0):
87
+ """Determine if an image is blurry based on Laplacian variance"""
88
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
89
+ blur_score = variance_of_laplacian(gray)
90
+ return blur_score < threshold, blur_score
91
 
92
+ def detect_beard(face_image):
93
+ """Detect beard using texture analysis of lower face region"""
94
+ h, w = face_image.shape[:2]
95
+ lower_face = face_image[h//2:, :]
96
+
97
+ if lower_face.size == 0:
98
+ return False, 0
99
+
100
+ # Convert to grayscale for texture analysis
101
+ gray = cv2.cvtColor(lower_face, cv2.COLOR_BGR2GRAY)
102
+
103
+ # Calculate standard deviation (texture measure)
104
+ std_val = np.std(gray)
105
+
106
+ # Calculate gradient magnitude (another texture measure)
107
+ sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
108
+ sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
109
+ gradient_magnitude = np.sqrt(sobelx**2 + sobely**2)
110
+ gradient_mean = np.mean(gradient_magnitude)
111
+
112
+ # Combined score
113
+ beard_score = std_val * 0.5 + gradient_mean * 0.5
114
+ threshold = 45 # Adjustable threshold
115
+
116
+ return beard_score > threshold, beard_score
117
 
118
+ def predict_mask(face_img):
119
+ """Predict if a face is wearing a mask"""
120
+ global mask_model
121
+
122
+ # Resize and preprocess
123
+ face_rgb = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
124
+ face_resized = cv2.resize(face_rgb, (224, 224))
125
+ face_array = img_to_array(face_resized)
126
+ face_array = np.expand_dims(face_array, axis=0)
127
+ face_array = preprocess_input(face_array)
128
+
129
+ # Check if we're using TFLite
130
+ if isinstance(mask_model, tf.lite.Interpreter):
131
+ # Get input and output tensors
132
+ input_details = mask_model.get_input_details()
133
+ output_details = mask_model.get_output_details()
134
+
135
+ # Set input tensor
136
+ mask_model.set_tensor(input_details[0]['index'], face_array.astype(np.float32))
137
+
138
+ # Run inference
139
+ mask_model.invoke()
140
+
141
+ # Get output
142
+ preds = mask_model.get_tensor(output_details[0]['index'])
143
+ else:
144
+ # Use standard TF model
145
+ preds = mask_model.predict(face_array, verbose=0)
146
+
147
+ mask_prob = float(preds[0][0])
148
+ return mask_prob > 0.5, mask_prob
149
 
150
+ def analyze_frame(frame, face_detector, min_detection_confidence=0.5, blur_threshold=100):
151
+ """
152
+ Analyze a single frame for faces, masks, beards, and blur
153
+ """
154
+ # Make a copy to avoid modifying the original
155
+ annotated_frame = frame.copy()
156
+ h, w = frame.shape[:2]
157
+
158
+ # Convert to RGB for MediaPipe
159
+ rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
160
+
161
+ # Detect faces
162
+ results = face_detector.process(rgb_frame)
163
+
164
+ # Blur detection for the whole frame
165
+ is_blurry, blur_score = is_image_blurry(frame, blur_threshold)
166
+ blur_status = "Blurry" if is_blurry else "Clear"
167
+ blur_color = (0, 0, 255) if is_blurry else (0, 255, 0)
168
+
169
+ # Add blur information
170
+ cv2.putText(annotated_frame, f"Video Quality: {blur_status} ({blur_score:.1f})",
171
+ (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, blur_color, 2)
172
+
173
+ face_count = 0
174
+
175
+ # Process detected faces
176
+ if results.detections:
177
+ for detection in results.detections:
178
+ # Get face bounding box
179
+ bbox = detection.location_data.relative_bounding_box
180
+ x = int(bbox.xmin * w)
181
+ y = int(bbox.ymin * h)
182
+ face_width = int(bbox.width * w)
183
+ face_height = int(bbox.height * h)
184
+
185
+ # Ensure coordinates are within frame boundaries
186
+ x = max(0, x)
187
+ y = max(0, y)
188
+ right = min(w, x + face_width)
189
+ bottom = min(h, y + face_height)
190
+
191
+ # Extract face
192
+ face_img = frame[y:bottom, x:right]
193
+ if face_img.size == 0:
194
+ continue
195
+
196
+ face_count += 1
197
+
198
+ # Predict mask
199
+ has_mask, mask_prob = predict_mask(face_img)
200
+ mask_status = "Mask" if has_mask else "No Mask"
201
+ mask_color = (0, 255, 0) if has_mask else (0, 0, 255)
202
+
203
+ # Draw face bounding box
204
+ cv2.rectangle(annotated_frame, (x, y), (right, bottom), mask_color, 2)
205
+
206
+ # Add mask information
207
+ cv2.putText(annotated_frame, f"{mask_status}: {mask_prob:.2f}",
208
+ (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, mask_color, 2)
209
+
210
+ # Detect beard only if no mask
211
+ if not has_mask:
212
+ has_beard, beard_score = detect_beard(face_img)
213
+ beard_status = "Beard" if has_beard else "No Beard"
214
+ cv2.putText(annotated_frame, f"{beard_status}: {beard_score:.1f}",
215
+ (x, bottom + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 165, 0), 2)
216
+
217
+ # Add face count
218
+ cv2.putText(annotated_frame, f"Faces: {face_count}",
219
+ (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
220
+
221
+ return annotated_frame
222
 
223
+ def process_video(video_path, progress=gr.Progress(), min_detection_confidence=0.5, blur_threshold=100):
224
+ """Process video file and return the path to the processed video"""
225
+ if not load_mask_model():
226
+ return None, "Error: Could not load the mask detection model."
227
+
228
+ # Create a temporary file for the output
229
+ with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as temp_file:
230
+ output_path = temp_file.name
231
+
232
+ # Initialize video capture
233
  cap = cv2.VideoCapture(video_path)
234
+ if not cap.isOpened():
235
+ return None, "Error: Could not open video file."
236
+
237
+ # Get video properties
238
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
239
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
240
+ fps = cap.get(cv2.CAP_PROP_FPS)
241
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
242
+
243
+ # Initialize video writer with H.264 codec
244
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
245
+ out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
246
+
247
+ # Initialize face detector
248
+ with mp_face_detection.FaceDetection(
249
+ model_selection=1, # 0 for short-range, 1 for full-range detection
250
+ min_detection_confidence=min_detection_confidence
251
+ ) as face_detector:
252
+
253
+ # Process frames
254
+ frame_count = 0
255
+ start_time = time.time()
256
+
257
+ while True:
258
+ ret, frame = cap.read()
259
+ if not ret:
260
+ break
261
+
262
+ # Update progress
263
+ progress((frame_count + 1) / total_frames, "Processing video...")
264
+
265
+ # Process frame
266
+ annotated_frame = analyze_frame(frame, face_detector, min_detection_confidence, blur_threshold)
267
+
268
+ # Write to output video
269
+ out.write(annotated_frame)
270
+
271
+ frame_count += 1
272
+
273
+ # Clean up
274
  cap.release()
275
  out.release()
276
+
277
+ # Calculate processing speed
278
+ elapsed_time = time.time() - start_time
279
+ processing_speed = frame_count / elapsed_time if elapsed_time > 0 else 0
280
+
281
+ return output_path, f"Processed {frame_count} frames in {elapsed_time:.1f} seconds ({processing_speed:.1f} FPS)"
282
 
283
+ def process_webcam_frame(frame, min_detection_confidence, blur_threshold):
284
+ """Process a single webcam frame"""
285
+ if not load_mask_model():
286
+ return None
287
+
288
+ # Initialize face detector for each frame in webcam mode
289
+ # This is less efficient but necessary for the Gradio webcam interface
290
+ with mp_face_detection.FaceDetection(
291
+ model_selection=1,
292
+ min_detection_confidence=min_detection_confidence
293
+ ) as face_detector:
294
+ return analyze_frame(frame, face_detector, min_detection_confidence, blur_threshold)
 
 
 
 
 
 
 
 
 
295
 
296
+ # Create Gradio interface
297
+ with gr.Blocks(title="Enhanced Face Analysis System") as demo:
298
+ gr.Markdown("""
299
+ # Advanced Face Analysis System
300
+
301
+ This app detects and analyzes faces in videos to determine:
302
+
303
+ * 😷 If a person is wearing a **mask**
304
+ * 🧔 If a person has a **beard** (when no mask is present)
305
+ * 🎥 The **quality/blurriness** of the video
306
+
307
+ Upload a video or use your webcam for real-time analysis.
308
+ """)
309
+
310
+ with gr.Tabs():
311
+ with gr.TabItem("Video Upload"):
312
+ with gr.Row():
313
+ with gr.Column(scale=1):
314
+ video_input = gr.Video(label="Upload Video")
315
+ with gr.Row():
316
+ min_confidence = gr.Slider(
317
+ minimum=0.1, maximum=0.9, value=0.5, step=0.1,
318
+ label="Face Detection Confidence"
319
+ )
320
+ blur_threshold = gr.Slider(
321
+ minimum=50, maximum=200, value=100, step=10,
322
+ label="Blur Threshold"
323
+ )
324
+ process_btn = gr.Button("Process Video")
325
+ status_text = gr.Textbox(label="Processing Status")
326
+
327
+ with gr.Column(scale=1):
328
+ video_output = gr.Video(label="Processed Video")
329
+
330
+ process_btn.click(
331
+ fn=process_video,
332
+ inputs=[video_input, min_confidence, blur_threshold],
333
+ outputs=[video_output, status_text]
334
+ )
335
+
336
+ with gr.TabItem("Webcam (Real-time)"):
337
+ with gr.Row():
338
+ with gr.Column(scale=1):
339
+ webcam_confidence = gr.Slider(
340
+ minimum=0.1, maximum=0.9, value=0.5, step=0.1,
341
+ label="Face Detection Confidence"
342
+ )
343
+ webcam_blur = gr.Slider(
344
+ minimum=50, maximum=200, value=100, step=10,
345
+ label="Blur Threshold"
346
+ )
347
+
348
+ with gr.Column(scale=2):
349
+ webcam = gr.Image(sources=["webcam"], streaming=True, label="Webcam Feed")
350
+
351
+ webcam.stream(
352
+ fn=process_webcam_frame,
353
+ inputs=[webcam_confidence, webcam_blur]
354
+ )
355
+
356
+ gr.Markdown("""
357
+ ### How to Use
358
+
359
+ 1. **Video Upload Tab**: Upload a video file and click "Process Video." Adjust sliders to tune detection parameters.
360
+ 2. **Webcam Tab**: Allow camera access for real-time analysis.
361
+
362
+ ### Tips
363
+
364
+ - Higher face detection confidence gives fewer false positives but might miss some faces
365
+ - Higher blur threshold means more tolerance for blurry video
366
+ """)
367
 
368
  if __name__ == "__main__":
369
+ demo.launch()