ftx7go commited on
Commit
554d7bc
·
verified ·
1 Parent(s): 355fc13

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -403
app.py DELETED
@@ -1,403 +0,0 @@
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
- COMMON_STYLES = """
88
- body {
89
- font-family: system-ui, -apple-system, sans-serif;
90
- background: #f0f2f5;
91
- margin: 0;
92
- padding: 20px;
93
- color: #1a1a1a;
94
- }
95
- ::-webkit-scrollbar {
96
- width: 8px;
97
- height: 8px;
98
- }
99
-
100
- ::-webkit-scrollbar-track {
101
- background: transparent;
102
- }
103
-
104
- ::-webkit-scrollbar-thumb {
105
- background-color: rgba(156, 163, 175, 0.5);
106
- border-radius: 4px;
107
- }
108
-
109
- .container {
110
- max-width: 1200px;
111
- margin: 0 auto;
112
- background: white;
113
- padding: 20px;
114
- border-radius: 10px;
115
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
116
- }
117
- .button {
118
- background: #2d2d2d;
119
- color: white;
120
- border: none;
121
- padding: 12px 30px;
122
- border-radius: 8px;
123
- cursor: pointer;
124
- font-size: 1.1em;
125
- transition: all 0.3s ease;
126
- position: relative;
127
- }
128
- .button:hover {
129
- background: #404040;
130
- }
131
- @keyframes progress {
132
- 0% { width: 0; }
133
- 100% { width: 100%; }
134
- }
135
- .button-progress {
136
- position: absolute;
137
- bottom: 0;
138
- left: 0;
139
- height: 4px;
140
- background: rgba(255, 255, 255, 0.5);
141
- width: 0;
142
- }
143
- .button:active .button-progress {
144
- animation: progress 2s linear forwards;
145
- }
146
- img {
147
- max-width: 100%;
148
- height: auto;
149
- border-radius: 8px;
150
- }
151
- @keyframes blink {
152
- 0% { opacity: 1; }
153
- 50% { opacity: 0; }
154
- 100% { opacity: 1; }
155
- }
156
- #loading {
157
- display: none;
158
- color: white;
159
- margin-top: 10px;
160
- animation: blink 1s infinite;
161
- text-align: center;
162
- }
163
- """
164
-
165
- @app.get("/", response_class=HTMLResponse)
166
- async def main():
167
- content = f"""
168
- <!DOCTYPE html>
169
- <html>
170
- <head>
171
- <title>Fraktur Detektion</title>
172
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
173
- <style>
174
- {COMMON_STYLES}
175
-
176
- .upload-section {{
177
- background: #2d2d2d;
178
- padding: 40px;
179
- border-radius: 12px;
180
- margin: 20px 0;
181
- text-align: center;
182
- border: 2px dashed #404040;
183
- transition: all 0.3s ease;
184
- color: white;
185
- }}
186
- .upload-section:hover {{
187
- border-color: #555;
188
- }}
189
- input[type="file"] {{
190
- font-size: 1.1em;
191
- margin: 20px 0;
192
- color: white;
193
- }}
194
- input[type="file"]::file-selector-button {{
195
- font-size: 1em;
196
- padding: 10px 20px;
197
- border-radius: 8px;
198
- border: 1px solid #404040;
199
- background: #2d2d2d;
200
- color: white;
201
- transition: all 0.3s ease;
202
- cursor: pointer;
203
- }}
204
- input[type="file"]::file-selector-button:hover {{
205
- background: #404040;
206
- }}
207
- .confidence-slider {{
208
- width: 100%;
209
- max-width: 300px;
210
- margin: 20px auto;
211
- }}
212
- input[type="range"] {{
213
- width: 100%;
214
- height: 8px;
215
- border-radius: 4px;
216
- background: #404040;
217
- outline: none;
218
- transition: all 0.3s ease;
219
- -webkit-appearance: none;
220
- }}
221
- input[type="range"]::-webkit-slider-thumb {{
222
- -webkit-appearance: none;
223
- width: 20px;
224
- height: 20px;
225
- border-radius: 50%;
226
- background: white;
227
- cursor: pointer;
228
- border: none;
229
- }}
230
- </style>
231
- </head>
232
- <body>
233
- <div class="container">
234
- <div class="upload-section">
235
- <form action="/analyze" method="post" enctype="multipart/form-data" onsubmit="document.getElementById('loading').style.display = 'block';">
236
- <div>
237
- <input type="file" name="file" accept="image/*" required>
238
- </div>
239
- <div class="confidence-slider">
240
- <label for="threshold">Konfidenzschwelle: <span id="thresholdValue">0.60</span></label>
241
- <input type="range" id="threshold" name="threshold"
242
- min="0" max="1" step="0.05" value="0.60"
243
- oninput="document.getElementById('thresholdValue').textContent = parseFloat(this.value).toFixed(2)">
244
- </div>
245
- <button type="submit" class="button">
246
- Analysieren
247
- <div class="button-progress"></div>
248
- </button>
249
- <div id="loading">Loading...</div>
250
- </form>
251
- </div>
252
- </div>
253
- </body>
254
- </html>
255
- """
256
- return content
257
-
258
- @app.post("/analyze", response_class=HTMLResponse)
259
- async def analyze_file(file: UploadFile = File(...)):
260
- try:
261
- contents = await file.read()
262
- image = Image.open(io.BytesIO(contents))
263
-
264
- predictions_watcher = models["KnochenWächter"](image)
265
- predictions_master = models["RöntgenMeister"](image)
266
- predictions_locator = models["KnochenAuge"](image)
267
-
268
- filtered_preds = [p for p in predictions_locator if p['score'] >= 0.6]
269
- if filtered_preds:
270
- result_image = draw_boxes(image, filtered_preds)
271
- else:
272
- result_image = image
273
-
274
- result_image_b64 = image_to_base64(result_image)
275
-
276
- results_html = f"""
277
- <!DOCTYPE html>
278
- <html>
279
- <head>
280
- <title>Ergebnisse</title>
281
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
282
- <style>
283
- {COMMON_STYLES}
284
-
285
- .results-grid {{
286
- display: grid;
287
- grid-template-columns: 1fr 1fr;
288
- gap: 20px;
289
- margin-top: 20px;
290
- }}
291
- .result-box {{
292
- background: white;
293
- padding: 20px;
294
- border-radius: 12px;
295
- margin: 10px 0;
296
- border: 1px solid #e9ecef;
297
- }}
298
- .score-high {{
299
- color: #0066cc;
300
- font-weight: bold;
301
- }}
302
-
303
- .score-medium {{
304
- color: #ffa500;
305
- font-weight: bold;
306
- }}
307
- .back-button {{
308
- display: inline-block;
309
- text-decoration: none;
310
- margin-top: 20px;
311
- }}
312
- h3 {{
313
- color: #0066cc;
314
- margin-top: 0;
315
- }}
316
- @media (max-width: 768px) {{
317
- .results-grid {{
318
- grid-template-columns: 1fr;
319
- }}
320
- }}
321
- </style>
322
- </head>
323
- <body>
324
- <div class="container">
325
- <div class="results-grid">
326
- <div>
327
- <div class="result-box"><h3>KnochenWächter</h3>
328
- """
329
-
330
- for pred in predictions_watcher:
331
- confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
332
- results_html += f"""
333
- <div>
334
- <span class="{confidence_class}">{pred['score']:.1%}</span> -
335
- {translate_label(pred['label'])}
336
- </div>
337
- """
338
- results_html += "</div>"
339
-
340
- results_html += "<div class='result-box'><h3>RöntgenMeister</h3>"
341
- for pred in predictions_master:
342
- confidence_class = "score-high" if pred['score'] > 0.7 else "score-medium"
343
- results_html += f"""
344
- <div>
345
- <span class="{confidence_class}">{pred['score']:.1%}</span> -
346
- {translate_label(pred['label'])}
347
- </div>
348
- """
349
- results_html += "</div></div>"
350
-
351
- results_html += f"""
352
- <div class='result-box'>
353
- <h3>Fraktur Lokalisation</h3>
354
- <img src="{result_image_b64}" alt="Analyzed image">
355
- </div>
356
- </div>
357
-
358
- <a href="/" class="button back-button">
359
- ← Zurück
360
- <div class="button-progress"></div>
361
- </a>
362
- </div>
363
- </body>
364
- </html>
365
- """
366
-
367
- return results_html
368
-
369
- except Exception as e:
370
- return f"""
371
- <!DOCTYPE html>
372
- <html>
373
- <head>
374
- <title>Fehler</title>
375
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
376
- <style>
377
- {COMMON_STYLES}
378
- .error-box {{
379
- background: #fee2e2;
380
- border: 1px solid #ef4444;
381
- padding: 20px;
382
- border-radius: 8px;
383
- margin: 20px 0;
384
- }}
385
- </style>
386
- </head>
387
- <body>
388
- <div class="container">
389
- <div class="error-box">
390
- <h3>Fehler</h3>
391
- <p>{str(e)}</p>
392
- </div>
393
- <a href="/" class="button back-button">
394
- ← Zurück
395
- <div class="button-progress"></div>
396
- </a>
397
- </div>
398
- </body>
399
- </html>
400
- """
401
-
402
- if __name__ == "__main__":
403
- uvicorn.run(app, host="0.0.0.0", port=7860)