ratneshpasi03 commited on
Commit
d701140
·
verified ·
1 Parent(s): 88819ce

Create 3_Skin_Tone_Bias.py

Browse files
Files changed (1) hide show
  1. pages/3_Skin_Tone_Bias.py +187 -0
pages/3_Skin_Tone_Bias.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image, ImageDraw, ImageOps
3
+ import cv2
4
+ import numpy as np
5
+ import pandas as pd
6
+ import dlib
7
+ import requests
8
+ import os
9
+ from io import BytesIO
10
+ from facenet_pytorch import MTCNN
11
+
12
+ # ---------------------------------------------------------------
13
+ # Cache Models
14
+ @st.cache_resource
15
+ def load_mtcnn_model():
16
+ return MTCNN(keep_all=True)
17
+
18
+ @st.cache_resource
19
+ def load_dlib_detector():
20
+ return dlib.get_frontal_face_detector()
21
+
22
+ # ---------------------------------------------------------------
23
+ # Face Detection Functions
24
+ def detect_faces_opencv(image):
25
+ cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
26
+ gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
27
+ face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
28
+ faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
29
+
30
+ result = []
31
+ for idx, (x, y, w, h) in enumerate(faces):
32
+ cv2.rectangle(cv_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
33
+ cv2.putText(cv_image, f"Face {idx+1}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
34
+ result.append({
35
+ "Face ID": f"Face {idx+1}",
36
+ "X": x,
37
+ "Y": y,
38
+ "W": w,
39
+ "H": h,
40
+ "Confidence": "N/A"
41
+ })
42
+
43
+ cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
44
+ return cv_image, result
45
+
46
+ def detect_faces_mtcnn(image):
47
+ mtcnn = load_mtcnn_model()
48
+ boxes, probs = mtcnn.detect(image, landmarks=False)
49
+ result = []
50
+ draw_image = image.copy()
51
+ draw = ImageDraw.Draw(draw_image)
52
+
53
+ if boxes is not None:
54
+ for idx, (box, prob) in enumerate(zip(boxes, probs)):
55
+ x1, y1, x2, y2 = [int(v) for v in box]
56
+ draw.rectangle([x1, y1, x2, y2], outline="blue", width=2)
57
+ draw.text((x1, y1 - 15), f"Face {idx+1}", fill="blue")
58
+ result.append({
59
+ "Face ID": f"Face {idx+1}",
60
+ "X": x1,
61
+ "Y": y1,
62
+ "W": x2 - x1,
63
+ "H": y2 - y1,
64
+ "Confidence": f"{prob:.2f}"
65
+ })
66
+ return draw_image, result
67
+
68
+ def detect_faces_dlib(image):
69
+ dlib_detector = load_dlib_detector()
70
+ cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
71
+ dets, scores, _ = dlib_detector.run(cv_image, 1, -1)
72
+
73
+ result = []
74
+ draw_image = image.copy()
75
+ draw = ImageDraw.Draw(draw_image)
76
+
77
+ for idx, (d, score) in enumerate(zip(dets, scores)):
78
+ if score > 0.0:
79
+ x1, y1, x2, y2 = d.left(), d.top(), d.right(), d.bottom()
80
+ draw.rectangle([x1, y1, x2, y2], outline="red", width=2)
81
+ draw.text((x1, y1 - 15), f"Face {idx+1}", fill="red")
82
+ result.append({
83
+ "Face ID": f"Face {idx+1}",
84
+ "X": x1,
85
+ "Y": y1,
86
+ "W": x2 - x1,
87
+ "H": y2 - y1,
88
+ "Confidence": f"{score:.2f}"
89
+ })
90
+ return draw_image, result
91
+
92
+ # ---------------------------------------------------------------
93
+ # Face Cropping
94
+ def get_face_crops(image, boxes):
95
+ faces = []
96
+ for box in boxes:
97
+ x, y, w, h = box["X"], box["Y"], box["W"], box["H"]
98
+ cropped_face = image.crop((x, y, x + w, y + h))
99
+ cropped_face = ImageOps.fit(cropped_face, (80, 80))
100
+ faces.append(cropped_face)
101
+ return faces
102
+
103
+ # ---------------------------------------------------------------
104
+ # Main Page UI
105
+ st.header("👤 Skin Tone Bias in Face Detection")
106
+
107
+ st.markdown(
108
+ """
109
+ This page explores potential **skin tone bias** in face detection models like **Dlib**, **MTCNN**, and **OpenCV**.
110
+ Upload group photos with varying skin tones to observe detection differences.
111
+ """
112
+ )
113
+
114
+ model_choice = st.selectbox("Select a Face Detection Model", ["Dlib", "MTCNN", "OpenCV"])
115
+ input_method = st.selectbox("Select Input Method", ["Default Images", "Upload Image", "Use Image URL"])
116
+ image = None
117
+
118
+ # Image Loading
119
+ if input_method == "Upload Image":
120
+ uploaded_file = st.file_uploader("Upload a group image", type=["jpg", "jpeg", "png"])
121
+ if uploaded_file:
122
+ image = Image.open(uploaded_file).convert("RGB")
123
+
124
+ elif input_method == "Use Image URL":
125
+ image_url = st.text_input("Paste an image URL")
126
+ if image_url:
127
+ try:
128
+ response = requests.get(image_url)
129
+ image = Image.open(BytesIO(response.content)).convert("RGB")
130
+ except Exception:
131
+ st.error("Couldn't load image from the provided URL.")
132
+
133
+ elif input_method == "Default Images":
134
+ default_path = "default_images/skin_tone_bias"
135
+ if os.path.exists(default_path):
136
+ default_images = sorted([f for f in os.listdir(default_path) if f.lower().endswith((".jpg", ".jpeg", ".png"))])
137
+ if default_images:
138
+ selected = st.selectbox("Choose a default image", default_images)
139
+ image = Image.open(os.path.join(default_path, selected)).convert("RGB")
140
+ else:
141
+ st.warning("No images found in 'default_images/skin_tone_bias'.")
142
+ else:
143
+ st.warning("Folder 'default_images/skin_tone_bias' does not exist.")
144
+
145
+ # Image Preview
146
+ if image is not None:
147
+ st.image(image, caption="Input Image", width=400)
148
+
149
+ # Detection
150
+ if st.button("🔍 Detect Faces"):
151
+ if image is None:
152
+ st.warning("⚠️ Please provide an image before detection.")
153
+ else:
154
+ with st.spinner(f"Detecting faces using {model_choice}..."):
155
+ if model_choice == "OpenCV":
156
+ draw_image, result = detect_faces_opencv(image)
157
+ elif model_choice == "MTCNN":
158
+ draw_image, result = detect_faces_mtcnn(image)
159
+ elif model_choice == "Dlib":
160
+ draw_image, result = detect_faces_dlib(image)
161
+
162
+ if result:
163
+ st.success(f"✅ Detected {len(result)} face(s) with {model_choice}")
164
+ st.image(draw_image, caption=f"{model_choice} Detection Output", use_container_width=True)
165
+
166
+ face_images = get_face_crops(image, result)
167
+
168
+ st.markdown("### 👤 Cropped Face Previews")
169
+ num_faces = len(face_images)
170
+ cols_per_row = 3
171
+
172
+ for i in range(0, num_faces, cols_per_row):
173
+ cols = st.columns(cols_per_row, gap="large")
174
+ for j in range(cols_per_row):
175
+ if i + j < num_faces:
176
+ with cols[j]:
177
+ st.image(face_images[i + j], use_container_width=True)
178
+ st.markdown(
179
+ f"<div style='text-align: center;'>"
180
+ f"<b>{result[i + j]['Face ID']}</b><br>"
181
+ f"Confidence: <code>{result[i + j]['Confidence']}</code>"
182
+ f"</div>",
183
+ unsafe_allow_html=True
184
+ )
185
+ else:
186
+ st.warning(f"⚠️ No faces detected by {model_choice}. Try a different model or image.")
187
+