jarondon82 commited on
Commit
e6cd628
·
1 Parent(s): a46f7ec

Fix numpy array truth value error by redirecting face_labeling.py to simplified version

Browse files
Files changed (1) hide show
  1. utils/face_labeling.py +26 -473
utils/face_labeling.py CHANGED
@@ -1,493 +1,46 @@
1
  """
2
- Module for face labeling of detected faces.
3
 
4
- This module contains functions to display, select, and label detected faces in images.
 
5
  """
6
  import streamlit as st
7
  import numpy as np
8
- import cv2
9
- from typing import List, Dict, Tuple, Any, Set
10
- from utils.face_visualization import (
11
- initialize_face_visualization_state,
12
- draw_faces_with_state,
13
- face_control_panel,
14
- on_face_select,
15
- on_face_remove
16
- )
17
 
18
- def draw_labeled_faces(image: np.ndarray, faces: List[Tuple[int, int, int, int]],
19
- max_faces: int = 5) -> np.ndarray:
20
- """
21
- Draws rectangles and labels on detected faces.
22
-
23
- Args:
24
- image: Image in numpy array format (RGB)
25
- faces: List of tuples (x, y, w, h) with face coordinates
26
- max_faces: Maximum number of faces to display
27
-
28
- Returns:
29
- Image with labeled faces
30
- """
31
- # Work with a copy to avoid modifying the original
32
- labeled_img = image.copy()
33
-
34
- # Limit to max_faces faces
35
- faces_to_draw = faces[:max_faces] if len(faces) > max_faces else faces
36
-
37
- # Draw each face
38
- for i, (x, y, w, h) in enumerate(faces_to_draw):
39
- # Skip faces marked as false positives
40
- face_key = f"face_{i}"
41
- if "removed_faces" in st.session_state and face_key in st.session_state.removed_faces:
42
- continue
43
-
44
- # Draw green rectangle
45
- cv2.rectangle(labeled_img, (x, y), (x+w, y+h), (0, 255, 0), 2)
46
-
47
- # Add numbered label
48
- label = f"Face {i+1}"
49
- cv2.putText(labeled_img, label, (x, y-10),
50
- cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
51
-
52
- return labeled_img
53
 
54
- def extract_face_thumbnails(image: np.ndarray, faces: List[Tuple[int, int, int, int]],
55
- max_faces: int = 5) -> Dict[int, np.ndarray]:
56
  """
57
- Extracts thumbnails of detected faces.
58
 
59
  Args:
60
  image: Image in numpy array format (RGB)
61
- faces: List of tuples (x, y, w, h) with face coordinates
62
- max_faces: Maximum number of faces to process
63
 
64
  Returns:
65
- Dictionary with face index and its cropped image
66
  """
67
- thumbnails = {}
68
-
69
- # Limit to max_faces faces
70
- faces_to_extract = faces[:max_faces] if len(faces) > max_faces else faces
71
-
72
- # Extract each thumbnail
73
- for i, (x, y, w, h) in enumerate(faces_to_extract):
74
- # Apply a small margin around the face if possible
75
- margin = int(min(w, h) * 0.1) # 10% margin
76
-
77
- # Ensure we don't go out of the image bounds
78
- img_h, img_w = image.shape[:2]
79
- x_start = max(0, x - margin)
80
- y_start = max(0, y - margin)
81
- x_end = min(img_w, x + w + margin)
82
- y_end = min(img_h, y + h + margin)
83
-
84
- # Extract the thumbnail with margin
85
- face_thumbnail = image[y_start:y_end, x_start:x_end]
86
- thumbnails[i] = face_thumbnail
87
-
88
- return thumbnails
89
 
90
- def show_face_selection_ui(image: np.ndarray, faces: List[Tuple[int, int, int, int]],
91
- max_faces: int = 5) -> Dict[str, Any]:
92
- """
93
- Displays the interface for selecting and labeling faces.
94
-
95
- Args:
96
- image: Image in numpy array format (RGB)
97
- faces: List of tuples (x, y, w, h) with face coordinates
98
- max_faces: Maximum number of faces to process
99
-
100
- Returns:
101
- Dictionary with information about labeled faces
102
- """
103
- # Initialize visualization state
104
- initialize_face_visualization_state()
105
-
106
- # Limit to max_faces faces
107
- faces_to_show = faces[:max_faces] if len(faces) > max_faces else faces
108
- num_faces = len(faces_to_show)
109
-
110
- # Show face control panel
111
- display_mode = face_control_panel()
112
-
113
- # Create dynamic image with current state
114
- labeled_image = draw_faces_with_state(image, faces_to_show)
115
-
116
- # Display the image with labeled faces
117
- st.image(labeled_image, caption="Detected Faces", use_column_width=True)
118
-
119
- # Informative message
120
- if num_faces > 0:
121
- st.success(f"{num_faces} face(s) detected in the image")
122
-
123
- # Extract thumbnails to display alongside options
124
- thumbnails = extract_face_thumbnails(image, faces_to_show)
125
-
126
- # Section for selecting and labeling faces
127
- st.subheader("Select and label the faces")
128
-
129
- # Radio buttons for face selection
130
- face_options = [f"Face {i+1}" for i in range(num_faces)]
131
- current_face_idx = st.radio(
132
- "Select a face to label:",
133
- range(num_faces),
134
- format_func=lambda x: face_options[x],
135
- horizontal=True
136
- )
137
-
138
- # Initialize face key and selection state
139
- face_key = f"face_{current_face_idx}"
140
- if face_key not in st.session_state.selected_faces:
141
- st.session_state.selected_faces[face_key] = True
142
-
143
- # Visual separator
144
- st.markdown("---")
145
-
146
- # Create layout for the selected face
147
- col1, col2 = st.columns([1, 2])
148
-
149
- with col1:
150
- # Show thumbnail of selected face
151
- if current_face_idx in thumbnails:
152
- st.image(thumbnails[current_face_idx], caption=f"Face {current_face_idx+1}", width=150)
153
-
154
- with col2:
155
- # Check if this face is marked as removed
156
- is_removed = face_key in st.session_state.removed_faces
157
-
158
- if is_removed:
159
- st.warning("This face has been marked as a wrong detection.")
160
- if st.button("Restore this face", key=f"restore_{face_key}"):
161
- # Remove from the set of removed faces
162
- st.session_state.removed_faces.remove(face_key)
163
- st.session_state.selected_faces[face_key] = True
164
- else:
165
- # Face selection - renamed for clarity
166
- selected = st.checkbox(
167
- "Include in analysis",
168
- key=f"select_{current_face_idx}",
169
- value=st.session_state.selected_faces.get(face_key, True),
170
- on_change=on_face_select,
171
- args=(current_face_idx, not st.session_state.selected_faces.get(face_key, True))
172
- )
173
-
174
- # Update selection state
175
- st.session_state.selected_faces[face_key] = selected
176
-
177
- # Input field for name (only if face is selected)
178
- if selected:
179
- label = st.text_input(
180
- f"Name for Face {current_face_idx+1}:",
181
- value=st.session_state.face_labels.get(face_key, ""),
182
- key=f"label_{current_face_idx}"
183
- )
184
- st.session_state.face_labels[face_key] = label
185
-
186
- # Button to mark as false positive (renamed for clarity)
187
- if st.button("Mark as wrong detection", key=f"remove_{current_face_idx}"):
188
- on_face_remove(current_face_idx)
189
-
190
- # Visual separator
191
- st.markdown("---")
192
-
193
- # Options for unselected faces
194
- st.subheader("Options for unselected faces")
195
-
196
- non_selected_option = st.radio(
197
- "What to do with unselected faces?",
198
- ["Skip completely",
199
- "Group under a label",
200
- "Label as 'Unknown-N'"],
201
- key="non_selected_option"
202
- )
203
-
204
- # If grouping is selected, display field for group label
205
- group_label = ""
206
- if non_selected_option == "Group under a label":
207
- group_label = st.text_input("Label for the group:",
208
- value=st.session_state.get("group_label", "Group-Party"),
209
- key="group_label_input")
210
- st.session_state["group_label"] = group_label
211
-
212
- # Check if at least one face is labeled to enable analysis
213
- any_face_selected = any(st.session_state.selected_faces.values())
214
- all_selected_labeled = all(st.session_state.face_labels.get(k, "") != ""
215
- for k, v in st.session_state.selected_faces.items() if v)
216
-
217
- can_proceed = any_face_selected and all_selected_labeled
218
-
219
- # Display message if analysis cannot proceed
220
- if not can_proceed:
221
- if not any_face_selected:
222
- st.warning("You must select at least one face to continue.")
223
- elif not all_selected_labeled:
224
- st.warning("You must assign names to all selected faces.")
225
-
226
- # Add clear spacing at the end of the section to prevent overlap with next section
227
- st.markdown("<div style='height: 50px;'></div>", unsafe_allow_html=True)
228
-
229
- # Prepare result to return
230
- result = {
231
- "success": True,
232
- "num_faces": num_faces,
233
- "labeled_image": labeled_image,
234
- "can_proceed": can_proceed,
235
- "selected_faces": {},
236
- "non_selected_option": non_selected_option,
237
- "group_label": group_label,
238
- "faces_coordinates": {}
239
- }
240
-
241
- # Add information about selected faces
242
- for i, (x, y, w, h) in enumerate(faces_to_show):
243
- face_key = f"face_{i}"
244
- if st.session_state.selected_faces.get(face_key, False):
245
- label = st.session_state.face_labels.get(face_key, "")
246
- if label: # Only include if it has a label
247
- result["selected_faces"][face_key] = {
248
- "index": i,
249
- "label": label,
250
- "coordinates": (x, y, w, h)
251
- }
252
-
253
- # Save coordinates of all faces
254
- result["faces_coordinates"][face_key] = (x, y, w, h)
255
-
256
- return result
257
- else:
258
- st.warning("No faces detected in the image.")
259
- return {
260
- "success": False,
261
- "num_faces": 0,
262
- "message": "No faces detected in the image."
263
- }
264
 
265
- def prepare_faces_for_analysis(image: np.ndarray, selection_result: Dict[str, Any]) -> Dict[str, Any]:
266
- """
267
- Prepares the data of selected faces for emotion analysis.
268
-
269
- Args:
270
- image: Image in numpy array format (RGB)
271
- selection_result: Result of the show_face_selection_ui function
272
-
273
- Returns:
274
- Dictionary with processed data for analysis
275
- """
276
- # Get the faces from session state
277
- faces = st.session_state.get("detected_faces", [])
278
-
279
- # If no faces or not successful, return error
280
- if not faces or len(faces) == 0:
281
- return {
282
- "success": False,
283
- "can_proceed": False,
284
- "message": "No faces to analyze",
285
- "faces_to_analyze": []
286
- }
287
-
288
- # Extract information about faces
289
- selected_faces = {}
290
- for face_key, is_selected in st.session_state.selected_faces.items():
291
- if is_selected and face_key not in st.session_state.removed_faces:
292
- # Extract the face index from the key (e.g., "face_0" -> 0)
293
- idx = int(face_key.split('_')[1])
294
- if idx < len(faces):
295
- selected_faces[face_key] = {
296
- "index": idx,
297
- "label": st.session_state.face_labels.get(face_key, ""),
298
- "coordinates": faces[idx]
299
- }
300
-
301
- # Get options for unselected faces
302
- non_selected_option = st.session_state.get("non_selected_option", "Skip completely")
303
- group_label = st.session_state.get("group_label", "Group-Party")
304
-
305
- # List to store faces to analyze
306
- faces_to_analyze = []
307
-
308
- # Process selected faces
309
- for face_key, face_info in selected_faces.items():
310
- # Only include faces with labels
311
- if face_info.get("label", ""):
312
- faces_to_analyze.append({
313
- "key": face_key,
314
- "label": face_info.get("label", ""),
315
- "coordinates": face_info.get("coordinates", (0, 0, 0, 0)),
316
- "thumbnail": extract_thumbnail_from_coordinates(image, face_info.get("coordinates", (0, 0, 0, 0)))
317
- })
318
-
319
- # Process unselected faces based on the chosen option
320
- if non_selected_option != "Skip completely":
321
- # Identify unselected faces
322
- non_selected_faces = {}
323
- for i, coords in enumerate(faces):
324
- face_key = f"face_{i}"
325
- if (face_key not in selected_faces and
326
- face_key not in st.session_state.removed_faces and
327
- not st.session_state.selected_faces.get(face_key, True)):
328
- non_selected_faces[face_key] = coords
329
-
330
- # Process based on the chosen option
331
- if non_selected_option == "Group under a label":
332
- for i, (face_key, coords) in enumerate(non_selected_faces.items()):
333
- faces_to_analyze.append({
334
- "key": face_key,
335
- "label": group_label,
336
- "coordinates": coords,
337
- "thumbnail": extract_thumbnail_from_coordinates(image, coords),
338
- "is_grouped": True
339
- })
340
- elif non_selected_option == "Label as 'Unknown-N'":
341
- for i, (face_key, coords) in enumerate(non_selected_faces.items()):
342
- faces_to_analyze.append({
343
- "key": face_key,
344
- "label": f"Unknown-{i+1}",
345
- "coordinates": coords,
346
- "thumbnail": extract_thumbnail_from_coordinates(image, coords),
347
- "is_unknown": True
348
- })
349
-
350
- # Check if we can proceed with analysis
351
- can_proceed = len(faces_to_analyze) > 0
352
-
353
- return {
354
- "success": True,
355
- "can_proceed": can_proceed,
356
- "faces_to_analyze": faces_to_analyze
357
- }
358
 
359
- def extract_thumbnail_from_coordinates(image: np.ndarray, coordinates: Tuple[int, int, int, int]) -> np.ndarray:
360
  """
361
- Extracts a thumbnail from the image based on the provided coordinates.
362
-
363
- Args:
364
- image: Image in numpy array format (RGB)
365
- coordinates: Tuple (x, y, w, h) with face coordinates
366
-
367
- Returns:
368
- Cropped image (thumbnail)
369
  """
370
- x, y, w, h = coordinates
371
-
372
- # Apply a small margin around the face if possible
373
- margin = int(min(w, h) * 0.1) # 10% margin
374
-
375
- # Ensure we don't go out of the image bounds
376
- img_h, img_w = image.shape[:2]
377
- x_start = max(0, x - margin)
378
- y_start = max(0, y - margin)
379
- x_end = min(img_w, x + w + margin)
380
- y_end = min(img_h, y + h + margin)
381
-
382
- # Extract the thumbnail with margin
383
- return image[y_start:y_end, x_start:x_end]
384
-
385
- def show_face_detection_and_labeling_ui(image: np.ndarray, face_service: Any) -> Dict[str, Any]:
386
- """
387
- Main function for the face detection and labeling section.
388
- Integrates all related functionalities.
389
-
390
- Args:
391
- image: Image in numpy array format (RGB)
392
- face_service: Face detection service
393
-
394
- Returns:
395
- Dictionary with processed results
396
- """
397
- # Insert custom CSS for better UI layout
398
- st.markdown("""
399
- <style>
400
- div.row-widget.stRadio > div {
401
- flex-direction: row;
402
- align-items: stretch;
403
- }
404
- div.row-widget.stRadio > div > label {
405
- padding: 10px;
406
- border: 1px solid #ddd;
407
- border-radius: 4px;
408
- margin-right: 5px;
409
- text-align: center;
410
- }
411
- .main > div {
412
- padding-top: 2rem;
413
- padding-bottom: 2rem;
414
- }
415
- </style>
416
- <div style='height: 20px;'></div>
417
- """, unsafe_allow_html=True)
418
-
419
- # Ensure we have the image in the correct format for detection
420
- if image is None:
421
- st.warning("No image available for processing.")
422
- return {
423
- "success": False,
424
- "message": "No image available for processing."
425
- }
426
-
427
- # Configure processing limits
428
- max_faces = 5 # Maximum faces as per specification
429
-
430
- # Show debug info about the image
431
- st.write(f"Image type: {type(image)}, Shape: {image.shape}")
432
-
433
- # Convert to BGR for detection if necessary
434
- img_bgr = None
435
- if len(image.shape) == 3 and image.shape[2] == 3:
436
- img_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
437
- else:
438
- # If already in BGR or grayscale format
439
- img_bgr = image.copy()
440
-
441
- # Perform face detection
442
- with st.spinner("Detecting faces..."):
443
- faces = face_service.detect_faces(img_bgr)
444
- st.write(f"Faces detected: {len(faces) if faces is not None else 0}")
445
-
446
- if faces is None or len(faces) == 0:
447
- st.warning("No faces detected in the image.")
448
-
449
- # Show the image even if no faces are detected
450
- st.image(image, caption="Uploaded image (no faces detected)", use_column_width=True)
451
-
452
- return {
453
- "success": False,
454
- "message": "No faces detected in the image."
455
- }
456
-
457
- # Store detected faces in session state for future reference
458
- st.session_state["detected_faces"] = faces
459
-
460
- # Display the UI for face selection and labeling
461
- selection_result = show_face_selection_ui(image, faces, max_faces)
462
-
463
- # Save the labeled image in session state (if available)
464
- if selection_result.get("success", False) and "labeled_image" in selection_result:
465
- st.session_state["labeled_image"] = selection_result["labeled_image"]
466
-
467
- # Check if we can proceed with analysis
468
- can_proceed = selection_result.get("can_proceed", False)
469
-
470
- # Prepare faces for analysis if proceeding is possible
471
- analysis_data = None
472
- if can_proceed:
473
- analysis_data = prepare_faces_for_analysis(image, selection_result)
474
-
475
- # Save analysis data in session state
476
- if analysis_data.get("success", False):
477
- st.session_state["faces_to_analyze"] = analysis_data.get("faces_to_analyze", [])
478
-
479
- # Display button to start analysis
480
- if st.button("START ANALYSIS", key="start_analysis",
481
- disabled=not analysis_data.get("can_proceed", False)):
482
- return {
483
- "success": True,
484
- "proceed_to_analysis": True,
485
- "faces_to_analyze": analysis_data.get("faces_to_analyze", [])
486
- }
487
 
488
- # Return the result without page reloads
489
- return {
490
- "success": selection_result.get("success", False),
491
- "proceed_to_analysis": False,
492
- "message": selection_result.get("message", "")
493
- }
 
1
  """
2
+ Face labeling module (DEPRECATED).
3
 
4
+ This module has been replaced by simple_face_labeling.py.
5
+ All functionality is redirected to the new module for backwards compatibility.
6
  """
7
  import streamlit as st
8
  import numpy as np
9
+ from typing import Dict, Any, List, Tuple
 
 
 
 
 
 
 
 
10
 
11
+ # Redirect to the new module
12
+ from utils.simple_face_labeling import simple_face_detection_and_labeling_ui
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # Keep the function name for backwards compatibility
15
+ def show_face_detection_and_labeling_ui(image: np.ndarray, face_service: Any) -> Dict[str, Any]:
16
  """
17
+ Redirects to the simplified version.
18
 
19
  Args:
20
  image: Image in numpy array format (RGB)
21
+ face_service: Face detection service
 
22
 
23
  Returns:
24
+ Dictionary with processed results
25
  """
26
+ return simple_face_detection_and_labeling_ui(image, face_service)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ # For backwards compatibility, any other functions that might be imported elsewhere
29
+ def draw_face_rectangles(*args, **kwargs):
30
+ """Deprecated function"""
31
+ st.warning("Using deprecated face_labeling.py module. Please update your imports.")
32
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ def extract_face_thumbnails(*args, **kwargs):
35
+ """Deprecated function"""
36
+ st.warning("Using deprecated face_labeling.py module. Please update your imports.")
37
+ return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ def prepare_faces_for_analysis(image, selection_result):
40
  """
41
+ Deprecated function - Fixed to avoid the numpy array truth value error
 
 
 
 
 
 
 
42
  """
43
+ st.warning("Using deprecated face_labeling.py module. Please update your imports.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ # Just return an empty result to avoid errors
46
+ return {"faces_to_analyze": []}