File size: 3,069 Bytes
99cb69f
 
 
 
 
 
 
 
 
 
cd0d4f4
d5ef073
 
 
cd0d4f4
d5ef073
cd0d4f4
d5ef073
 
 
99cb69f
 
 
 
 
 
 
 
cd0d4f4
d5ef073
 
 
99cb69f
 
 
 
 
 
 
cd0d4f4
d5ef073
 
 
 
 
 
cd0d4f4
d5ef073
99cb69f
d5ef073
 
 
99cb69f
 
 
 
d5ef073
 
99cb69f
 
 
 
 
d5ef073
 
 
 
 
 
99cb69f
 
d5ef073
99cb69f
d5ef073
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99cb69f
 
 
 
d5ef073
 
 
99cb69f
d5ef073
 
 
 
99cb69f
 
 
d5ef073
 
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
# ----------------------------
# FIX FOR HUGGING FACE TIMEOUT
# ----------------------------
import os
os.environ["STREAMLIT_BROWSER_GATHER_USAGE_STATS"] = "false"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"

# ----------------------------
# IMPORTS
# ----------------------------
import streamlit as st
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
from PIL import Image

# ----------------------------
# PAGE CONFIG
# ----------------------------
st.set_page_config(
    page_title="Captcha OCR",
    page_icon="๐Ÿ”",
    layout="centered"
)

st.title("๐Ÿ” Captcha OCR")
st.markdown("CRNN + CTC Model Deployment")

# ----------------------------
# LOAD CHARACTERS
# ----------------------------
@st.cache_resource
def load_characters():
    with open("characters.txt", "r") as f:
        characters = list(f.read().strip())
    return characters

characters = load_characters()

charToNum = layers.StringLookup(vocabulary=characters, mask_token=None)
numToChar = layers.StringLookup(
    vocabulary=charToNum.get_vocabulary(),
    mask_token=None,
    invert=True
)

# ----------------------------
# LOAD MODEL (LAZY + SAFE)
# ----------------------------
@st.cache_resource
def load_model():
    model = keras.models.load_model(
        "ocr_model.keras",
        compile=False  # IMPORTANT for memory reduction
    )
    return model

# Lazy loading (prevents reload crash)
if "model" not in st.session_state:
    st.session_state.model = load_model()

model = st.session_state.model

# ----------------------------
# PREPROCESS FUNCTION
# ----------------------------
def preprocess_image(image):
    image = image.convert("L")  # grayscale
    image = image.resize((200, 50))  # same as training
    image = np.array(image).astype("float32") / 255.0
    image = np.expand_dims(image, axis=-1)
    image = np.transpose(image, (1, 0, 2))  # IMPORTANT (match training)
    image = np.expand_dims(image, axis=0)
    return image

# ----------------------------
# DECODE FUNCTION
# ----------------------------
def decode_prediction(pred):
    input_len = np.ones(pred.shape[0]) * pred.shape[1]

    results = keras.backend.ctc_decode(
        pred,
        input_length=input_len,
        greedy=True
    )[0][0]

    output_text = []
    for res in results:
        res = tf.gather(res, tf.where(res != -1))
        res = tf.squeeze(res)
        text = tf.strings.reduce_join(numToChar(res)).numpy().decode("utf-8")
        output_text.append(text)

    return output_text[0]

# ----------------------------
# FILE UPLOADER
# ----------------------------
uploaded_file = st.file_uploader(
    "Upload Captcha Image",
    type=["png", "jpg", "jpeg"]
)

if uploaded_file is not None:
    image = Image.open(uploaded_file)

    st.image(image, caption="Uploaded Image", use_column_width=True)

    processed = preprocess_image(image)

    with st.spinner("Predicting..."):
        prediction = model.predict(processed)
        text = decode_prediction(prediction)

    st.success(f"๐ŸŽฏ Prediction: {text}")