Views
No views yet

[!Note] The WASP-2B-VL-Highlights model is a fine-tuned version of Qwen2-VL-2B-Instruct, specifically optimized for image highlights extraction, messy handwriting recognition, Optical Character Recognition (OCR), English language understanding, and math problem solving with LaTeX formatting. This model uses a conversational visual-language interface to effectively handle multi-modal tasks.
1%%capture
2!pip install -q gradio spaces transformers accelerate
3!pip install -q numpy requests torch torchvision
4!pip install -q qwen-vl-utils av ipython reportlab
5!pip install -q fpdf python-docx pillow huggingface_hub1#Demo
2import gradio as gr
3import spaces
4from transformers import Qwen2VLForConditionalGeneration, AutoProcessor, TextIteratorStreamer
5from qwen_vl_utils import process_vision_info
6import torch
7from PIL import Image
8import os
9import uuid
10import io
11from threading import Thread
12from reportlab.lib.pagesizes import A4
13from reportlab.lib.styles import getSampleStyleSheet
14from reportlab.lib import colors
15from reportlab.platypus import SimpleDocTemplate, Image as RLImage, Paragraph, Spacer
16from reportlab.lib.units import inch
17from reportlab.pdfbase import pdfmetrics
18from reportlab.pdfbase.ttfonts import TTFont
19import docx
20from docx.enum.text import WD_ALIGN_PARAGRAPH
21
22# Define model options
23MODEL_OPTIONS = {
24 "Needle-2B-VL-Highlights": "prithivMLmods/WASP-2B-VL-Highlights",
25}
26
27# Preload models and processors into CUDA
28models = {}
29processors = {}
30for name, model_id in MODEL_OPTIONS.items():
31 print(f"Loading {name}...")
32 models[name] = Qwen2VLForConditionalGeneration.from_pretrained(
33 model_id,
34 trust_remote_code=True,
35 torch_dtype=torch.float16
36 ).to("cuda").eval()
37 processors[name] = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
38
39image_extensions = Image.registered_extensions()
40
41def identify_and_save_blob(blob_path):
42 """Identifies if the blob is an image and saves it."""
43 try:
44 with open(blob_path, 'rb') as file:
45 blob_content = file.read()
46 try:
47 Image.open(io.BytesIO(blob_content)).verify() # Check if it's a valid image
48 extension = ".png" # Default to PNG for saving
49 media_type = "image"
50 except (IOError, SyntaxError):
51 raise ValueError("Unsupported media type. Please upload a valid image.")
52
53 filename = f"temp_{uuid.uuid4()}_media{extension}"
54 with open(filename, "wb") as f:
55 f.write(blob_content)
56
57 return filename, media_type
58
59 except FileNotFoundError:
60 raise ValueError(f"The file {blob_path} was not found.")
61 except Exception as e:
62 raise ValueError(f"An error occurred while processing the file: {e}")
63
64@spaces.GPU
65def qwen_inference(model_name, media_input, text_input=None):
66 """Handles inference for the selected model."""
67 model = models[model_name]
68 processor = processors[model_name]
69
70 if isinstance(media_input, str):
71 media_path = media_input
72 if media_path.endswith(tuple([i for i in image_extensions.keys()])):
73 media_type = "image"
74 else:
75 try:
76 media_path, media_type = identify_and_save_blob(media_input)
77 except Exception as e:
78 raise ValueError("Unsupported media type. Please upload a valid image.")
79
80 messages = [
81 {
82 "role": "user",
83 "content": [
84 {
85 "type": media_type,
86 media_type: media_path
87 },
88 {"type": "text", "text": text_input},
89 ],
90 }
91 ]
92
93 text = processor.apply_chat_template(
94 messages, tokenize=False, add_generation_prompt=True
95 )
96 image_inputs, _ = process_vision_info(messages)
97 inputs = processor(
98 text=[text],
99 images=image_inputs,
100 padding=True,
101 return_tensors="pt",
102 ).to("cuda")
103
104 streamer = TextIteratorStreamer(
105 processor.tokenizer, skip_prompt=True, skip_special_tokens=True
106 )
107 generation_kwargs = dict(inputs, streamer=streamer, max_new_tokens=1024)
108
109 thread = Thread(target=model.generate, kwargs=generation_kwargs)
110 thread.start()
111
112 buffer = ""
113 for new_text in streamer:
114 buffer += new_text
115 # Remove <|im_end|> or similar tokens from the output
116 buffer = buffer.replace("<|im_end|>", "")
117 yield buffer
118
119def format_plain_text(output_text):
120 """Formats the output text as plain text without LaTeX delimiters."""
121 # Remove LaTeX delimiters and convert to plain text
122 plain_text = output_text.replace("\\(", "").replace("\\)", "").replace("\\[", "").replace("\\]", "")
123 return plain_text
124
125def generate_document(media_path, output_text, file_format, font_size, line_spacing, alignment, image_size):
126 """Generates a document with the input image and plain text output."""
127 plain_text = format_plain_text(output_text)
128 if file_format == "pdf":
129 return generate_pdf(media_path, plain_text, font_size, line_spacing, alignment, image_size)
130 elif file_format == "docx":
131 return generate_docx(media_path, plain_text, font_size, line_spacing, alignment, image_size)
132
133def generate_pdf(media_path, plain_text, font_size, line_spacing, alignment, image_size):
134 """Generates a PDF document."""
135 filename = f"output_{uuid.uuid4()}.pdf"
136 doc = SimpleDocTemplate(
137 filename,
138 pagesize=A4,
139 rightMargin=inch,
140 leftMargin=inch,
141 topMargin=inch,
142 bottomMargin=inch
143 )
144 styles = getSampleStyleSheet()
145 styles["Normal"].fontSize = int(font_size)
146 styles["Normal"].leading = int(font_size) * line_spacing
147 styles["Normal"].alignment = {
148 "Left": 0,
149 "Center": 1,
150 "Right": 2,
151 "Justified": 4
152 }[alignment]
153
154 story = []
155
156 # Add image with size adjustment
157 image_sizes = {
158 "Small": (200, 200),
159 "Medium": (400, 400),
160 "Large": (600, 600)
161 }
162 img = RLImage(media_path, width=image_sizes[image_size][0], height=image_sizes[image_size][1])
163 story.append(img)
164 story.append(Spacer(1, 12))
165
166 # Add plain text output
167 text = Paragraph(plain_text, styles["Normal"])
168 story.append(text)
169
170 doc.build(story)
171 return filename
172
173def generate_docx(media_path, plain_text, font_size, line_spacing, alignment, image_size):
174 """Generates a DOCX document."""
175 filename = f"output_{uuid.uuid4()}.docx"
176 doc = docx.Document()
177
178 # Add image with size adjustment
179 image_sizes = {
180 "Small": docx.shared.Inches(2),
181 "Medium": docx.shared.Inches(4),
182 "Large": docx.shared.Inches(6)
183 }
184 doc.add_picture(media_path, width=image_sizes[image_size])
185 doc.add_paragraph()
186
187 # Add plain text output
188 paragraph = doc.add_paragraph()
189 paragraph.paragraph_format.line_spacing = line_spacing
190 paragraph.paragraph_format.alignment = {
191 "Left": WD_ALIGN_PARAGRAPH.LEFT,
192 "Center": WD_ALIGN_PARAGRAPH.CENTER,
193 "Right": WD_ALIGN_PARAGRAPH.RIGHT,
194 "Justified": WD_ALIGN_PARAGRAPH.JUSTIFY
195 }[alignment]
196 run = paragraph.add_run(plain_text)
197 run.font.size = docx.shared.Pt(int(font_size))
198
199 doc.save(filename)
200 return filename
201
202# CSS for output styling
203css = """
204 #output {
205 height: 500px;
206 overflow: auto;
207 border: 1px solid #ccc;
208 }
209.submit-btn {
210 background-color: #cf3434 !important;
211 color: white !important;
212}
213.submit-btn:hover {
214 background-color: #ff2323 !important;
215}
216.download-btn {
217 background-color: #35a6d6 !important;
218 color: white !important;
219}
220.download-btn:hover {
221 background-color: #22bcff !important;
222}
223"""
224
225# Gradio app setup
226with gr.Blocks(css=css) as demo:
227 gr.Markdown("# Qwen2VL Models: Vision and Language Processing")
228
229 with gr.Tab(label="Image Input"):
230
231 with gr.Row():
232 with gr.Column():
233 model_choice = gr.Dropdown(
234 label="Model Selection",
235 choices=list(MODEL_OPTIONS.keys()),
236 value="WASP-2B-VL-Highlights"
237 )
238 input_media = gr.File(
239 label="Upload Image", type="filepath"
240 )
241 text_input = gr.Textbox(label="Question", placeholder="Ask a question about the image...")
242 submit_btn = gr.Button(value="Submit", elem_classes="submit-btn")
243
244 with gr.Column():
245 output_text = gr.Textbox(label="Output Text", lines=10)
246 plain_text_output = gr.Textbox(label="Standardized Plain Text", lines=10)
247
248 submit_btn.click(
249 qwen_inference, [model_choice, input_media, text_input], [output_text]
250 ).then(
251 lambda output_text: format_plain_text(output_text), [output_text], [plain_text_output]
252 )
253
254 # Add examples directly usable by clicking
255 with gr.Row():
256 with gr.Column():
257 line_spacing = gr.Dropdown(
258 choices=[0.5, 1.0, 1.15, 1.5, 2.0, 2.5, 3.0],
259 value=1.5,
260 label="Line Spacing"
261 )
262 font_size = gr.Dropdown(
263 choices=["8", "10", "12", "14", "16", "18", "20", "22", "24"],
264 value="18",
265 label="Font Size"
266 )
267 alignment = gr.Dropdown(
268 choices=["Left", "Center", "Right", "Justified"],
269 value="Justified",
270 label="Text Alignment"
271 )
272 image_size = gr.Dropdown(
273 choices=["Small", "Medium", "Large"],
274 value="Small",
275 label="Image Size"
276 )
277 file_format = gr.Radio(["pdf", "docx"], label="File Format", value="pdf")
278 get_document_btn = gr.Button(value="Get Document", elem_classes="download-btn")
279
280 get_document_btn.click(
281 generate_document, [input_media, output_text, file_format, font_size, line_spacing, alignment, image_size], gr.File(label="Download Document")
282 )
283
284demo.launch(debug=True)