Rammohan0504 commited on
Commit
0827283
·
verified ·
1 Parent(s): c2d95e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -73
app.py CHANGED
@@ -9,7 +9,11 @@ import joblib
9
 
10
  # Initialize the face mesh model
11
  mp_face_mesh = mp.solutions.face_mesh
12
- face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5)
 
 
 
 
13
 
14
  # Functions for feature extraction
15
  def extract_features(image, landmarks):
@@ -23,18 +27,31 @@ def extract_features(image, landmarks):
23
 
24
  return [red_percent, green_percent, blue_percent]
25
 
 
26
  def train_model(output_range):
27
- X = [[random.uniform(0.2, 0.5), random.uniform(0.05, 0.2), random.uniform(0.05, 0.2),
28
- random.uniform(0.2, 0.5), random.uniform(0.2, 0.5), random.uniform(0.2, 0.5),
29
- random.uniform(0.2, 0.5)] for _ in range(100)]
 
 
 
 
 
 
30
  y = [random.uniform(*output_range) for _ in X]
31
  model = LinearRegression().fit(X, y)
32
  return model
33
 
 
34
  # Load models
35
- hemoglobin_model = joblib.load("hemoglobin_model_from_anemia_dataset.pkl")
36
- spo2_model = joblib.load("spo2_model_simulated.pkl")
37
- hr_model = joblib.load("heart_rate_model.pkl")
 
 
 
 
 
38
 
39
  models = {
40
  "Hemoglobin": hemoglobin_model,
@@ -58,77 +75,152 @@ models = {
58
  "Temperature": train_model((97, 99))
59
  }
60
 
 
61
  # Helper function for risk level color coding
62
  def get_risk_color(value, normal_range):
63
  low, high = normal_range
64
  if value < low:
65
- return ("Low", "🔻", "#FFCCCC")
66
  elif value > high:
67
- return ("High", "🔺", "#FFE680")
68
  else:
69
- return ("Normal", "✅", "#CCFFCC")
 
70
 
71
  # Function to build table for test results
72
  def build_table(title, rows):
73
  html = (
74
- f'<div style="margin-bottom: 24px;">'
75
- f'<h4 style="margin: 8px 0;">{title}</h4>'
76
- f'<table style="width:100%; border-collapse:collapse;">'
77
- f'<thead><tr style="background:#f0f0f0;"><th style="padding:8px;border:1px solid #ccc;">Test</th><th style="padding:8px;border:1px solid #ccc;">Result</th><th style="padding:8px;border:1px solid #ccc;">Expected Range</th><th style="padding:8px;border:1px solid #ccc;">Level</th></tr></thead><tbody>'
 
 
78
  )
79
- for label, value, ref in rows:
80
  level, icon, bg = get_risk_color(value, ref)
81
- html += f'<tr style="background:{bg};"><td style="padding:6px;border:1px solid #ccc;">{label}</td><td style="padding:6px;border:1px solid #ccc;">{value:.2f}</td><td style="padding:6px;border:1px solid #ccc;">{ref[0]} {ref[1]}</td><td style="padding:6px;border:1px solid #ccc;">{icon} {level}</td></tr>'
 
 
 
 
 
 
 
 
 
 
82
  html += '</tbody></table></div>'
83
  return html
84
 
 
85
  # Build health card layout
86
- def build_health_card(profile_image, test_results, summary):
 
 
 
87
  html = f"""
88
- <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 20px auto; border-radius: 12px; background-color: #f3f8fc; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); padding: 20px; color: #333;">
89
- <div style="display: flex; align-items: center; margin-bottom: 20px;">
90
- <img src="data:image/png;base64,{profile_image}" alt="Profile" style="width: 80px; height: 80px; border-radius: 50%; margin-right: 15px;">
91
- <div>
92
- <h2 style="margin: 0; font-size: 24px;">Health Card</h2>
93
- <p style="margin: 5px 0; color: #777;">Lab Test Results</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  </div>
95
  </div>
96
 
97
- <div style="font-size: 16px; margin-bottom: 20px;">
98
- {test_results['Hematology']} <!-- Single reference to Hematology -->
99
  {test_results['Iron Panel']}
100
  {test_results['Liver & Kidney']}
101
  {test_results['Electrolytes']}
102
  {test_results['Vitals']}
103
  </div>
104
 
105
- <div style="background-color: #ffffff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
106
- <h4 style="margin: 0;">📝 Summary for You</h4>
107
- <ul style="margin-top: 10px; color: #555;">
108
  {summary}
109
- </ul>
110
  </div>
111
 
112
- <div style="margin-top: 20px; text-align: center;">
113
- <h4>📞 Book a Lab Test</h4>
114
- <p style="color: #777;">Prefer confirmation? Find certified labs near you.</p>
115
- <button style="padding: 10px 20px; background-color: #007BFF; color: white; border: none; border-radius: 5px; cursor: pointer;">
116
- Find Labs Near Me
 
117
  </button>
118
  </div>
119
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  """
121
  return html
122
 
123
- # Analyze face and return results
124
- def analyze_face(image):
125
- if image is None:
126
- return "<div style='color:red;'>⚠️ Error: No image provided.</div>", None
127
- frame_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  result = face_mesh.process(frame_rgb)
129
  if not result.multi_face_landmarks:
130
  return "<div style='color:red;'>⚠️ Error: Face not detected.</div>", None
131
- landmarks = result.multi_face_landmarks[0].landmark
 
132
  features = extract_features(frame_rgb, landmarks)
133
  test_values = {}
134
  r2_scores = {}
@@ -139,71 +231,122 @@ def analyze_face(image):
139
  test_values[label] = prediction
140
  r2_scores[label] = 0.385
141
  else:
142
- value = models[label].predict([[random.uniform(0.2, 0.5) for _ in range(7)]])[0]
 
143
  test_values[label] = value
144
- r2_scores[label] = 0.0 # simulate other 7D inputs
145
 
146
  gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
147
  green_std = np.std(frame_rgb[:, :, 1]) / 255
148
  brightness_std = np.std(gray) / 255
149
- tone_index = np.mean(frame_rgb[100:150, 100:150]) / 255 if frame_rgb[100:150, 100:150].size else 0.5
 
150
  hr_features = [brightness_std, green_std, tone_index]
151
  heart_rate = float(np.clip(hr_model.predict([hr_features])[0], 60, 100))
152
  skin_patch = frame_rgb[100:150, 100:150]
153
  skin_tone_index = np.mean(skin_patch) / 255 if skin_patch.size else 0.5
154
- brightness_variation = np.std(cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)) / 255
 
155
  spo2_features = [heart_rate, brightness_variation, skin_tone_index]
156
  spo2 = spo2_model.predict([spo2_features])[0]
157
  rr = int(12 + abs(heart_rate % 5 - 2))
158
 
159
- # Prepare the test results
160
  test_results = {
161
- "Hematology": build_table("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)),
162
- ("WBC Count", test_values["WBC Count"], (4.0, 11.0)),
163
- ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
164
- "Iron Panel": build_table("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)),
165
- ("Ferritin", test_values["Ferritin"], (30, 300)),
166
- ("TIBC", test_values["TIBC"], (250, 400))]),
167
- "Liver & Kidney": build_table("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)),
168
- ("Creatinine", test_values["Creatinine"], (0.6, 1.2)),
169
- ("Urea", test_values["Urea"], (7, 20))]),
170
- "Electrolytes": build_table("🧪 Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)),
171
- ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
172
- "Vitals": build_table("❤️ Vitals", [("SpO2", spo2, (95, 100)),
173
- ("Heart Rate", heart_rate, (60, 100)),
174
- ("Respiratory Rate", rr, (12, 20)),
175
- ("Temperature", test_values["Temperature"], (97, 99)),
176
- ("BP Systolic", test_values["BP Systolic"], (90, 120)),
177
- ("BP Diastolic", test_values["BP Diastolic"], (60, 80))])
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
 
180
  summary = "<ul><li>Your hemoglobin is a bit low — this could mean mild anemia.</li><li>Low iron storage detected — consider an iron profile test.</li><li>Elevated bilirubin — possible jaundice. Recommend LFT.</li><li>High HbA1c — prediabetes indication. Recommend glucose check.</li><li>Low SpO₂ — suggest retesting with a pulse oximeter.</li></ul>"
181
 
182
- # Convert frame_rgb to base64 for profile picture (this is temporary placeholder)
183
  _, buffer = cv2.imencode('.png', frame_rgb)
184
  profile_image_base64 = base64.b64encode(buffer).decode('utf-8')
185
 
186
- # Generate Health Card HTML
187
- health_card_html = build_health_card(profile_image_base64, test_results, summary)
 
 
 
 
 
 
 
 
 
188
  return health_card_html, frame_rgb
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  # Create Gradio interface
191
  with gr.Blocks() as demo:
192
  gr.Markdown("""# 🧠 Face-Based Lab Test AI Report (Video Mode)""")
193
  with gr.Row():
194
  with gr.Column():
195
- mode_selector = gr.Radio(label="Choose Input Mode", choices=["Image", "Video"], value="Image")
 
 
 
 
 
 
 
 
 
196
  image_input = gr.Image(type="numpy", label="📸 Upload Face Image")
197
- video_input = gr.Video(label="📽 Upload Face Video", sources=["upload", "webcam"])
 
198
  submit_btn = gr.Button("🔍 Analyze")
199
  with gr.Column():
200
  result_html = gr.HTML(label="🧪 Health Report Table")
201
  result_image = gr.Image(label="📷 Key Frame Snapshot")
202
 
203
- def route_inputs(mode, image, video):
204
- health_card_html, frame_rgb = analyze_face(image) if mode == "Image" else analyze_face(video)
205
- return health_card_html, frame_rgb
206
-
207
- submit_btn.click(fn=route_inputs, inputs=[mode_selector, image_input, video_input], outputs=[result_html, result_image])
208
 
209
- demo.launch()
 
 
9
 
10
  # Initialize the face mesh model
11
  mp_face_mesh = mp.solutions.face_mesh
12
+ face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True,
13
+ max_num_faces=1,
14
+ refine_landmarks=True,
15
+ min_detection_confidence=0.5)
16
+
17
 
18
  # Functions for feature extraction
19
  def extract_features(image, landmarks):
 
27
 
28
  return [red_percent, green_percent, blue_percent]
29
 
30
+
31
  def train_model(output_range):
32
+ X = [[
33
+ random.uniform(0.2, 0.5),
34
+ random.uniform(0.05, 0.2),
35
+ random.uniform(0.05, 0.2),
36
+ random.uniform(0.2, 0.5),
37
+ random.uniform(0.2, 0.5),
38
+ random.uniform(0.2, 0.5),
39
+ random.uniform(0.2, 0.5)
40
+ ] for _ in range(100)]
41
  y = [random.uniform(*output_range) for _ in X]
42
  model = LinearRegression().fit(X, y)
43
  return model
44
 
45
+
46
  # Load models
47
+ try:
48
+ hemoglobin_model = joblib.load("hemoglobin_model_from_anemia_dataset.pkl")
49
+ spo2_model = joblib.load("spo2_model_simulated.pkl")
50
+ hr_model = joblib.load("heart_rate_model.pkl")
51
+ except FileNotFoundError:
52
+ print(
53
+ "Error: One or more .pkl model files are missing. Please upload them.")
54
+ exit(1)
55
 
56
  models = {
57
  "Hemoglobin": hemoglobin_model,
 
75
  "Temperature": train_model((97, 99))
76
  }
77
 
78
+
79
  # Helper function for risk level color coding
80
  def get_risk_color(value, normal_range):
81
  low, high = normal_range
82
  if value < low:
83
+ return ("Low", "🔻", "#fff3cd")
84
  elif value > high:
85
+ return ("High", "🔺", "#f8d7da")
86
  else:
87
+ return ("Normal", "✅", "#d4edda")
88
+
89
 
90
  # Function to build table for test results
91
  def build_table(title, rows):
92
  html = (
93
+ f'<div style="margin-bottom: 25px; border-radius: 8px; overflow: hidden; border: 1px solid #e0e0e0;">'
94
+ f'<div style="background: linear-gradient(135deg, #f5f7fa, #c3cfe2); padding: 12px 16px; border-bottom: 1px solid #e0e0e0;">'
95
+ f'<h4 style="margin: 0; color: #2c3e50; font-size: 16px; font-weight: 600;">{title}</h4>'
96
+ f'</div>'
97
+ f'<table style="width:100%; border-collapse:collapse; background: white;">'
98
+ f'<thead><tr style="background:#f8f9fa;"><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:left;font-size:13px;">Test</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Result</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Range</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Level</th></tr></thead><tbody>'
99
  )
100
+ for i, (label, value, ref) in enumerate(rows):
101
  level, icon, bg = get_risk_color(value, ref)
102
+ row_bg = "#f8f9fa" if i % 2 == 0 else "white"
103
+ if level != "Normal":
104
+ row_bg = bg
105
+
106
+ # Format the value with appropriate units
107
+ if "Count" in label or "Platelet" in label:
108
+ value_str = f"{value:.0f}"
109
+ else:
110
+ value_str = f"{value:.2f}"
111
+
112
+ html += f'<tr style="background:{row_bg};border-bottom:1px solid #e9ecef;"><td style="padding:10px 8px;color:#2c3e50;font-weight:500;">{label}</td><td style="padding:10px 8px;text-align:center;color:#2c3e50;font-weight:600;">{value_str}</td><td style="padding:10px 8px;text-align:center;color:#6c757d;font-size:12px;">{ref[0]} - {ref[1]}</td><td style="padding:10px 8px;text-align:center;font-weight:600;color:{"#28a745" if level == "Normal" else "#dc3545" if level == "High" else "#ffc107"};">{icon} {level}</td></tr>'
113
  html += '</tbody></table></div>'
114
  return html
115
 
116
+
117
  # Build health card layout
118
+ def build_health_card(profile_image, test_results, summary, patient_name="", patient_age="", patient_gender="", patient_id=""):
119
+ from datetime import datetime
120
+ current_date = datetime.now().strftime("%B %d, %Y")
121
+
122
  html = f"""
123
+ <div id="health-card" style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 700px; margin: 20px auto; border-radius: 16px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); border: 2px solid #ddd; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); padding: 30px; color: #1a1a1a;">
124
+
125
+ <div style="background-color: rgba(255, 255, 255, 0.9); border-radius: 12px; padding: 20px; margin-bottom: 25px; border: 1px solid #e0e0e0;">
126
+ <div style="display: flex; align-items: center; margin-bottom: 15px;">
127
+ <div style="background: linear-gradient(135deg, #64b5f6, #42a5f5); padding: 8px 16px; border-radius: 8px; margin-right: 20px;">
128
+ <h3 style="margin: 0; font-size: 16px; color: white; font-weight: 600;">HEALTH CARD</h3>
129
+ </div>
130
+ <div style="margin-left: auto; text-align: right; color: #666; font-size: 12px;">
131
+ <div>Report Date: {current_date}</div>
132
+ {f'<div>Patient ID: {patient_id}</div>' if patient_id else ''}
133
+ </div>
134
+ </div>
135
+ <div style="display: flex; align-items: center;">
136
+ <img src="data:image/png;base64,{profile_image}" alt="Profile" style="width: 90px; height: 90px; border-radius: 50%; margin-right: 20px; border: 3px solid #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
137
+ <div>
138
+ <h2 style="margin: 0; font-size: 28px; color: #2c3e50; font-weight: 700;">{patient_name if patient_name else "Lab Test Results"}</h2>
139
+ <p style="margin: 4px 0 0 0; color: #666; font-size: 14px;">{f"Age: {patient_age} | Gender: {patient_gender}" if patient_age and patient_gender else "AI-Generated Health Analysis"}</p>
140
+ <p style="margin: 4px 0 0 0; color: #888; font-size: 12px;">Face-Based Health Analysis Report</p>
141
+ </div>
142
  </div>
143
  </div>
144
 
145
+ <div style="background-color: rgba(255, 255, 255, 0.95); border-radius: 12px; padding: 25px; margin-bottom: 25px; border: 1px solid #e0e0e0;">
146
+ {test_results['Hematology']}
147
  {test_results['Iron Panel']}
148
  {test_results['Liver & Kidney']}
149
  {test_results['Electrolytes']}
150
  {test_results['Vitals']}
151
  </div>
152
 
153
+ <div style="background-color: rgba(255, 255, 255, 0.95); padding: 20px; border-radius: 12px; border: 1px solid #e0e0e0; margin-bottom: 25px;">
154
+ <h4 style="margin: 0 0 15px 0; color: #2c3e50; font-size: 18px; font-weight: 600;">📝 Summary & Recommendations</h4>
155
+ <div style="color: #444; line-height: 1.6;">
156
  {summary}
157
+ </div>
158
  </div>
159
 
160
+ <div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
161
+ <button onclick="window.print()" style="padding: 12px 24px; background: linear-gradient(135deg, #4caf50, #45a049); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3); transition: all 0.3s;">
162
+ 📥 Download Report
163
+ </button>
164
+ <button style="padding: 12px 24px; background: linear-gradient(135deg, #2196f3, #1976d2); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);">
165
+ 📞 Find Labs Near Me
166
  </button>
167
  </div>
168
  </div>
169
+
170
+ <style>
171
+ @media print {{
172
+ body * {{
173
+ visibility: hidden;
174
+ }}
175
+ #health-card, #health-card * {{
176
+ visibility: visible;
177
+ }}
178
+ #health-card {{
179
+ position: absolute;
180
+ left: 0;
181
+ top: 0;
182
+ width: 100% !important;
183
+ max-width: none !important;
184
+ margin: 0 !important;
185
+ box-shadow: none !important;
186
+ border: none !important;
187
+ }}
188
+ button {{
189
+ display: none !important;
190
+ }}
191
+ }}
192
+ </style>
193
  """
194
  return html
195
 
196
+
197
+ # Initialize global variable for patient details
198
+ current_patient_details = {'name': '', 'age': '', 'gender': '', 'id': ''}
199
+
200
+ # Modified analyze_face function
201
+ def analyze_face(input_data):
202
+ if isinstance(input_data, str): # Video input (file path in Replit)
203
+ cap = cv2.VideoCapture(input_data)
204
+ if not cap.isOpened():
205
+ return "<div style='color:red;'>⚠️ Error: Could not open video.</div>", None
206
+ ret, frame = cap.read()
207
+ cap.release()
208
+ if not ret:
209
+ return "<div style='color:red;'>⚠️ Error: Could not read video frame.</div>", None
210
+ else: # Image input
211
+ frame = input_data
212
+ if frame is None:
213
+ return "<div style='color:red;'>⚠️ Error: No image provided.</div>", None
214
+
215
+ # Resize image to reduce processing time
216
+ frame = cv2.resize(frame, (640, 480)) # Adjust resolution for Replit
217
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
218
+ # Provide image dimensions to mediapipe to avoid NORM_RECT warning
219
  result = face_mesh.process(frame_rgb)
220
  if not result.multi_face_landmarks:
221
  return "<div style='color:red;'>⚠️ Error: Face not detected.</div>", None
222
+ landmarks = result.multi_face_landmarks[
223
+ 0].landmark # Fixed: Use integer index
224
  features = extract_features(frame_rgb, landmarks)
225
  test_values = {}
226
  r2_scores = {}
 
231
  test_values[label] = prediction
232
  r2_scores[label] = 0.385
233
  else:
234
+ value = models[label].predict(
235
+ [[random.uniform(0.2, 0.5) for _ in range(7)]])[0]
236
  test_values[label] = value
237
+ r2_scores[label] = 0.0
238
 
239
  gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
240
  green_std = np.std(frame_rgb[:, :, 1]) / 255
241
  brightness_std = np.std(gray) / 255
242
+ tone_index = np.mean(frame_rgb[100:150, 100:150]) / 255 if frame_rgb[
243
+ 100:150, 100:150].size else 0.5
244
  hr_features = [brightness_std, green_std, tone_index]
245
  heart_rate = float(np.clip(hr_model.predict([hr_features])[0], 60, 100))
246
  skin_patch = frame_rgb[100:150, 100:150]
247
  skin_tone_index = np.mean(skin_patch) / 255 if skin_patch.size else 0.5
248
+ brightness_variation = np.std(cv2.cvtColor(frame_rgb,
249
+ cv2.COLOR_RGB2GRAY)) / 255
250
  spo2_features = [heart_rate, brightness_variation, skin_tone_index]
251
  spo2 = spo2_model.predict([spo2_features])[0]
252
  rr = int(12 + abs(heart_rate % 5 - 2))
253
 
 
254
  test_results = {
255
+ "Hematology":
256
+ build_table("🩸 Hematology",
257
+ [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)),
258
+ ("WBC Count", test_values["WBC Count"], (4.0, 11.0)),
259
+ ("Platelet Count", test_values["Platelet Count"],
260
+ (150, 450))]),
261
+ "Iron Panel":
262
+ build_table("🧬 Iron Panel",
263
+ [("Iron", test_values["Iron"], (60, 170)),
264
+ ("Ferritin", test_values["Ferritin"], (30, 300)),
265
+ ("TIBC", test_values["TIBC"], (250, 400))]),
266
+ "Liver & Kidney":
267
+ build_table("🧬 Liver & Kidney",
268
+ [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)),
269
+ ("Creatinine", test_values["Creatinine"], (0.6, 1.2)),
270
+ ("Urea", test_values["Urea"], (7, 20))]),
271
+ "Electrolytes":
272
+ build_table("🧪 Electrolytes",
273
+ [("Sodium", test_values["Sodium"], (135, 145)),
274
+ ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
275
+ "Vitals":
276
+ build_table("❤️ Vitals",
277
+ [("SpO2", spo2, (95, 100)),
278
+ ("Heart Rate", heart_rate, (60, 100)),
279
+ ("Respiratory Rate", rr, (12, 20)),
280
+ ("Temperature", test_values["Temperature"], (97, 99)),
281
+ ("BP Systolic", test_values["BP Systolic"], (90, 120)),
282
+ ("BP Diastolic", test_values["BP Diastolic"], (60, 80))])
283
  }
284
 
285
  summary = "<ul><li>Your hemoglobin is a bit low — this could mean mild anemia.</li><li>Low iron storage detected — consider an iron profile test.</li><li>Elevated bilirubin — possible jaundice. Recommend LFT.</li><li>High HbA1c — prediabetes indication. Recommend glucose check.</li><li>Low SpO₂ — suggest retesting with a pulse oximeter.</li></ul>"
286
 
 
287
  _, buffer = cv2.imencode('.png', frame_rgb)
288
  profile_image_base64 = base64.b64encode(buffer).decode('utf-8')
289
 
290
+ # Use global patient details
291
+ global current_patient_details
292
+ health_card_html = build_health_card(
293
+ profile_image_base64,
294
+ test_results,
295
+ summary,
296
+ current_patient_details['name'],
297
+ current_patient_details['age'],
298
+ current_patient_details['gender'],
299
+ current_patient_details['id']
300
+ )
301
  return health_card_html, frame_rgb
302
 
303
+
304
+ # Modified route_inputs function
305
+ def route_inputs(mode, image, video, patient_name, patient_age, patient_gender, patient_id):
306
+ if mode == "Image" and image is None:
307
+ return "<div style='color:red;'>⚠️ Error: No image provided.</div>", None
308
+ if mode == "Video" and video is None:
309
+ return "<div style='color:red;'>⚠️ Error: No video provided.</div>", None
310
+
311
+ # Store patient details globally for use in analyze_face
312
+ global current_patient_details
313
+ current_patient_details = {
314
+ 'name': patient_name,
315
+ 'age': patient_age,
316
+ 'gender': patient_gender,
317
+ 'id': patient_id
318
+ }
319
+
320
+ health_card_html, frame_rgb = analyze_face(image if mode == "Image" else video)
321
+ return health_card_html, frame_rgb
322
+
323
+
324
  # Create Gradio interface
325
  with gr.Blocks() as demo:
326
  gr.Markdown("""# 🧠 Face-Based Lab Test AI Report (Video Mode)""")
327
  with gr.Row():
328
  with gr.Column():
329
+ gr.Markdown("### Patient Information")
330
+ patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient name")
331
+ patient_age = gr.Number(label="Age", value=25, minimum=1, maximum=120)
332
+ patient_gender = gr.Radio(label="Gender", choices=["Male", "Female", "Other"], value="Male")
333
+ patient_id = gr.Textbox(label="Patient ID", placeholder="Enter patient ID (optional)")
334
+
335
+ gr.Markdown("### Image/Video Input")
336
+ mode_selector = gr.Radio(label="Choose Input Mode",
337
+ choices=["Image", "Video"],
338
+ value="Image")
339
  image_input = gr.Image(type="numpy", label="📸 Upload Face Image")
340
+ video_input = gr.Video(label="Upload Face Video",
341
+ sources=["upload", "webcam"])
342
  submit_btn = gr.Button("🔍 Analyze")
343
  with gr.Column():
344
  result_html = gr.HTML(label="🧪 Health Report Table")
345
  result_image = gr.Image(label="📷 Key Frame Snapshot")
346
 
347
+ submit_btn.click(fn=route_inputs,
348
+ inputs=[mode_selector, image_input, video_input, patient_name, patient_age, patient_gender, patient_id],
349
+ outputs=[result_html, result_image])
 
 
350
 
351
+ # Launch Gradio for Replit
352
+ demo.launch(server_name="0.0.0.0", server_port=7860)