koesan commited on
Commit
6f84ffe
·
1 Parent(s): cc1acfb

Add brain tumor segmentation model (LFS) and app files

Browse files
Files changed (6) hide show
  1. Dockerfile +32 -0
  2. app.py +209 -0
  3. brain1.h5 +3 -0
  4. image/20251012_09h06m52s_grim.png +0 -0
  5. requirements.txt +7 -0
  6. templates/index.html +507 -0
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies for OpenCV
6
+ RUN apt-get update && apt-get install -y \
7
+ libglib2.0-0 \
8
+ libsm6 \
9
+ libxext6 \
10
+ libxrender-dev \
11
+ libgomp1 \
12
+ libgl1 \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Copy requirements first for better caching
16
+ COPY requirements.txt .
17
+ RUN pip install --no-cache-dir -r requirements.txt
18
+
19
+ # Copy application files
20
+ COPY app.py .
21
+ COPY brain1.h5 .
22
+ COPY templates/ templates/
23
+ COPY image/ image/
24
+
25
+ # Create uploads directory with proper permissions
26
+ RUN mkdir -p uploads && chmod 777 uploads
27
+
28
+ # Expose port
29
+ EXPOSE 7860
30
+
31
+ # Run the application
32
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import numpy as np
4
+ from flask import Flask, request, render_template, jsonify, send_from_directory
5
+ from werkzeug.utils import secure_filename
6
+ from datetime import datetime
7
+ import base64
8
+ from io import BytesIO
9
+ from PIL import Image
10
+
11
+ # Suppress TensorFlow warnings
12
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
13
+
14
+ import tensorflow as tf
15
+ from tensorflow.keras.models import load_model
16
+
17
+ app = Flask(__name__)
18
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
19
+ app.config['UPLOAD_FOLDER'] = 'uploads'
20
+ app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'mha'}
21
+
22
+ # Create uploads folder with proper permissions
23
+ os.makedirs(app.config['UPLOAD_FOLDER'], mode=0o777, exist_ok=True)
24
+
25
+ # Load the brain segmentation model
26
+ print("Loading Brain Segmentation Model...")
27
+ import warnings
28
+ warnings.filterwarnings('ignore')
29
+
30
+ try:
31
+ model = load_model('brain1.h5', compile=False)
32
+ print("✓ Model loaded successfully!")
33
+ except Exception as e:
34
+ print(f"❌ Error loading model: {e}")
35
+ import traceback
36
+ traceback.print_exc()
37
+ raise
38
+
39
+ def allowed_file(filename):
40
+ """Check if file extension is allowed"""
41
+ return '.' in filename and \
42
+ filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
43
+
44
+ def preprocess_image(image_path):
45
+ """Preprocess image for brain segmentation"""
46
+ # Read image in grayscale
47
+ img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
48
+
49
+ if img is None:
50
+ raise ValueError("Could not read image")
51
+
52
+ # Get original shape
53
+ original_shape = img.shape
54
+
55
+ # Resize to model input size (assuming 256x256)
56
+ img_resized = cv2.resize(img, (256, 256))
57
+
58
+ # Normalize to [0, 1]
59
+ img_normalized = img_resized.astype(np.float32) / 255.0
60
+
61
+ # Add batch and channel dimensions
62
+ img_input = np.expand_dims(img_normalized, axis=0)
63
+ img_input = np.expand_dims(img_input, axis=-1)
64
+
65
+ return img_input, original_shape
66
+
67
+ def postprocess_mask(mask, original_shape):
68
+ """Postprocess segmentation mask"""
69
+ # Remove batch dimension
70
+ mask = np.squeeze(mask)
71
+
72
+ # Resize back to original shape
73
+ mask_resized = cv2.resize(mask, (original_shape[1], original_shape[0]))
74
+
75
+ # Threshold
76
+ mask_binary = (mask_resized > 0.5).astype(np.uint8) * 255
77
+
78
+ return mask_binary
79
+
80
+ def create_overlay(original, mask):
81
+ """Create overlay of mask on original image"""
82
+ # Ensure original is RGB
83
+ if len(original.shape) == 2:
84
+ original_rgb = cv2.cvtColor(original, cv2.COLOR_GRAY2RGB)
85
+ else:
86
+ original_rgb = original.copy()
87
+
88
+ # Create colored mask (red for tumor)
89
+ colored_mask = np.zeros_like(original_rgb)
90
+ colored_mask[:, :, 2] = mask # Red channel
91
+
92
+ # Blend
93
+ overlay = cv2.addWeighted(original_rgb, 0.7, colored_mask, 0.3, 0)
94
+
95
+ return overlay
96
+
97
+ def img_to_base64(img_array):
98
+ """Convert numpy array to base64 string"""
99
+ # Ensure uint8
100
+ if img_array.dtype != np.uint8:
101
+ img_array = (img_array * 255).astype(np.uint8)
102
+
103
+ # Convert to PIL Image
104
+ if len(img_array.shape) == 2:
105
+ img = Image.fromarray(img_array, mode='L')
106
+ else:
107
+ img = Image.fromarray(img_array, mode='RGB')
108
+
109
+ # Save to buffer
110
+ buffer = BytesIO()
111
+ img.save(buffer, format='PNG')
112
+ buffer.seek(0)
113
+
114
+ # Encode to base64
115
+ img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
116
+
117
+ return f"data:image/png;base64,{img_base64}"
118
+
119
+ @app.route('/')
120
+ def index():
121
+ """Render main page"""
122
+ return render_template('index.html')
123
+
124
+ @app.route('/predict', methods=['POST'])
125
+ def predict():
126
+ """Handle image upload and prediction"""
127
+ try:
128
+ # Check if file was uploaded
129
+ if 'file' not in request.files:
130
+ return jsonify({'error': 'No file uploaded'}), 400
131
+
132
+ file = request.files['file']
133
+
134
+ if file.filename == '':
135
+ return jsonify({'error': 'No file selected'}), 400
136
+
137
+ if not allowed_file(file.filename):
138
+ return jsonify({'error': 'Invalid file type. Please upload PNG, JPG, or JPEG'}), 400
139
+
140
+ # Save uploaded file
141
+ timestamp = datetime.now().strftime('%Y%m%d_%Hh%Mm%Ss')
142
+ filename = secure_filename(f"{timestamp}_{file.filename}")
143
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
144
+ file.save(filepath)
145
+
146
+ # Read original image
147
+ original_img = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
148
+
149
+ # Preprocess
150
+ img_input, original_shape = preprocess_image(filepath)
151
+
152
+ # Predict
153
+ print("Making prediction...")
154
+ prediction = model.predict(img_input, verbose=0)
155
+
156
+ # Postprocess
157
+ mask = postprocess_mask(prediction[0], original_shape)
158
+
159
+ # Create overlay
160
+ overlay = create_overlay(original_img, mask)
161
+
162
+ # Convert to base64
163
+ original_base64 = img_to_base64(original_img)
164
+ mask_base64 = img_to_base64(mask)
165
+ overlay_base64 = img_to_base64(overlay)
166
+
167
+ # Calculate statistics
168
+ tumor_pixels = np.sum(mask > 127)
169
+ total_pixels = mask.shape[0] * mask.shape[1]
170
+ tumor_percentage = (tumor_pixels / total_pixels) * 100
171
+
172
+ result = {
173
+ 'original': original_base64,
174
+ 'mask': mask_base64,
175
+ 'overlay': overlay_base64,
176
+ 'tumor_percentage': float(tumor_percentage),
177
+ 'image_size': f"{original_shape[1]}x{original_shape[0]}"
178
+ }
179
+
180
+ print(f"✓ Prediction completed: {tumor_percentage:.2f}% tumor detected")
181
+
182
+ return jsonify(result)
183
+
184
+ except Exception as e:
185
+ print(f"Error during prediction: {e}")
186
+ import traceback
187
+ traceback.print_exc()
188
+ return jsonify({'error': str(e)}), 500
189
+
190
+ @app.route('/example')
191
+ def example():
192
+ """Get example image"""
193
+ example_path = 'image/20251012_09h06m52s_grim.png'
194
+ if os.path.exists(example_path):
195
+ with open(example_path, 'rb') as f:
196
+ img_data = f.read()
197
+ img_base64 = base64.b64encode(img_data).decode('utf-8')
198
+ return jsonify({'image': f"data:image/png;base64,{img_base64}"})
199
+ return jsonify({'error': 'Example image not found'}), 404
200
+
201
+ if __name__ == '__main__':
202
+ print("\n" + "="*60)
203
+ print("🧠 Brain Tumor Segmentation App")
204
+ print("="*60)
205
+ print("✓ Model loaded and ready!")
206
+ print("✓ Server starting on port 7860...")
207
+ print("="*60 + "\n")
208
+
209
+ app.run(host='0.0.0.0', port=7860, debug=False)
brain1.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d11bf3e0889b75fb7e68103dd531b392dba0d5583eaa6fb413c4e7f8dd6bdcf
3
+ size 372892632
image/20251012_09h06m52s_grim.png ADDED
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Flask==3.0.0
2
+ tensorflow==2.16.1
3
+ opencv-python-headless==4.9.0.80
4
+ numpy==1.26.4
5
+ Pillow==10.2.0
6
+ Werkzeug==3.0.1
7
+ h5py==3.11.0
templates/index.html ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Brain Tumor Segmentation</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ display: flex;
20
+ flex-direction: column;
21
+ align-items: center;
22
+ }
23
+
24
+ .container {
25
+ max-width: 1400px;
26
+ width: 100%;
27
+ background: rgba(255, 255, 255, 0.95);
28
+ border-radius: 20px;
29
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
30
+ overflow: hidden;
31
+ }
32
+
33
+ .header {
34
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
35
+ color: white;
36
+ padding: 40px;
37
+ text-align: center;
38
+ }
39
+
40
+ .header h1 {
41
+ font-size: 2.5em;
42
+ margin-bottom: 10px;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ gap: 15px;
47
+ }
48
+
49
+ .header p {
50
+ font-size: 1.1em;
51
+ opacity: 0.95;
52
+ }
53
+
54
+ .content {
55
+ padding: 40px;
56
+ }
57
+
58
+ .upload-section {
59
+ background: #f8f9fa;
60
+ border-radius: 15px;
61
+ padding: 40px;
62
+ text-align: center;
63
+ margin-bottom: 30px;
64
+ border: 3px dashed #667eea;
65
+ transition: all 0.3s;
66
+ }
67
+
68
+ .upload-section:hover {
69
+ border-color: #764ba2;
70
+ background: #f0f1f5;
71
+ }
72
+
73
+ .upload-section.dragover {
74
+ background: #e3e8ff;
75
+ border-color: #764ba2;
76
+ }
77
+
78
+ .upload-icon {
79
+ font-size: 4em;
80
+ margin-bottom: 20px;
81
+ color: #667eea;
82
+ }
83
+
84
+ .file-input-wrapper {
85
+ position: relative;
86
+ display: inline-block;
87
+ margin: 20px 0;
88
+ }
89
+
90
+ input[type="file"] {
91
+ display: none;
92
+ }
93
+
94
+ .file-input-label {
95
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
96
+ color: white;
97
+ padding: 15px 40px;
98
+ border-radius: 50px;
99
+ cursor: pointer;
100
+ font-size: 1.1em;
101
+ font-weight: 600;
102
+ transition: all 0.3s;
103
+ display: inline-block;
104
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
105
+ }
106
+
107
+ .file-input-label:hover {
108
+ transform: translateY(-2px);
109
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
110
+ }
111
+
112
+ .example-btn {
113
+ background: #6c757d;
114
+ color: white;
115
+ padding: 12px 30px;
116
+ border: none;
117
+ border-radius: 50px;
118
+ cursor: pointer;
119
+ font-size: 1em;
120
+ margin-left: 15px;
121
+ transition: all 0.3s;
122
+ }
123
+
124
+ .example-btn:hover {
125
+ background: #5a6268;
126
+ transform: translateY(-2px);
127
+ }
128
+
129
+ .selected-file {
130
+ margin-top: 20px;
131
+ color: #667eea;
132
+ font-weight: 600;
133
+ font-size: 1.1em;
134
+ }
135
+
136
+ .analyze-btn {
137
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
138
+ color: white;
139
+ padding: 15px 50px;
140
+ border: none;
141
+ border-radius: 50px;
142
+ cursor: pointer;
143
+ font-size: 1.2em;
144
+ font-weight: 600;
145
+ margin-top: 20px;
146
+ transition: all 0.3s;
147
+ box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
148
+ }
149
+
150
+ .analyze-btn:hover:not(:disabled) {
151
+ transform: translateY(-2px);
152
+ box-shadow: 0 6px 20px rgba(17, 153, 142, 0.6);
153
+ }
154
+
155
+ .analyze-btn:disabled {
156
+ background: #ccc;
157
+ cursor: not-allowed;
158
+ box-shadow: none;
159
+ }
160
+
161
+ .loading {
162
+ display: none;
163
+ margin: 30px 0;
164
+ text-align: center;
165
+ }
166
+
167
+ .spinner {
168
+ border: 4px solid #f3f3f3;
169
+ border-top: 4px solid #667eea;
170
+ border-radius: 50%;
171
+ width: 50px;
172
+ height: 50px;
173
+ animation: spin 1s linear infinite;
174
+ margin: 0 auto 20px;
175
+ }
176
+
177
+ @keyframes spin {
178
+ 0% { transform: rotate(0deg); }
179
+ 100% { transform: rotate(360deg); }
180
+ }
181
+
182
+ .results {
183
+ display: none;
184
+ margin-top: 40px;
185
+ }
186
+
187
+ .results-grid {
188
+ display: grid;
189
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
190
+ gap: 25px;
191
+ margin-top: 25px;
192
+ }
193
+
194
+ .result-card {
195
+ background: white;
196
+ border-radius: 15px;
197
+ padding: 20px;
198
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
199
+ transition: all 0.3s;
200
+ }
201
+
202
+ .result-card:hover {
203
+ transform: translateY(-5px);
204
+ box-shadow: 0 6px 30px rgba(0, 0, 0, 0.15);
205
+ }
206
+
207
+ .result-card h3 {
208
+ color: #667eea;
209
+ margin-bottom: 15px;
210
+ font-size: 1.3em;
211
+ }
212
+
213
+ .result-card img {
214
+ width: 100%;
215
+ border-radius: 10px;
216
+ margin-bottom: 15px;
217
+ }
218
+
219
+ .stats {
220
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
221
+ color: white;
222
+ border-radius: 15px;
223
+ padding: 30px;
224
+ margin-top: 25px;
225
+ text-align: center;
226
+ }
227
+
228
+ .stats h3 {
229
+ font-size: 1.5em;
230
+ margin-bottom: 20px;
231
+ }
232
+
233
+ .stats-grid {
234
+ display: grid;
235
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
236
+ gap: 20px;
237
+ }
238
+
239
+ .stat-item {
240
+ background: rgba(255, 255, 255, 0.2);
241
+ padding: 20px;
242
+ border-radius: 10px;
243
+ }
244
+
245
+ .stat-value {
246
+ font-size: 2em;
247
+ font-weight: bold;
248
+ margin: 10px 0;
249
+ }
250
+
251
+ .stat-label {
252
+ font-size: 0.9em;
253
+ opacity: 0.9;
254
+ }
255
+
256
+ .error {
257
+ background: #f8d7da;
258
+ color: #721c24;
259
+ padding: 20px;
260
+ border-radius: 10px;
261
+ margin: 20px 0;
262
+ display: none;
263
+ }
264
+
265
+ .footer {
266
+ background: #f8f9fa;
267
+ padding: 30px;
268
+ text-align: center;
269
+ color: #6c757d;
270
+ margin-top: 40px;
271
+ }
272
+
273
+ @media (max-width: 768px) {
274
+ .header h1 {
275
+ font-size: 1.8em;
276
+ }
277
+
278
+ .results-grid {
279
+ grid-template-columns: 1fr;
280
+ }
281
+
282
+ .content {
283
+ padding: 20px;
284
+ }
285
+ }
286
+ </style>
287
+ </head>
288
+ <body>
289
+ <div class="container">
290
+ <div class="header">
291
+ <h1>
292
+ <span>🧠</span>
293
+ Brain Tumor Segmentation
294
+ <span>🔬</span>
295
+ </h1>
296
+ <p>AI-Powered Medical Image Analysis for Brain MRI Scans</p>
297
+ </div>
298
+
299
+ <div class="content">
300
+ <div class="upload-section" id="dropZone">
301
+ <div class="upload-icon">📤</div>
302
+ <h2 style="color: #667eea; margin-bottom: 15px;">Upload Brain MRI Scan</h2>
303
+ <p style="color: #6c757d; margin-bottom: 20px;">
304
+ Drag and drop your MRI image here or click to browse
305
+ </p>
306
+
307
+ <div class="file-input-wrapper">
308
+ <input type="file" id="fileInput" accept="image/*">
309
+ <label for="fileInput" class="file-input-label">
310
+ Choose File
311
+ </label>
312
+ <button class="example-btn" onclick="loadExample()">
313
+ Load Example
314
+ </button>
315
+ </div>
316
+
317
+ <div class="selected-file" id="selectedFile"></div>
318
+
319
+ <button class="analyze-btn" id="analyzeBtn" onclick="analyzeBrain()" disabled>
320
+ Analyze Brain Scan
321
+ </button>
322
+ </div>
323
+
324
+ <div class="loading" id="loading">
325
+ <div class="spinner"></div>
326
+ <p style="color: #667eea; font-size: 1.2em;">Analyzing brain scan...</p>
327
+ </div>
328
+
329
+ <div class="error" id="error"></div>
330
+
331
+ <div class="results" id="results">
332
+ <h2 style="color: #667eea; margin-bottom: 20px;">Segmentation Results</h2>
333
+
334
+ <div class="results-grid">
335
+ <div class="result-card">
336
+ <h3>🖼️ Original MRI</h3>
337
+ <img id="originalImg" src="" alt="Original">
338
+ </div>
339
+
340
+ <div class="result-card">
341
+ <h3>🎯 Tumor Mask</h3>
342
+ <img id="maskImg" src="" alt="Mask">
343
+ </div>
344
+
345
+ <div class="result-card">
346
+ <h3>🔍 Overlay</h3>
347
+ <img id="overlayImg" src="" alt="Overlay">
348
+ </div>
349
+ </div>
350
+
351
+ <div class="stats">
352
+ <h3>📊 Analysis Statistics</h3>
353
+ <div class="stats-grid">
354
+ <div class="stat-item">
355
+ <div class="stat-label">Tumor Coverage</div>
356
+ <div class="stat-value" id="tumorPercentage">-</div>
357
+ <div class="stat-label">of brain area</div>
358
+ </div>
359
+ <div class="stat-item">
360
+ <div class="stat-label">Image Resolution</div>
361
+ <div class="stat-value" id="imageSize">-</div>
362
+ <div class="stat-label">pixels</div>
363
+ </div>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </div>
368
+
369
+ <div class="footer">
370
+ <p>⚠️ For research and educational purposes only. Not for clinical diagnosis.</p>
371
+ <p style="margin-top: 10px;">Powered by Deep Learning | TensorFlow & Keras</p>
372
+ </div>
373
+ </div>
374
+
375
+ <script>
376
+ let selectedFile = null;
377
+
378
+ // File input change handler
379
+ document.getElementById('fileInput').addEventListener('change', function(e) {
380
+ if (e.target.files.length > 0) {
381
+ selectedFile = e.target.files[0];
382
+ document.getElementById('selectedFile').textContent = '✓ ' + selectedFile.name;
383
+ document.getElementById('analyzeBtn').disabled = false;
384
+ hideResults();
385
+ }
386
+ });
387
+
388
+ // Drag and drop handlers
389
+ const dropZone = document.getElementById('dropZone');
390
+
391
+ dropZone.addEventListener('dragover', (e) => {
392
+ e.preventDefault();
393
+ dropZone.classList.add('dragover');
394
+ });
395
+
396
+ dropZone.addEventListener('dragleave', () => {
397
+ dropZone.classList.remove('dragover');
398
+ });
399
+
400
+ dropZone.addEventListener('drop', (e) => {
401
+ e.preventDefault();
402
+ dropZone.classList.remove('dragover');
403
+
404
+ const files = e.dataTransfer.files;
405
+ if (files.length > 0) {
406
+ selectedFile = files[0];
407
+ document.getElementById('fileInput').files = files;
408
+ document.getElementById('selectedFile').textContent = '✓ ' + selectedFile.name;
409
+ document.getElementById('analyzeBtn').disabled = false;
410
+ hideResults();
411
+ }
412
+ });
413
+
414
+ // Load example image
415
+ function loadExample() {
416
+ fetch('/example')
417
+ .then(response => response.json())
418
+ .then(data => {
419
+ if (data.image) {
420
+ // Convert base64 to blob
421
+ fetch(data.image)
422
+ .then(res => res.blob())
423
+ .then(blob => {
424
+ const file = new File([blob], "example.png", { type: "image/png" });
425
+ const dataTransfer = new DataTransfer();
426
+ dataTransfer.items.add(file);
427
+ document.getElementById('fileInput').files = dataTransfer.files;
428
+ selectedFile = file;
429
+ document.getElementById('selectedFile').textContent = '✓ Example Brain MRI';
430
+ document.getElementById('analyzeBtn').disabled = false;
431
+ hideResults();
432
+ });
433
+ }
434
+ })
435
+ .catch(error => {
436
+ showError('Failed to load example image');
437
+ });
438
+ }
439
+
440
+ // Analyze brain scan
441
+ function analyzeBrain() {
442
+ if (!selectedFile) {
443
+ showError('Please select a file first');
444
+ return;
445
+ }
446
+
447
+ const formData = new FormData();
448
+ formData.append('file', selectedFile);
449
+
450
+ // Show loading
451
+ document.getElementById('loading').style.display = 'block';
452
+ document.getElementById('results').style.display = 'none';
453
+ document.getElementById('error').style.display = 'none';
454
+ document.getElementById('analyzeBtn').disabled = true;
455
+
456
+ fetch('/predict', {
457
+ method: 'POST',
458
+ body: formData
459
+ })
460
+ .then(response => response.json())
461
+ .then(data => {
462
+ if (data.error) {
463
+ showError(data.error);
464
+ } else {
465
+ showResults(data);
466
+ }
467
+ })
468
+ .catch(error => {
469
+ showError('Analysis failed: ' + error.message);
470
+ })
471
+ .finally(() => {
472
+ document.getElementById('loading').style.display = 'none';
473
+ document.getElementById('analyzeBtn').disabled = false;
474
+ });
475
+ }
476
+
477
+ // Show results
478
+ function showResults(data) {
479
+ document.getElementById('originalImg').src = data.original;
480
+ document.getElementById('maskImg').src = data.mask;
481
+ document.getElementById('overlayImg').src = data.overlay;
482
+ document.getElementById('tumorPercentage').textContent = data.tumor_percentage.toFixed(2) + '%';
483
+ document.getElementById('imageSize').textContent = data.image_size;
484
+
485
+ document.getElementById('results').style.display = 'block';
486
+
487
+ // Scroll to results
488
+ document.getElementById('results').scrollIntoView({ behavior: 'smooth' });
489
+ }
490
+
491
+ // Show error
492
+ function showError(message) {
493
+ document.getElementById('error').textContent = '❌ ' + message;
494
+ document.getElementById('error').style.display = 'block';
495
+ setTimeout(() => {
496
+ document.getElementById('error').style.display = 'none';
497
+ }, 5000);
498
+ }
499
+
500
+ // Hide results
501
+ function hideResults() {
502
+ document.getElementById('results').style.display = 'none';
503
+ document.getElementById('error').style.display = 'none';
504
+ }
505
+ </script>
506
+ </body>
507
+ </html>