tddf commited on
Commit
fad1dff
·
verified ·
1 Parent(s): c5c521b

Update Main.py

Browse files
Files changed (1) hide show
  1. Main.py +143 -146
Main.py CHANGED
@@ -1,167 +1,164 @@
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))
 
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 { background: linear-gradient(180deg, #f8f9fa, #e9f0f7); }
19
+ h1 { color: #1e3a8a; text-align: center; margin-bottom: 0.2rem; }
 
 
 
 
 
 
 
 
 
 
 
20
  .stButton > button {
21
+ background: linear-gradient(90deg, #3b82f6, #1e40af);
22
+ color: white;
23
+ border-radius: 12px;
24
+ padding: 12px 32px;
25
+ font-weight: 600;
26
+ border: none;
27
+ box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
 
28
  }
29
  .stButton > button:hover {
30
+ transform: translateY(-2px);
31
+ box-shadow: 0 8px 20px rgba(59, 130, 246, 0.4);
32
  }
33
  .result-box {
34
+ background: #ffffff;
35
+ border-radius: 16px;
36
+ padding: 24px;
37
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
38
+ border: 1px solid #e5e7eb;
39
+ margin-top: 20px;
 
 
 
 
 
40
  }
41
+ .header-emoji { font-size: 3.5rem; text-align: center; margin: 10px 0; }
42
  </style>
43
+ """, unsafe_allow_html=True)
44
 
45
+ # ==================== Загрузка модели ====================
46
+ @st.cache_resource(show_spinner="Загрузка модели LightOnOCR-1B-1025...")
47
+ def load_model():
48
+ model_name = "lightonai/LightOnOCR-1B-1025"
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ if torch.backends.mps.is_available():
51
+ device = "mps"
52
+ dtype = torch.float32
53
+ elif torch.cuda.is_available():
54
+ device = "cuda"
55
+ dtype = torch.bfloat16
56
+ else:
57
+ device = "cpu"
58
+ dtype = torch.float32
59
+
60
+ model = LightOnOcrForConditionalGeneration.from_pretrained(
61
+ model_name,
62
+ torch_dtype=dtype,
63
+ trust_remote_code=True,
64
+ device_map=None # загружаем вручную
65
  ).to(device)
 
 
 
 
66
 
67
+ processor = LightOnOcrProcessor.from_pretrained(model_name)
68
+
69
+ return processor, model, device, dtype
70
+
71
+ # ==================== Загрузка изображения ====================
72
+ def load_image():
73
+ uploaded_file = st.file_uploader(
74
+ "📸 Загрузите изображение (фото, скан, документ)",
75
+ type=['png', 'jpg', 'jpeg', 'webp']
76
  )
77
+ if uploaded_file is not None:
78
+ image_data = uploaded_file.getvalue()
79
+ st.image(image_data, use_container_width=True, caption="Загруженное изображение")
80
+ return Image.open(io.BytesIO(image_data)).convert('RGB')
81
+ return None
82
+
83
+ # ==================== Основной интерфейс ====================
84
+ st.markdown('<div class="header-emoji">📄✨</div>', unsafe_allow_html=True)
85
+ st.title("LightOnOCR")
86
+ st.markdown("**Мгновенное распознавание текста на английском и других языках**")
87
+ st.caption(одель LightOnOCR-1B-1025 • Отлично работает с документами, чеками, таблицами и фото")
88
+
89
+ # Загружаем модель один раз
90
+ processor, model, device, dtype = load_model()
91
+
92
+ # Сайдбар
93
+ with st.sidebar:
94
+ st.markdown("### 🚀 О модели")
95
+ st.info("LightOnOCR-1B-1025 — компактная end-to-end модель для OCR и понимания документов.")
96
+ st.markdown("**Поддержка:** Английский + латиница, таблицы, сложная вёрстка")
97
+ st.caption(f"Устройство: **{device.upper()}** dtype: **{dtype}**")
98
+
99
+ # Загрузка изображения
100
+ img = load_image()
101
+
102
+ # Кнопка распознавания
103
+ if st.button("🔍 Распознать текст", use_container_width=True, type="primary"):
104
+ if img is None:
105
+ st.error("Пожалуйста, сначала загрузите изображение")
106
+ else:
107
+ with st.spinner("Распознавание текста… (на CPU может занять 10–30 секунд)"):
108
+ # Правильный способ работы с этой моделью (chat template)
109
+ conversation = [
110
+ {
111
+ "role": "user",
112
+ "content": [
113
+ {"type": "image"},
114
+ {"type": "te
115
+
116
+
117
+ xt", "text": "Extract all the text from this image accurately. Preserve formatting, tables, and line breaks as much as possible."}
118
+ ]
119
+ }
120
+ ]
121
+
122
+ inputs = processor.apply_chat_template(
123
+ conversation,
124
+ add_generation_prompt=True,
125
+ tokenize=True,
126
+ return_dict=True,
127
+ return_tensors="pt"
128
+ )
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
+ generated_ids = output_ids[0, inputs["input_ids"].shape[1]:]
146
+ generated_text = processor.decode(generated_ids, skip_special_tokens=True)
147
+
148
+ # Вывод результата
149
+ st.success("✅ Распознавание завершено!")
150
+ st.markdown('<div class="result-box">', unsafe_allow_html=True)
151
+ st.subheader("📝 Распознанный текст")
152
+ st.markdown(f"```\n{generated_text}\n```")
153
+ st.markdown('</div>', unsafe_allow_html=True)
154
+
155
+ # Кнопка скачивания
156
+ st.download_button(
157
+ label="💾 Скачать текст (.txt)",
158
+ data=generated_text,
159
+ file_name="recognized_text.txt",
160
+ mime="text/plain"
 
 
 
161
  )
162
 
163
+ st.markdown("---")
164
+ st.markdown("**Сделано на базе [lightonai/LightOnOCR-1B-1025](https://huggingface.co/lightonai/LightOnOCR-1B-1025)**")