import os import io import streamlit as st import torch from transformers import LightOnOcrForConditionalGeneration, LightOnOcrProcessor from PIL import Image # Ускоряем скачивание os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1" st.set_page_config( page_title="LightOnOCR • Распознай текст", page_icon="📄", layout="centered", initial_sidebar_state="expanded" ) st.markdown(""" """, unsafe_allow_html=True) @st.cache_resource(show_spinner="⏳ Загрузка модели LightOnOCR-1B-1025... (2–6 минут при первом запуске)") def load_model(): model_name = "lightonai/LightOnOCR-1B-1025" device = "cuda" if torch.cuda.is_available() else "cpu" dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32 model = LightOnOcrForConditionalGeneration.from_pretrained( model_name, torch_dtype=dtype, trust_remote_code=True, ).to(device) processor = LightOnOcrProcessor.from_pretrained(model_name) if processor.tokenizer.pad_token is None: processor.tokenizer.pad_token = processor.tokenizer.eos_token return processor, model, device, dtype def load_image(): uploaded_file = st.file_uploader( "📸 Загрузите изображение (png, jpg, jpeg, webp)", type=['png', 'jpg', 'jpeg', 'webp'] ) if uploaded_file is not None: image_data = uploaded_file.getvalue() st.image(image_data, use_container_width=True, caption="Загруженное изображение") return Image.open(io.BytesIO(image_data)).convert('RGB') return None # ==================== Интерфейс ==================== st.markdown('
📄✨
', unsafe_allow_html=True) st.title("LightOnOCR") st.markdown("**Распознавание текста с изображений**") st.caption("Модель: lightonai/LightOnOCR-1B-1025") processor, model, device, dtype = load_model() with st.sidebar: st.success(f"✅ Модель загружена на **{device.upper()}**") img = load_image() # ==================== Распознавание ==================== if st.button("🔍 Распознать текст", use_container_width=True, type="primary"): if img is None: st.error("Сначала загрузите изображение") else: with st.spinner("Распознавание текста... (5–30 сек на CPU)"): # Промпт для модели prompt = "Extract all the text from this image as accurately as possible. Preserve line breaks, formatting and tables." # 1. Получаем текстовый шаблон чата (без токенизации) conversation = [ { "role": "user", "content": [ {"type": "image"}, {"type": "text", "text": prompt} ] } ] text_prompt = processor.apply_chat_template( conversation, tokenize=False, add_generation_prompt=True ) # 2. Правильный вызов процессора (ключевой момент!) inputs = processor( text=[text_prompt], images=[[img]], # двойной список — обязательно! return_tensors="pt", padding=True, size={"longest_edge": 1540} # рекомендуемый размер модели ) # Переносим на устройство inputs = { k: (v.to(device=device, dtype=dtype) if v.is_floating_point() else v.to(device)) for k, v in inputs.items() } # Генерация output_ids = model.generate( **inputs, max_new_tokens=2048, do_sample=False, temperature=0.0, num_beams=1, pad_token_id=processor.tokenizer.pad_token_id, eos_token_id=processor.tokenizer.eos_token_id, ) # Убираем промпт prompt_length = inputs["input_ids"].shape[1] generated_ids = output_ids[0, prompt_length:] generated_text = processor.decode( generated_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True ).strip() # Результат st.success("✅ Распознавание завершено!") st.markdown('
', unsafe_allow_html=True) st.subheader("📝 Распознанный текст") st.code(generated_text, language=None) st.markdown('
', unsafe_allow_html=True) st.download_button( label="💾 Скачать как .txt", data=generated_text, file_name="recognized_text.txt", mime="text/plain" ) st.markdown("---") st.caption("Сделано на базе [lightonai/LightOnOCR-1B-1025](https://huggingface.co/lightonai/LightOnOCR-1B-1025)")