Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import google.generativeai as genai
|
3 |
+
from PIL import Image
|
4 |
+
import PyPDF2
|
5 |
+
import tempfile
|
6 |
+
import os
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
import time
|
9 |
+
from gtts import gTTS
|
10 |
+
import base64
|
11 |
+
import requests # Added for Hugging Face API requests
|
12 |
+
from google.api_core import exceptions
|
13 |
+
|
14 |
+
load_dotenv()
|
15 |
+
|
16 |
+
# Configure the Gemini AI model for text analysis
|
17 |
+
gemini_api_key = os.getenv("GEMINI_API_KEY")
|
18 |
+
if not gemini_api_key:
|
19 |
+
st.error("Gemini API key not found. Please set the GEMINI_API_KEY environment variable.")
|
20 |
+
st.stop()
|
21 |
+
|
22 |
+
genai.configure(api_key=gemini_api_key)
|
23 |
+
gemini_model = genai.GenerativeModel('gemini-1.5-flash')
|
24 |
+
|
25 |
+
# Configure the Hugging Face model for image analysis
|
26 |
+
huggingface_api_key = os.getenv("HUGGINGFACE_API_KEY")
|
27 |
+
if not huggingface_api_key:
|
28 |
+
st.error("Hugging Face API key not found. Please set the HUGGINGFACE_API_KEY environment variable.")
|
29 |
+
st.stop()
|
30 |
+
|
31 |
+
HUGGINGFACE_API_URL = os.getenv("HUGGINGFACE_API_URL")
|
32 |
+
if not HUGGINGFACE_API_URL:
|
33 |
+
st.error("Hugging Face API URL not found. Please set the HUGGINGFACE_API_URL environment variable.")
|
34 |
+
st.stop()
|
35 |
+
|
36 |
+
MAX_RETRIES = 3
|
37 |
+
RETRY_DELAY = 2 # seconds
|
38 |
+
|
39 |
+
# Dictionary for language support (including Urdu)
|
40 |
+
LANGUAGES = {
|
41 |
+
"English": "en",
|
42 |
+
"Spanish": "es",
|
43 |
+
"French": "fr",
|
44 |
+
"German": "de",
|
45 |
+
"Italian": "it",
|
46 |
+
"Portuguese": "pt",
|
47 |
+
"Urdu": "ur"
|
48 |
+
}
|
49 |
+
|
50 |
+
def analyze_text_report(content, lang):
|
51 |
+
prompt = "Analyze this medical report concisely. Provide key findings, diagnoses, and recommendations:"
|
52 |
+
|
53 |
+
# Adjust prompt language if not English
|
54 |
+
if lang != "en":
|
55 |
+
translations = {
|
56 |
+
"es": "Analiza este informe médico de manera concisa. Proporcione hallazgos clave, diagnósticos y recomendaciones:",
|
57 |
+
"fr": "Analysez ce rapport médical de manière concise. Fournissez les résultats clés, les diagnostics et les recommandations :",
|
58 |
+
"de": "Analysieren Sie diesen medizinischen Bericht kurz und prägnant. Geben Sie wichtige Ergebnisse, Diagnosen und Empfehlungen an:",
|
59 |
+
"it": "Analizza questo rapporto medico in modo conciso. Fornisci risultati chiave, diagnosi e raccomandazioni:",
|
60 |
+
"pt": "Analise este relatório médico de forma concisa. Forneça os principais resultados, diagnósticos e recomendações:",
|
61 |
+
"ur": "اس طبی رپورٹ کا مختصر تجزیہ کریں۔ اہم نتائج، تشخیصات، اور سفارشات فراہم کریں:"
|
62 |
+
}
|
63 |
+
prompt = translations.get(lang, prompt)
|
64 |
+
|
65 |
+
for attempt in range(MAX_RETRIES):
|
66 |
+
try:
|
67 |
+
response = gemini_model.generate_content(f"{prompt}\n\n{content}")
|
68 |
+
return response.text
|
69 |
+
except exceptions.GoogleAPIError as e:
|
70 |
+
if attempt < MAX_RETRIES - 1:
|
71 |
+
st.warning(f"An error occurred. Retrying in {RETRY_DELAY} seconds... (Attempt {attempt + 1}/{MAX_RETRIES})")
|
72 |
+
time.sleep(RETRY_DELAY)
|
73 |
+
else:
|
74 |
+
st.error(f"Failed to analyze the report after {MAX_RETRIES} attempts. Error: {str(e)}")
|
75 |
+
return fallback_analysis(content, "text")
|
76 |
+
|
77 |
+
def analyze_image_report(image_path, lang):
|
78 |
+
headers = {
|
79 |
+
"Authorization": f"Bearer {huggingface_api_key}",
|
80 |
+
"Content-Type": "application/octet-stream"
|
81 |
+
}
|
82 |
+
|
83 |
+
for attempt in range(MAX_RETRIES):
|
84 |
+
try:
|
85 |
+
with open(image_path, "rb") as img_file:
|
86 |
+
image_data = img_file.read()
|
87 |
+
response = requests.post(HUGGINGFACE_API_URL, headers=headers, data=image_data)
|
88 |
+
|
89 |
+
if response.status_code == 200:
|
90 |
+
result = response.json()
|
91 |
+
# Parse the response based on the model's output structure
|
92 |
+
analysis = ""
|
93 |
+
if isinstance(result, list):
|
94 |
+
for condition in result:
|
95 |
+
label = condition.get('label', 'Unknown')
|
96 |
+
score = condition.get('score', 0)
|
97 |
+
analysis += f"{label}: {score:.2f}\n"
|
98 |
+
elif isinstance(result, dict):
|
99 |
+
for key, value in result.items():
|
100 |
+
analysis += f"{key}: {value:.2f}\n"
|
101 |
+
else:
|
102 |
+
st.warning("Unexpected response format from Hugging Face API.")
|
103 |
+
return fallback_analysis(None, "image")
|
104 |
+
return analysis
|
105 |
+
elif response.status_code == 503:
|
106 |
+
# Model is loading
|
107 |
+
st.warning("Model is loading. Waiting for 30 seconds before retrying...")
|
108 |
+
time.sleep(30)
|
109 |
+
continue
|
110 |
+
else:
|
111 |
+
st.warning(f"Hugging Face API returned status code {response.status_code}: {response.text}")
|
112 |
+
if attempt < MAX_RETRIES - 1:
|
113 |
+
st.warning(f"Retrying in {RETRY_DELAY} seconds... (Attempt {attempt + 1}/{MAX_RETRIES})")
|
114 |
+
time.sleep(RETRY_DELAY)
|
115 |
+
else:
|
116 |
+
st.error(f"Failed to analyze the image after {MAX_RETRIES} attempts.")
|
117 |
+
return fallback_analysis(None, "image")
|
118 |
+
except Exception as e:
|
119 |
+
if attempt < MAX_RETRIES - 1:
|
120 |
+
st.warning(f"An error occurred: {str(e)}. Retrying in {RETRY_DELAY} seconds... (Attempt {attempt + 1}/{MAX_RETRIES})")
|
121 |
+
time.sleep(RETRY_DELAY)
|
122 |
+
else:
|
123 |
+
st.error(f"Failed to analyze the image after {MAX_RETRIES} attempts. Error: {str(e)}")
|
124 |
+
return fallback_analysis(None, "image")
|
125 |
+
|
126 |
+
def fallback_analysis(content, content_type):
|
127 |
+
st.warning("Using fallback analysis method due to API issues.")
|
128 |
+
if content_type == "image":
|
129 |
+
return "Unable to analyze the image due to API issues. Please try again later or consult a medical professional for accurate interpretation."
|
130 |
+
else: # text
|
131 |
+
word_count = len(content.split()) if content else 0
|
132 |
+
return f"""
|
133 |
+
**Fallback Analysis:**
|
134 |
+
1. **Document Type:** Text-based medical report
|
135 |
+
2. **Word Count:** Approximately {word_count} words
|
136 |
+
3. **Content:** The document appears to contain medical information, but detailed analysis is unavailable due to technical issues.
|
137 |
+
4. **Recommendation:** Please review the document manually or consult with a healthcare professional for accurate interpretation.
|
138 |
+
5. **Note:** This is a simplified analysis due to temporary unavailability of the AI service. For a comprehensive analysis, please try again later.
|
139 |
+
"""
|
140 |
+
|
141 |
+
def extract_text_from_pdf(pdf_file):
|
142 |
+
pdf_reader = PyPDF2.PdfReader(pdf_file)
|
143 |
+
text = ""
|
144 |
+
for page in pdf_reader.pages:
|
145 |
+
page_text = page.extract_text()
|
146 |
+
if page_text:
|
147 |
+
text += page_text
|
148 |
+
return text
|
149 |
+
|
150 |
+
def generate_tts_audio(text, lang_code):
|
151 |
+
tts = gTTS(text=text, lang=lang_code)
|
152 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file:
|
153 |
+
tts.save(tmp_file.name)
|
154 |
+
return tmp_file.name
|
155 |
+
|
156 |
+
def audio_player(audio_file_path):
|
157 |
+
with open(audio_file_path, "rb") as audio_file:
|
158 |
+
audio_bytes = audio_file.read()
|
159 |
+
b64_audio = base64.b64encode(audio_bytes).decode()
|
160 |
+
audio_html = f"""
|
161 |
+
<audio controls>
|
162 |
+
<source src="data:audio/mp3;base64,{b64_audio}" type="audio/mp3">
|
163 |
+
Your browser does not support the audio element.
|
164 |
+
</audio>
|
165 |
+
"""
|
166 |
+
st.markdown(audio_html, unsafe_allow_html=True)
|
167 |
+
|
168 |
+
def main():
|
169 |
+
st.title("AI-driven Medical Report Analyzer with Multilingual Audio Feedback")
|
170 |
+
st.write("Upload a medical report (image or PDF) for analysis")
|
171 |
+
|
172 |
+
# Language selection
|
173 |
+
language = st.selectbox("Select language for analysis and audio feedback:", list(LANGUAGES.keys()))
|
174 |
+
lang_code = LANGUAGES[language]
|
175 |
+
|
176 |
+
file_type = st.radio("Select file type:", ("Image", "PDF"))
|
177 |
+
|
178 |
+
if file_type == "Image":
|
179 |
+
uploaded_file = st.file_uploader("Choose a medical report image", type=["jpg", "jpeg", "png"])
|
180 |
+
if uploaded_file is not None:
|
181 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp_file:
|
182 |
+
tmp_file.write(uploaded_file.getvalue())
|
183 |
+
tmp_file_path = tmp_file.name
|
184 |
+
|
185 |
+
image = Image.open(tmp_file_path)
|
186 |
+
st.image(image, caption="Uploaded Medical Report", use_column_width=True)
|
187 |
+
|
188 |
+
if st.button("Analyze Image Report"):
|
189 |
+
with st.spinner("Analyzing the medical report image..."):
|
190 |
+
analysis = analyze_image_report(tmp_file_path, lang_code)
|
191 |
+
st.subheader("Analysis Results:")
|
192 |
+
st.write(analysis)
|
193 |
+
|
194 |
+
# Generate and play audio for analysis
|
195 |
+
audio_path = generate_tts_audio(analysis, lang_code)
|
196 |
+
st.write("Listen to the analysis:")
|
197 |
+
audio_player(audio_path)
|
198 |
+
|
199 |
+
os.unlink(tmp_file_path)
|
200 |
+
|
201 |
+
else: # PDF
|
202 |
+
uploaded_file = st.file_uploader("Choose a medical report PDF", type=["pdf"])
|
203 |
+
if uploaded_file is not None:
|
204 |
+
st.write("PDF uploaded successfully")
|
205 |
+
|
206 |
+
if st.button("Analyze PDF Report"):
|
207 |
+
with st.spinner("Analyzing the medical report PDF..."):
|
208 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
|
209 |
+
tmp_file.write(uploaded_file.getvalue())
|
210 |
+
tmp_file_path = tmp_file.name
|
211 |
+
|
212 |
+
with open(tmp_file_path, 'rb') as pdf_file:
|
213 |
+
pdf_text = extract_text_from_pdf(pdf_file)
|
214 |
+
|
215 |
+
analysis = analyze_text_report(pdf_text, lang_code)
|
216 |
+
st.subheader("Analysis Results:")
|
217 |
+
st.write(analysis)
|
218 |
+
|
219 |
+
# Generate and play audio for analysis
|
220 |
+
audio_path = generate_tts_audio(analysis, lang_code)
|
221 |
+
st.write("Listen to the analysis:")
|
222 |
+
audio_player(audio_path)
|
223 |
+
|
224 |
+
os.unlink(tmp_file_path)
|
225 |
+
|
226 |
+
if __name__ == "__main__":
|
227 |
+
main()
|