Anupam007 commited on
Commit
ef216d5
·
verified ·
1 Parent(s): 8cf1eb4

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +436 -0
app.py ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ import time
4
+ import gradio as gr
5
+ import torch
6
+ import matplotlib.pyplot as plt
7
+ from PIL import Image
8
+ import cv2
9
+ import pygame
10
+ from sklearn.preprocessing import StandardScaler
11
+ from sklearn.model_selection import train_test_split
12
+ import os # Import the 'os' module
13
+
14
+ # Flag to track if pygame is initialized successfully
15
+ pygame_initialized = False
16
+
17
+ try:
18
+ # Initialize pygame mixer for sound
19
+ pygame.mixer.init()
20
+ pygame_initialized = True
21
+ print("Pygame mixer initialized successfully.")
22
+ except Exception as e:
23
+ print(f"Error initializing pygame: {e}. Sound will be disabled.")
24
+
25
+ # Check if GPU is available
26
+ print("CUDA available:", torch.cuda.is_available())
27
+ if torch.cuda.is_available():
28
+ device = torch.device("cuda:0")
29
+ print("Using GPU:", torch.cuda.get_device_name(0))
30
+ else:
31
+ device = torch.device("cpu")
32
+ print("Using CPU")
33
+
34
+ # Simulated dataset of finger pressure patterns and blood glucose readings
35
+ # In a real application, this would be replaced with actual training data
36
+ def generate_simulated_data(n_samples=1000):
37
+ # Generate synthetic features that might correlate with blood glucose
38
+ # In reality, these would be derived from screen pressure, touch area,
39
+ # blood perfusion estimated from camera, etc.
40
+ np.random.seed(42)
41
+
42
+ # Feature 1: Simulated pressure values (0-100)
43
+ pressure = np.random.normal(60, 15, n_samples).clip(20, 100)
44
+
45
+ # Feature 2: Simulated touch area (mm²)
46
+ touch_area = np.random.normal(150, 30, n_samples).clip(80, 220)
47
+
48
+ # Feature 3: Simulated blood perfusion level (0-1)
49
+ perfusion = np.random.normal(0.7, 0.15, n_samples).clip(0.3, 1.0)
50
+
51
+ # Feature 4: Simulated tissue opacity (0-1)
52
+ opacity = np.random.normal(0.5, 0.1, n_samples).clip(0.2, 0.8)
53
+
54
+ # Generate glucose levels with some correlation to the features
55
+ # Normal range: 70-140 mg/dL, with some values outside this range
56
+ base_glucose = 100 + (pressure - 60) * 0.5 - (touch_area - 150) * 0.1 + (perfusion - 0.7) * 40 - (opacity - 0.5) * 30
57
+ glucose = base_glucose + np.random.normal(0, 15, n_samples)
58
+ glucose = glucose.clip(40, 400) # Set realistic min/max values
59
+
60
+ # Create a dataframe
61
+ df = pd.DataFrame({
62
+ 'pressure': pressure,
63
+ 'touch_area': touch_area,
64
+ 'perfusion': perfusion,
65
+ 'opacity': opacity,
66
+ 'glucose': glucose
67
+ })
68
+
69
+ return df
70
+
71
+ # Simple PyTorch model for glucose prediction
72
+ class GlucosePredictor(torch.nn.Module):
73
+ def __init__(self):
74
+ super(GlucosePredictor, self).__init__()
75
+ self.model = torch.nn.Sequential(
76
+ torch.nn.Linear(4, 16),
77
+ torch.nn.ReLU(),
78
+ torch.nn.Linear(16, 32),
79
+ torch.nn.ReLU(),
80
+ torch.nn.Linear(32, 16),
81
+ torch.nn.ReLU(),
82
+ torch.nn.Linear(16, 1)
83
+ )
84
+
85
+ def forward(self, x):
86
+ return self.model(x)
87
+
88
+ # Train the model
89
+ def train_model(df):
90
+ # Split features and target
91
+ X = df[['pressure', 'touch_area', 'perfusion', 'opacity']].values
92
+ y = df['glucose'].values.reshape(-1, 1)
93
+
94
+ # Scale the data
95
+ scaler_X = StandardScaler()
96
+ scaler_y = StandardScaler()
97
+
98
+ X_scaled = scaler_X.fit_transform(X)
99
+ y_scaled = scaler_y.fit_transform(y)
100
+
101
+ # Split into train and test sets
102
+ X_train, X_test, y_train, y_test = train_test_split(X_scaled, y_scaled, test_size=0.2, random_state=42)
103
+
104
+ # Convert to PyTorch tensors
105
+ X_train_tensor = torch.FloatTensor(X_train).to(device)
106
+ y_train_tensor = torch.FloatTensor(y_train).to(device)
107
+ X_test_tensor = torch.FloatTensor(X_test).to(device)
108
+ y_test_tensor = torch.FloatTensor(y_test).to(device)
109
+
110
+ # Initialize the model and move to GPU if available
111
+ model = GlucosePredictor().to(device)
112
+
113
+ # Loss function and optimizer
114
+ criterion = torch.nn.MSELoss()
115
+ optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
116
+
117
+ # Training loop
118
+ epochs = 100
119
+ for epoch in range(epochs):
120
+ # Forward pass
121
+ y_pred = model(X_train_tensor)
122
+ loss = criterion(y_pred, y_train_tensor)
123
+
124
+ # Backward pass and optimize
125
+ optimizer.zero_grad()
126
+ loss.backward()
127
+ optimizer.step()
128
+
129
+ if (epoch+1) % 10 == 0:
130
+ print(f'Epoch {epoch+1}/{epochs}, Loss: {loss.item():.4f}')
131
+
132
+ # Evaluate the model
133
+ model.eval()
134
+ with torch.no_grad():
135
+ y_pred_test = model(X_test_tensor)
136
+ test_loss = criterion(y_pred_test, y_test_tensor)
137
+ print(f'Test Loss: {test_loss.item():.4f}')
138
+
139
+ return model, scaler_X, scaler_y
140
+
141
+ # Function to play beep sound
142
+ def play_beep(glucose_value):
143
+ """Play different sounds based on glucose level"""
144
+ global pygame_initialized # Access the global flag
145
+
146
+ if not pygame_initialized:
147
+ print("Pygame not initialized, skipping sound.")
148
+ return # Exit if pygame is not initialized
149
+
150
+ try:
151
+ # Define frequency and duration based on glucose level
152
+ if glucose_value < 70:
153
+ # Low glucose - urgent double beep (low frequency)
154
+ freq = 300
155
+ duration = 300
156
+ # Play twice with a short pause
157
+ for _ in range(2):
158
+ sound_array = np.sin(2*np.pi*np.arange(44100)*freq/44100).astype(np.float32)
159
+ sound = pygame.sndarray.make_sound(sound_array)
160
+ sound.play()
161
+ time.sleep(duration/1000)
162
+ time.sleep(0.1) # Pause between beeps
163
+ elif glucose_value <= 140:
164
+ # Normal glucose - standard beep (medium frequency)
165
+ freq = 440
166
+ duration = 400
167
+ sound_array = np.sin(2*np.pi*np.arange(44100)*freq/44100).astype(np.float32)
168
+ sound = pygame.sndarray.make_sound(sound_array)
169
+ sound.play()
170
+ time.sleep(duration/1000)
171
+ elif glucose_value <= 200:
172
+ # Elevated glucose - medium alert beep (higher frequency)
173
+ freq = 600
174
+ duration = 400
175
+ sound_array = np.sin(2*np.pi*np.arange(44100)*freq/44100).astype(np.float32)
176
+ sound = pygame.sndarray.make_sound(sound_array)
177
+ sound.play()
178
+ time.sleep(duration/1000)
179
+ else:
180
+ # High glucose - urgent beep (high frequency)
181
+ freq = 800
182
+ duration = 300
183
+ # Play twice with a short pause
184
+ for _ in range(3):
185
+ sound_array = np.sin(2*np.pi*np.arange(44100)*freq/44100).astype(np.float32)
186
+ sound = pygame.sndarray.make_sound(sound_array)
187
+ sound.play()
188
+ time.sleep(duration/1000)
189
+ time.sleep(0.1) # Pause between beeps
190
+
191
+ print(f"Beep sound played for glucose level: {glucose_value}")
192
+ except Exception as e:
193
+ print(f"Error playing sound: {e}")
194
+
195
+ # Simulate camera usage for blood perfusion measurement
196
+ def activate_camera():
197
+ """
198
+ In a real application, this would activate the device's camera
199
+ to measure blood perfusion in the fingertip.
200
+ Here we just simulate this process.
201
+ """
202
+ print("Camera activated for blood perfusion analysis")
203
+
204
+ # Attempt to use real camera if available (for demonstration)
205
+ try:
206
+ cap = cv2.VideoCapture(0)
207
+ if cap.isOpened():
208
+ ret, frame = cap.read()
209
+ if ret:
210
+ # In a real app, we would analyze this frame
211
+ # For now, just display that we captured it
212
+ print("Camera frame captured")
213
+ # Here you'd analyze blood perfusion from the image
214
+ # For example by looking at the red channel intensity
215
+
216
+ # Convert to grayscale and use a placeholder image for demonstration
217
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
218
+ img = Image.fromarray(frame_rgb)
219
+
220
+ # Clean up
221
+ cap.release()
222
+
223
+ return img
224
+ else:
225
+ print("Could not open camera - using simulated data")
226
+
227
+ except Exception as e:
228
+ print(f"Camera error: {e} - using simulated data")
229
+
230
+ # If camera isn't working, return a placeholder
231
+ placeholder = np.ones((240, 320, 3), dtype=np.uint8) * 200
232
+ # Add text to placeholder
233
+ cv2.putText(placeholder, "Camera Simulation", (50, 120),
234
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
235
+
236
+ return Image.fromarray(placeholder)
237
+
238
+ # Simulate finger press data collection
239
+ def collect_finger_data():
240
+ """
241
+ In a real application, this would collect data from:
242
+ 1. Screen pressure sensors
243
+ 2. Touch area measurement
244
+ 3. Camera-based blood perfusion estimation
245
+ 4. Optional: PPG (photoplethysmography) if available
246
+
247
+ Here we're just simulating the data.
248
+ """
249
+ # Simulate data collection with some random values
250
+ pressure = np.random.normal(60, 10)
251
+ touch_area = np.random.normal(150, 20)
252
+ perfusion = np.random.normal(0.7, 0.1)
253
+ opacity = np.random.normal(0.5, 0.08)
254
+
255
+ # Add some constraints to make values realistic
256
+ pressure = max(20, min(100, pressure))
257
+ touch_area = max(80, min(220, touch_area))
258
+ perfusion = max(0.3, min(1.0, perfusion))
259
+ opacity = max(0.2, min(0.8, opacity))
260
+
261
+ return pressure, touch_area, perfusion, opacity
262
+
263
+ # Function to predict glucose level
264
+ def predict_glucose(model, scaler_X, scaler_y, features):
265
+ # Scale the features
266
+ features_scaled = scaler_X.transform(np.array(features).reshape(1, -1))
267
+
268
+ # Convert to tensor and predict
269
+ features_tensor = torch.FloatTensor(features_scaled).to(device)
270
+ with torch.no_grad():
271
+ prediction_scaled = model(features_tensor)
272
+
273
+ # Convert back to original scale
274
+ prediction = scaler_y.inverse_transform(prediction_scaled.cpu().numpy())
275
+
276
+ return prediction[0][0]
277
+
278
+ # Create a fancy glucose meter display
279
+ def create_glucose_meter(glucose_value):
280
+ # Define colors based on glucose range
281
+ if glucose_value < 70:
282
+ color = 'blue' # Low
283
+ status = 'LOW'
284
+ elif glucose_value <= 140:
285
+ color = 'green' # Normal
286
+ status = 'NORMAL'
287
+ elif glucose_value <= 200:
288
+ color = 'orange' # Elevated
289
+ status = 'ELEVATED'
290
+ else:
291
+ color = 'red' # High
292
+ status = 'HIGH'
293
+
294
+ # Create a figure
295
+ fig, ax = plt.subplots(figsize=(6, 4))
296
+
297
+ # Create a gauge-like visualization
298
+ ax.add_patch(plt.Rectangle((-1, -1), 2, 2, fc='lightgray', ec='gray'))
299
+
300
+ # Create a gauge arc
301
+ theta = np.linspace(-0.75 * np.pi, 0.75 * np.pi, 100)
302
+ r = 0.8
303
+ x = r * np.cos(theta)
304
+ y = r * np.sin(theta)
305
+ ax.plot(x, y, 'k-', lw=2)
306
+
307
+ # Create tick marks
308
+ for i in range(40, 401, 40):
309
+ angle = -0.75 * np.pi + (i - 40) / (400 - 40) * 1.5 * np.pi
310
+ x_tick = (r + 0.1) * np.cos(angle)
311
+ y_tick = (r + 0.1) * np.sin(angle)
312
+ ax.plot([r * np.cos(angle), x_tick], [r * np.sin(angle), y_tick], 'k-', lw=1)
313
+ ax.text(x_tick * 1.1, y_tick * 1.1, str(i), ha='center', va='center', fontsize=8)
314
+
315
+ # Create the needle
316
+ angle = -0.75 * np.pi + (glucose_value - 40) / (400 - 40) * 1.5 * np.pi
317
+ ax.plot([0, 0.9 * np.cos(angle)], [0, 0.9 * np.sin(angle)], color=color, lw=3)
318
+
319
+ # Add a center circle
320
+ ax.add_patch(plt.Circle((0, 0), 0.05, fc=color, ec='k'))
321
+
322
+ # Add text
323
+ ax.text(0, -0.4, f"{glucose_value:.1f} mg/dL", ha='center', va='center', fontsize=14, fontweight='bold')
324
+ ax.text(0, -0.6, status, ha='center', va='center', fontsize=12, color=color, fontweight='bold')
325
+
326
+ # Remove axes
327
+ ax.set_xlim(-1.2, 1.2)
328
+ ax.set_ylim(-1.2, 1)
329
+ ax.axis('off')
330
+ ax.set_aspect('equal')
331
+
332
+ # Add title
333
+ ax.set_title('Digital Glucometer Reading', fontsize=16, pad=20)
334
+
335
+ # Add disclaimer
336
+ fig.text(0.5, 0.01, 'PROTOTYPE ONLY - NOT FOR MEDICAL USE',
337
+ ha='center', va='bottom', fontsize=10, style='italic', color='gray')
338
+
339
+ return fig
340
+ # Gradio interface for finger press simulation with yield for progressive updates
341
+ def finger_press_simulation():
342
+ """
343
+ This function simulates the process of measuring glucose from a finger press
344
+ with progressive updates to show the process step by step.
345
+ """
346
+ # Step 1: Display a message that the camera is activating
347
+ collection_info = "Activating camera for blood perfusion analysis..."
348
+ camera_placeholder = np.ones((240, 320, 3), dtype=np.uint8) * 200
349
+ cv2.putText(camera_placeholder, "Activating Camera...", (50, 120),
350
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
351
+ camera_img = Image.fromarray(camera_placeholder)
352
+
353
+ meter_fig = plt.figure(figsize=(6, 4))
354
+ plt.text(0.5, 0.5, "Measuring...", ha='center', va='center', fontsize=18)
355
+ plt.axis('off')
356
+
357
+ yield collection_info, camera_img, meter_fig
358
+
359
+ # Step 2: Activate the camera (simulated or real)
360
+ time.sleep(1)
361
+ collection_info = "Camera activated, analyzing blood perfusion..."
362
+ try:
363
+ camera_img = activate_camera()
364
+ except Exception as e:
365
+ print(f"Error activating camera: {e}. Using placeholder image.")
366
+ camera_placeholder = np.ones((240, 320, 3), dtype=np.uint8) * 200
367
+ cv2.putText(camera_placeholder, "Camera Error", (50, 120),
368
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 0), 2)
369
+ camera_img = Image.fromarray(camera_placeholder)
370
+
371
+ yield collection_info, camera_img, meter_fig
372
+
373
+ # Step 3: Collect finger data
374
+ time.sleep(1)
375
+ collection_info = "Collecting finger press data..."
376
+ pressure, touch_area, perfusion, opacity = collect_finger_data()
377
+ yield collection_info, camera_img, meter_fig
378
+
379
+ # Step 4: Predict glucose level
380
+ time.sleep(1)
381
+ collection_info = "Analyzing data and predicting glucose level..."
382
+ features = [pressure, touch_area, perfusion, opacity]
383
+ glucose_value = predict_glucose(model, scaler_X, scaler_y, features)
384
+ yield collection_info, camera_img, meter_fig
385
+
386
+ # Step 5: Display final results
387
+ time.sleep(1)
388
+ collection_info = f"Measurement complete!\nPressure: {pressure:.1f}\nTouch Area: {touch_area:.1f}\nPerfusion: {perfusion:.2f}\nOpacity: {opacity:.2f}"
389
+ meter_fig = create_glucose_meter(glucose_value)
390
+
391
+ # Play sound based on glucose level
392
+ try:
393
+ play_beep(glucose_value)
394
+ except Exception as e:
395
+ print(f"Error playing sound: {e}. Skipping sound.")
396
+
397
+ yield collection_info, camera_img, meter_fig
398
+
399
+ # Main execution
400
+ # [Previous code remains unchanged up to the main execution block]
401
+
402
+ if __name__ == "__main__":
403
+ # Generate simulated data and train the model
404
+ df = generate_simulated_data()
405
+ model, scaler_X, scaler_y = train_model(df)
406
+
407
+ # Create Gradio interface
408
+ with gr.Blocks(title="Non-Invasive Glucose Monitor Prototype") as demo:
409
+ gr.Markdown("# Non-Invasive Glucose Monitor Prototype")
410
+ gr.Markdown("Press 'Measure Glucose' to simulate a finger-based glucose measurement")
411
+
412
+ with gr.Row():
413
+ with gr.Column(scale=1):
414
+ measure_button = gr.Button("Measure Glucose")
415
+ info_output = gr.Textbox(label="Measurement Process")
416
+ with gr.Column(scale=1):
417
+ camera_output = gr.Image(label="Camera Feed")
418
+ with gr.Column(scale=1):
419
+ meter_output = gr.Plot(label="Glucose Meter")
420
+
421
+ gr.Markdown("*Note: This is a prototype simulation only - not for actual medical use*")
422
+
423
+ # Connect the button to the simulation function (removed _js parameter)
424
+ measure_button.click(
425
+ fn=finger_press_simulation,
426
+ inputs=None,
427
+ outputs=[info_output, camera_output, meter_output]
428
+ )
429
+
430
+ # Launch the interface
431
+ try:
432
+ demo.launch()
433
+ except Exception as e:
434
+ print(f"Error launching Gradio: {e}")
435
+ print("Attempting to launch in share mode (for debugging).")
436
+ demo.launch(share=True) # Try share mode as a fallback