yassonee commited on
Commit
8c90d7a
·
verified ·
1 Parent(s): 2005bb3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -122
app.py CHANGED
@@ -2,9 +2,10 @@ 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
  import uvicorn
 
8
 
9
  app = FastAPI()
10
 
@@ -19,7 +20,6 @@ def load_models():
19
 
20
  models = load_models()
21
 
22
- # Fonctions d'analyse existantes restent identiques
23
  def translate_label(label):
24
  translations = {
25
  "fracture": "Knochenbruch",
@@ -31,20 +31,59 @@ def translate_label(label):
31
  }
32
  return translations.get(label.lower(), label)
33
 
34
- # Autres fonctions helper restent identiques
35
  def create_heatmap_overlay(image, box, score):
36
- # Votre code existant reste le même
37
- [...]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  def draw_boxes(image, predictions):
40
- # Votre code existant reste le même
41
- [...]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  def image_to_base64(image):
44
- # Votre code existant reste le même
45
- [...]
 
 
46
 
47
- # Page d'accueil simplifiée
48
  @app.get("/", response_class=HTMLResponse)
49
  async def main():
50
  content = """
@@ -53,37 +92,47 @@ async def main():
53
  <head>
54
  <title>Fraktur Detektion</title>
55
  <style>
56
- :root { color-scheme: light dark; }
57
-
58
  body {
59
  font-family: system-ui, -apple-system, sans-serif;
 
60
  margin: 0;
61
- padding: 1rem;
62
- max-width: 100%;
63
- overflow-x: hidden;
64
  }
65
 
66
- @media (prefers-color-scheme: dark) {
67
- body {
68
- background: #1a1a1a;
69
- color: #fff;
70
- }
71
- .container { background: #2d2d2d; }
72
- .upload-section { background: #3d3d3d; }
 
 
 
 
 
 
 
 
 
 
73
  }
74
 
75
  .container {
76
- background: #ffffff;
77
- padding: 1.5rem;
78
- border-radius: 0.5rem;
 
 
79
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
80
  }
81
 
82
  .upload-section {
83
- background: #f5f5f5;
84
- padding: 1.5rem;
85
- border-radius: 0.5rem;
86
- margin: 1rem 0;
87
  text-align: center;
88
  }
89
 
@@ -91,53 +140,41 @@ async def main():
91
  background: #0066cc;
92
  color: white;
93
  border: none;
94
- padding: 0.5rem 1rem;
95
- border-radius: 0.25rem;
96
  cursor: pointer;
97
- margin-top: 1rem;
98
- }
99
-
100
- .results-container {
101
- display: grid;
102
- gap: 1rem;
103
- margin-top: 1rem;
104
- }
105
-
106
- img {
107
- max-width: 100%;
108
- height: auto;
109
- border-radius: 0.5rem;
110
- }
111
-
112
- ::-webkit-scrollbar {
113
- width: 8px;
114
- height: 8px;
115
  }
116
 
117
- ::-webkit-scrollbar-track {
118
- background: transparent;
119
  }
120
 
121
- ::-webkit-scrollbar-thumb {
122
- background-color: rgba(0, 0, 0, 0.2);
123
- border-radius: 4px;
 
124
  }
125
 
126
- @media (prefers-color-scheme: dark) {
127
- ::-webkit-scrollbar-thumb {
128
- background-color: rgba(255, 255, 255, 0.2);
129
- }
130
  }
131
  </style>
132
  </head>
133
  <body>
134
  <div class="container">
135
- <h2>Fraktur Detektion</h2>
136
-
137
  <div class="upload-section">
138
  <form action="/analyze" method="post" enctype="multipart/form-data">
139
- <input type="file" name="file" accept="image/*" required>
140
- <br>
 
 
 
 
 
 
 
141
  <button type="submit" class="button">Analysieren</button>
142
  </form>
143
  </div>
@@ -147,11 +184,9 @@ async def main():
147
  """
148
  return content
149
 
150
- # Page de résultats simplifiée
151
  @app.post("/analyze", response_class=HTMLResponse)
152
  async def analyze_file(file: UploadFile = File(...)):
153
  try:
154
- # Votre logique d'analyse existante
155
  contents = await file.read()
156
  image = Image.open(io.BytesIO(contents))
157
 
@@ -160,7 +195,11 @@ async def analyze_file(file: UploadFile = File(...)):
160
  predictions_locator = models["KnochenAuge"](image)
161
 
162
  filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
163
- result_image = draw_boxes(image, filtered_preds) if filtered_preds else image
 
 
 
 
164
  result_image_b64 = image_to_base64(result_image)
165
 
166
  results_html = """
@@ -169,101 +208,110 @@ async def analyze_file(file: UploadFile = File(...)):
169
  <head>
170
  <title>Ergebnisse</title>
171
  <style>
172
- :root { color-scheme: light dark; }
173
-
174
  body {
175
  font-family: system-ui, -apple-system, sans-serif;
 
176
  margin: 0;
177
- padding: 1rem;
 
178
  }
179
 
180
- @media (prefers-color-scheme: dark) {
181
- body {
182
- background: #1a1a1a;
183
- color: #fff;
184
- }
185
- .container { background: #2d2d2d; }
186
- .result-box { background: #3d3d3d; }
 
 
 
 
 
 
 
 
 
 
187
  }
188
 
189
  .container {
190
- background: #ffffff;
191
- padding: 1.5rem;
192
- border-radius: 0.5rem;
 
 
193
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
194
  }
195
 
196
  .results-grid {
197
  display: grid;
198
- gap: 1rem;
199
- margin-top: 1rem;
 
200
  }
201
 
202
  .result-box {
203
- background: #f5f5f5;
204
- padding: 1rem;
205
- border-radius: 0.5rem;
 
 
206
  }
207
 
 
 
 
208
  .back-button {
209
  display: inline-block;
210
  background: #0066cc;
211
  color: white;
212
- padding: 0.5rem 1rem;
213
- border-radius: 0.25rem;
214
  text-decoration: none;
215
- margin-top: 1rem;
216
  }
217
 
218
  img {
219
  max-width: 100%;
220
- height: auto;
221
- border-radius: 0.5rem;
222
- }
223
-
224
- ::-webkit-scrollbar {
225
- width: 8px;
226
- height: 8px;
227
- }
228
-
229
- ::-webkit-scrollbar-track {
230
- background: transparent;
231
- }
232
-
233
- ::-webkit-scrollbar-thumb {
234
- background-color: rgba(0, 0, 0, 0.2);
235
- border-radius: 4px;
236
- }
237
-
238
- @media (prefers-color-scheme: dark) {
239
- ::-webkit-scrollbar-thumb {
240
- background-color: rgba(255, 255, 255, 0.2);
241
- }
242
  }
243
  </style>
244
  </head>
245
  <body>
246
  <div class="container">
247
- <h2>Analyse Ergebnisse</h2>
248
-
249
  <div class="results-grid">
 
250
  """
251
 
252
  # KnochenWächter results
253
- results_html += "<div class='result-box'><h3>KnochenWächter</h3>"
254
  for pred in predictions_watcher:
255
- results_html += f"<p>{pred['score']:.1%} - {translate_label(pred['label'])}</p>"
256
- results_html += "</div>"
 
 
 
 
 
257
 
258
  # RöntgenMeister results
259
- results_html += "<div class='result-box'><h3>RöntgenMeister</h3>"
260
  for pred in predictions_master:
261
- results_html += f"<p>{pred['score']:.1%} - {translate_label(pred['label'])}</p>"
262
- results_html += "</div>"
 
 
 
 
 
263
 
264
- # Image result
265
  results_html += f"""
266
- <div class='result-box'>
 
267
  <h3>Fraktur Lokalisation</h3>
268
  <img src="{result_image_b64}" alt="Analyzed image">
269
  </div>
@@ -284,15 +332,24 @@ async def analyze_file(file: UploadFile = File(...)):
284
  <head>
285
  <title>Fehler</title>
286
  <style>
287
- :root { color-scheme: light dark; }
288
- body {{
289
  font-family: system-ui, -apple-system, sans-serif;
290
- margin: 1rem;
 
 
 
 
 
 
 
 
 
 
291
  }}
292
  </style>
293
  </head>
294
  <body>
295
- <h2>Fehler</h2>
296
  <p>{str(e)}</p>
297
  <a href="/" class="back-button">← Zurück</a>
298
  </body>
 
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()
11
 
 
20
 
21
  models = load_models()
22
 
 
23
  def translate_label(label):
24
  translations = {
25
  "fracture": "Knochenbruch",
 
31
  }
32
  return translations.get(label.lower(), label)
33
 
 
34
  def create_heatmap_overlay(image, box, score):
35
+ overlay = Image.new('RGBA', image.size, (0, 0, 0, 0))
36
+ draw = ImageDraw.Draw(overlay)
37
+
38
+ x1, y1 = box['xmin'], box['ymin']
39
+ x2, y2 = box['xmax'], box['ymax']
40
+
41
+ if score > 0.8:
42
+ fill_color = (255, 0, 0, 100)
43
+ border_color = (255, 0, 0, 255)
44
+ elif score > 0.6:
45
+ fill_color = (255, 165, 0, 100)
46
+ border_color = (255, 165, 0, 255)
47
+ else:
48
+ fill_color = (255, 255, 0, 100)
49
+ border_color = (255, 255, 0, 255)
50
+
51
+ draw.rectangle([x1, y1, x2, y2], fill=fill_color)
52
+ draw.rectangle([x1, y1, x2, y2], outline=border_color, width=2)
53
+
54
+ return overlay
55
 
56
  def draw_boxes(image, predictions):
57
+ result_image = image.copy().convert('RGBA')
58
+
59
+ for pred in predictions:
60
+ box = pred['box']
61
+ score = pred['score']
62
+
63
+ overlay = create_heatmap_overlay(image, box, score)
64
+ result_image = Image.alpha_composite(result_image, overlay)
65
+
66
+ draw = ImageDraw.Draw(result_image)
67
+ temp = 36.5 + (score * 2.5)
68
+ label = f"{translate_label(pred['label'])} ({score:.1%} • {temp:.1f}°C)"
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
 
81
  def image_to_base64(image):
82
+ buffered = io.BytesIO()
83
+ image.save(buffered, format="PNG")
84
+ img_str = base64.b64encode(buffered.getvalue()).decode()
85
+ return f"data:image/png;base64,{img_str}"
86
 
 
87
  @app.get("/", response_class=HTMLResponse)
88
  async def main():
89
  content = """
 
92
  <head>
93
  <title>Fraktur Detektion</title>
94
  <style>
 
 
95
  body {
96
  font-family: system-ui, -apple-system, sans-serif;
97
+ background: #f0f2f5;
98
  margin: 0;
99
+ padding: 20px;
100
+ color: #1a1a1a;
 
101
  }
102
 
103
+ /* Scrollbar personnalisée */
104
+ ::-webkit-scrollbar {
105
+ width: 8px;
106
+ height: 8px;
107
+ }
108
+
109
+ ::-webkit-scrollbar-track {
110
+ background: transparent;
111
+ }
112
+
113
+ ::-webkit-scrollbar-thumb {
114
+ background-color: rgba(156, 163, 175, 0.5);
115
+ border-radius: 4px;
116
+ }
117
+
118
+ .dark ::-webkit-scrollbar-thumb {
119
+ background-color: rgba(75, 85, 99, 0.5);
120
  }
121
 
122
  .container {
123
+ max-width: 1200px;
124
+ margin: 0 auto;
125
+ background: white;
126
+ padding: 20px;
127
+ border-radius: 10px;
128
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
129
  }
130
 
131
  .upload-section {
132
+ background: #f8f9fa;
133
+ padding: 20px;
134
+ border-radius: 8px;
135
+ margin: 20px 0;
136
  text-align: center;
137
  }
138
 
 
140
  background: #0066cc;
141
  color: white;
142
  border: none;
143
+ padding: 10px 20px;
144
+ border-radius: 5px;
145
  cursor: pointer;
146
+ transition: all 0.3s ease;
147
+ font-size: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  }
149
 
150
+ .button:hover {
151
+ background: #0052a3;
152
  }
153
 
154
+ .confidence-slider {
155
+ width: 100%;
156
+ max-width: 300px;
157
+ margin: 20px auto;
158
  }
159
 
160
+ input[type="file"] {
161
+ margin-bottom: 10px;
 
 
162
  }
163
  </style>
164
  </head>
165
  <body>
166
  <div class="container">
 
 
167
  <div class="upload-section">
168
  <form action="/analyze" method="post" enctype="multipart/form-data">
169
+ <div>
170
+ <input type="file" name="file" accept="image/*" required>
171
+ </div>
172
+ <div class="confidence-slider">
173
+ <label for="threshold">Konfidenzschwelle: <span id="thresholdValue">0.60</span></label>
174
+ <input type="range" id="threshold" name="threshold"
175
+ min="0" max="1" step="0.05" value="0.60"
176
+ oninput="document.getElementById('thresholdValue').textContent = parseFloat(this.value).toFixed(2)">
177
+ </div>
178
  <button type="submit" class="button">Analysieren</button>
179
  </form>
180
  </div>
 
184
  """
185
  return content
186
 
 
187
  @app.post("/analyze", response_class=HTMLResponse)
188
  async def analyze_file(file: UploadFile = File(...)):
189
  try:
 
190
  contents = await file.read()
191
  image = Image.open(io.BytesIO(contents))
192
 
 
195
  predictions_locator = models["KnochenAuge"](image)
196
 
197
  filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
198
+ if filtered_preds:
199
+ result_image = draw_boxes(image, filtered_preds)
200
+ else:
201
+ result_image = image
202
+
203
  result_image_b64 = image_to_base64(result_image)
204
 
205
  results_html = """
 
208
  <head>
209
  <title>Ergebnisse</title>
210
  <style>
 
 
211
  body {
212
  font-family: system-ui, -apple-system, sans-serif;
213
+ background: #f0f2f5;
214
  margin: 0;
215
+ padding: 20px;
216
+ color: #1a1a1a;
217
  }
218
 
219
+ /* Scrollbar personnalisée */
220
+ ::-webkit-scrollbar {
221
+ width: 8px;
222
+ height: 8px;
223
+ }
224
+
225
+ ::-webkit-scrollbar-track {
226
+ background: transparent;
227
+ }
228
+
229
+ ::-webkit-scrollbar-thumb {
230
+ background-color: rgba(156, 163, 175, 0.5);
231
+ border-radius: 4px;
232
+ }
233
+
234
+ .dark ::-webkit-scrollbar-thumb {
235
+ background-color: rgba(75, 85, 99, 0.5);
236
  }
237
 
238
  .container {
239
+ max-width: 1200px;
240
+ margin: 0 auto;
241
+ background: white;
242
+ padding: 20px;
243
+ border-radius: 10px;
244
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
245
  }
246
 
247
  .results-grid {
248
  display: grid;
249
+ grid-template-columns: 1fr 1fr;
250
+ gap: 20px;
251
+ margin-top: 20px;
252
  }
253
 
254
  .result-box {
255
+ background: #f8f9fa;
256
+ padding: 15px;
257
+ border-radius: 8px;
258
+ margin: 10px 0;
259
+ border: 1px solid #e9ecef;
260
  }
261
 
262
+ .score-high { color: #0066cc; font-weight: bold; }
263
+ .score-medium { color: #ffa500; font-weight: bold; }
264
+
265
  .back-button {
266
  display: inline-block;
267
  background: #0066cc;
268
  color: white;
269
+ padding: 10px 20px;
270
+ border-radius: 5px;
271
  text-decoration: none;
272
+ margin-top: 20px;
273
  }
274
 
275
  img {
276
  max-width: 100%;
277
+ max-height: 60vh;
278
+ border-radius: 8px;
279
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
280
+ object-fit: contain;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  }
282
  </style>
283
  </head>
284
  <body>
285
  <div class="container">
 
 
286
  <div class="results-grid">
287
+ <div>
288
  """
289
 
290
  # KnochenWächter results
291
+ results_html += "<h3>KnochenWächter</h3>"
292
  for pred in predictions_watcher:
293
+ confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
294
+ results_html += f"""
295
+ <div class="result-box">
296
+ <span class="{confidence_class}">{pred['score']:.1%}</span> -
297
+ {translate_label(pred['label'])}
298
+ </div>
299
+ """
300
 
301
  # RöntgenMeister results
302
+ results_html += "<h3>RöntgenMeister</h3>"
303
  for pred in predictions_master:
304
+ confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
305
+ results_html += f"""
306
+ <div class="result-box">
307
+ <span class="{confidence_class}">{pred['score']:.1%}</span> -
308
+ {translate_label(pred['label'])}
309
+ </div>
310
+ """
311
 
 
312
  results_html += f"""
313
+ </div>
314
+ <div>
315
  <h3>Fraktur Lokalisation</h3>
316
  <img src="{result_image_b64}" alt="Analyzed image">
317
  </div>
 
332
  <head>
333
  <title>Fehler</title>
334
  <style>
335
+ body {{
 
336
  font-family: system-ui, -apple-system, sans-serif;
337
+ margin: 0;
338
+ padding: 20px;
339
+ }}
340
+ .back-button {{
341
+ display: inline-block;
342
+ background: #0066cc;
343
+ color: white;
344
+ padding: 10px 20px;
345
+ border-radius: 5px;
346
+ text-decoration: none;
347
+ margin-top: 20px;
348
  }}
349
  </style>
350
  </head>
351
  <body>
352
+ <h3>Fehler</h3>
353
  <p>{str(e)}</p>
354
  <a href="/" class="back-button">← Zurück</a>
355
  </body>