Spaces:
Sleeping
Sleeping
| import os | |
| import torch | |
| import gradio as gr | |
| from PIL import Image | |
| from peft import PeftModel | |
| from qwen_vl_utils import process_vision_info | |
| from transformers import Qwen2VLForConditionalGeneration, AutoProcessor | |
| # --- Professional "Cyber-Dark" CSS --- | |
| custom_css = """ | |
| .gradio-container {background-color: #050505; color: #e5e7eb;} | |
| .feedback-card {border: 1px solid #6366f1; padding: 20px; border-radius: 12px; background: #0f172a; margin: 10px 0;} | |
| #header-text {text-align: center; background: linear-gradient(to right, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-size: 2.5rem; font-weight: 800;} | |
| button.primary {background: linear-gradient(90deg, #6366f1, #a855f7) !important; color: white !important; font-weight: bold !important; border: none !important; transition: 0.3s;} | |
| button.primary:hover {transform: scale(1.02); opacity: 0.9;} | |
| .tabs {border: none !important;} | |
| footer {visibility: hidden} | |
| """ | |
| # --- Path Configuration --- | |
| CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| MODEL_ID = "Qwen/Qwen2-VL-2B-Instruct" | |
| # Files are in the root directory alongside app.py | |
| LORA_PATH = CURRENT_DIR | |
| # --- Model Loading --- | |
| print(f"Initializing Base Model: {MODEL_ID}") | |
| base_model = Qwen2VLForConditionalGeneration.from_pretrained( | |
| MODEL_ID, | |
| torch_dtype=torch.float16, | |
| device_map="auto" | |
| ) | |
| print(f"Applying LoRA Adapters from: {LORA_PATH}") | |
| model = PeftModel.from_pretrained(base_model, LORA_PATH) | |
| processor = AutoProcessor.from_pretrained(LORA_PATH) | |
| model.eval() | |
| # --- Inference Logic --- | |
| def analyze_document(input_img, custom_prompt): | |
| if input_img is None: | |
| return "Error: No image provided. Please upload a document." | |
| if not custom_prompt: | |
| custom_prompt = "Convert this document image into structured Markdown." | |
| try: | |
| # Prepare content for Qwen2-VL | |
| messages = [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "image", "image": input_img}, | |
| {"type": "text", "text": custom_prompt}, | |
| ], | |
| } | |
| ] | |
| # 1. Apply Template | |
| text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) | |
| # 2. Extract Vision Info | |
| image_inputs, video_inputs = process_vision_info(messages) | |
| # 3. Process Inputs (Force float16 to match model) | |
| inputs = processor( | |
| text=[text], | |
| images=image_inputs, | |
| videos=video_inputs, | |
| padding=True, | |
| return_tensors="pt" | |
| ).to(model.device).to(torch.float16) | |
| # 4. Generate | |
| with torch.no_grad(): | |
| generated_ids = model.generate( | |
| **inputs, | |
| max_new_tokens=1024, | |
| do_sample=False # Keep it deterministic for document tasks | |
| ) | |
| # Trim the prompt tokens | |
| generated_ids_trimmed = [ | |
| out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) | |
| ] | |
| output_text = processor.batch_decode( | |
| generated_ids_trimmed, | |
| skip_special_tokens=True, | |
| clean_up_tokenization_spaces=False | |
| ) | |
| return output_text[0] | |
| except Exception as e: | |
| return f"System Error: {str(e)}" | |
| # --- UI Layout --- | |
| with gr.Blocks(css=custom_css, theme=gr.themes.Monochrome()) as demo: | |
| gr.Markdown("# 🔮 STRUCTURA-VL: DOCUMENT INTELLIGENCE", elem_id="header-text") | |
| with gr.Row(): | |
| with gr.Column(scale=1, variant="panel"): | |
| img_input = gr.Image(type="pil", label="Input Document") | |
| prompt_input = gr.Textbox( | |
| label="System Prompt", | |
| value="Convert this document image into structured Markdown.", | |
| lines=2 | |
| ) | |
| submit_btn = gr.Button("✨ EXTRACT MARKDOWN", variant="primary") | |
| with gr.Column(scale=2): | |
| md_output = gr.Markdown(label="Rendered Result") | |
| with gr.Accordion("Raw Markdown Source", open=False): | |
| raw_output = gr.Code(label="Code", language="markdown") | |
| # Map Events | |
| submit_btn.click( | |
| fn=analyze_document, | |
| inputs=[img_input, prompt_input], | |
| outputs=[md_output] | |
| ).then( | |
| fn=lambda x: x, | |
| inputs=[md_output], | |
| outputs=[raw_output] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |