rezvan98 commited on
Commit
ddbe9e9
·
0 Parent(s):

Medical image processing backend API

Browse files
Files changed (5) hide show
  1. .gitattributes +35 -0
  2. Dockerfile +29 -0
  3. README.md +166 -0
  4. app.py +242 -0
  5. requirements.txt +6 -0
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies for OpenCV
7
+ RUN apt-get update && apt-get install -y \
8
+ libglib2.0-0 \
9
+ libsm6 \
10
+ libxext6 \
11
+ libxrender1 \
12
+ libgomp1 \
13
+ libgl1 \
14
+ && rm -rf /var/lib/apt/lists/*
15
+
16
+ # Copy requirements file
17
+ COPY requirements.txt .
18
+
19
+ # Install Python dependencies
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ # Copy application code
23
+ COPY app.py .
24
+
25
+ # Expose port 7860 (Hugging Face Spaces default)
26
+ EXPOSE 7860
27
+
28
+ # Run the application
29
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Medical Phase Simulator Backend
3
+ emoji: 🏥
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ # MedTech Image Processing Backend
11
+
12
+ FastAPI-based backend service for processing medical images with arterial and venous phase filters.
13
+
14
+ ## Features
15
+
16
+ - **Arterial Phase Processing**: CLAHE-based contrast enhancement for improved visibility
17
+ - **Venous Phase Processing**: Gaussian smoothing/blur for noise reduction
18
+ - **RESTful API**: Simple POST endpoint for image processing
19
+ - **CORS Enabled**: Works with frontend hosted on GitHub Pages
20
+ - **Docker Ready**: Containerized for easy deployment on Hugging Face Spaces
21
+
22
+ ## API Endpoints
23
+
24
+ ### `POST /process`
25
+
26
+ Process a medical image with specified phase filter.
27
+
28
+ **Parameters:**
29
+ - `image` (file): Medical image file (JPG/PNG)
30
+ - `phase` (form): Processing phase - `"arterial"` or `"venous"`
31
+
32
+ **Response:**
33
+ - Processed image as PNG
34
+
35
+ **Example using curl:**
36
+ ```bash
37
+ curl -X POST "http://localhost:7860/process" \
38
+ -F "image=@sample.jpg" \
39
+ -F "phase=arterial" \
40
+ --output processed.png
41
+ ```
42
+
43
+ ### `GET /`
44
+
45
+ Health check and API information.
46
+
47
+ ### `GET /health`
48
+
49
+ Detailed health check with library versions.
50
+
51
+ ## Local Development
52
+
53
+ ### Prerequisites
54
+ - Python 3.10+
55
+ - pip
56
+
57
+ ### Setup
58
+
59
+ 1. Install dependencies:
60
+ ```bash
61
+ pip install -r requirements.txt
62
+ ```
63
+
64
+ 2. Run the server:
65
+ ```bash
66
+ python app.py
67
+ ```
68
+
69
+ The API will be available at `http://localhost:7860`
70
+
71
+ ### Using uvicorn directly:
72
+ ```bash
73
+ uvicorn app:app --reload --port 7860
74
+ ```
75
+
76
+ ## Docker Deployment
77
+
78
+ ### Build locally:
79
+ ```bash
80
+ docker build -t medtech-backend .
81
+ docker run -p 7860:7860 medtech-backend
82
+ ```
83
+
84
+ ## Hugging Face Spaces Deployment
85
+
86
+ 1. Create a new Space at [huggingface.co/spaces](https://huggingface.co/spaces)
87
+ 2. Select **Docker** as the SDK
88
+ 3. Clone the Space repository:
89
+ ```bash
90
+ git clone https://huggingface.co/spaces/YOUR-USERNAME/YOUR-SPACE-NAME
91
+ ```
92
+
93
+ 4. Copy backend files:
94
+ ```bash
95
+ cp app.py Dockerfile requirements.txt YOUR-SPACE-NAME/
96
+ cd YOUR-SPACE-NAME
97
+ ```
98
+
99
+ 5. Commit and push:
100
+ ```bash
101
+ git add .
102
+ git commit -m "Initial commit"
103
+ git push
104
+ ```
105
+
106
+ 6. Your Space will automatically build and deploy at:
107
+ `https://YOUR-USERNAME-YOUR-SPACE-NAME.hf.space`
108
+
109
+ ## Image Processing Details
110
+
111
+ ### Arterial Phase (Contrast Enhancement)
112
+ - Uses CLAHE (Contrast Limited Adaptive Histogram Equalization)
113
+ - Operates in LAB color space for better results
114
+ - Parameters: `clipLimit=3.0`, `tileGridSize=(8,8)`
115
+
116
+ ### Venous Phase (Gaussian Smoothing)
117
+ - Applies Gaussian blur with 15x15 kernel
118
+ - Reduces noise and smooths fine details
119
+ - Simulates venous phase imaging characteristics
120
+
121
+ ## Tech Stack
122
+
123
+ - **FastAPI**: Modern Python web framework
124
+ - **OpenCV**: Image processing library
125
+ - **NumPy**: Numerical computing
126
+ - **Pillow**: Image I/O
127
+ - **Uvicorn**: ASGI server
128
+
129
+ ## Error Handling
130
+
131
+ The API includes comprehensive error handling:
132
+ - Invalid file types → 400 Bad Request
133
+ - Invalid phase parameter → 400 Bad Request
134
+ - Processing errors → 500 Internal Server Error
135
+
136
+ ## CORS Configuration
137
+
138
+ CORS is configured to accept requests from:
139
+ - GitHub Pages (`*.github.io`)
140
+ - Localhost (development)
141
+ - All origins (can be restricted for production)
142
+
143
+ ## Testing
144
+
145
+ Test the API with sample medical images:
146
+
147
+ ```python
148
+ import requests
149
+
150
+ url = "http://localhost:7860/process"
151
+ files = {"image": open("sample.jpg", "rb")}
152
+ data = {"phase": "arterial"}
153
+
154
+ response = requests.post(url, files=files, data=data)
155
+
156
+ with open("output.png", "wb") as f:
157
+ f.write(response.content)
158
+ ```
159
+
160
+ ## License
161
+
162
+ This is a demonstration project for educational purposes only. Not intended for clinical use.
163
+
164
+ ## Disclaimer
165
+
166
+ ⚠️ **Important**: This application is for educational and demonstration purposes only. It should NOT be used for actual medical diagnosis or clinical decision-making. Always consult qualified healthcare professionals for medical imaging interpretation.
app.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MedTech Image Processing Backend API
3
+ Provides endpoints for processing medical images with arterial and venous phase filters
4
+ """
5
+
6
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import Response
9
+ import cv2
10
+ import numpy as np
11
+ from io import BytesIO
12
+ from PIL import Image
13
+ import logging
14
+
15
+ # Configure logging
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Initialize FastAPI app
20
+ app = FastAPI(
21
+ title="MedTech Image Processing API",
22
+ description="Backend API for processing medical images",
23
+ version="1.0.0"
24
+ )
25
+
26
+ # Configure CORS to allow requests from GitHub Pages and localhost
27
+ app.add_middleware(
28
+ CORSMiddleware,
29
+ allow_origins=[
30
+ "https://*.github.io",
31
+ "http://localhost:*",
32
+ "http://127.0.0.1:*",
33
+ "*" # Allow all origins for development - restrict in production
34
+ ],
35
+ allow_credentials=True,
36
+ allow_methods=["*"],
37
+ allow_headers=["*"],
38
+ )
39
+
40
+
41
+ def increase_contrast(img: np.ndarray) -> np.ndarray:
42
+ """
43
+ Apply CLAHE (Contrast Limited Adaptive Histogram Equalization) to enhance contrast
44
+ Used for arterial phase processing
45
+
46
+ Args:
47
+ img: Input image as numpy array (BGR format)
48
+
49
+ Returns:
50
+ Enhanced image as numpy array
51
+ """
52
+ try:
53
+ # Convert to LAB color space for better contrast enhancement
54
+ lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
55
+ l, a, b = cv2.split(lab)
56
+
57
+ # Apply CLAHE to L channel (lightness)
58
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
59
+ l_enhanced = clahe.apply(l)
60
+
61
+ # Merge channels back and convert to BGR
62
+ enhanced_lab = cv2.merge([l_enhanced, a, b])
63
+ enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
64
+
65
+ logger.info("Contrast enhancement applied successfully")
66
+ return enhanced
67
+ except Exception as e:
68
+ logger.error(f"Error in contrast enhancement: {str(e)}")
69
+ raise
70
+
71
+
72
+ def apply_gaussian_blur(img: np.ndarray) -> np.ndarray:
73
+ """
74
+ Apply Gaussian blur for smoothing
75
+ Used for venous phase processing
76
+
77
+ Args:
78
+ img: Input image as numpy array (BGR format)
79
+
80
+ Returns:
81
+ Blurred image as numpy array
82
+ """
83
+ try:
84
+ # Apply Gaussian blur with 15x15 kernel
85
+ blurred = cv2.GaussianBlur(img, (15, 15), 0)
86
+ logger.info("Gaussian blur applied successfully")
87
+ return blurred
88
+ except Exception as e:
89
+ logger.error(f"Error in Gaussian blur: {str(e)}")
90
+ raise
91
+
92
+
93
+ def read_image_file(file_bytes: bytes) -> np.ndarray:
94
+ """
95
+ Read image file bytes and convert to OpenCV format
96
+
97
+ Args:
98
+ file_bytes: Raw image file bytes
99
+
100
+ Returns:
101
+ Image as numpy array in BGR format
102
+ """
103
+ try:
104
+ # Convert bytes to PIL Image
105
+ image = Image.open(BytesIO(file_bytes))
106
+
107
+ # Convert to RGB if needed
108
+ if image.mode != 'RGB':
109
+ image = image.convert('RGB')
110
+
111
+ # Convert PIL Image to numpy array
112
+ img_array = np.array(image)
113
+
114
+ # Convert RGB to BGR for OpenCV
115
+ img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
116
+
117
+ logger.info(f"Image loaded successfully: shape={img_bgr.shape}")
118
+ return img_bgr
119
+ except Exception as e:
120
+ logger.error(f"Error reading image file: {str(e)}")
121
+ raise HTTPException(status_code=400, detail=f"Invalid image file: {str(e)}")
122
+
123
+
124
+ def encode_image(img: np.ndarray) -> bytes:
125
+ """
126
+ Encode OpenCV image to PNG bytes
127
+
128
+ Args:
129
+ img: Image as numpy array (BGR format)
130
+
131
+ Returns:
132
+ PNG encoded bytes
133
+ """
134
+ try:
135
+ # Encode image to PNG format
136
+ success, buffer = cv2.imencode('.png', img)
137
+ if not success:
138
+ raise Exception("Failed to encode image")
139
+
140
+ return buffer.tobytes()
141
+ except Exception as e:
142
+ logger.error(f"Error encoding image: {str(e)}")
143
+ raise
144
+
145
+
146
+ @app.get("/")
147
+ async def root():
148
+ """Health check endpoint"""
149
+ return {
150
+ "status": "online",
151
+ "service": "MedTech Image Processing API",
152
+ "version": "1.0.0",
153
+ "endpoints": {
154
+ "/process": "POST - Process medical images with arterial or venous phase filters"
155
+ }
156
+ }
157
+
158
+
159
+ @app.post("/process")
160
+ async def process_image(
161
+ image: UploadFile = File(..., description="Medical image file (JPG/PNG)"),
162
+ phase: str = Form(..., description="Processing phase: 'arterial' or 'venous'")
163
+ ):
164
+ """
165
+ Process medical image with selected phase filter
166
+
167
+ Args:
168
+ image: Uploaded image file (JPG/PNG)
169
+ phase: Processing phase - 'arterial' (contrast enhancement) or 'venous' (blur)
170
+
171
+ Returns:
172
+ Processed image as PNG
173
+ """
174
+ logger.info(f"Processing request received - Phase: {phase}")
175
+
176
+ # Validate phase parameter
177
+ if phase not in ['arterial', 'venous']:
178
+ raise HTTPException(
179
+ status_code=400,
180
+ detail="Invalid phase. Must be 'arterial' or 'venous'"
181
+ )
182
+
183
+ # Validate file type
184
+ if not image.content_type.startswith('image/'):
185
+ raise HTTPException(
186
+ status_code=400,
187
+ detail="File must be an image (JPG/PNG)"
188
+ )
189
+
190
+ try:
191
+ # Read uploaded file
192
+ file_bytes = await image.read()
193
+ logger.info(f"File received: {image.filename}, size: {len(file_bytes)} bytes")
194
+
195
+ # Convert to OpenCV format
196
+ img = read_image_file(file_bytes)
197
+
198
+ # Apply processing based on phase
199
+ if phase == 'arterial':
200
+ processed_img = increase_contrast(img)
201
+ logger.info("Arterial phase processing completed")
202
+ else: # venous
203
+ processed_img = apply_gaussian_blur(img)
204
+ logger.info("Venous phase processing completed")
205
+
206
+ # Encode processed image
207
+ output_bytes = encode_image(processed_img)
208
+
209
+ # Return processed image
210
+ return Response(
211
+ content=output_bytes,
212
+ media_type="image/png",
213
+ headers={
214
+ "Content-Disposition": f"inline; filename=processed_{image.filename}",
215
+ "X-Processing-Phase": phase
216
+ }
217
+ )
218
+
219
+ except HTTPException:
220
+ raise
221
+ except Exception as e:
222
+ logger.error(f"Unexpected error processing image: {str(e)}")
223
+ raise HTTPException(
224
+ status_code=500,
225
+ detail=f"Error processing image: {str(e)}"
226
+ )
227
+
228
+
229
+ @app.get("/health")
230
+ async def health_check():
231
+ """Detailed health check endpoint"""
232
+ return {
233
+ "status": "healthy",
234
+ "opencv_version": cv2.__version__,
235
+ "numpy_version": np.__version__
236
+ }
237
+
238
+
239
+ if __name__ == "__main__":
240
+ import uvicorn
241
+ # Run server on port 7860 (Hugging Face Spaces default)
242
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn[standard]==0.24.0
3
+ python-multipart==0.0.6
4
+ opencv-python-headless==4.8.1.78
5
+ numpy==1.26.2
6
+ Pillow==10.1.0