Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| from PIL import Image | |
| import os | |
| import base64 | |
| import io | |
| from dotenv import load_dotenv | |
| from groq import Groq | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer | |
| from reportlab.lib.styles import getSampleStyleSheet | |
| # ====================== | |
| # CONFIGURATION SETTINGS | |
| # ====================== | |
| PAGE_CONFIG = { | |
| "page_title": "Radiology Analyzer", | |
| "page_icon": "π©Ί", | |
| "layout": "wide", | |
| "initial_sidebar_state": "expanded" | |
| } | |
| ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg'] | |
| CSS_STYLES = """ | |
| <style> | |
| .main { background-color: #f4f9f9; color: #000000; } | |
| .sidebar .sidebar-content { background-color: #d1e7dd; } | |
| .stTextInput textarea { color: #000000 !important; } | |
| .stSelectbox div[data-baseweb="select"], | |
| .stSelectbox option, | |
| .stSelectbox div[role="listbox"] div { | |
| color: black !important; | |
| background-color: #d1e7dd !important; | |
| } | |
| .stSelectbox svg { fill: black !important; } | |
| .main-title { | |
| font-size: 88px; | |
| font-weight: bold; | |
| color: rgb(33, 238, 238); | |
| } | |
| .sub-title { | |
| font-size: 100px; | |
| color: #6B6B6B; | |
| margin-top: -1px; | |
| } | |
| .stButton>button { | |
| background-color: rgb(33, 225, 250); | |
| color: white; | |
| font-size: 69px; | |
| } | |
| .stImage img { | |
| border-radius: 10px; | |
| box-shadow: 2px 2px 10px rgba(0,0,0,0.1); | |
| } | |
| .logo { | |
| text-align: center; | |
| margin-bottom: 20px; | |
| } | |
| .report-container { | |
| background-color: #ffffff; | |
| border-radius: 15px; | |
| padding: 25px; | |
| margin-top: 20px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| border-left: 5px solid #21eeef; | |
| } | |
| .report-text { | |
| font-family: 'Courier New', monospace; | |
| font-size: 16px; | |
| line-height: 1.6; | |
| color: #2c3e50; | |
| } | |
| .download-btn { | |
| background-color: #21eeef !important; | |
| color: white !important; | |
| border: none !important; | |
| border-radius: 8px !important; | |
| padding: 12px 24px !important; | |
| } | |
| </style> | |
| """ | |
| # ====================== | |
| # CORE FUNCTIONS | |
| # ====================== | |
| def configure_application(): | |
| """Initialize application settings and styling""" | |
| st.set_page_config(**PAGE_CONFIG) | |
| st.markdown(CSS_STYLES, unsafe_allow_html=True) | |
| def initialize_api_client(): | |
| """Create and validate Groq API client""" | |
| load_dotenv() | |
| api_key = os.getenv("GROQ_API_KEY") | |
| if not api_key: | |
| st.error("API key not found. Please verify .env configuration.") | |
| st.stop() | |
| return Groq(api_key=api_key) | |
| def encode_logo(image_path): | |
| """Encode logo image to base64""" | |
| try: | |
| with open(image_path, "rb") as img_file: | |
| return base64.b64encode(img_file.read()).decode("utf-8") | |
| except FileNotFoundError: | |
| st.error("Logo image not found! Using placeholder.") | |
| return "" | |
| def process_image_data(uploaded_file): | |
| """Convert image to base64 encoded string""" | |
| try: | |
| image = Image.open(uploaded_file) | |
| buffer = io.BytesIO() | |
| image.save(buffer, format=image.format) | |
| return base64.b64encode(buffer.getvalue()).decode('utf-8'), image.format | |
| except Exception as e: | |
| st.error(f"Image processing error: {str(e)}") | |
| return None, None | |
| def generate_pdf_report(report_text): | |
| """Generate PDF document from report text""" | |
| buffer = io.BytesIO() | |
| doc = SimpleDocTemplate(buffer, pagesize=letter) | |
| styles = getSampleStyleSheet() | |
| story = [] | |
| # Add title | |
| title = Paragraph("<b>Radiology Report</b>", styles['Title']) | |
| story.append(title) | |
| story.append(Spacer(1, 12)) | |
| # Add report content | |
| content = Paragraph(report_text.replace('\n', '<br/>'), styles['BodyText']) | |
| story.append(content) | |
| doc.build(story) | |
| buffer.seek(0) | |
| return buffer | |
| def generate_radiology_report(uploaded_file, client): | |
| """Generate AI-powered radiology analysis""" | |
| base64_image, img_format = process_image_data(uploaded_file) | |
| if not base64_image: | |
| return None | |
| image_url = f"data:image/{img_format.lower()};base64,{base64_image}" | |
| try: | |
| response = client.chat.completions.create( | |
| model="llama-3.2-90b-vision-preview", | |
| messages=[{ | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": ( | |
| "As an AI radiologist, provide a detailed structured report including: " | |
| "1. Imaging modality identification\n2. Anatomical structures visualized\n" | |
| "3. Abnormal findings description\n4. Differential diagnoses\n" | |
| "5. Clinical correlation recommendations" | |
| )}, | |
| {"type": "image_url", "image_url": {"url": image_url}}, | |
| ] | |
| }], | |
| temperature=0.2, | |
| max_tokens=1000, | |
| top_p=0.5 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| st.error(f"API communication error: {str(e)}") | |
| return None | |
| # ====================== | |
| # UI COMPONENTS | |
| # ====================== | |
| def display_main_interface(): | |
| """Render primary application interface""" | |
| # Encode logo image | |
| logo_b64 = encode_logo("src/radiology.png") | |
| # Center the logo and title using HTML and CSS | |
| st.markdown( | |
| f""" | |
| <div style="text-align: center;"> | |
| <div class="logo"> | |
| <img src="data:image/png;base64,{logo_b64}" width="100"> | |
| </div> | |
| <p class="main-title"> Radiology Analyzer</p> | |
| <p class="sub-title">Advanced Medical Imaging Analysis</p> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| st.markdown("---") | |
| # Action buttons | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| if st.session_state.get('analysis_result'): | |
| pdf_report = generate_pdf_report(st.session_state.analysis_result) | |
| st.download_button( | |
| label="π Download PDF Report", | |
| data=pdf_report, | |
| file_name="radiology_report.pdf", | |
| mime="application/pdf", | |
| use_container_width=True, | |
| help="Download formal PDF version of the report", | |
| key="download_pdf" | |
| ) | |
| with col2: | |
| if st.button("Clear Analysis ποΈ", use_container_width=True, help="Remove current results"): | |
| st.session_state.pop('analysis_result') | |
| st.rerun() | |
| # Display analysis results | |
| if st.session_state.get('analysis_result'): | |
| st.markdown("### π― Radiological Findings Report") | |
| st.markdown( | |
| f'<div class="report-container"><div class="report-text">{st.session_state.analysis_result}</div></div>', | |
| unsafe_allow_html=True | |
| ) | |
| def render_sidebar(client): | |
| """Create sidebar interface elements""" | |
| with st.sidebar: | |
| st.divider() | |
| st.markdown("### Diagnostic Capabilities") | |
| st.markdown(""" | |
| - **Multi-Modality Analysis**: X-ray, MRI, CT, Ultrasound | |
| - **Pathology Detection**: Fractures, tumors, infections | |
| - **Comparative Analysis**: Track disease progression | |
| - **Structured Reporting**: Standardized output format | |
| - **Clinical Correlation**: Suggested next steps | |
| - **Disclaimer: This service does not provide medical advice, consult a doctor for analysis and treatment | |
| """) | |
| st.divider() | |
| st.subheader("Image Upload Section") | |
| uploaded_file = st.file_uploader( | |
| "Select Medical Image", | |
| type=ALLOWED_FILE_TYPES, | |
| help="Supported formats: PNG, JPG, JPEG" | |
| ) | |
| if uploaded_file: | |
| st.image(Image.open(uploaded_file), | |
| caption="Uploaded Medical Image", | |
| use_container_width=True) | |
| if st.button("Initiate Analysis π", use_container_width=True): | |
| with st.spinner("Analyzing image. This may take 20-30 seconds..."): | |
| report = generate_radiology_report(uploaded_file, client) | |
| st.session_state.analysis_result = report | |
| st.rerun() | |
| # ====================== | |
| # APPLICATION ENTRYPOINT | |
| # ====================== | |
| def main(): | |
| """Primary application controller""" | |
| configure_application() | |
| groq_client = initialize_api_client() | |
| display_main_interface() | |
| render_sidebar(groq_client) | |
| if __name__ == "__main__": | |
| main() |