yassonee commited on
Commit
368435b
·
verified ·
1 Parent(s): c0c6567

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +244 -87
app.py CHANGED
@@ -2,7 +2,9 @@ from fastapi import FastAPI, File, UploadFile
2
  from fastapi.responses import HTMLResponse
3
  from transformers import pipeline
4
  from PIL import Image, ImageDraw
 
5
  import io
 
6
  import base64
7
 
8
  app = FastAPI()
@@ -67,7 +69,12 @@ def draw_boxes(image, predictions):
67
 
68
  text_bbox = draw.textbbox((box['xmin'], box['ymin']-20), label)
69
  draw.rectangle(text_bbox, fill=(0, 0, 0, 180))
70
- draw.text((box['xmin'], box['ymin']-20), label, fill=(255, 255, 255, 255))
 
 
 
 
 
71
 
72
  return result_image
73
 
@@ -77,116 +84,121 @@ def image_to_base64(image):
77
  img_str = base64.b64encode(buffered.getvalue()).decode()
78
  return f"data:image/png;base64,{img_str}"
79
 
80
- STYLES = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  body {
82
  font-family: system-ui, -apple-system, sans-serif;
83
- background: #2d3748;
84
  margin: 0;
85
  padding: 20px;
86
- color: #e2e8f0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
  .container {
90
  max-width: 1200px;
91
  margin: 0 auto;
92
- background: #1a202c;
93
  padding: 20px;
94
  border-radius: 10px;
95
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
96
- }
97
-
98
- .upload-section {
99
- background: #2d3748;
100
- padding: 40px;
101
- border-radius: 12px;
102
- margin: 20px 0;
103
- text-align: center;
104
- border: 2px dashed #4a5568;
105
- }
106
-
107
- /* Animation de chargement */
108
- @keyframes loading {
109
- 0% { background-position: 200% 0; }
110
- 100% { background-position: -200% 0; }
111
  }
112
 
113
  .button {
114
- background: #4299e1;
115
  color: white;
116
  border: none;
117
  padding: 12px 30px;
118
  border-radius: 8px;
119
  cursor: pointer;
120
  font-size: 1.1em;
121
- overflow: hidden;
 
122
  }
123
 
124
- .button:active {
125
- background: linear-gradient(90deg, #4299e1 0%, #63b3ed 50%, #4299e1 100%);
126
- background-size: 200% 100%;
127
- animation: loading 2s infinite linear;
128
  }
129
 
130
- input[type="file"] {
131
- font-size: 1.1em;
132
- margin: 20px 0;
133
- color: #e2e8f0;
134
- }
135
-
136
- input[type="file"]::file-selector-button {
137
- font-size: 1em;
138
- padding: 10px 20px;
139
- border-radius: 8px;
140
- border: 1px solid #4a5568;
141
- background: #2d3748;
142
- color: #e2e8f0;
143
- cursor: pointer;
144
  }
145
 
146
- input[type="file"]::file-selector-button:hover {
147
- background: #4a5568;
 
148
  }
149
 
150
- .result-box {
151
- background: #2d3748;
152
- padding: 20px;
153
- border-radius: 12px;
154
- margin: 10px 0;
155
- border: 1px solid #4a5568;
 
156
  }
157
 
158
- .score-high {
159
- color: #48bb78;
160
- font-weight: bold;
161
- }
162
-
163
- .score-medium {
164
- color: #ed8936;
165
- font-weight: bold;
166
  }
167
 
168
  img {
169
  max-width: 100%;
 
170
  border-radius: 8px;
171
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
172
- }
173
-
174
- h3 {
175
- color: #63b3ed;
176
- margin-top: 0;
177
- }
178
-
179
- .results-grid {
180
- display: grid;
181
- grid-template-columns: 1fr 1fr;
182
- gap: 20px;
183
- margin-top: 20px;
184
- }
185
-
186
- @media (max-width: 768px) {
187
- .results-grid {
188
- grid-template-columns: 1fr;
189
- }
190
  }
191
  """
192
 
@@ -198,15 +210,86 @@ async def main():
198
  <head>
199
  <title>Fraktur Detektion</title>
200
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
201
- <style>{STYLES}</style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  </head>
203
  <body>
204
  <div class="container">
205
  <div class="upload-section">
206
  <form action="/analyze" method="post" enctype="multipart/form-data">
207
- <input type="file" name="file" accept="image/*" required>
208
- <br>
209
- <button type="submit" class="button">Analysieren</button>
 
 
 
 
 
 
 
 
 
 
210
  </form>
211
  </div>
212
  </div>
@@ -226,7 +309,11 @@ async def analyze_file(file: UploadFile = File(...)):
226
  predictions_locator = models["KnochenAuge"](image)
227
 
228
  filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
229
- result_image = draw_boxes(image, filtered_preds) if filtered_preds else image
 
 
 
 
230
  result_image_b64 = image_to_base64(result_image)
231
 
232
  results_html = f"""
@@ -235,7 +322,57 @@ async def analyze_file(file: UploadFile = File(...)):
235
  <head>
236
  <title>Ergebnisse</title>
237
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
238
- <style>{STYLES}</style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  </head>
240
  <body>
241
  <div class="container">
@@ -275,7 +412,10 @@ async def analyze_file(file: UploadFile = File(...)):
275
  </div>
276
  </div>
277
 
278
- <a href="/" class="button">← Zurück</a>
 
 
 
279
  </div>
280
  </body>
281
  </html>
@@ -290,20 +430,37 @@ async def analyze_file(file: UploadFile = File(...)):
290
  <head>
291
  <title>Fehler</title>
292
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
293
- <style>{STYLES}</style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  </head>
295
  <body>
296
  <div class="container">
297
- <div class="result-box">
298
  <h3>Fehler</h3>
299
  <p>{str(e)}</p>
300
  </div>
301
- <a href="/" class="button">← Zurück</a>
 
 
 
302
  </div>
303
  </body>
304
  </html>
305
  """
306
 
307
  if __name__ == "__main__":
308
- import uvicorn
309
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
2
  from fastapi.responses import HTMLResponse
3
  from transformers import pipeline
4
  from PIL import Image, ImageDraw
5
+ import numpy as np
6
  import io
7
+ import uvicorn
8
  import base64
9
 
10
  app = FastAPI()
 
69
 
70
  text_bbox = draw.textbbox((box['xmin'], box['ymin']-20), label)
71
  draw.rectangle(text_bbox, fill=(0, 0, 0, 180))
72
+
73
+ draw.text(
74
+ (box['xmin'], box['ymin']-20),
75
+ label,
76
+ fill=(255, 255, 255, 255)
77
+ )
78
 
79
  return result_image
80
 
 
84
  img_str = base64.b64encode(buffered.getvalue()).decode()
85
  return f"data:image/png;base64,{img_str}"
86
 
87
+ # Styles communs pour les deux pages
88
+ COMMON_STYLES = """
89
+ :root {
90
+ color-scheme: light dark;
91
+ }
92
+
93
+ @media (prefers-color-scheme: dark) {
94
+ body {
95
+ background: #1a1a1a !important;
96
+ color: #ffffff !important;
97
+ }
98
+ .container {
99
+ background: #2d2d2d !important;
100
+ }
101
+ .result-box, .upload-section {
102
+ background: #363636 !important;
103
+ border-color: #555 !important;
104
+ }
105
+ .button {
106
+ background: #0066cc !important;
107
+ }
108
+ input[type="file"]::file-selector-button {
109
+ background: #444 !important;
110
+ color: #fff !important;
111
+ border-color: #555 !important;
112
+ }
113
+ input[type="range"] {
114
+ background: #444 !important;
115
+ }
116
+ .button-progress {
117
+ background: rgba(255, 255, 255, 0.2) !important;
118
+ }
119
+ }
120
+
121
  body {
122
  font-family: system-ui, -apple-system, sans-serif;
123
+ background: #f0f2f5;
124
  margin: 0;
125
  padding: 20px;
126
+ color: #1a1a1a;
127
+ }
128
+
129
+ /* Scrollbar personnalisée */
130
+ ::-webkit-scrollbar {
131
+ width: 8px;
132
+ height: 8px;
133
+ }
134
+
135
+ ::-webkit-scrollbar-track {
136
+ background: transparent;
137
+ }
138
+
139
+ ::-webkit-scrollbar-thumb {
140
+ background-color: rgba(156, 163, 175, 0.5);
141
+ border-radius: 4px;
142
+ }
143
+
144
+ @media (prefers-color-scheme: dark) {
145
+ ::-webkit-scrollbar-thumb {
146
+ background-color: rgba(75, 85, 99, 0.5);
147
+ }
148
  }
149
 
150
  .container {
151
  max-width: 1200px;
152
  margin: 0 auto;
153
+ background: white;
154
  padding: 20px;
155
  border-radius: 10px;
156
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
158
 
159
  .button {
160
+ background: #0066cc;
161
  color: white;
162
  border: none;
163
  padding: 12px 30px;
164
  border-radius: 8px;
165
  cursor: pointer;
166
  font-size: 1.1em;
167
+ transition: all 0.3s ease;
168
+ position: relative;
169
  }
170
 
171
+ .button:hover {
172
+ background: #0052a3;
 
 
173
  }
174
 
175
+ /* Nouvelle barre de progression */
176
+ .button {
177
+ position: relative;
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
 
180
+ @keyframes progress {
181
+ 0% { width: 0; }
182
+ 100% { width: 100%; }
183
  }
184
 
185
+ .button-progress {
186
+ position: absolute;
187
+ bottom: 0;
188
+ left: 0;
189
+ height: 4px;
190
+ background: rgba(255, 255, 255, 0.5);
191
+ width: 0;
192
  }
193
 
194
+ .button:active .button-progress {
195
+ animation: progress 2s linear forwards;
 
 
 
 
 
 
196
  }
197
 
198
  img {
199
  max-width: 100%;
200
+ height: auto;
201
  border-radius: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  }
203
  """
204
 
 
210
  <head>
211
  <title>Fraktur Detektion</title>
212
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
213
+ <style>
214
+ {COMMON_STYLES}
215
+
216
+ .upload-section {{
217
+ background: #f8f9fa;
218
+ padding: 40px;
219
+ border-radius: 12px;
220
+ margin: 20px 0;
221
+ text-align: center;
222
+ border: 2px dashed #ccc;
223
+ transition: all 0.3s ease;
224
+ }}
225
+
226
+ .upload-section:hover {{
227
+ border-color: #0066cc;
228
+ }}
229
+
230
+ input[type="file"] {{
231
+ font-size: 1.1em;
232
+ margin: 20px 0;
233
+ }}
234
+
235
+ input[type="file"]::file-selector-button {{
236
+ font-size: 1em;
237
+ padding: 10px 20px;
238
+ border-radius: 8px;
239
+ border: 1px solid #ccc;
240
+ background: #f8f9fa;
241
+ transition: all 0.3s ease;
242
+ cursor: pointer;
243
+ }}
244
+
245
+ input[type="file"]::file-selector-button:hover {{
246
+ background: #e9ecef;
247
+ }}
248
+
249
+ .confidence-slider {{
250
+ width: 100%;
251
+ max-width: 300px;
252
+ margin: 20px auto;
253
+ }}
254
+
255
+ input[type="range"] {{
256
+ width: 100%;
257
+ height: 8px;
258
+ border-radius: 4px;
259
+ background: #e9ecef;
260
+ outline: none;
261
+ transition: all 0.3s ease;
262
+ -webkit-appearance: none;
263
+ }}
264
+
265
+ input[type="range"]::-webkit-slider-thumb {{
266
+ -webkit-appearance: none;
267
+ width: 20px;
268
+ height: 20px;
269
+ border-radius: 50%;
270
+ background: #0066cc;
271
+ cursor: pointer;
272
+ border: none;
273
+ }}
274
+ </style>
275
  </head>
276
  <body>
277
  <div class="container">
278
  <div class="upload-section">
279
  <form action="/analyze" method="post" enctype="multipart/form-data">
280
+ <div>
281
+ <input type="file" name="file" accept="image/*" required>
282
+ </div>
283
+ <div class="confidence-slider">
284
+ <label for="threshold">Konfidenzschwelle: <span id="thresholdValue">0.60</span></label>
285
+ <input type="range" id="threshold" name="threshold"
286
+ min="0" max="1" step="0.05" value="0.60"
287
+ oninput="document.getElementById('thresholdValue').textContent = parseFloat(this.value).toFixed(2)">
288
+ </div>
289
+ <button type="submit" class="button">
290
+ Analysieren
291
+ <div class="button-progress"></div>
292
+ </button>
293
  </form>
294
  </div>
295
  </div>
 
309
  predictions_locator = models["KnochenAuge"](image)
310
 
311
  filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
312
+ if filtered_preds:
313
+ result_image = draw_boxes(image, filtered_preds)
314
+ else:
315
+ result_image = image
316
+
317
  result_image_b64 = image_to_base64(result_image)
318
 
319
  results_html = f"""
 
322
  <head>
323
  <title>Ergebnisse</title>
324
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
325
+ <style>
326
+ {COMMON_STYLES}
327
+
328
+ .results-grid {{
329
+ display: grid;
330
+ grid-template-columns: 1fr 1fr;
331
+ gap: 20px;
332
+ margin-top: 20px;
333
+ }}
334
+
335
+ .result-box {{
336
+ background: #f8f9fa;
337
+ padding: 20px;
338
+ border-radius: 12px;
339
+ margin: 10px 0;
340
+ border: 1px solid #e9ecef;
341
+ }}
342
+
343
+ .score-high {{
344
+ color: #0066cc;
345
+ font-weight: bold;
346
+ }}
347
+
348
+ .score-medium {{
349
+ color: #ffa500;
350
+ font-weight: bold;
351
+ }}
352
+
353
+ .back-button {{
354
+ display: inline-block;
355
+ text-decoration: none;
356
+ margin-top: 20px;
357
+ }}
358
+
359
+ h3 {{
360
+ color: #0066cc;
361
+ margin-top: 0;
362
+ }}
363
+
364
+ @media (prefers-color-scheme: dark) {{
365
+ h3 {{
366
+ color: #66b3ff;
367
+ }}
368
+ }}
369
+
370
+ @media (max-width: 768px) {{
371
+ .results-grid {{
372
+ grid-template-columns: 1fr;
373
+ }}
374
+ }}
375
+ </style>
376
  </head>
377
  <body>
378
  <div class="container">
 
412
  </div>
413
  </div>
414
 
415
+ <a href="/" class="button back-button">
416
+ ← Zurück
417
+ <div class="button-progress"></div>
418
+ </a>
419
  </div>
420
  </body>
421
  </html>
 
430
  <head>
431
  <title>Fehler</title>
432
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
433
+ <style>
434
+ {COMMON_STYLES}
435
+ .error-box {{
436
+ background: #fee2e2;
437
+ border: 1px solid #ef4444;
438
+ padding: 20px;
439
+ border-radius: 8px;
440
+ margin: 20px 0;
441
+ }}
442
+ @media (prefers-color-scheme: dark) {{
443
+ .error-box {{
444
+ background: #471818;
445
+ border-color: #dc2626;
446
+ }}
447
+ }}
448
+ </style>
449
  </head>
450
  <body>
451
  <div class="container">
452
+ <div class="error-box">
453
  <h3>Fehler</h3>
454
  <p>{str(e)}</p>
455
  </div>
456
+ <a href="/" class="button back-button">
457
+ ← Zurück
458
+ <div class="button-progress"></div>
459
+ </a>
460
  </div>
461
  </body>
462
  </html>
463
  """
464
 
465
  if __name__ == "__main__":
 
466
  uvicorn.run(app, host="0.0.0.0", port=7860)