yassonee commited on
Commit
d813e66
·
verified ·
1 Parent(s): d21ffe2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +301 -28
app.py CHANGED
@@ -1,68 +1,341 @@
1
  from fastapi import FastAPI, File, UploadFile
2
  from fastapi.responses import HTMLResponse
3
  from transformers import pipeline
4
- from PIL import Image
 
5
  import io
6
  import uvicorn
 
7
 
8
  app = FastAPI()
9
 
10
  # Chargement des modèles
11
  def load_models():
12
  return {
13
- "model": pipeline("image-classification", model="Heem2/bone-fracture-detection-using-xray")
 
 
 
14
  }
15
 
16
  models = load_models()
17
 
18
- # Page d'accueil simple
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  @app.get("/", response_class=HTMLResponse)
20
  async def main():
21
  content = """
22
- <body style="font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px;">
23
- <h1>Fraktur Detektion</h1>
24
- <form action="/upload" enctype="multipart/form-data" method="post">
25
- <input type="file" name="file" accept="image/*"><br><br>
26
- <input type="submit" value="Analysieren">
27
- </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  </body>
 
29
  """
30
  return content
31
 
32
- # Route pour l'upload et l'analyse
33
- @app.post("/upload", response_class=HTMLResponse)
34
- async def upload_file(file: UploadFile = File(...)):
35
  try:
36
  # Lecture de l'image
37
  contents = await file.read()
38
  image = Image.open(io.BytesIO(contents))
39
 
40
- # Analyse
41
- predictions = models["model"](image)
 
 
42
 
43
- # Formatage des résultats
44
- results = "<h2>Ergebnisse:</h2>"
45
- for pred in predictions:
46
- confidence = pred['score'] * 100
47
- label = "Knochenbruch" if "fracture" in pred['label'].lower() else "Kein Knochenbruch"
48
- color = "red" if "fracture" in pred['label'].lower() else "green"
49
- results += f'<p style="color: {color};">{label}: {confidence:.1f}%</p>'
 
 
50
 
51
- return f"""
52
- <body style="font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px;">
53
- <h1>Analyse Ergebnisse</h1>
54
- {results}
55
- <br>
56
- <a href="/">Zurück</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  </body>
 
58
  """
 
 
 
59
  except Exception as e:
60
  return f"""
61
  <body style="font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px;">
62
  <h1>Fehler</h1>
63
  <p>Ein Fehler ist aufgetreten: {str(e)}</p>
64
  <br>
65
- <a href="/">Zurück</a>
66
  </body>
67
  """
68
 
 
1
  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 numpy as np
6
  import io
7
  import uvicorn
8
+ import base64
9
 
10
  app = FastAPI()
11
 
12
  # Chargement des modèles
13
  def load_models():
14
  return {
15
+ "KnochenAuge": pipeline("object-detection", model="D3STRON/bone-fracture-detr"),
16
+ "KnochenWächter": pipeline("image-classification", model="Heem2/bone-fracture-detection-using-xray"),
17
+ "RöntgenMeister": pipeline("image-classification",
18
+ model="nandodeomkar/autotrain-fracture-detection-using-google-vit-base-patch-16-54382127388")
19
  }
20
 
21
  models = load_models()
22
 
23
+ def translate_label(label):
24
+ translations = {
25
+ "fracture": "Knochenbruch",
26
+ "no fracture": "Kein Knochenbruch",
27
+ "normal": "Normal",
28
+ "abnormal": "Auffällig",
29
+ "F1": "Knochenbruch",
30
+ "NF": "Kein 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
+ # Page d'accueil avec interface améliorée
88
  @app.get("/", response_class=HTMLResponse)
89
  async def main():
90
  content = """
91
+ <!DOCTYPE html>
92
+ <html>
93
+ <head>
94
+ <title>Fraktur Detektion</title>
95
+ <style>
96
+ body {
97
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
98
+ background: #f0f2f5;
99
+ margin: 0;
100
+ padding: 20px;
101
+ color: #1a1a1a;
102
+ }
103
+ .container {
104
+ max-width: 1200px;
105
+ margin: 0 auto;
106
+ background: white;
107
+ padding: 20px;
108
+ border-radius: 10px;
109
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
110
+ }
111
+ .upload-section {
112
+ background: #f8f9fa;
113
+ padding: 20px;
114
+ border-radius: 8px;
115
+ margin: 20px 0;
116
+ text-align: center;
117
+ }
118
+ .result-box {
119
+ background: #f8f9fa;
120
+ padding: 15px;
121
+ border-radius: 8px;
122
+ margin: 10px 0;
123
+ border: 1px solid #e9ecef;
124
+ }
125
+ .button {
126
+ background: #0066cc;
127
+ color: white;
128
+ border: none;
129
+ padding: 10px 20px;
130
+ border-radius: 5px;
131
+ cursor: pointer;
132
+ transition: all 0.3s ease;
133
+ font-size: 16px;
134
+ }
135
+ .button:hover {
136
+ background: #0052a3;
137
+ transform: translateY(-1px);
138
+ }
139
+ .results-grid {
140
+ display: grid;
141
+ grid-template-columns: 1fr 1fr;
142
+ gap: 20px;
143
+ margin-top: 20px;
144
+ }
145
+ .confidence-slider {
146
+ width: 100%;
147
+ max-width: 300px;
148
+ margin: 20px auto;
149
+ }
150
+ img {
151
+ max-width: 100%;
152
+ border-radius: 8px;
153
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
154
+ }
155
+ .loading {
156
+ display: none;
157
+ text-align: center;
158
+ padding: 20px;
159
+ font-weight: bold;
160
+ }
161
+ .score-high { color: #0066cc; }
162
+ .score-medium { color: #ffa500; }
163
+ .score-low { color: #dc3545; }
164
+ </style>
165
+ </head>
166
+ <body>
167
+ <div class="container">
168
+ <h1>📤 Fraktur Detektion</h1>
169
+
170
+ <div class="upload-section">
171
+ <form action="/analyze" method="post" enctype="multipart/form-data">
172
+ <div>
173
+ <input type="file" name="file" accept="image/*" required>
174
+ </div>
175
+ <div class="confidence-slider">
176
+ <label for="threshold">Konfidenzschwelle: <span id="thresholdValue">0.60</span></label>
177
+ <input type="range" id="threshold" name="threshold"
178
+ min="0" max="1" step="0.05" value="0.60"
179
+ oninput="updateThreshold(this.value)">
180
+ </div>
181
+ <button type="submit" class="button">Analysieren</button>
182
+ </form>
183
+ </div>
184
+
185
+ <div id="loading" class="loading">
186
+ Bild wird analysiert... ⏳
187
+ </div>
188
+
189
+ <script>
190
+ function updateThreshold(value) {
191
+ document.getElementById('thresholdValue').textContent = parseFloat(value).toFixed(2);
192
+ }
193
+
194
+ document.querySelector('form').onsubmit = function() {
195
+ document.getElementById('loading').style.display = 'block';
196
+ }
197
+ </script>
198
+ </div>
199
  </body>
200
+ </html>
201
  """
202
  return content
203
 
204
+ @app.post("/analyze", response_class=HTMLResponse)
205
+ async def analyze_file(file: UploadFile = File(...)):
 
206
  try:
207
  # Lecture de l'image
208
  contents = await file.read()
209
  image = Image.open(io.BytesIO(contents))
210
 
211
+ # Analyse avec tous les modèles
212
+ predictions_watcher = models["KnochenWächter"](image)
213
+ predictions_master = models["RöntgenMeister"](image)
214
+ predictions_locator = models["KnochenAuge"](image)
215
 
216
+ # Création de l'image annotée
217
+ filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
218
+ if filtered_preds:
219
+ result_image = draw_boxes(image, filtered_preds)
220
+ else:
221
+ result_image = image
222
+
223
+ # Conversion des images en base64
224
+ result_image_b64 = image_to_base64(result_image)
225
 
226
+ # Construction du HTML pour les résultats
227
+ results_html = """
228
+ <!DOCTYPE html>
229
+ <html>
230
+ <head>
231
+ <title>Analyse Ergebnisse</title>
232
+ <style>
233
+ body {
234
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
235
+ background: #f0f2f5;
236
+ margin: 0;
237
+ padding: 20px;
238
+ color: #1a1a1a;
239
+ }
240
+ .container {
241
+ max-width: 1200px;
242
+ margin: 0 auto;
243
+ background: white;
244
+ padding: 20px;
245
+ border-radius: 10px;
246
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
247
+ }
248
+ .results-grid {
249
+ display: grid;
250
+ grid-template-columns: 1fr 1fr;
251
+ gap: 20px;
252
+ margin-top: 20px;
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
+ .score-high { color: #0066cc; font-weight: bold; }
262
+ .score-medium { color: #ffa500; font-weight: bold; }
263
+ .back-button {
264
+ display: inline-block;
265
+ background: #0066cc;
266
+ color: white;
267
+ padding: 10px 20px;
268
+ border-radius: 5px;
269
+ text-decoration: none;
270
+ margin-top: 20px;
271
+ transition: all 0.3s ease;
272
+ }
273
+ .back-button:hover {
274
+ background: #0052a3;
275
+ transform: translateY(-1px);
276
+ }
277
+ img {
278
+ max-width: 100%;
279
+ border-radius: 8px;
280
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
281
+ }
282
+ </style>
283
+ </head>
284
+ <body>
285
+ <div class="container">
286
+ <h1>🔍 Analyse Ergebnisse</h1>
287
+
288
+ <div class="results-grid">
289
+ <div>
290
+ <h2>🤖 KI-Diagnose</h2>
291
+ """
292
+
293
+ # KnochenWächter results
294
+ results_html += "<h3>🛡️ KnochenWächter</h3>"
295
+ for pred in predictions_watcher:
296
+ confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
297
+ results_html += f"""
298
+ <div class="result-box">
299
+ <span class="{confidence_class}">{pred['score']:.1%}</span> -
300
+ {translate_label(pred['label'])}
301
+ </div>
302
+ """
303
+
304
+ # RöntgenMeister results
305
+ results_html += "<h3>🎓 RöntgenMeister</h3>"
306
+ for pred in predictions_master:
307
+ confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
308
+ results_html += f"""
309
+ <div class="result-box">
310
+ <span class="{confidence_class}">{pred['score']:.1%}</span> -
311
+ {translate_label(pred['label'])}
312
+ </div>
313
+ """
314
+
315
+ # Add image and close HTML
316
+ results_html += f"""
317
+ </div>
318
+ <div>
319
+ <h2>🎯 Fraktur Lokalisation</h2>
320
+ <img src="{result_image_b64}" alt="Analyzed image">
321
+ </div>
322
+ </div>
323
+
324
+ <a href="/" class="back-button">← Zurück</a>
325
+ </div>
326
  </body>
327
+ </html>
328
  """
329
+
330
+ return results_html
331
+
332
  except Exception as e:
333
  return f"""
334
  <body style="font-family: Arial; max-width: 800px; margin: 0 auto; padding: 20px;">
335
  <h1>Fehler</h1>
336
  <p>Ein Fehler ist aufgetreten: {str(e)}</p>
337
  <br>
338
+ <a href="/" class="back-button">← Zurück</a>
339
  </body>
340
  """
341