Spaces:
Sleeping
Sleeping
File size: 6,789 Bytes
0d30c79 4d80bbb 0d30c79 75cf37f | 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 | import gradio as gr
import pytesseract
from PIL import Image
import torch
import re
import os
from transformers import T5ForConditionalGeneration, T5TokenizerFast
from gtts import gTTS
# --- Modular Imports ---
# Ensure these files are in the same folder as app.py
from utils import clean_text, strict_clean_ocr, is_medical_text
from nlp_utils import translate_to_malayalam, get_highlighted_summary
from medical_domain import HIGHLIGHT_TERMS
# =========================
# SETUP & MODELS
# =========================
# Update this path if your Tesseract installation is in a different folder
#pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
summarizer_model = T5ForConditionalGeneration.from_pretrained("t5-base").to(DEVICE)
summarizer_tokenizer = T5TokenizerFast.from_pretrained("t5-base", model_max_length=512)
# =========================
# MAIN PIPELINE
# =========================
def process_image(image, progress=gr.Progress()):
if image is None:
return "", "Please upload an image.", "Please upload an image.", None
# --- 1. OCR Extraction ---
progress(0.2, desc="Extracting Text...")
raw_text = pytesseract.image_to_string(image.convert("L"), config="--psm 6")
# Clean text (Handles clinical units like g/dL, blood pressure formats, etc.)
cleaned = strict_clean_ocr(clean_text(raw_text))[:1200]
# --- 2. Domain Validation ---
if not is_medical_text(cleaned):
return cleaned, "### ⚠️ ERROR\nINVALID MEDICAL REPORT DETECTED.\nPlease upload a valid clinical report.", "", None
# --- 3. Summarization (Memory Efficient) ---
progress(0.5, desc="Analyzing Report...")
input_text = "summarize: " + cleaned.strip()
inputs = summarizer_tokenizer(
input_text,
return_tensors="pt",
truncation=True,
max_length=512
).to(DEVICE)
with torch.no_grad():
outputs = summarizer_model.generate(
input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"],
max_new_tokens=200,
min_length=50,
num_beams=6,
repetition_penalty=1.5,
no_repeat_ngram_size=3,
length_penalty=1.2,
early_stopping=True
)
raw_summary = summarizer_tokenizer.decode(outputs[0], skip_special_tokens=True).strip()
# Capitalize sentences for professionalism
raw_summary = re.sub(r'(^|[.!?]\s+)([a-z])', lambda m: m.group(1) + m.group(2).upper(), raw_summary)
# --- 4. Highlighting (English UI Only) ---
progress(0.7, desc="Processing Highlights...")
# Using the new multi-word phrase logic from nlp_utils
highlighted_eng = get_highlighted_summary(raw_summary, HIGHLIGHT_TERMS)
# --- 5. Translation (Clean Input) ---
progress(0.9, desc="Translating to Malayalam...")
mal_summary = translate_to_malayalam(raw_summary)
mal_summary = mal_summary.strip()
# Find the last occurrence of Malayalam punctuation (।) or English (.)
last_punc = max(mal_summary.rfind('।'), mal_summary.rfind('.'))
if last_punc != -1:
# Keep everything up to and including the last full stop
mal_summary = mal_summary[:last_punc + 1]
# ==========================================
# --- 6. Audio Generation ---
audio_file = "output.mp3"
try:
# Generate audio from the Malayalam translation
tts = gTTS(text=mal_summary, lang="ml")
tts.save(audio_file)
audio_output = audio_file
except Exception as e:
print(f"Audio Error: {e}")
audio_output = None
return cleaned, highlighted_eng, mal_summary, audio_output
# =========================
# UI & CSS
# =========================
custom_css = """
footer {visibility: hidden}
.gradio-container {background-color: #f0f4f8}
/* Hides all timers and 'Preparing Output' status text */
.timer, .meta-text, .meta-text-item, .status-tracker, .progress-text, .progress-container, .loading {
display: none !important;
visibility: hidden !important;
}
.output-box {
border-radius: 10px; padding: 10px; background: white;
margin-bottom: 10px; border: 1px solid #d1d9e0;
}
#medical-header {text-align: center; color: #004d99; font-family: 'Segoe UI', sans-serif; margin-bottom: 20px;}
"""
with gr.Blocks(title="MEDILITE") as demo:
gr.HTML("<div id='medical-header'><h1>🩺 MEDILITE - Medical Report Summarizer</h1></div>")
with gr.Tabs():
with gr.Tab("📋 Patient Summary"):
with gr.Row():
# LEFT COLUMN: Upload
with gr.Column(scale=1):
gr.Markdown("### Upload Medical Image")
input_img = gr.Image(type="pil", label=None)
with gr.Row():
clear_btn = gr.Button("Clear", variant="secondary")
submit_btn = gr.Button("Submit", variant="primary")
# RIGHT COLUMN: Boxed UI results
with gr.Column(scale=1):
with gr.Group(elem_classes="output-box"):
gr.Markdown("#### English Summary")
eng_display = gr.Markdown(value="*Results will appear here...*", container=True)
# Malayalam Section
with gr.Group(elem_classes="output-box"):
gr.Markdown("#### മലയാളം സംഗ്രഹം (Malayalam Summary)")
mal_display = gr.Markdown(value="*ഫലങ്ങൾ ഇവിടെ ദൃശ്യമാകും...*", container=True)
# Audio component without internal container
with gr.Group(elem_classes="output-box"):
gr.Markdown("#### Malayalam Audio")
audio_out = gr.Audio(label=None, type="filepath", container=True)
# Technical Tab
with gr.Tab("⚙️ OCR Extraction"):
gr.Markdown("#### Raw OCR Extraction")
extracted_text = gr.Textbox(lines=20, interactive=False)
# --- Button Actions ---
submit_btn.click(
fn=process_image,
inputs=input_img,
outputs=[extracted_text, eng_display, mal_display, audio_out]
)
clear_btn.click(
lambda: [None, "", "*Summary will appear here...*", "*മലയാളം സംഗ്രഹം ഇവിടെ കാണാം...*", None],
outputs=[input_img, extracted_text, eng_display, mal_display, audio_out]
)
if __name__ == "__main__":
# Final launch command
demo.launch(css=custom_css) |