openfree commited on
Commit
85c5cbc
Β·
verified Β·
1 Parent(s): ddda509

Upload 2 files

Browse files
Files changed (2) hide show
  1. app (30).py +391 -0
  2. requirements (11).txt +16 -0
app (30).py ADDED
@@ -0,0 +1,391 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Authenticate with Hugging Face
2
+ from huggingface_hub import login
3
+ import os
4
+ # Log in to Hugging Face using the provided token
5
+ hf_token = os.getenv("HF_TOKEN")
6
+ login(hf_token)
7
+
8
+ # Required imports
9
+ import gradio as gr
10
+ import spaces
11
+ from transformers import Qwen2VLForConditionalGeneration, AutoProcessor, TextIteratorStreamer
12
+ from qwen_vl_utils import process_vision_info
13
+ import torch
14
+ from PIL import Image
15
+ import os
16
+ import uuid
17
+ import io
18
+ from threading import Thread
19
+ from reportlab.lib.pagesizes import A4
20
+ from reportlab.lib.styles import getSampleStyleSheet
21
+ from reportlab.lib import colors
22
+ from reportlab.platypus import SimpleDocTemplate, Image as RLImage, Paragraph, Spacer
23
+ from reportlab.pdfbase import pdfmetrics
24
+ from reportlab.pdfbase.ttfonts import TTFont
25
+ import docx
26
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
27
+ from reportlab.lib.units import inch
28
+
29
+ # Define model options
30
+ MODEL_OPTIONS = {
31
+ "ChemQwen-1": "prithivMLmods/ChemQwen-vL",
32
+ "ChemQwen-2": "prithivMLmods/ChemQwen2-vL",
33
+ }
34
+
35
+ # Preload models and processors into CUDA
36
+ models = {}
37
+ processors = {}
38
+ for name, model_id in MODEL_OPTIONS.items():
39
+ print(f"Loading {name}...")
40
+ models[name] = Qwen2VLForConditionalGeneration.from_pretrained(
41
+ model_id,
42
+ trust_remote_code=True,
43
+ torch_dtype=torch.float16
44
+ ).to("cuda").eval()
45
+ processors[name] = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
46
+
47
+ image_extensions = Image.registered_extensions()
48
+
49
+ def identify_and_save_blob(blob_path):
50
+ """Identifies if the blob is an image and saves it."""
51
+ try:
52
+ with open(blob_path, 'rb') as file:
53
+ blob_content = file.read()
54
+ try:
55
+ Image.open(io.BytesIO(blob_content)).verify()
56
+ extension = ".png"
57
+ media_type = "image"
58
+ except (IOError, SyntaxError):
59
+ raise ValueError("Unsupported media type. Please upload a valid image.")
60
+
61
+ filename = f"temp_{uuid.uuid4()}_media{extension}"
62
+ with open(filename, "wb") as f:
63
+ f.write(blob_content)
64
+
65
+ return filename, media_type
66
+
67
+ except FileNotFoundError:
68
+ raise ValueError(f"The file {blob_path} was not found.")
69
+ except Exception as e:
70
+ raise ValueError(f"An error occurred while processing the file: {e}")
71
+
72
+
73
+ @spaces.GPU
74
+ def qwen_inference(model_name, media_input, text_input=None):
75
+ """Handles inference for the selected model."""
76
+ model = models[model_name]
77
+ processor = processors[model_name]
78
+
79
+ if isinstance(media_input, str):
80
+ media_path = media_input
81
+ if media_path.endswith(tuple([i for i in image_extensions.keys()])):
82
+ media_type = "image"
83
+ else:
84
+ try:
85
+ media_path, media_type = identify_and_save_blob(media_input)
86
+ except Exception as e:
87
+ raise ValueError("Unsupported media type. Please upload a valid image.")
88
+
89
+ messages = [
90
+ {
91
+ "role": "user",
92
+ "content": [
93
+ {
94
+ "type": media_type,
95
+ media_type: media_path
96
+ },
97
+ {"type": "text", "text": text_input},
98
+ ],
99
+ }
100
+ ]
101
+
102
+ text = processor.apply_chat_template(
103
+ messages, tokenize=False, add_generation_prompt=True
104
+ )
105
+ image_inputs, _ = process_vision_info(messages)
106
+ inputs = processor(
107
+ text=[text],
108
+ images=image_inputs,
109
+ padding=True,
110
+ return_tensors="pt",
111
+ ).to("cuda")
112
+
113
+ streamer = TextIteratorStreamer(
114
+ processor.tokenizer, skip_prompt=True, skip_special_tokens=True
115
+ )
116
+ generation_kwargs = dict(inputs, streamer=streamer, max_new_tokens=1024)
117
+
118
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
119
+ thread.start()
120
+
121
+ buffer = ""
122
+ for new_text in streamer:
123
+ buffer += new_text
124
+ # Remove <|im_end|> or similar tokens from the output
125
+ buffer = buffer.replace("<|im_end|>", "")
126
+ yield buffer
127
+
128
+
129
+ def format_plain_text(output_text):
130
+ """Formats the output text as plain text without LaTeX delimiters."""
131
+ plain_text = output_text.replace("\\(", "").replace("\\)", "").replace("\\[", "").replace("\\]", "")
132
+ return plain_text
133
+
134
+ def generate_document(media_path, output_text, file_format, font_size, line_spacing, alignment, image_size):
135
+ """Generates a document with the input image and plain text output."""
136
+ plain_text = format_plain_text(output_text)
137
+ if file_format == "pdf":
138
+ return generate_pdf(media_path, plain_text, font_size, line_spacing, alignment, image_size)
139
+ elif file_format == "docx":
140
+ return generate_docx(media_path, plain_text, font_size, line_spacing, alignment, image_size)
141
+
142
+ def generate_pdf(media_path, plain_text, font_size, line_spacing, alignment, image_size):
143
+ """Generates a PDF document."""
144
+ filename = f"output_{uuid.uuid4()}.pdf"
145
+ doc = SimpleDocTemplate(
146
+ filename,
147
+ pagesize=A4,
148
+ rightMargin=inch,
149
+ leftMargin=inch,
150
+ topMargin=inch,
151
+ bottomMargin=inch
152
+ )
153
+ styles = getSampleStyleSheet()
154
+ styles["Normal"].fontSize = int(font_size)
155
+ styles["Normal"].leading = int(font_size) * line_spacing
156
+ styles["Normal"].alignment = {
157
+ "Left": 0,
158
+ "Center": 1,
159
+ "Right": 2,
160
+ "Justified": 4
161
+ }[alignment]
162
+
163
+ story = []
164
+
165
+ image_sizes = {
166
+ "Small": (200, 200),
167
+ "Medium": (400, 400),
168
+ "Large": (600, 600)
169
+ }
170
+ img = RLImage(media_path, width=image_sizes[image_size][0], height=image_sizes[image_size][1])
171
+ story.append(img)
172
+ story.append(Spacer(1, 12))
173
+
174
+ text = Paragraph(plain_text, styles["Normal"])
175
+ story.append(text)
176
+
177
+ doc.build(story)
178
+ return filename
179
+
180
+ def generate_docx(media_path, plain_text, font_size, line_spacing, alignment, image_size):
181
+ """Generates a DOCX document."""
182
+ filename = f"output_{uuid.uuid4()}.docx"
183
+ doc = docx.Document()
184
+
185
+ # Convert image to PNG format before adding to document
186
+ try:
187
+ img = Image.open(media_path)
188
+ temp_image_path = f"temp_{uuid.uuid4()}.png"
189
+ img.save(temp_image_path, "PNG")
190
+
191
+ image_sizes = {
192
+ "Small": docx.shared.Inches(2),
193
+ "Medium": docx.shared.Inches(4),
194
+ "Large": docx.shared.Inches(6)
195
+ }
196
+
197
+ doc.add_picture(temp_image_path, width=image_sizes[image_size])
198
+
199
+ # Clean up temporary image file
200
+ os.remove(temp_image_path)
201
+
202
+ except Exception as e:
203
+ print(f"Error processing image: {e}")
204
+ # Continue without image if there's an error
205
+
206
+ doc.add_paragraph()
207
+
208
+ paragraph = doc.add_paragraph()
209
+ paragraph.paragraph_format.line_spacing = line_spacing
210
+ paragraph.paragraph_format.alignment = {
211
+ "Left": WD_ALIGN_PARAGRAPH.LEFT,
212
+ "Center": WD_ALIGN_PARAGRAPH.CENTER,
213
+ "Right": WD_ALIGN_PARAGRAPH.RIGHT,
214
+ "Justified": WD_ALIGN_PARAGRAPH.JUSTIFY
215
+ }[alignment]
216
+ run = paragraph.add_run(plain_text)
217
+ run.font.size = docx.shared.Pt(int(font_size))
218
+
219
+ doc.save(filename)
220
+ return filename
221
+
222
+ # CSS styling
223
+ css = """
224
+ #output {
225
+ height: 500px;
226
+ overflow: auto;
227
+ border: 1px solid #ccc;
228
+ }
229
+ .container {
230
+ background: linear-gradient(145deg, #f0f0f0, #ffffff);
231
+ border-radius: 20px;
232
+ box-shadow: 20px 20px 60px #bebebe, -20px -20px 60px #ffffff;
233
+ padding: 2rem;
234
+ margin: 1rem;
235
+ }
236
+ .title {
237
+ text-align: center;
238
+ font-size: 2.5rem;
239
+ color: #2d3436;
240
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
241
+ margin-bottom: 2rem;
242
+ }
243
+ .submit-btn {
244
+ background: linear-gradient(145deg, #ff4757, #ff6b81) !important;
245
+ color: white !important;
246
+ border: none !important;
247
+ border-radius: 10px !important;
248
+ padding: 0.8rem 1.5rem !important;
249
+ font-weight: bold !important;
250
+ transform: translateY(0);
251
+ transition: all 0.3s ease !important;
252
+ box-shadow: 0 4px 15px rgba(255, 71, 87, 0.3) !important;
253
+ }
254
+ .submit-btn:hover {
255
+ transform: translateY(-2px) !important;
256
+ box-shadow: 0 6px 20px rgba(255, 71, 87, 0.4) !important;
257
+ }
258
+ .download-btn {
259
+ background: linear-gradient(145deg, #00b894, #00cec9) !important;
260
+ color: white !important;
261
+ border: none !important;
262
+ border-radius: 10px !important;
263
+ padding: 0.8rem 1.5rem !important;
264
+ font-weight: bold !important;
265
+ transform: translateY(0);
266
+ transition: all 0.3s ease !important;
267
+ box-shadow: 0 4px 15px rgba(0, 184, 148, 0.3) !important;
268
+ }
269
+ .download-btn:hover {
270
+ transform: translateY(-2px) !important;
271
+ box-shadow: 0 6px 20px rgba(0, 184, 148, 0.4) !important;
272
+ }
273
+ .input-box {
274
+ border-radius: 10px !important;
275
+ border: 2px solid #dfe6e9 !important;
276
+ transition: all 0.3s ease !important;
277
+ }
278
+ .input-box:focus {
279
+ border-color: #00b894 !important;
280
+ box-shadow: 0 0 10px rgba(0, 184, 148, 0.2) !important;
281
+ }
282
+ """
283
+
284
+ # Gradio app setup
285
+ with gr.Blocks(css=css) as demo:
286
+ gr.Markdown("# πŸ§ͺ ChemQwen Chemical Identifier AI πŸ€–", elem_classes="title")
287
+
288
+ with gr.Tab(label="πŸ–ΌοΈ Image Analysis"):
289
+ with gr.Row(elem_classes="container"):
290
+ with gr.Column():
291
+ model_choice = gr.Dropdown(
292
+ label="πŸ” Select Model",
293
+ choices=list(MODEL_OPTIONS.keys()),
294
+ value="ChemQwen-1",
295
+ elem_classes="input-box"
296
+ )
297
+ input_media = gr.File(
298
+ label="πŸ“€ Upload Image",
299
+ type="filepath",
300
+ elem_classes="input-box"
301
+ )
302
+ text_input = gr.Textbox(
303
+ label="❓ Your Question",
304
+ placeholder="Ask anything about the image...",
305
+ elem_classes="input-box"
306
+ )
307
+ submit_btn = gr.Button(value="πŸš€ Analyze", elem_classes="submit-btn")
308
+
309
+ with gr.Column():
310
+ output_text = gr.Textbox(
311
+ label="πŸ“ AI Response",
312
+ lines=10,
313
+ elem_classes="input-box"
314
+ )
315
+ plain_text_output = gr.Textbox(
316
+ label="πŸ“‹ Standardized Text",
317
+ lines=10,
318
+ elem_classes="input-box"
319
+ )
320
+
321
+ with gr.Row(elem_classes="container"):
322
+ with gr.Column():
323
+ gr.Markdown("### πŸ“„ Document Settings")
324
+ line_spacing = gr.Dropdown(
325
+ choices=[0.5, 1.0, 1.15, 1.5, 2.0, 2.5, 3.0],
326
+ value=1.5,
327
+ label="↕️ Line Spacing",
328
+ elem_classes="input-box"
329
+ )
330
+ font_size = gr.Dropdown(
331
+ choices=["8", "10", "12", "14", "16", "18", "20", "22", "24"],
332
+ value="18",
333
+ label="πŸ“ Font Size",
334
+ elem_classes="input-box"
335
+ )
336
+ alignment = gr.Dropdown(
337
+ choices=["Left", "Center", "Right", "Justified"],
338
+ value="Justified",
339
+ label="⚑ Text Alignment",
340
+ elem_classes="input-box"
341
+ )
342
+ image_size = gr.Dropdown(
343
+ choices=["Small", "Medium", "Large"],
344
+ value="Medium",
345
+ label="πŸ–ΌοΈ Image Size",
346
+ elem_classes="input-box"
347
+ )
348
+ file_format = gr.Radio(
349
+ ["pdf", "docx"],
350
+ label="πŸ“ File Format",
351
+ value="pdf",
352
+ elem_classes="input-box"
353
+ )
354
+ get_document_btn = gr.Button(
355
+ value="πŸ’Ύ Generate Document",
356
+ elem_classes="download-btn"
357
+ )
358
+
359
+
360
+
361
+ submit_btn.click(
362
+ qwen_inference,
363
+ [model_choice, input_media, text_input],
364
+ output_text,
365
+ ).then(
366
+ format_plain_text,
367
+ output_text,
368
+ plain_text_output
369
+ )
370
+
371
+
372
+ get_document_btn.click(
373
+ generate_document,
374
+ [input_media, output_text, file_format, font_size, line_spacing, alignment, image_size],
375
+ gr.File(label="πŸ“₯ Download Document")
376
+ )
377
+
378
+ gr.Markdown("""
379
+ ### 🌟 Features
380
+ - πŸ”¬ Advanced Chemical Structure Analysis
381
+ - πŸ“Š Multiple Model Support
382
+ - πŸ’« Real-time Processing
383
+ - πŸ“‘ Custom Document Generation
384
+
385
+ ### πŸ’‘ Tips
386
+ - πŸ“Έ Upload clear images for better results
387
+ - ✍️ Be specific with your questions
388
+ - πŸ“ Use the document generator for professional reports
389
+ """)
390
+
391
+ demo.launch(debug=True)
requirements (11).txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ spaces
3
+ transformers
4
+ accelerate
5
+ numpy
6
+ requests
7
+ torch
8
+ torchvision
9
+ qwen-vl-utils
10
+ av
11
+ ipython
12
+ reportlab
13
+ fpdf
14
+ python-docx
15
+ pillow
16
+ huggingface_hub