tddf commited on
Commit
6e8c2a0
·
verified ·
1 Parent(s): 7de5a1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -67
app.py CHANGED
@@ -1,81 +1,167 @@
1
- import io
2
- import os
3
- import streamlit as st
4
- import torch
5
- from PIL import Image
6
- from transformers import AutoProcessor, AutoModelForSeq2SeqLM
7
 
8
- import os
9
- import tempfile
 
 
 
 
 
10
 
11
- # Определяем папку для кэша: /data если есть (persistent), иначе /tmp
12
- if os.path.exists("/data") and os.access("/data", os.W_OK):
13
- CACHE_DIR = "/data/.huggingface"
14
- else:
15
- CACHE_DIR = os.path.join(tempfile.gettempdir(), ".huggingface")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- os.makedirs(CACHE_DIR, exist_ok=True)
18
- os.environ["HF_HOME"] = CACHE_DIR
19
- os.environ["HF_HUB_CACHE"] = os.path.join(CACHE_DIR, "hub")
20
- os.environ["TRANSFORMERS_CACHE"] = os.path.join(CACHE_DIR, "transformers")
21
-
22
- # --- Функция загрузки модели с кэшированием через Streamlit ---
23
- @st.cache_resource # Эта декорация сохраняет модель в памяти между запусками
24
- def load_model():
25
- model_name = "lightonai/LightOnOCR-1B-1025"
 
 
 
 
 
 
26
 
27
- # Определяем, есть ли GPU (CUDA)
28
- device = "cuda" if torch.cuda.is_available() else "cpu"
29
- st.write(f"Using device: {device}") # Для отладки в логах
 
 
30
 
31
- # Загружаем процессор (преобразует изображение в тензоры) и модель
32
- processor = AutoProcessor.from_pretrained(model_name, cache_dir=CACHE_DIR)
33
- model = AutoModelForSeq2SeqLM.from_pretrained(model_name, cache_dir=CACHE_DIR)
34
- model = model.to(device) # Перемещаем модель на GPU/CPU
35
 
36
- return processor, model, device
37
 
38
- # --- Интерфейс загрузки изображения ---
39
- def load_image():
40
- uploaded_file = st.file_uploader(
41
- 'Выберите изображение с английским текстом',
42
- type=['png', 'jpg', 'jpeg']
43
  )
44
- if uploaded_file is not None:
45
- # Читаем байты и показываем картинку
46
- image_data = uploaded_file.getvalue()
47
- st.image(image_data, use_column_width=True)
48
- # Конвертируем в RGB (на всякий случай)
49
- return Image.open(io.BytesIO(image_data)).convert('RGB')
50
- return None
 
 
 
 
51
 
52
- # --- Заголовок приложения ---
53
- st.title('🇬🇧 Распознавание английского текста (LightOnOCR)')
54
 
55
- # --- Загружаем модель (один раз) ---
56
- with st.spinner('Загрузка модели... Это может занять 1-2 минуты при первом запуске'):
57
- processor, model, device = load_model()
 
 
 
 
58
 
59
- # --- Загружаем изображение ---
60
- img = load_image()
61
 
62
- # --- Кнопка распознавания ---
63
- if st.button('Распознать текст') and img is not None:
64
- with st.spinner(аспознавание...'):
65
- # Преобразуем изображение в формат, понятный модели
66
- inputs = processor(images=img, return_tensors="pt").to(device)
67
-
68
- # Генерируем текст (без вычисления градиентов, чтобы экономить память)
69
- with torch.no_grad():
70
- generated_ids = model.generate(
 
 
 
 
 
 
 
 
 
 
71
  **inputs,
72
- max_new_tokens=512, # Максимум символов на выходе
73
- do_sample=False, # Детерминированный режим (лучше для OCR)
74
- num_beams=1
75
  )
76
- # Декодируем ID токенов обратно в строку
77
- generated_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
78
-
79
- st.success('✅ Распознано!')
80
- st.markdown('**📝 Текст на изображении:**')
81
- st.markdown(f'`{generated_text}`')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Import io
2
+ Import streamlit as st
3
+ Import torch
4
+ From transformers import LightOnOcrForConditionalGeneration, LightOnOcrProcessor
5
+ From PIL import Image
 
6
 
7
+ # Стильное оформление приложения
8
+ St.set_page_config(
9
+ Page_title=”LightOnOCR • Распознай текст”,
10
+ Page_icon=””,
11
+ Layout=”centered”,
12
+ Initial_sidebar_state=”expanded”
13
+ )
14
 
15
+ # Кастомный CSS для современного и стильного вида
16
+ St.markdown(“””
17
+ <style>
18
+ .main {
19
+ Background: linear-gradient(180deg, #f8f9fa, #e9f0f7);
20
+ }
21
+ .stApp {
22
+ Max-width: 1200px;
23
+ Margin: 0 auto;
24
+ }
25
+ H1 {
26
+ Font-family: ‘Segoe UI’, sans-serif;
27
+ Color: #1e3a8a;
28
+ Text-align: center;
29
+ Margin-bottom: 0.2rem;
30
+ }
31
+ .stButton > button {
32
+ Background: linear-gradient(90deg, #3b82f6, #1e40af);
33
+ Color: white;
34
+ Border-radius: 12px;
35
+ Padding: 12px 32px;
36
+ Font-weight: 600;
37
+ Border: none;
38
+ Box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
39
+ Transition: all 0.3s ease;
40
+ }
41
+ .stButton > button:hover {
42
+ Transform: translateY(-2px);
43
+ Box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
44
+ }
45
+ .result-box {
46
+ Background: #ffffff;
47
+ Border-radius: 16px;
48
+ Padding: 24px;
49
+ Box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
50
+ Border: 1px solid #e5e7eb;
51
+ }
52
+ .header-emoji {
53
+ Font-size: 3rem;
54
+ Display: block;
55
+ Text-align: center;
56
+ Margin-bottom: 10px;
57
+ }
58
+ </style>
59
+ “””, unsafe_allow_html=True)
60
 
61
+ @st.cache_resource(show_spinner=False)
62
+ Def load_model():
63
+ “””Загрузка модели LightOnOCR-1B-1025 (один раз)”””
64
+ Model_name = “lightonai/LightOnOCR-1B-1025”
65
+
66
+ # Автоопределение устройства и типа данных
67
+ If torch.backends.mps.is_available():
68
+ Device = “mps”
69
+ Dtype = torch.float32
70
+ Elif torch.cuda.is_available():
71
+ Device = “cuda”
72
+ Dtype = torch.bfloat16
73
+ Else:
74
+ Device = “cpu”
75
+ Dtype = torch.float32
76
 
77
+ Model = LightOnOcrForConditionalGeneration.from_pretrained(
78
+ Model_name,
79
+ Torch_dtype=dtype,
80
+ Trust_remote_code=True
81
+ ).to(device)
82
 
83
+ Processor = LightOnOcrProcessor.from_pretrained(model_name)
 
 
 
84
 
85
+ Return processor, model, device, dtype
86
 
87
+ Def load_image():
88
+ “””Загрузка изображения”””
89
+ Uploaded_file = st.file_uploader(
90
+ “ Загрузите изображение (фото, скан, документ)”,
91
+ Type=[png, jpg, jpeg’, ‘webp’]
92
  )
93
+ If uploaded_file is not None:
94
+ Image_data = uploaded_file.getvalue()
95
+ St.image(image_data, use_container_width=True, caption=”Загруженное изображение”)
96
+ Return Image.open(io.BytesIO(image_data)).convert(‘RGB’)
97
+ Return None
98
+
99
+ # Заголовок и описание (стильное)
100
+ St.markdown(‘<div class=”header-emoji”>✨</div>’, unsafe_allow_html=True)
101
+ St.title(“LightOnOCR”)
102
+ St.markdown(“**Мгновенное распознавание текста на английском и других языках**”)
103
+ St.caption(“Современная end-to-end нейросеть LightOnOCR-1B-1025 • Поддерживает документы, чеки, фото, таблицы и сложную вёрстку”)
104
 
105
+ # Загрузка модели
106
+ Processor, model, device, dtype = load_model()
107
 
108
+ # Инфо в сайдбаре
109
+ With st.sidebar:
110
+ St.markdown(“### О модели”)
111
+ St.info(“LightOnOCR-1B-1025 — компактная, но очень точная модель для OCR. Отлично работает с английским, латиницей, документами и сложными макетами.”)
112
+ St.markdown(“**Поддержка:** Английский + 8 других языков (латиница)”)
113
+ St.markdown(“**Скорость:** до 5+ страниц/сек на GPU”)
114
+ St.caption(f”Устройство: **{device.upper()}** • dtype: **{dtype}**”)
115
 
116
+ # Основной интерфейс
117
+ Img = load_image()
118
 
119
+ If st.button(“ Распознать текст”, use_container_width=True, type=”primary”):
120
+ If img is None:
121
+ St.error(“Пожалуйста, загрузите изображение)
122
+ Else:
123
+ With st.spinner(“Распознавание текста… Это может занять несколько секунд (особенно на CPU)):
124
+ # Подготовка промпта
125
+ Prompt = “Extract all the text from this image accurately. Preserve original formatting, layout, tables and line breaks as much as possible.”
126
+
127
+ # Подготовка входных данных
128
+ Inputs = processor(images=img, text=prompt, return_tensors=”pt”)
129
+
130
+ # Перенос на устройство
131
+ Inputs = {
132
+ K: v.to(device=device, dtype=dtype) if v.is_floating_point() else v.to(device)
133
+ For k, v in inputs.items()
134
+ }
135
+
136
+ # Генерация
137
+ Output_ids = model.generate(
138
  **inputs,
139
+ Max_new_tokens=2048,
140
+ Do_sample=False,
141
+ Temperature=0.0
142
  )
143
+
144
+ # Берём только сгенерированные токены (убираем промпт)
145
+ Input_len = inputs[“input_ids”].shape[1]
146
+ Generated_ids = output_ids[0, input_len:]
147
+
148
+ # Декодирование
149
+ Generated_text = processor.decode(generated_ids, skip_special_tokens=True)
150
+
151
+ # Результат
152
+ St.success(“✅ Распознавание завершено!”)
153
+ St.markdown(‘<div class=”result-box”>’, unsafe_allow_html=True)
154
+ St.subheader(“ Распознанный текст”)
155
+ St.markdown(f”```\n{generated_text}\n```”)
156
+ St.markdown(‘</div>’, unsafe_allow_html=True)
157
+
158
+ # Кнопка копирования
159
+ St.download_button(
160
+ Label=” Скачать текст как .txt”,
161
+ Data=generated_text,
162
+ File_name=”recognized_text.txt”,
163
+ Mime=”text/plain”
164
+ )
165
+
166
+ St.markdown(“---“)
167
+ St.markdown(“**Сделано с ️ на базе LightOnOCR-1B-1025** • [Модель на Hugging Face](https://huggingface.co/lightonai/LightOnOCR-1B-1025)”)