Marcel0123 commited on
Commit
6baf719
·
verified ·
1 Parent(s): 76b67b7

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +12 -64
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import cv2 as cv
2
  import numpy as np
3
  import gradio as gr
@@ -48,18 +49,15 @@ EN_TO_NL = {
48
  }
49
 
50
  def to_dutch_lower(label: str) -> str:
51
- """Zet emotielabel om naar NL en lowercase (fallback: originele lowercase)."""
52
  if not label:
53
  return "onbekend"
54
  key = label.strip().lower()
55
  return EN_TO_NL.get(key, key)
56
 
57
- # In-memory statistieken
58
  emotion_stats = defaultdict(int)
59
 
60
- # ---------- Confidence helpers ----------
61
  def _format_pct(conf):
62
- """Format confidence naar '82%' (int). Conf kan in [0,1] of [0,100] of None."""
63
  if conf is None:
64
  return None
65
  try:
@@ -72,14 +70,6 @@ def _format_pct(conf):
72
  return f"{int(round(c))}%"
73
 
74
  def _parse_infer_output(result):
75
- """
76
- Probeer robuust (label_idx, confidence) uit infer-output te halen.
77
- Ondersteunt:
78
- - (label, score) tuple/list
79
- - [probs...] ndarray (neemt argmax + max)
80
- - [label] of scalar -> (label, None)
81
- """
82
- # numpy array?
83
  if isinstance(result, np.ndarray):
84
  arr = result
85
  if arr.ndim == 1 and arr.size > 1:
@@ -89,14 +79,12 @@ def _parse_infer_output(result):
89
  elif arr.size == 1:
90
  return int(arr.flat[0]), None
91
  else:
92
- # onbekende vorm
93
  try:
94
  idx = int(arr[0])
95
  return idx, None
96
  except Exception:
97
  return 0, None
98
 
99
- # list/tuple?
100
  if isinstance(result, (list, tuple)):
101
  if len(result) >= 2 and isinstance(result[1], (float, np.floating, int, np.integer)):
102
  try:
@@ -109,15 +97,12 @@ def _parse_infer_output(result):
109
  except Exception:
110
  return 0, None
111
 
112
- # scalar label
113
  try:
114
  return int(result), None
115
  except Exception:
116
  return 0, None
117
- # ---------------------------------------
118
 
119
  def visualize(image, det_res, labels, confs):
120
- """Tekent bbox + NL-lowercase emotielabel + confidence op de output."""
121
  output = image.copy()
122
  landmark_color = [(255, 0, 0), (0, 0, 255), (0, 255, 0), (255, 0, 255), (0, 255, 255)]
123
  for i, (det, lab) in enumerate(zip(det_res, labels)):
@@ -128,16 +113,7 @@ def visualize(image, det_res, labels, confs):
128
  txt = f"{fer_type_str_nl}" + (f" {pct}" if pct else "")
129
 
130
  cv.rectangle(output, (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3]), (0, 255, 0), 2)
131
- cv.putText(
132
- output,
133
- txt,
134
- (bbox[0], max(0, bbox[1] - 10)),
135
- cv.FONT_HERSHEY_SIMPLEX,
136
- 0.7,
137
- (0, 0, 255),
138
- 2,
139
- cv.LINE_AA
140
- )
141
 
142
  landmarks = det[4:14].astype(np.int32).reshape((5, 2))
143
  for idx, landmark in enumerate(landmarks):
@@ -145,27 +121,18 @@ def visualize(image, det_res, labels, confs):
145
  return output
146
 
147
  def summarize_emotions(labels, confs):
148
- """Maakt de grote groene NL-lowercase samenvatting met gemiddelden per emotie."""
149
  if not labels:
150
  return "## **geen gezicht gedetecteerd**"
151
 
152
- names_nl = []
153
- for lab in labels:
154
- names_nl.append(to_dutch_lower(FacialExpressionRecog.getDesc(lab)))
155
-
156
- # tel per emotie + verzamel confidences
157
  counts = Counter(names_nl)
158
  conf_bucket = defaultdict(list)
159
  for i, name in enumerate(names_nl):
160
  if i < len(confs) and confs[i] is not None:
161
  conf_bucket[name].append(float(confs[i]))
162
 
163
- # top-emotie op basis van count
164
  top = counts.most_common(1)[0][0]
165
-
166
- # details: "blij (2, gem. 79%)"
167
  parts = []
168
- # sorteer op frequentie aflopend, dan alfabetisch
169
  for name, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
170
  if conf_bucket[name]:
171
  avg = sum(conf_bucket[name]) / len(conf_bucket[name])
@@ -177,7 +144,6 @@ def summarize_emotions(labels, confs):
177
  return f"# **{top}**\n\n_Gedetecteerde emoties: {details}_"
178
 
179
  def process_image(input_image):
180
- """Helper: run detectie en retourneer (output_img, labels[int], confs[float|None])."""
181
  image = cv.cvtColor(input_image, cv.COLOR_RGB2BGR)
182
  h, w, _ = image.shape
183
  detect_model.setInputSize([w, h])
@@ -194,10 +160,8 @@ def process_image(input_image):
194
  return cv.cvtColor(output, cv.COLOR_BGR2RGB), labels, confs, dets
195
 
196
  def detect_expression(input_image):
197
- """Versie die WÉL statistieken bijwerkt (gebruik voor 'Verstuur')."""
198
  output_img, labels, confs, _ = process_image(input_image)
199
  emotion_md = summarize_emotions(labels, confs)
200
- # update stats in NL-lowercase
201
  for lab in labels:
202
  name_nl = to_dutch_lower(FacialExpressionRecog.getDesc(lab))
203
  emotion_stats[name_nl] += 1
@@ -205,13 +169,10 @@ def detect_expression(input_image):
205
  return output_img, emotion_md, stats_plot
206
 
207
  def detect_expression_no_stats(input_image):
208
- """Versie die GEEN statistieken bijwerkt (gebruik voor gr.Examples & caching)."""
209
  output_img, labels, confs, _ = process_image(input_image)
210
  emotion_md = summarize_emotions(labels, confs)
211
- # géén stats update en ook géén stats_image teruggeven
212
  return output_img, emotion_md
213
 
214
- # --- Staafdiagram tekenen met OpenCV (geen matplotlib nodig) ---
215
  def draw_bar_chart_cv(stats: dict, width=640, height=320):
216
  img = np.full((height, width, 3), 255, dtype=np.uint8)
217
  cv.putText(img, "Live emotie-statistieken", (12, 28), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2, cv.LINE_AA)
@@ -224,8 +185,8 @@ def draw_bar_chart_cv(stats: dict, width=640, height=320):
224
  plot_h = height - top - bottom
225
  origin = (left, height - bottom)
226
 
227
- cv.line(img, origin, (left + plot_w, height - bottom), (0, 0, 0), 2) # x-as
228
- cv.line(img, origin, (left, height - bottom - plot_h), (0, 0, 0), 2) # y-as
229
 
230
  labels = list(stats.keys())
231
  values = [stats[k] for k in labels]
@@ -241,7 +202,7 @@ def draw_bar_chart_cv(stats: dict, width=640, height=320):
241
  h_px = int((val / max_val) * (plot_h - 10))
242
  y1 = height - bottom - h_px
243
  y2 = height - bottom - 1
244
- cv.rectangle(img, (x1, y1), (x2, y2), (0, 170, 60), -1) # groene balk
245
  cv.putText(img, str(val), (x1 + 2, y1 - 6), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 90, 30), 1, cv.LINE_AA)
246
 
247
  show_lab = lab if len(lab) <= 12 else lab[:11] + "…"
@@ -252,7 +213,6 @@ def draw_bar_chart_cv(stats: dict, width=640, height=320):
252
 
253
  return cv.cvtColor(img, cv.COLOR_BGR2RGB)
254
 
255
- # Voorbeelden automatisch laden
256
  IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
257
  EXAMPLES_DIR = Path("examples")
258
  if EXAMPLES_DIR.exists() and EXAMPLES_DIR.is_dir():
@@ -262,7 +222,6 @@ else:
262
  example_list = [[p] for p in example_paths]
263
  CACHE_EXAMPLES = bool(example_list)
264
 
265
- # Uitlegblok (HTML) – netjes opgemaakt
266
  INFO_HTML = """
267
  <div>
268
  <h3>Hoe werkt deze gezichtsuitdrukking-herkenner?</h3>
@@ -288,15 +247,12 @@ INFO_HTML = """
288
  </div>
289
  """
290
 
291
- # CSS (groene emotietekst + uitlegblok styling)
292
  custom_css = """
293
  #emotie-uitslag { color: #16a34a; }
294
  #emotie-uitslag h1, #emotie-uitslag h2, #emotie-uitslag h3 { margin: 0.25rem 0; }
295
-
296
- /* Uitlegblok onder de mugshots */
297
  #uitleg-blok {
298
- background: #f3f4f6; /* lichtgrijs */
299
- border: 1px solid #e5e7eb; /* subtiele rand */
300
  border-radius: 10px;
301
  padding: 12px 14px;
302
  }
@@ -310,7 +266,6 @@ with gr.Blocks(css=custom_css) as demo:
310
  gr.Markdown("## Herkenning van gezichtsuitdrukkingen (FER) met OpenCV DNN")
311
  gr.Markdown("Detecteert gezichten en herkent gezichtsuitdrukkingen met YuNet + MobileFaceNet (ONNX).")
312
 
313
- # Rij 1: Links upload/knoppen, Rechts output + emotie
314
  with gr.Row():
315
  with gr.Column():
316
  input_image = gr.Image(type="numpy", label="Afbeelding uploaden")
@@ -321,41 +276,34 @@ with gr.Blocks(css=custom_css) as demo:
321
  output_image = gr.Image(type="numpy", label="Resultaat gezichtsuitdrukking")
322
  emotion_md = gr.Markdown("## **Nog geen resultaat**", elem_id="emotie-uitslag")
323
 
324
- # Rij 2: Links mugshots (Examples + uitleg), Rechts statistieken
325
  with gr.Row():
326
  with gr.Column():
327
  gr.Markdown("**Voorbeelden (klik om te testen):**")
328
  gr.Examples(
329
  examples=example_list,
330
  inputs=input_image,
331
- outputs=[output_image, emotion_md], # <- 2 outputs
332
- fn=detect_expression_no_stats, # <- geeft 2 outputs terug
333
  examples_per_page=20,
334
  cache_examples=CACHE_EXAMPLES
335
  )
336
- # Uitlegblok onder de mugshots
337
  gr.HTML(INFO_HTML, elem_id="uitleg-blok")
338
 
339
  with gr.Column():
340
  stats_image = gr.Image(
341
  label="Statistieken",
342
  type="numpy",
343
- value=draw_bar_chart_cv(emotion_stats) # start met lege/actuele chart
344
  )
345
 
346
- # Clear-helpers
347
  def clear_all_on_new():
348
  return None, "## **Nog geen resultaat**"
349
 
350
  def clear_all_button():
351
- # reset inputs/outputs; statistieken blijven behouden
352
  return None, None, "## **Nog geen resultaat**", draw_bar_chart_cv(emotion_stats)
353
 
354
- # Nieuwe upload wist output + emotietekst (grafiek blijft staan)
355
  input_image.change(fn=clear_all_on_new, outputs=[output_image, emotion_md])
356
- # Verwerken
357
  submit_btn.click(fn=detect_expression, inputs=input_image, outputs=[output_image, emotion_md, stats_image])
358
- # Wissen-knop: ook grafiek opnieuw tekenen (maar stats niet resetten)
359
  clear_btn.click(fn=clear_all_button, outputs=[input_image, output_image, emotion_md, stats_image])
360
 
361
  if __name__ == "__main__":
 
1
+
2
  import cv2 as cv
3
  import numpy as np
4
  import gradio as gr
 
49
  }
50
 
51
  def to_dutch_lower(label: str) -> str:
 
52
  if not label:
53
  return "onbekend"
54
  key = label.strip().lower()
55
  return EN_TO_NL.get(key, key)
56
 
 
57
  emotion_stats = defaultdict(int)
58
 
59
+ # Confidence helpers
60
  def _format_pct(conf):
 
61
  if conf is None:
62
  return None
63
  try:
 
70
  return f"{int(round(c))}%"
71
 
72
  def _parse_infer_output(result):
 
 
 
 
 
 
 
 
73
  if isinstance(result, np.ndarray):
74
  arr = result
75
  if arr.ndim == 1 and arr.size > 1:
 
79
  elif arr.size == 1:
80
  return int(arr.flat[0]), None
81
  else:
 
82
  try:
83
  idx = int(arr[0])
84
  return idx, None
85
  except Exception:
86
  return 0, None
87
 
 
88
  if isinstance(result, (list, tuple)):
89
  if len(result) >= 2 and isinstance(result[1], (float, np.floating, int, np.integer)):
90
  try:
 
97
  except Exception:
98
  return 0, None
99
 
 
100
  try:
101
  return int(result), None
102
  except Exception:
103
  return 0, None
 
104
 
105
  def visualize(image, det_res, labels, confs):
 
106
  output = image.copy()
107
  landmark_color = [(255, 0, 0), (0, 0, 255), (0, 255, 0), (255, 0, 255), (0, 255, 255)]
108
  for i, (det, lab) in enumerate(zip(det_res, labels)):
 
113
  txt = f"{fer_type_str_nl}" + (f" {pct}" if pct else "")
114
 
115
  cv.rectangle(output, (bbox[0], bbox[1]), (bbox[0]+bbox[2], bbox[1]+bbox[3]), (0, 255, 0), 2)
116
+ cv.putText(output, txt, (bbox[0], max(0, bbox[1] - 10)), cv.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2, cv.LINE_AA)
 
 
 
 
 
 
 
 
 
117
 
118
  landmarks = det[4:14].astype(np.int32).reshape((5, 2))
119
  for idx, landmark in enumerate(landmarks):
 
121
  return output
122
 
123
  def summarize_emotions(labels, confs):
 
124
  if not labels:
125
  return "## **geen gezicht gedetecteerd**"
126
 
127
+ names_nl = [to_dutch_lower(FacialExpressionRecog.getDesc(lab)) for lab in labels]
 
 
 
 
128
  counts = Counter(names_nl)
129
  conf_bucket = defaultdict(list)
130
  for i, name in enumerate(names_nl):
131
  if i < len(confs) and confs[i] is not None:
132
  conf_bucket[name].append(float(confs[i]))
133
 
 
134
  top = counts.most_common(1)[0][0]
 
 
135
  parts = []
 
136
  for name, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
137
  if conf_bucket[name]:
138
  avg = sum(conf_bucket[name]) / len(conf_bucket[name])
 
144
  return f"# **{top}**\n\n_Gedetecteerde emoties: {details}_"
145
 
146
  def process_image(input_image):
 
147
  image = cv.cvtColor(input_image, cv.COLOR_RGB2BGR)
148
  h, w, _ = image.shape
149
  detect_model.setInputSize([w, h])
 
160
  return cv.cvtColor(output, cv.COLOR_BGR2RGB), labels, confs, dets
161
 
162
  def detect_expression(input_image):
 
163
  output_img, labels, confs, _ = process_image(input_image)
164
  emotion_md = summarize_emotions(labels, confs)
 
165
  for lab in labels:
166
  name_nl = to_dutch_lower(FacialExpressionRecog.getDesc(lab))
167
  emotion_stats[name_nl] += 1
 
169
  return output_img, emotion_md, stats_plot
170
 
171
  def detect_expression_no_stats(input_image):
 
172
  output_img, labels, confs, _ = process_image(input_image)
173
  emotion_md = summarize_emotions(labels, confs)
 
174
  return output_img, emotion_md
175
 
 
176
  def draw_bar_chart_cv(stats: dict, width=640, height=320):
177
  img = np.full((height, width, 3), 255, dtype=np.uint8)
178
  cv.putText(img, "Live emotie-statistieken", (12, 28), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2, cv.LINE_AA)
 
185
  plot_h = height - top - bottom
186
  origin = (left, height - bottom)
187
 
188
+ cv.line(img, origin, (left + plot_w, height - bottom), (0, 0, 0), 2)
189
+ cv.line(img, origin, (left, height - bottom - plot_h), (0, 0, 0), 2)
190
 
191
  labels = list(stats.keys())
192
  values = [stats[k] for k in labels]
 
202
  h_px = int((val / max_val) * (plot_h - 10))
203
  y1 = height - bottom - h_px
204
  y2 = height - bottom - 1
205
+ cv.rectangle(img, (x1, y1), (x2, y2), (0, 170, 60), -1)
206
  cv.putText(img, str(val), (x1 + 2, y1 - 6), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 90, 30), 1, cv.LINE_AA)
207
 
208
  show_lab = lab if len(lab) <= 12 else lab[:11] + "…"
 
213
 
214
  return cv.cvtColor(img, cv.COLOR_BGR2RGB)
215
 
 
216
  IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}
217
  EXAMPLES_DIR = Path("examples")
218
  if EXAMPLES_DIR.exists() and EXAMPLES_DIR.is_dir():
 
222
  example_list = [[p] for p in example_paths]
223
  CACHE_EXAMPLES = bool(example_list)
224
 
 
225
  INFO_HTML = """
226
  <div>
227
  <h3>Hoe werkt deze gezichtsuitdrukking-herkenner?</h3>
 
247
  </div>
248
  """
249
 
 
250
  custom_css = """
251
  #emotie-uitslag { color: #16a34a; }
252
  #emotie-uitslag h1, #emotie-uitslag h2, #emotie-uitslag h3 { margin: 0.25rem 0; }
 
 
253
  #uitleg-blok {
254
+ background: #f3f4f6;
255
+ border: 1px solid #e5e7eb;
256
  border-radius: 10px;
257
  padding: 12px 14px;
258
  }
 
266
  gr.Markdown("## Herkenning van gezichtsuitdrukkingen (FER) met OpenCV DNN")
267
  gr.Markdown("Detecteert gezichten en herkent gezichtsuitdrukkingen met YuNet + MobileFaceNet (ONNX).")
268
 
 
269
  with gr.Row():
270
  with gr.Column():
271
  input_image = gr.Image(type="numpy", label="Afbeelding uploaden")
 
276
  output_image = gr.Image(type="numpy", label="Resultaat gezichtsuitdrukking")
277
  emotion_md = gr.Markdown("## **Nog geen resultaat**", elem_id="emotie-uitslag")
278
 
 
279
  with gr.Row():
280
  with gr.Column():
281
  gr.Markdown("**Voorbeelden (klik om te testen):**")
282
  gr.Examples(
283
  examples=example_list,
284
  inputs=input_image,
285
+ outputs=[output_image, emotion_md],
286
+ fn=detect_expression_no_stats,
287
  examples_per_page=20,
288
  cache_examples=CACHE_EXAMPLES
289
  )
 
290
  gr.HTML(INFO_HTML, elem_id="uitleg-blok")
291
 
292
  with gr.Column():
293
  stats_image = gr.Image(
294
  label="Statistieken",
295
  type="numpy",
296
+ value=draw_bar_chart_cv(emotion_stats)
297
  )
298
 
 
299
  def clear_all_on_new():
300
  return None, "## **Nog geen resultaat**"
301
 
302
  def clear_all_button():
 
303
  return None, None, "## **Nog geen resultaat**", draw_bar_chart_cv(emotion_stats)
304
 
 
305
  input_image.change(fn=clear_all_on_new, outputs=[output_image, emotion_md])
 
306
  submit_btn.click(fn=detect_expression, inputs=input_image, outputs=[output_image, emotion_md, stats_image])
 
307
  clear_btn.click(fn=clear_all_button, outputs=[input_image, output_image, emotion_md, stats_image])
308
 
309
  if __name__ == "__main__":