""" Sinhala Handwritten OCR - Intelligent Text Recognition System Professional OCR Application for Sinhala Handwritten Text """ # ==================== PAGE CONFIGURATION ==================== import streamlit as st st.set_page_config( page_title="Sinhala OCR | Intelligent Text Recognition", page_icon="✍️", layout="wide", initial_sidebar_state="expanded" ) # ==================== MODEL INFRASTRUCTURE ==================== from model_pipeline import load_sinhala_ocr_model, extract_text_from_handwriting, get_model_details @st.cache_resource def initialize_pipeline(): """Load and cache the OCR model""" try: return load_sinhala_ocr_model() except Exception as e: st.error(f"⚠️ Model loading error: {str(e)}") return None, None, None, None # Initialize pipeline pipeline_assets = initialize_pipeline() if pipeline_assets and all(pipeline_assets): model, processor, device, config = pipeline_assets ocr_ready = True else: model, processor, device, config = None, None, None, None ocr_ready = False # ==================== IMPORTS ==================== import time import re from datetime import datetime from PIL import Image import hashlib # ==================== SESSION STATE ==================== if 'authenticated' not in st.session_state: st.session_state.authenticated = False if 'current_page' not in st.session_state: st.session_state.current_page = 'login' if 'user_email' not in st.session_state: st.session_state.user_email = None if 'user_name' not in st.session_state: st.session_state.user_name = None if 'users_db' not in st.session_state: # Demo account for testing st.session_state.users_db = { "demo@example.com": { "name": "Demo User", "password": hashlib.sha256("demo123".encode()).hexdigest(), "registered_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } } if 'ocr_processed' not in st.session_state: st.session_state.ocr_processed = False if 'ocr_result' not in st.session_state: st.session_state.ocr_result = "" # ==================== NOTE: get_model_details is now imported from model_pipeline ==================== # No need to define it here anymore # ==================== AUTHENTICATION FUNCTIONS ==================== def hash_password(password): return hashlib.sha256(password.encode()).hexdigest() def authenticate_user(email, password): hashed = hash_password(password) if email in st.session_state.users_db: if st.session_state.users_db[email]['password'] == hashed: st.session_state.authenticated = True st.session_state.user_email = email st.session_state.user_name = st.session_state.users_db[email]['name'] st.session_state.current_page = 'dashboard' return True return False def register_user(name, email, password, confirm_password): if not all([name, email, password, confirm_password]): return False, "Please fill in all fields" if password != confirm_password: return False, "Passwords do not match" if email in st.session_state.users_db: return False, "Email already registered" if len(password) < 6: return False, "Password must be at least 6 characters" if not re.match(r"[^@]+@[^@]+\.[^@]+", email): return False, "Invalid email format" st.session_state.users_db[email] = { 'name': name, 'password': hash_password(password), 'registered_date': datetime.now().strftime("%Y-%m-%d %H:%M:%S") } return True, "Registration successful" # ==================== CSS - CLEAN & MODERN ==================== st.markdown(""" """, unsafe_allow_html=True) # ==================== LOGIN PAGE ==================== if not st.session_state.authenticated: st.markdown("""

✍️ Sinhala Handwritten OCR

Intelligent Text Recognition for Sinhala Script

""", unsafe_allow_html=True) col1, col2, col3 = st.columns([1, 1.2, 1]) with col2: st.markdown('
', unsafe_allow_html=True) if st.session_state.current_page == 'login': st.markdown("

👋 Welcome Back

", unsafe_allow_html=True) st.markdown("

Sign in to access your OCR workspace

", unsafe_allow_html=True) login_email = st.text_input("Email Address", placeholder="demo@example.com", key="login_email") login_pass = st.text_input("Password", type="password", placeholder="••••••••", key="login_pass") if st.button("Log In →", key="login_btn"): if login_email and login_pass: if authenticate_user(login_email, login_pass): st.success("✅ Login successful! Redirecting...") time.sleep(0.5) st.rerun() else: st.error("❌ Invalid credentials. Use demo@example.com / demo123") else: st.warning("⚠️ Please fill in all fields") st.markdown("
New to the platform?
", unsafe_allow_html=True) if st.button("Create New Account", key="goto_signup", use_container_width=True): st.session_state.current_page = 'signup' st.rerun() elif st.session_state.current_page == 'signup': st.markdown("

📝 Create Account

", unsafe_allow_html=True) st.markdown("

Join us for advanced Sinhala OCR capabilities

", unsafe_allow_html=True) reg_name = st.text_input("Full Name", placeholder="e.g., John Silva", key="reg_name") reg_email = st.text_input("Email Address", placeholder="you@example.com", key="reg_email") reg_pass = st.text_input("Password", type="password", placeholder="Minimum 6 characters", key="reg_pass") reg_conf = st.text_input("Confirm Password", type="password", placeholder="Re-enter password", key="reg_conf") if st.button("Register & Continue →", key="signup_btn"): success, msg = register_user(reg_name, reg_email, reg_pass, reg_conf) if success: st.success("✅ Registration successful! Redirecting to dashboard...") # Auto-login after registration st.session_state.authenticated = True st.session_state.user_email = reg_email st.session_state.user_name = reg_name st.session_state.current_page = 'dashboard' time.sleep(1) st.rerun() else: st.error(f"❌ {msg}") st.markdown("
Already have an account?
", unsafe_allow_html=True) if st.button("Back to Login", key="back_to_login", use_container_width=True): st.session_state.current_page = 'login' st.rerun() st.markdown('
', unsafe_allow_html=True) # ==================== MAIN DASHBOARD ==================== else: # SIDEBAR with st.sidebar: st.markdown(f"""

👋 Hello, {st.session_state.user_name.split()[0]}

{st.session_state.user_email}

""", unsafe_allow_html=True) st.markdown(f"""
🖥️ OCR Engine: {'ACTIVE' if ocr_ready else 'OFFLINE'}
""", unsafe_allow_html=True) if st.button("🚪 Sign Out", key="logout_btn", use_container_width=True): st.session_state.authenticated = False st.session_state.user_email = None st.session_state.user_name = None st.session_state.current_page = 'login' st.rerun() st.markdown("""

💡 Best Practices

✓ Use clear, isolated handwritten text
✓ Ensure adequate lighting
✓ Crop tightly to text region
✓ High contrast images preferred

""", unsafe_allow_html=True) # MAIN DASHBOARD CONTENT st.markdown("""

✍️ Sinhala Handwritten OCR Hub

Upload handwritten Sinhala text for instant digital conversion

""", unsafe_allow_html=True) tab_workspace, tab_specs = st.tabs(["🔍 Recognition Workspace", "📊 Technical Architecture & AI Model Specs"]) # ==================== TAB 1: RECOGNITION WORKSPACE ==================== with tab_workspace: col_left, col_right = st.columns(2, gap="large") with col_left: st.markdown('
', unsafe_allow_html=True) st.markdown("

📤 Upload Handwritten Image

", unsafe_allow_html=True) uploaded_image = st.file_uploader( "Select a Sinhala handwritten image", type=['png', 'jpg', 'jpeg'], key="image_uploader", help="Supports PNG, JPG, JPEG formats" ) if uploaded_image: preview_img = Image.open(uploaded_image) st.image(preview_img, caption="✍️ Handwritten Preview", use_column_width=True) st.markdown("
", unsafe_allow_html=True) # EXTRACT TEXT BUTTON - WORKING if st.button("✨ Extract Text Now", key="extract_btn", use_container_width=True): if ocr_ready and model: with st.spinner("🔍 Analyzing handwritten patterns and extracting text..."): try: result_text = extract_text_from_handwriting( uploaded_image, model, processor, device, config ) if result_text and not result_text.startswith("Recognition Error"): st.session_state.ocr_result = result_text st.session_state.ocr_processed = True st.success("✅ Text extracted successfully!") st.balloons() else: st.error(f"❌ {result_text}") except Exception as e: st.error(f"❌ Recognition error: {str(e)}") else: st.error("❌ OCR Engine not initialized. Please refresh or check model files.") else: st.info("📸 No image selected. Upload a handwritten Sinhala image to begin.") st.markdown('
', unsafe_allow_html=True) with col_right: st.markdown('
', unsafe_allow_html=True) st.markdown("

📝 Recognition Result

", unsafe_allow_html=True) if st.session_state.ocr_processed and st.session_state.ocr_result: st.text_area( "", value=st.session_state.ocr_result, height=300, key="result_area", label_visibility="collapsed", help="Extracted Sinhala text from your handwritten image" ) col_copy, col_download = st.columns(2) with col_copy: if st.button("📋 Copy to Clipboard", key="copy_btn", use_container_width=True): st.success("✅ Copied to clipboard!") with col_download: st.download_button( label="💾 Download as Text", data=st.session_state.ocr_result.encode('utf-8'), file_name=f"sinhala_ocr_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt", mime="text/plain", use_container_width=True, key="download_btn" ) else: st.markdown("""

✨ No result yet

Upload an image and click "Extract Text Now"

""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # ==================== TAB 2: TECHNICAL ARCHITECTURE (USER-FRIENDLY) ==================== with tab_specs: specs = get_model_details() st.markdown('
', unsafe_allow_html=True) st.markdown("

📊 Technical Architecture & AI Model Specifications

", unsafe_allow_html=True) # Section 1: Architecture Overview - Simple and Clean st.markdown("#### 🧠 Core Architecture") col1, col2 = st.columns(2) with col1: st.markdown(f"""
{specs['architecture']}
Model Architecture
""", unsafe_allow_html=True) with col2: st.markdown(f"""
{specs['backbone']}
Backbone Model
""", unsafe_allow_html=True) st.markdown(f""" - **Vision Encoder:** {specs['encoder_type']} - Extracts visual features from handwritten images - **Text Decoder:** {specs['decoder_type']} - Converts visual features to Sinhala text - **Training Dataset:** {specs['dataset']} """) st.markdown("---") # Section 2: Preprocessing Pipeline - Simple Explanation st.markdown("#### 📐 Image Preprocessing (Letterboxing)") st.markdown(""" To preserve Sinhala character shapes, each image goes through: 1. **Aspect Ratio Preservation** - Maintains original proportions 2. **Resize to 384×384** - Standardized input size 3. **White Padding** - Adds margins to prevent distortion 4. **Normalization** - Prepares pixels for the model > **Why this matters:** Sinhala characters contain critical diacritics (පිලි) that get distorted with standard resizing. """) st.markdown("---") # Section 3: Core Model Configuration - Clean Metrics st.markdown("#### 📊 Model Configuration") col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Training Epochs", specs["training_epochs"]) with col2: st.metric("Final Loss", f"{specs['training_loss']:.2f}") with col3: st.metric("Image Size", f"{specs['image_size']}×{specs['image_size']}px") with col4: st.metric("Max Length", f"{specs['max_length']} tokens") st.markdown("---") # Section 4: Generation Controls - Simple Cards st.markdown("#### 🔧 Generation Controls") control_col1, control_col2 = st.columns(2) with control_col1: st.markdown("""
🎯 Repetition Penalty (2.0)
Prevents character loops and repetitive patterns
""", unsafe_allow_html=True) st.markdown("""
🔍 Beam Search (4-Beam)
Explores multiple decoding paths for accuracy
""", unsafe_allow_html=True) with control_col2: st.markdown("""
📏 Length Penalty (0.6)
Discourages unnecessary extra characters
""", unsafe_allow_html=True) st.markdown("""
⏹️ Early Stopping
Stops generation when complete
""", unsafe_allow_html=True) st.markdown("""

💡 Additional Settings: `no_repeat_ngram_size=2` blocks duplicate patterns, ensuring clean, natural-looking Sinhala text output.

""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # FOOTER st.markdown("""

© 2026 Sinhala Handwritten OCR | Powered by Fine-Tuned TrOCR | Intelligent Text Recognition System

Designed for Sinhala Handwritten Text Recognition | Version 2.0

""", unsafe_allow_html=True) # ==================== END OF APP ====================