import os import gradio as gr import cv2 import numpy as np import mediapipe as mp from sklearn.linear_model import LinearRegression import random import base64 import joblib from datetime import datetime import shutil from reportlab.lib.pagesizes import letter from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib import colors import atexit import glob # Cleanup temporary files on exit def cleanup_temp_files(): for temp_file in glob.glob("/tmp/Health_Report_*.pdf"): os.remove(temp_file) atexit.register(cleanup_temp_files) # Initialize the face mesh model 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) # Functions for feature extraction 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 # Load models try: hemoglobin_model = joblib.load("hemoglobin_model_from_anemia_dataset.pkl") spo2_model = joblib.load("spo2_model_simulated.pkl") hr_model = joblib.load("heart_rate_model.pkl") except FileNotFoundError: print("Error: One or more .pkl model files are missing. Please upload them.") exit(1) 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)) } # Helper function for risk level color coding def get_risk_color(value, normal_range): low, high = normal_range if value < low: return ("Low", "๐Ÿ”ป", "#fff3cd") elif value > high: return ("High", "๐Ÿ”บ", "#f8d7da") else: return ("Normal", "โœ…", "#d4edda") # Function to build table for test results def build_table(title, rows): color_map = { "Normal": "#28a745", "High": "#dc3545", "Low": "#ffc107" } html = ( f'
' f'
' f'

{title}

' f'
' f'' f'' ) for i, (label, value, ref) in enumerate(rows): level, icon, bg = get_risk_color(value, ref) row_bg = "#f8f9fa" if i % 2 == 0 else "white" if level != "Normal": row_bg = bg if "Count" in label or "Platelet" in label: value_str = f"{value:.0f}" else: value_str = f"{value:.2f}" html += f'' html += '
TestResultRangeLevel
{label}{value_str}{ref[0]} - {ref[1]}{icon} {level}
' return html # Function to save the health report to PDF def save_results_to_pdf(test_results, test_values, filename): try: doc = SimpleDocTemplate(filename, pagesize=letter) styles = getSampleStyleSheet() title_style = ParagraphStyle( name='Title', fontSize=16, leading=20, alignment=1, spaceAfter=20, textColor=colors.black, fontName='Helvetica-Bold' ) body_style = ParagraphStyle( name='Body', fontSize=12, leading=14, spaceAfter=10, textColor=colors.black, fontName='Helvetica' ) flowables = [Paragraph("Health Report", title_style), Spacer(1, 12)] # Define ranges for all tests test_ranges = { "Hemoglobin": (13.5, 17.5), "WBC Count": (4.0, 11.0), "Platelet Count": (150, 450), "Iron": (60, 170), "Ferritin": (30, 300), "TIBC": (250, 400), "Bilirubin": (0.3, 1.2), "Creatinine": (0.6, 1.2), "Urea": (7, 20), "Sodium": (135, 145), "Potassium": (3.5, 5.1), "SpO2": (95, 100), "Heart Rate": (60, 100), "Respiratory Rate": (12, 20), "Temperature": (97, 99), "BP Systolic": (90, 120), "BP Diastolic": (60, 80) } for section_name, html in test_results.items(): flowables.append(Paragraph(section_name, styles['Heading2'])) table_data = [["Test", "Result", "Range", "Level"]] for label in test_ranges: if any(label in html for section_html in test_results.values()): value = test_values.get(label, test_ranges[label][0] + random.uniform(-1, 1)) # Use actual value or fallback level, _, _ = get_risk_color(value, test_ranges[label]) table_data.append([label, f"{value:.2f}" if label not in ["WBC Count", "Platelet Count"] else f"{value:.0f}", f"{test_ranges[label][0]} - {test_ranges[label][1]}", level]) table = Table(table_data) table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), colors.grey), ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), ('ALIGN', (0, 0), (-1, -1), 'CENTER'), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, 0), 12), ('BOTTOMPADDING', (0, 0), (-1, 0), 12), ('BACKGROUND', (0, 1), (-1, -1), colors.beige), ('GRID', (0, 0), (-1, -1), 1, colors.black) ])) flowables.append(table) flowables.append(Spacer(1, 12)) doc.build(flowables) return f"PDF saved successfully as {filename}", filename except Exception as e: return f"Error saving PDF: {str(e)}", None # Build health card layout def build_health_card(profile_image, test_results, summary, pdf_filepath, patient_name="", patient_age="", patient_gender="", patient_id=""): from datetime import datetime current_date = datetime.now().strftime("%B %d, %Y") pdf_filename = os.path.basename(pdf_filepath) if pdf_filepath else "health_report.pdf" html = f"""

HEALTH CARD

Report Date: {current_date}
{f'
Patient ID: {patient_id}
' if patient_id else ''}
Profile

{patient_name if patient_name else 'Lab Test Results'}

{f'Age: {patient_age} | Gender: {patient_gender}' if patient_age and patient_gender else 'AI-Generated Health Analysis'}

Face-Based Health Analysis Report

{test_results['Hematology']} {test_results['Iron Panel']} {test_results['Liver & Kidney']} {test_results['Electrolytes']} {test_results['Vitals']}

๐Ÿ“ Summary & Recommendations

{summary}
""" return html # Initialize global variable for patient details current_patient_details = {'name': '', 'age': '', 'gender': '', 'id': ''} # Modified analyze_face function def analyze_face(input_data): if isinstance(input_data, str): # Video input (file path in Replit) cap = cv2.VideoCapture(input_data) if not cap.isOpened(): return "
โš ๏ธ Error: Could not open video.
", None ret, frame = cap.read() cap.release() if not ret: return "
โš ๏ธ Error: Could not read video frame.
", None else: # Image input frame = input_data if frame is None: return "
โš ๏ธ Error: No image provided.
", None # Resize image to reduce processing time frame = cv2.resize(frame, (640, 480)) frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) result = face_mesh.process(frame_rgb) if not result.multi_face_landmarks: return "
โš ๏ธ Error: Face not detected.
", None landmarks = result.multi_face_landmarks[0].landmark features = extract_features(frame_rgb, landmarks) test_values = {} for label in models: if label == "Hemoglobin": prediction = models[label].predict([features])[0] test_values[label] = prediction else: value = models[label].predict([[random.uniform(0.2, 0.5) for _ in range(7)]])[0] test_values[label] = value 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)) test_values.update({ "SpO2": spo2, "Heart Rate": heart_rate, "Respiratory Rate": rr }) test_results = { "Hematology": 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))]), "Iron Panel": build_table("๐Ÿงฌ Iron Panel", [("Iron", test_values["Iron"], (60, 170)), ("Ferritin", test_values["Ferritin"], (30, 300)), ("TIBC", test_values["TIBC"], (250, 400))]), "Liver & Kidney": 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))]), "Electrolytes": build_table("๐Ÿงช Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)), ("Potassium", test_values["Potassium"], (3.5, 5.1))]), "Vitals": build_table("โค๏ธ Vitals", [("SpO2", test_values["SpO2"], (95, 100)), ("Heart Rate", test_values["Heart Rate"], (60, 100)), ("Respiratory Rate", test_values["Respiratory Rate"], (12, 20)), ("Temperature", test_values["Temperature"], (97, 99)), ("BP Systolic", test_values["BP Systolic"], (90, 120)), ("BP Diastolic", test_values["BP Diastolic"], (60, 80))]) } summary = "" _, buffer = cv2.imencode('.png', frame_rgb) profile_image_base64 = base64.b64encode(buffer).decode('utf-8') pdf_filename = f"Health_Report_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.pdf" pdf_result, pdf_filepath = save_results_to_pdf(test_results, test_values, pdf_filename) if pdf_filepath: temp_pdf_path = "/tmp/" + os.path.basename(pdf_filepath) shutil.copy(pdf_filepath, temp_pdf_path) if os.path.exists(temp_pdf_path) and os.access(temp_pdf_path, os.R_OK): health_card_html = build_health_card( profile_image_base64, test_results, summary, temp_pdf_path, current_patient_details['name'], current_patient_details['age'], current_patient_details['gender'], current_patient_details['id'] ) return health_card_html, temp_pdf_path else: return "
โš ๏ธ Error: PDF file not accessible.
", None return "
โš ๏ธ Error: Failed to generate PDF.
", None # Modified route_inputs function def route_inputs(mode, image, video, patient_name, patient_age, patient_gender, patient_id): if mode == "Image" and image is None: return "
โš ๏ธ Error: No image provided.
", None if mode == "Video" and video is None: return "
โš ๏ธ Error: No video provided.
", None global current_patient_details current_patient_details = { 'name': patient_name, 'age': patient_age, 'gender': patient_gender, 'id': patient_id } health_card_html, pdf_file_path = analyze_face(image if mode == "Image" else video) return health_card_html, pdf_file_path # Create Gradio interface with gr.Blocks() as demo: gr.Markdown("""# ๐Ÿง  Face-Based Lab Test AI Report (Video Mode)""") with gr.Row(): with gr.Column(): gr.Markdown("### Patient Information") patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient name") patient_age = gr.Number(label="Age", value=25, minimum=1, maximum=120) patient_gender = gr.Radio(label="Gender", choices=["Male", "Female", "Other"], value="Male") patient_id = gr.Textbox(label="Patient ID", placeholder="Enter patient ID (optional)") gr.Markdown("### Image/Video Input") 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_pdf = gr.File(label="Download Health Report PDF", interactive=False) submit_btn.click(fn=route_inputs, inputs=[mode_selector, image_input, video_input, patient_name, patient_age, patient_gender, patient_id], outputs=[result_html, result_pdf]) # Launch Gradio for Replit demo.launch(server_name="0.0.0.0", server_port=7860)