"""
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("""
""", 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 ====================