Ziad Meligy commited on
Commit
733a570
·
1 Parent(s): 8390b91

dicm endpoint

Browse files
Files changed (2) hide show
  1. main.py +146 -0
  2. requirements.txt +6 -1
main.py CHANGED
@@ -2,6 +2,82 @@ from fastapi import FastAPI, File, UploadFile
2
  from fastapi.responses import JSONResponse
3
  from inference_service import generate_report_serviceFn
4
  from PIL import Image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  app = FastAPI()
6
  @app.post("/generate_report")
7
  async def generate_report(file: UploadFile = File(...)):
@@ -16,3 +92,73 @@ async def generate_report(file: UploadFile = File(...)):
16
  except Exception as e:
17
  return JSONResponse({"error": str(e)}, status_code=500)
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from fastapi.responses import JSONResponse
3
  from inference_service import generate_report_serviceFn
4
  from PIL import Image
5
+ import pydicom
6
+ import tempfile
7
+ from fastapi import HTTPException
8
+ import numpy as np
9
+
10
+
11
+ def is_xray_dicom(dicom_data):
12
+ """
13
+ Check if the DICOM file is an X-ray image based on metadata
14
+ """
15
+ try:
16
+ # Check modality
17
+ if hasattr(dicom_data, 'Modality') and dicom_data.Modality in ['CR', 'DX', 'RF']:
18
+ return True
19
+
20
+ # Check body part examined for chest X-ray indicators
21
+ if hasattr(dicom_data, 'BodyPartExamined'):
22
+ chest_keywords = ['CHEST', 'THORAX', 'LUNG', 'PULMONARY']
23
+ body_part = str(dicom_data.BodyPartExamined).upper()
24
+ if any(keyword in body_part for keyword in chest_keywords):
25
+ return True
26
+
27
+ # Check study description
28
+ if hasattr(dicom_data, 'StudyDescription'):
29
+ study_desc = str(dicom_data.StudyDescription).upper()
30
+ xray_keywords = ['X-RAY', 'XRAY', 'RADIOGRAPH', 'CHEST']
31
+ if any(keyword in study_desc for keyword in xray_keywords):
32
+ return True
33
+
34
+ return False
35
+ except Exception:
36
+ return False
37
+
38
+ def dicom_to_png(dicom_data):
39
+ """
40
+ Convert DICOM image data to PNG format
41
+ """
42
+ try:
43
+ # Ensure pixel data exists
44
+ if not hasattr(dicom_data, "PixelData"):
45
+ raise ValueError("No pixel data found in DICOM file.")
46
+
47
+ # Get and squeeze pixel array
48
+ pixel_array = dicom_data.pixel_array
49
+ pixel_array = np.squeeze(pixel_array) # Removes (1, 1, 512) → (512)
50
+
51
+ if pixel_array.ndim == 3:
52
+ pixel_array = pixel_array[pixel_array.shape[0] // 2]
53
+ if pixel_array.ndim == 1:
54
+ raise ValueError(f"Unsupported 1D image shape: {pixel_array.shape}")
55
+
56
+ # Normalize pixel values to 0-255 range
57
+ if pixel_array.dtype != np.uint8:
58
+ max_val = 2 ** dicom_data.get("BitsStored", 16) - 1
59
+ pixel_array = ((pixel_array - pixel_array.min()) / (max_val - pixel_array.min()) * 255).astype(np.uint8)
60
+
61
+ # Handle MONOCHROME1
62
+ if dicom_data.get("PhotometricInterpretation") == "MONOCHROME1":
63
+ pixel_array = 255 - pixel_array
64
+
65
+ # Convert to PIL image
66
+ if pixel_array.ndim == 2:
67
+ image = Image.fromarray(pixel_array, mode="L")
68
+ elif pixel_array.ndim == 3 and pixel_array.shape[-1] in [3, 4]: # RGB or RGBA
69
+ image = Image.fromarray(pixel_array)
70
+ else:
71
+ raise ValueError(f"Unsupported image shape: {pixel_array.shape}")
72
+
73
+ return image.convert("RGB")
74
+
75
+ except Exception as e:
76
+ raise ValueError(f"Failed to convert DICOM to PNG: {str(e)}")
77
+
78
+
79
+
80
+
81
  app = FastAPI()
82
  @app.post("/generate_report")
83
  async def generate_report(file: UploadFile = File(...)):
 
92
  except Exception as e:
93
  return JSONResponse({"error": str(e)}, status_code=500)
94
 
95
+
96
+ @app.post("/generate_report_dicom")
97
+ async def generate_report_dicom(file: UploadFile = File(...)):
98
+ """
99
+ Generate radiology report from DICOM file.
100
+ Checks if the DICOM is an X-ray, converts it to PNG, and generates a report.
101
+ """
102
+ try:
103
+ # Validate file type
104
+ if not file.filename.lower().endswith(('.dcm', '.dicom')):
105
+ raise HTTPException(status_code=400, detail="File must be a DICOM file (.dcm or .dicom)")
106
+
107
+ # Read DICOM file
108
+ file_content = await file.read()
109
+
110
+ # Create a temporary file to store DICOM data
111
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.dcm') as temp_file:
112
+ temp_file.write(file_content)
113
+ temp_file_path = temp_file.name
114
+
115
+ try:
116
+ # Read DICOM data
117
+ dicom_data = pydicom.dcmread(temp_file_path)
118
+
119
+ # # Check if it's an X-ray
120
+ # if not is_xray_dicom(dicom_data):
121
+ # return JSONResponse(
122
+ # {"error": "DICOM file does not appear to be an X-ray image"},
123
+ # status_code=400
124
+ # )
125
+
126
+ # Convert DICOM to PNG
127
+ image = dicom_to_png(dicom_data)
128
+
129
+ # Generate the report using the service function
130
+ report = generate_report_serviceFn(image)
131
+
132
+ # Get additional DICOM metadata for context
133
+ metadata = {}
134
+ if hasattr(dicom_data, 'PatientID'):
135
+ metadata['patient_id'] = str(dicom_data.PatientID)
136
+ if hasattr(dicom_data, 'StudyDate'):
137
+ metadata['study_date'] = str(dicom_data.StudyDate)
138
+ if hasattr(dicom_data, 'Modality'):
139
+ metadata['modality'] = str(dicom_data.Modality)
140
+ if hasattr(dicom_data, 'BodyPartExamined'):
141
+ metadata['body_part'] = str(dicom_data.BodyPartExamined)
142
+
143
+ return JSONResponse({
144
+ "generated_report": report,
145
+ "dicom_metadata": metadata,
146
+ "image_info": {
147
+ "width": image.width,
148
+ "height": image.height,
149
+ "mode": image.mode
150
+ }
151
+ })
152
+
153
+ finally:
154
+ # Clean up temporary file
155
+ import os
156
+ try:
157
+ os.unlink(temp_file_path)
158
+ except:
159
+ pass
160
+
161
+ except HTTPException:
162
+ raise
163
+ except Exception as e:
164
+ return JSONResponse({"error": str(e)}, status_code=500)
requirements.txt CHANGED
@@ -3,4 +3,9 @@ pillow
3
  torch
4
  transformers
5
  python-multipart
6
- uvicorn
 
 
 
 
 
 
3
  torch
4
  transformers
5
  python-multipart
6
+ uvicorn
7
+ nltk
8
+ scikit-learn
9
+ numpy
10
+ pandas
11
+ pydicom