3dredstone commited on
Commit
39557f3
·
verified ·
1 Parent(s): ef18197

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -38
app.py CHANGED
@@ -15,7 +15,7 @@ from reportlab.lib.units import inch
15
 
16
  app = FastAPI()
17
 
18
- # Chargement des modèles
19
  def load_models():
20
  return {
21
  "KnochenAuge": pipeline("object-detection", model="D3STRON/bone-fracture-detr"),
@@ -27,13 +27,14 @@ def load_models():
27
  models = load_models()
28
 
29
  def translate_label(label):
 
30
  translations = {
31
- "fracture": "Knochenbruch",
32
- "no fracture": "Kein Knochenbruch",
33
  "normal": "Normal",
34
- "abnormal": "Auffällig",
35
- "F1": "Knochenbruch",
36
- "NF": "Kein Knochenbruch"
37
  }
38
  return translations.get(label.lower(), label)
39
 
@@ -71,20 +72,17 @@ def draw_boxes(image, predictions):
71
 
72
  draw = ImageDraw.Draw(result_image)
73
  temp = 36.5 + (score * 2.5)
 
74
  label = f"{translate_label(pred['label'])} ({score:.1%} • {temp:.1f}°C)"
75
 
76
- # Calculate text bounding box more accurately
77
- # Temporarily create a dummy draw object to get text size if draw.textbbox is not accurate enough or available for current Pillow version
78
  try:
79
  text_bbox = draw.textbbox((box['xmin'], box['ymin'] - 20), label)
80
- except AttributeError: # Fallback for older Pillow versions
81
- # Estimate text size if textbbox is not available
82
- font_size = 10 # This might need to be adjusted based on actual font used
83
- text_width = len(label) * font_size * 0.6 # rough estimation
84
- text_height = font_size * 1.2 # rough estimation
85
  text_bbox = (box['xmin'], box['ymin'] - text_height, box['xmin'] + text_width, box['ymin'])
86
 
87
-
88
  draw.rectangle(text_bbox, fill=(0, 0, 0, 180))
89
 
90
  draw.text(
@@ -185,7 +183,7 @@ async def main():
185
  <!DOCTYPE html>
186
  <html>
187
  <head>
188
- <title>Fraktur Detektion</title>
189
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
190
  <style>
191
  {COMMON_STYLES}
@@ -268,20 +266,20 @@ async def main():
268
  <div class="upload-section">
269
  <form action="/analyze" method="post" enctype="multipart/form-data" onsubmit="document.getElementById('loading').style.display = 'block';">
270
  <div class="input-field">
271
- <label for="patient_name">Patientenname:</label>
272
  <input type="text" id="patient_name" name="patient_name" required>
273
  </div>
274
  <div>
275
  <input type="file" name="file" accept="image/*" required>
276
  </div>
277
  <div class="confidence-slider">
278
- <label for="threshold">Konfidenzschwelle: <span id="thresholdValue">0.60</span></label>
279
  <input type="range" id="threshold" name="threshold"
280
  min="0" max="1" step="0.05" value="0.60"
281
  oninput="document.getElementById('thresholdValue').textContent = parseFloat(this.value).toFixed(2)">
282
  </div>
283
  <button type="submit" class="button">
284
- Analysieren & PDF Erstellen
285
  <div class="button-progress"></div>
286
  </button>
287
  <div id="loading">Loading...</div>
@@ -344,13 +342,13 @@ async def analyze_file(patient_name: str = Form(...), file: UploadFile = File(..
344
 
345
  story = []
346
 
347
- story.append(Paragraph("<b>Fraktur Detektionsbericht</b>", heading_style))
348
  story.append(Spacer(1, 0.2 * inch))
349
- story.append(Paragraph(f"<b>Patientenname:</b> {patient_name}", subheading_style))
350
  story.append(Spacer(1, 0.4 * inch))
351
 
352
  # KnochenWächter results
353
- story.append(Paragraph("<b>KnochenWächter Ergebnisse:</b>", subheading_style))
354
  for pred in predictions_watcher:
355
  story.append(Paragraph(
356
  f"{translate_label(pred['label'])}: {pred['score']:.1%}",
@@ -359,7 +357,7 @@ async def analyze_file(patient_name: str = Form(...), file: UploadFile = File(..
359
  story.append(Spacer(1, 0.2 * inch))
360
 
361
  # RöntgenMeister results
362
- story.append(Paragraph("<b>RöntgenMeister Ergebnisse:</b>", subheading_style))
363
  for pred in predictions_master:
364
  story.append(Paragraph(
365
  f"{translate_label(pred['label'])}: {pred['score']:.1%}",
@@ -368,63 +366,59 @@ async def analyze_file(patient_name: str = Form(...), file: UploadFile = File(..
368
  story.append(Spacer(1, 0.4 * inch))
369
 
370
  # Analyzed Image
371
- story.append(Paragraph("<b>Röntgenbild Analyse:</b>", subheading_style))
372
 
373
- # Save the result image temporarily to a buffer to be added to PDF
374
  img_buffer = io.BytesIO()
375
  result_image.save(img_buffer, format="PNG")
376
  img_buffer.seek(0)
377
  img_rl = ReportLabImage(img_buffer)
378
 
379
- # Scale image to fit within page width while maintaining aspect ratio
380
  img_width, img_height = img_rl.drawWidth, img_rl.drawHeight
381
  aspect_ratio = img_height / img_width
382
- max_width = 5 * inch # Adjust as needed for page layout
383
  if img_width > max_width:
384
  img_rl.drawWidth = max_width
385
  img_rl.drawHeight = max_width * aspect_ratio
386
-
387
- # Center the image
388
  img_rl.hAlign = 'CENTER'
389
 
390
  story.append(img_rl)
391
  story.append(Spacer(1, 0.4 * inch))
392
 
393
-
394
  # Final report text based on object detection
395
  if filtered_preds:
396
  story.append(Paragraph(
397
- "<b>Die Analyse des Röntgenbildes zeigt eine mögliche Frakturlokalisation.</b>",
398
  report_text_style
399
  ))
400
  for pred in filtered_preds:
401
  score = pred['score']
402
  temp = 36.5 + (score * 2.5)
403
  story.append(Paragraph(
404
- f"Detektion: {translate_label(pred['label'])} mit {score:.1%} Konfidenz ({temp:.1f}°C)",
405
  report_text_style
406
  ))
407
  else:
408
  story.append(Paragraph(
409
- "<b>Basierend auf der Objektlokalisierungsanalyse wurde keine Fraktur mit ausreichender Konfidenz detektiert.</b>",
410
  report_text_style
411
  ))
412
  story.append(Spacer(1, 0.2 * inch))
413
- story.append(Paragraph("Dies ist ein automatisch generierter Bericht und sollte von einem Arzt überprüft werden.", centered_style))
414
 
415
 
416
  doc.build(story)
417
  buffer.seek(0)
418
 
419
  return StreamingResponse(buffer, media_type="application/pdf",
420
- headers={"Content-Disposition": f"attachment; filename=Fraktur_Bericht_{patient_name.replace(' ', '_')}.pdf"})
421
 
422
  except Exception as e:
423
  return HTMLResponse(f"""
424
  <!DOCTYPE html>
425
  <html>
426
  <head>
427
- <title>Fehler</title>
428
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
429
  <style>
430
  {COMMON_STYLES}
@@ -440,11 +434,11 @@ async def analyze_file(patient_name: str = Form(...), file: UploadFile = File(..
440
  <body>
441
  <div class="container">
442
  <div class="error-box">
443
- <h3>Fehler</h3>
444
  <p>{str(e)}</p>
445
  </div>
446
  <a href="/" class="button back-button">
447
- Zurück
448
  <div class="button-progress"></div>
449
  </a>
450
  </div>
@@ -454,4 +448,3 @@ async def analyze_file(patient_name: str = Form(...), file: UploadFile = File(..
454
 
455
  if __name__ == "__main__":
456
  uvicorn.run(app, host="0.0.0.0", port=7860)
457
-
 
15
 
16
  app = FastAPI()
17
 
18
+ # Load models
19
  def load_models():
20
  return {
21
  "KnochenAuge": pipeline("object-detection", model="D3STRON/bone-fracture-detr"),
 
27
  models = load_models()
28
 
29
  def translate_label(label):
30
+ # Keep translations for internal use if needed, but for the PDF we'll use English
31
  translations = {
32
+ "fracture": "Fracture",
33
+ "no fracture": "No Fracture",
34
  "normal": "Normal",
35
+ "abnormal": "Abnormal",
36
+ "F1": "Fracture", # Assuming F1 also means fracture
37
+ "NF": "No Fracture" # Assuming NF means no fracture
38
  }
39
  return translations.get(label.lower(), label)
40
 
 
72
 
73
  draw = ImageDraw.Draw(result_image)
74
  temp = 36.5 + (score * 2.5)
75
+ # Label in English
76
  label = f"{translate_label(pred['label'])} ({score:.1%} • {temp:.1f}°C)"
77
 
 
 
78
  try:
79
  text_bbox = draw.textbbox((box['xmin'], box['ymin'] - 20), label)
80
+ except AttributeError:
81
+ font_size = 10
82
+ text_width = len(label) * font_size * 0.6
83
+ text_height = font_size * 1.2
 
84
  text_bbox = (box['xmin'], box['ymin'] - text_height, box['xmin'] + text_width, box['ymin'])
85
 
 
86
  draw.rectangle(text_bbox, fill=(0, 0, 0, 180))
87
 
88
  draw.text(
 
183
  <!DOCTYPE html>
184
  <html>
185
  <head>
186
+ <title>Fracture Detection</title>
187
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
188
  <style>
189
  {COMMON_STYLES}
 
266
  <div class="upload-section">
267
  <form action="/analyze" method="post" enctype="multipart/form-data" onsubmit="document.getElementById('loading').style.display = 'block';">
268
  <div class="input-field">
269
+ <label for="patient_name">Patient Name:</label>
270
  <input type="text" id="patient_name" name="patient_name" required>
271
  </div>
272
  <div>
273
  <input type="file" name="file" accept="image/*" required>
274
  </div>
275
  <div class="confidence-slider">
276
+ <label for="threshold">Confidence Threshold: <span id="thresholdValue">0.60</span></label>
277
  <input type="range" id="threshold" name="threshold"
278
  min="0" max="1" step="0.05" value="0.60"
279
  oninput="document.getElementById('thresholdValue').textContent = parseFloat(this.value).toFixed(2)">
280
  </div>
281
  <button type="submit" class="button">
282
+ Analyze & Generate PDF
283
  <div class="button-progress"></div>
284
  </button>
285
  <div id="loading">Loading...</div>
 
342
 
343
  story = []
344
 
345
+ story.append(Paragraph("<b>Fracture Detection Report</b>", heading_style))
346
  story.append(Spacer(1, 0.2 * inch))
347
+ story.append(Paragraph(f"<b>Patient Name:</b> {patient_name}", subheading_style))
348
  story.append(Spacer(1, 0.4 * inch))
349
 
350
  # KnochenWächter results
351
+ story.append(Paragraph("<b>KnochenWächter Results:</b>", subheading_style))
352
  for pred in predictions_watcher:
353
  story.append(Paragraph(
354
  f"{translate_label(pred['label'])}: {pred['score']:.1%}",
 
357
  story.append(Spacer(1, 0.2 * inch))
358
 
359
  # RöntgenMeister results
360
+ story.append(Paragraph("<b>RöntgenMeister Results:</b>", subheading_style))
361
  for pred in predictions_master:
362
  story.append(Paragraph(
363
  f"{translate_label(pred['label'])}: {pred['score']:.1%}",
 
366
  story.append(Spacer(1, 0.4 * inch))
367
 
368
  # Analyzed Image
369
+ story.append(Paragraph("<b>X-ray Image Analysis:</b>", subheading_style))
370
 
 
371
  img_buffer = io.BytesIO()
372
  result_image.save(img_buffer, format="PNG")
373
  img_buffer.seek(0)
374
  img_rl = ReportLabImage(img_buffer)
375
 
 
376
  img_width, img_height = img_rl.drawWidth, img_rl.drawHeight
377
  aspect_ratio = img_height / img_width
378
+ max_width = 5 * inch
379
  if img_width > max_width:
380
  img_rl.drawWidth = max_width
381
  img_rl.drawHeight = max_width * aspect_ratio
382
+
 
383
  img_rl.hAlign = 'CENTER'
384
 
385
  story.append(img_rl)
386
  story.append(Spacer(1, 0.4 * inch))
387
 
 
388
  # Final report text based on object detection
389
  if filtered_preds:
390
  story.append(Paragraph(
391
+ "<b>The X-ray image analysis shows potential fracture localization.</b>",
392
  report_text_style
393
  ))
394
  for pred in filtered_preds:
395
  score = pred['score']
396
  temp = 36.5 + (score * 2.5)
397
  story.append(Paragraph(
398
+ f"Detection: {translate_label(pred['label'])} with {score:.1%} confidence ({temp:.1f}°C)",
399
  report_text_style
400
  ))
401
  else:
402
  story.append(Paragraph(
403
+ "<b>Based on object localization analysis, no fracture was detected with sufficient confidence.</b>",
404
  report_text_style
405
  ))
406
  story.append(Spacer(1, 0.2 * inch))
407
+ story.append(Paragraph("This is an automatically generated report and should be reviewed by a medical professional.", centered_style))
408
 
409
 
410
  doc.build(story)
411
  buffer.seek(0)
412
 
413
  return StreamingResponse(buffer, media_type="application/pdf",
414
+ headers={"Content-Disposition": f"attachment; filename=Fracture_Report_{patient_name.replace(' ', '_')}.pdf"})
415
 
416
  except Exception as e:
417
  return HTMLResponse(f"""
418
  <!DOCTYPE html>
419
  <html>
420
  <head>
421
+ <title>Error</title>
422
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
423
  <style>
424
  {COMMON_STYLES}
 
434
  <body>
435
  <div class="container">
436
  <div class="error-box">
437
+ <h3>Error</h3>
438
  <p>{str(e)}</p>
439
  </div>
440
  <a href="/" class="button back-button">
441
+ Back
442
  <div class="button-progress"></div>
443
  </a>
444
  </div>
 
448
 
449
  if __name__ == "__main__":
450
  uvicorn.run(app, host="0.0.0.0", port=7860)