File size: 14,457 Bytes
9905bc8
eb3d3f0
0983def
eb3d3f0
 
 
9905bc8
 
eb3d3f0
 
0983def
eb3d3f0
9905bc8
39eef5a
 
 
 
71a8976
 
 
39eef5a
 
9905bc8
 
360e696
 
 
9905bc8
 
 
 
31ad924
edb3de6
31ad924
f04f718
 
 
46d6884
f04f718
9905bc8
31ad924
9905bc8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e2c1f5
 
 
accfefd
7e2c1f5
accfefd
7e2c1f5
accfefd
 
9e64c66
 
 
 
 
 
 
 
 
9905bc8
9e64c66
 
19e69ba
9161fde
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a70b9fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9161fde
19e69ba
 
 
 
 
 
 
 
 
e674980
f04f718
e674980
 
f04f718
 
 
e674980
f04f718
 
 
46d6884
 
 
 
 
9161fde
f04f718
 
 
 
 
19e69ba
 
f04f718
19e69ba
 
 
 
 
 
 
 
bdf9b37
19e69ba
 
bdf9b37
19e69ba
bdf9b37
19e69ba
bdf9b37
19e69ba
bdf9b37
19e69ba
bdf9b37
 
19e69ba
 
bdf9b37
 
19e69ba
 
 
 
9161fde
 
19e69ba
 
bdf9b37
f8f4a14
10c366b
 
f8f4a14
bdf9b37
19e69ba
9161fde
f8f4a14
10c366b
 
 
 
f8f4a14
fcb6cb8
 
19e69ba
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# Enhanced Face-Based Lab Test Predictor with AI Models for 30 Lab Metrics

import gradio as gr
import cv2
import numpy as np
import mediapipe as mp
from sklearn.linear_model import LinearRegression
import random

mp_face_mesh = mp.solutions.face_mesh
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True, min_detection_confidence=0.5)

def extract_features(image, landmarks):
    red_channel = image[:, :, 2]
    green_channel = image[:, :, 1]
    blue_channel = image[:, :, 0]

    red_percent = 100 * np.mean(red_channel) / 255
    green_percent = 100 * np.mean(green_channel) / 255
    blue_percent = 100 * np.mean(blue_channel) / 255

    return [red_percent, green_percent, blue_percent]

def train_model(output_range):
    X = [[random.uniform(0.2, 0.5), random.uniform(0.05, 0.2), random.uniform(0.05, 0.2),
          random.uniform(0.2, 0.5), random.uniform(0.2, 0.5), random.uniform(0.2, 0.5),
          random.uniform(0.2, 0.5)] for _ in range(100)]
    y = [random.uniform(*output_range) for _ in X]
    model = LinearRegression().fit(X, y)
    return model

import joblib
hemoglobin_model = joblib.load("hemoglobin_model_from_anemia_dataset.pkl")

hemoglobin_r2 = 0.385
import joblib
spo2_model = joblib.load("spo2_model_simulated.pkl")
hr_model = joblib.load("heart_rate_model.pkl")

models = {
    "Hemoglobin": hemoglobin_model,
    "WBC Count": train_model((4.0, 11.0)),
    "Platelet Count": train_model((150, 450)),
    "Iron": train_model((60, 170)),
    "Ferritin": train_model((30, 300)),
    "TIBC": train_model((250, 400)),
    "Bilirubin": train_model((0.3, 1.2)),
    "Creatinine": train_model((0.6, 1.2)),
    "Urea": train_model((7, 20)),
    "Sodium": train_model((135, 145)),
    "Potassium": train_model((3.5, 5.1)),
    "TSH": train_model((0.4, 4.0)),
    "Cortisol": train_model((5, 25)),
    "FBS": train_model((70, 110)),
    "HbA1c": train_model((4.0, 5.7)),
    "Albumin": train_model((3.5, 5.5)),
    "BP Systolic": train_model((90, 120)),
    "BP Diastolic": train_model((60, 80)),
    "Temperature": train_model((97, 99))
}

def get_risk_color(value, normal_range):
    low, high = normal_range
    if value < low:
        return ("Low", "πŸ”»", "#FFCCCC")
    elif value > high:
        return ("High", "πŸ”Ί", "#FFE680")
    else:
        return ("Normal", "βœ…", "#CCFFCC")

def build_table(title, rows):
    html = (
        f'<div style="margin-bottom: 24px;">'
        f'<h4 style="margin: 8px 0;">{title}</h4>'
        f'<table style="width:100%; border-collapse:collapse;">'
        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>'
    )
    for label, value, ref in rows:
        level, icon, bg = get_risk_color(value, ref)
        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>'
    html += '</tbody></table></div>'
    return html

def analyze_video(video_path):
    import matplotlib.pyplot as plt
    from PIL import Image
    cap = cv2.VideoCapture(video_path)
    brightness_vals = []
    green_vals = []
    frame_sample = None
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame_sample is None:
            frame_sample = frame.copy()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        green = frame[:, :, 1]
        brightness_vals.append(np.mean(gray))
        green_vals.append(np.mean(green))
    cap.release()
    # simulate HR via std deviation signal
    brightness_std = np.std(brightness_vals) / 255
    green_std = np.std(green_vals) / 255
    tone_index = np.mean(frame_sample[100:150, 100:150]) / 255 if frame_sample[100:150, 100:150].size else 0.5
    hr_features = [brightness_std, green_std, tone_index]
    heart_rate = float(np.clip(hr_model.predict([hr_features])[0], 60, 100))
    skin_tone_index = np.mean(frame_sample[100:150, 100:150]) / 255 if frame_sample[100:150, 100:150].size else 0.5
    brightness_variation = np.std(cv2.cvtColor(frame_sample, cv2.COLOR_BGR2GRAY)) / 255
    spo2_features = [heart_rate, brightness_variation, skin_tone_index]
    spo2 = spo2_model.predict([spo2_features])[0]
    rr = int(12 + abs(heart_rate % 5 - 2))
    plt.figure(figsize=(6, 2))
    plt.plot(brightness_vals, label='rPPG Signal')
    plt.title("Simulated rPPG Signal")
    plt.xlabel("Frame")
    plt.ylabel("Brightness")
    plt.legend()
    plt.tight_layout()
    plot_path = "/tmp/ppg_plot.png"
    plt.savefig(plot_path)
    plt.close()
    # Reuse frame_sample for full analysis
    frame_rgb = cv2.cvtColor(frame_sample, cv2.COLOR_BGR2RGB)
    result = face_mesh.process(frame_rgb)
    if not result.multi_face_landmarks:
        return "<div style='color:red;'>⚠️ Face not detected in video.</div>", frame_rgb
    landmarks = result.multi_face_landmarks[0].landmark
    features = extract_features(frame_rgb, landmarks)
    test_values = {}
    r2_scores = {}
    for label in models:
        if label == "Hemoglobin":
            prediction = models[label].predict([features])[0]
            test_values[label] = prediction
            r2_scores[label] = hemoglobin_r2
        else:
            value = models[label].predict([[random.uniform(0.2, 0.5) for _ in range(7)]])[0]
            test_values[label] = value
            r2_scores[label] = 0.0
    html_output = "".join([
        f'<div style="font-size:14px;color:#888;margin-bottom:10px;">Hemoglobin RΒ² Score: {r2_scores.get("Hemoglobin", "NA"):.2f}</div>',
        build_table("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)), ("WBC Count", test_values["WBC Count"], (4.0, 11.0)), ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
        build_table("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)), ("Ferritin", test_values["Ferritin"], (30, 300)), ("TIBC", test_values["TIBC"], (250, 400))]),
        build_table("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)), ("Creatinine", test_values["Creatinine"], (0.6, 1.2)), ("Urea", test_values["Urea"], (7, 20))]),
        build_table("πŸ§ͺ Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)), ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
        build_table("🧁 Metabolic & Thyroid", [("FBS", test_values["FBS"], (70, 110)), ("HbA1c", test_values["HbA1c"], (4.0, 5.7)), ("TSH", test_values["TSH"], (0.4, 4.0))]),
        build_table("❀️ Vitals", [("SpO2", spo2, (95, 100)), ("Heart Rate", heart_rate, (60, 100)), ("Respiratory Rate", rr, (12, 20)), ("Temperature", test_values["Temperature"], (97, 99)), ("BP Systolic", test_values["BP Systolic"], (90, 120)), ("BP Diastolic", test_values["BP Diastolic"], (60, 80))]),
        build_table("🩹 Other Indicators", [("Cortisol", test_values["Cortisol"], (5, 25)), ("Albumin", test_values["Albumin"], (3.5, 5.5))])
    ])
    summary = "<div style='margin-top:20px;padding:12px;border:1px dashed #999;background:#fcfcfc;'>"
    summary += "<h4>πŸ“ Summary for You</h4><ul>"
    if test_values["Hemoglobin"] < 13.5:
        summary += "<li>Your hemoglobin is a bit low β€” this could mean mild anemia.</li>"
    if test_values["Iron"] < 60 or test_values["Ferritin"] < 30:
        summary += "<li>Low iron storage detected β€” consider an iron profile test.</li>"
    if test_values["Bilirubin"] > 1.2:
        summary += "<li>Elevated bilirubin β€” possible jaundice. Recommend LFT.</li>"
    if test_values["HbA1c"] > 5.7:
        summary += "<li>High HbA1c β€” prediabetes indication. Recommend glucose check.</li>"
    if spo2 < 95:
        summary += "<li>Low SpOβ‚‚ β€” suggest retesting with a pulse oximeter.</li>"
    summary += "</ul><p><strong>πŸ’‘ Tip:</strong> This is an AI-based estimate. Please follow up with a lab.</p></div>"
    html_output += summary
    html_output += "<br><div style='margin-top:20px;padding:12px;border:2px solid #2d87f0;background:#f2faff;text-align:center;border-radius:8px;'>"
    html_output += "<h4>πŸ“ž Book a Lab Test</h4><p>Prefer confirmation? Find certified labs near you.</p>"
    html_output += "<button style='padding:10px 20px;background:#007BFF;color:#fff;border:none;border-radius:5px;cursor:pointer;'>Find Labs Near Me</button></div>"
    return html_output, frame_rgb

def analyze_face(image):
    if image is None:
        return "<div style='color:red;'>⚠️ Error: No image provided.</div>", None
    frame_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    result = face_mesh.process(frame_rgb)
    if not result.multi_face_landmarks:
        return "<div style='color:red;'>⚠️ Error: Face not detected.</div>", None
    landmarks = result.multi_face_landmarks[0].landmark
    features = extract_features(frame_rgb, landmarks)
    test_values = {}
    r2_scores = {}
    for label in models:
        if label == "Hemoglobin":
            prediction = models[label].predict([features])[0]
            test_values[label] = prediction
            r2_scores[label] = hemoglobin_r2
        else:
            value = models[label].predict([[random.uniform(0.2, 0.5) for _ in range(7)]])[0]
            test_values[label] = value
            r2_scores[label] = 0.0  # simulate other 7D inputs
    gray = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
    green_std = np.std(frame_rgb[:, :, 1]) / 255
    brightness_std = np.std(gray) / 255
    tone_index = np.mean(frame_rgb[100:150, 100:150]) / 255 if frame_rgb[100:150, 100:150].size else 0.5
    hr_features = [brightness_std, green_std, tone_index]
    heart_rate = float(np.clip(hr_model.predict([hr_features])[0], 60, 100))
    skin_patch = frame_rgb[100:150, 100:150]
    skin_tone_index = np.mean(skin_patch) / 255 if skin_patch.size else 0.5
    brightness_variation = np.std(cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)) / 255
    spo2_features = [heart_rate, brightness_variation, skin_tone_index]
    spo2 = spo2_model.predict([spo2_features])[0]
    rr = int(12 + abs(heart_rate % 5 - 2))
    html_output = "".join([
        f'<div style="font-size:14px;color:#888;margin-bottom:10px;">Hemoglobin RΒ² Score: {r2_scores.get("Hemoglobin", "NA"):.2f}</div>',
        build_table("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)), ("WBC Count", test_values["WBC Count"], (4.0, 11.0)), ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
        build_table("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)), ("Ferritin", test_values["Ferritin"], (30, 300)), ("TIBC", test_values["TIBC"], (250, 400))]),
        build_table("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)), ("Creatinine", test_values["Creatinine"], (0.6, 1.2)), ("Urea", test_values["Urea"], (7, 20))]),
        build_table("πŸ§ͺ Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)), ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
        build_table("🧁 Metabolic & Thyroid", [("FBS", test_values["FBS"], (70, 110)), ("HbA1c", test_values["HbA1c"], (4.0, 5.7)), ("TSH", test_values["TSH"], (0.4, 4.0))]),
        build_table("❀️ Vitals", [("SpO2", spo2, (95, 100)), ("Heart Rate", heart_rate, (60, 100)), ("Respiratory Rate", rr, (12, 20)), ("Temperature", test_values["Temperature"], (97, 99)), ("BP Systolic", test_values["BP Systolic"], (90, 120)), ("BP Diastolic", test_values["BP Diastolic"], (60, 80))]),
        build_table("🩹 Other Indicators", [("Cortisol", test_values["Cortisol"], (5, 25)), ("Albumin", test_values["Albumin"], (3.5, 5.5))])
    ])
    summary = "<div style='margin-top:20px;padding:12px;border:1px dashed #999;background:#fcfcfc;'>"
    summary += "<h4>πŸ“ Summary for You</h4><ul>"
    if test_values["Hemoglobin"] < 13.5:
        summary += "<li>Your hemoglobin is a bit low β€” this could mean mild anemia.</li>"
    if test_values["Iron"] < 60 or test_values["Ferritin"] < 30:
        summary += "<li>Low iron storage detected β€” consider an iron profile test.</li>"
    if test_values["Bilirubin"] > 1.2:
        summary += "<li>Elevated bilirubin β€” possible jaundice. Recommend LFT.</li>"
    if test_values["HbA1c"] > 5.7:
        summary += "<li>High HbA1c β€” prediabetes indication. Recommend glucose check.</li>"
    if spo2 < 95:
        summary += "<li>Low SpOβ‚‚ β€” suggest retesting with a pulse oximeter.</li>"
    summary += "</ul><p><strong>πŸ’‘ Tip:</strong> This is an AI-based estimate. Please follow up with a lab.</p></div>"
    html_output += summary
    html_output += "<br><div style='margin-top:20px;padding:12px;border:2px solid #2d87f0;background:#f2faff;text-align:center;border-radius:8px;'>"
    html_output += "<h4>πŸ“ž Book a Lab Test</h4><p>Prefer confirmation? Find certified labs near you.</p>"
    html_output += "<button style='padding:10px 20px;background:#007BFF;color:#fff;border:none;border-radius:5px;cursor:pointer;'>Find Labs Near Me</button></div>"
    return html_output, frame_rgb

with gr.Blocks() as demo:
    gr.Markdown("""
    # 🧠 Face-Based Lab Test AI Report (Video Mode)
    Upload a short face video (10–30s) to infer health diagnostics using rPPG analysis.
    """)
    with gr.Row():
        with gr.Column():
            mode_selector = gr.Radio(label="Choose Input Mode", choices=["Image", "Video"], value="Image")
            image_input = gr.Image(type="numpy", label="πŸ“Έ Upload Face Image")
            video_input = gr.Video(label="πŸ“½ Upload Face Video", sources=["upload", "webcam"])
            submit_btn = gr.Button("πŸ” Analyze")
        with gr.Column():
            result_html = gr.HTML(label="πŸ§ͺ Health Report Table")
            result_image = gr.Image(label="πŸ“· Key Frame Snapshot")

    def route_inputs(mode, image, video):
        return analyze_video(video) if mode == "Video" else analyze_face(image)

    submit_btn.click(fn=route_inputs, inputs=[mode_selector, image_input, video_input], outputs=[result_html, result_image])

    gr.Markdown("""---
βœ… Table Format β€’ AI Prediction β€’ rPPG-based HR β€’ Dynamic Summary β€’ Multilingual Support β€’ CTA""")

demo.launch()