VOIDER commited on
Commit
f30ac2b
·
verified ·
1 Parent(s): 28178bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -100
app.py CHANGED
@@ -4,13 +4,16 @@ import re
4
  import json
5
  import tempfile
6
  import zipfile
 
7
  from huggingface_hub import hf_hub_download
8
- from llama_cpp import Llama
9
- from llama_cpp.llama_chat_format import Llava15ChatHandler
10
  import base64
11
  from PIL import Image
12
  from io import BytesIO
13
 
 
 
 
 
14
  # Константы
15
  REPO_ID = "mradermacher/VisualQuality-R1-7B-GGUF"
16
  MODEL_FILE = "VisualQuality-R1-7B.Q4_K_M.gguf"
@@ -29,10 +32,40 @@ QUESTION_TEMPLATE_NO_THINKING = "{Question} Please only output the final answer
29
  # Глобальные переменные
30
  llm = None
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def download_models():
34
  """Скачивание моделей"""
35
- print("Downloading model files...")
36
 
37
  model_path = hf_hub_download(
38
  repo_id=REPO_ID,
@@ -40,6 +73,7 @@ def download_models():
40
  )
41
  print(f"Model downloaded: {model_path}")
42
 
 
43
  mmproj_path = hf_hub_download(
44
  repo_id=REPO_ID,
45
  filename=MMPROJ_FILE,
@@ -51,31 +85,41 @@ def download_models():
51
 
52
  def load_model():
53
  """Загрузка модели"""
54
- global llm
55
 
56
  if llm is not None:
57
- return
58
-
59
- model_path, mmproj_path = download_models()
60
-
61
- print("Loading model...")
62
-
63
- # Используем Llava15ChatHandler для vision моделей
64
- chat_handler = Llava15ChatHandler(
65
- clip_model_path=mmproj_path,
66
- verbose=False
67
- )
68
-
69
- llm = Llama(
70
- model_path=model_path,
71
- chat_handler=chat_handler,
72
- n_ctx=4096,
73
- n_threads=4,
74
- n_gpu_layers=0,
75
- verbose=False,
76
- )
77
 
78
- print("Model loaded!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
 
81
  def image_to_data_uri(image):
@@ -86,7 +130,6 @@ def image_to_data_uri(image):
86
  if image.mode != "RGB":
87
  image = image.convert("RGB")
88
 
89
- # Сжимаем для ускорения
90
  max_size = 768
91
  if max(image.size) > max_size:
92
  ratio = max_size / max(image.size)
@@ -129,15 +172,20 @@ def score_single_image(image, use_thinking=True):
129
  """Оценка одного изображения"""
130
  global llm
131
 
132
- load_model()
133
 
134
  if image is None:
135
  return "❌ Upload an image first", "", ""
136
 
 
 
 
137
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
138
  prompt_text = template.format(Question=PROMPT)
139
 
 
140
  image_uri = image_to_data_uri(image)
 
141
 
142
  messages = [
143
  {
@@ -149,7 +197,7 @@ def score_single_image(image, use_thinking=True):
149
  }
150
  ]
151
 
152
- # Стриминг
153
  generated_text = ""
154
 
155
  try:
@@ -170,42 +218,47 @@ def score_single_image(image, use_thinking=True):
170
  thinking = extract_thinking(generated_text)
171
  score = extract_score(generated_text)
172
 
173
- if score is not None:
174
- score_display = f"⭐ **Score: {score:.2f} / 5.00**"
175
- else:
176
- score_display = "*Analyzing...*"
177
 
178
  yield generated_text, thinking, score_display
179
 
180
- # Финальный результат
 
181
  final_score = extract_score(generated_text)
182
  final_thinking = extract_thinking(generated_text) if use_thinking else ""
183
 
184
  if final_score is not None:
185
  score_display = f"⭐ **Quality Score: {final_score:.2f} / 5.00**\n\n📊 **For Leaderboard:** `{final_score:.2f}`"
186
  else:
187
- score_display = "❌ Could not extract score"
188
 
189
  yield generated_text, final_thinking, score_display
190
 
191
  except Exception as e:
192
- yield f"❌ Error: {str(e)}", "", ""
 
 
 
193
 
194
 
195
  def process_batch(files, use_thinking=True, progress=gr.Progress()):
196
  """Batch processing"""
197
  global llm
198
 
199
- load_model()
200
 
201
  if not files:
202
- return "❌ No files", None
 
 
 
203
 
204
  results = []
205
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
206
  prompt_text = template.format(Question=PROMPT)
207
 
208
  for i, file in enumerate(files):
 
209
  try:
210
  if hasattr(file, 'name'):
211
  image = Image.open(file.name)
@@ -214,6 +267,8 @@ def process_batch(files, use_thinking=True, progress=gr.Progress()):
214
  image = Image.open(file)
215
  filename = f"image_{i+1}.jpg"
216
 
 
 
217
  image_uri = image_to_data_uri(image)
218
 
219
  messages = [
@@ -244,112 +299,154 @@ def process_batch(files, use_thinking=True, progress=gr.Progress()):
244
  "raw_output": generated_text
245
  })
246
 
247
- progress((i + 1) / len(files), desc=f"Processed {i+1}/{len(files)}")
 
248
 
249
  except Exception as e:
 
250
  results.append({
251
- "filename": filename if 'filename' in dir() else f"image_{i+1}",
252
  "score": "ERROR",
253
  "thinking": "",
254
  "raw_output": str(e)
255
  })
256
 
257
- # Создаём файлы
258
- with tempfile.TemporaryDirectory() as tmpdir:
259
- # TXT для лидерборда
260
- txt_file = os.path.join(tmpdir, "scores.txt")
261
- with open(txt_file, "w") as f:
262
- for r in results:
263
- score_str = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
264
- f.write(f"{r['filename']}\t{score_str}\n")
265
-
266
- # JSON
267
- json_file = os.path.join(tmpdir, "results.json")
268
- with open(json_file, "w") as f:
269
- json.dump(results, f, indent=2, ensure_ascii=False)
270
-
271
- # CSV
272
- csv_file = os.path.join(tmpdir, "scores.csv")
273
- with open(csv_file, "w") as f:
274
- f.write("filename,score\n")
275
- for r in results:
276
- score_str = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
277
- f.write(f"{r['filename']},{score_str}\n")
278
-
279
- # ZIP
280
- zip_path = os.path.join(tmpdir, "results.zip")
281
- with zipfile.ZipFile(zip_path, 'w') as zipf:
282
- zipf.write(txt_file, "scores.txt")
283
- zipf.write(json_file, "results.json")
284
- zipf.write(csv_file, "scores.csv")
285
-
286
- # Копируем
287
- final_zip = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
288
- with open(zip_path, 'rb') as f:
289
- final_zip.write(f.read())
290
- final_zip.close()
 
 
 
291
 
292
  # Summary
293
  valid_scores = [r['score'] for r in results if isinstance(r['score'], float)]
294
- avg = sum(valid_scores)/len(valid_scores) if valid_scores else 0
295
 
296
- summary = f"""## ✅ Done!
297
 
298
  **Processed:** {len(results)} images
299
- **Success:** {len(valid_scores)}
300
  **Failed:** {len(results) - len(valid_scores)}
301
 
302
- **Average:** {avg:.2f}
303
- **Min:** {min(valid_scores):.2f if valid_scores else 'N/A'}
304
- **Max:** {max(valid_scores):.2f if valid_scores else 'N/A'}
 
305
 
306
- ### Preview:
307
- | File | Score |
308
- |------|-------|
309
- """ + "\n".join([f"| {r['filename'][:30]} | {r['score']:.2f if isinstance(r['score'], float) else r['score']} |" for r in results[:10]])
310
 
311
  return summary, final_zip.name
312
 
313
 
314
- # Интерфейс
315
- with gr.Blocks(title="VisualQuality-R1") as demo:
 
 
316
  gr.Markdown("""
317
  # 🎨 VisualQuality-R1 (GGUF/CPU)
318
 
319
- Image Quality Assessment | CPU Mode (~30-60 sec/image)
320
 
321
  [![Paper](https://img.shields.io/badge/arXiv-Paper-red)](https://arxiv.org/abs/2505.14460)
 
322
  """)
323
 
324
  with gr.Tabs():
325
  with gr.TabItem("📷 Single Image"):
326
  with gr.Row():
327
  with gr.Column():
328
- img_input = gr.Image(label="Upload", type="pil", height=350)
329
- thinking_cb = gr.Checkbox(label="🧠 Thinking Mode", value=True)
330
- btn = gr.Button("🔍 Analyze", variant="primary", size="lg")
331
 
332
  with gr.Column():
333
- score_out = gr.Markdown("*Upload image*")
334
- thinking_out = gr.Textbox(label="Thinking", lines=6)
335
- raw_out = gr.Textbox(label="Output", lines=8)
336
 
337
- btn.click(score_single_image, [img_input, thinking_cb], [raw_out, thinking_out, score_out])
 
 
 
 
338
 
339
- with gr.TabItem("📁 Batch (1000+ images)"):
340
- gr.Markdown("### Upload multiple images for leaderboard submission")
 
 
 
 
 
341
 
342
  with gr.Row():
343
  with gr.Column():
344
- batch_files = gr.File(label="Images", file_count="multiple", file_types=["image"])
345
- batch_thinking = gr.Checkbox(label="🧠 Thinking (slower)", value=False)
 
 
 
 
 
 
 
346
  batch_btn = gr.Button("🚀 Process All", variant="primary", size="lg")
347
 
348
  with gr.Column():
349
- batch_summary = gr.Markdown("*Upload and click Process*")
350
- batch_download = gr.File(label="📥 Download Results")
351
 
352
- batch_btn.click(process_batch, [batch_files, batch_thinking], [batch_summary, batch_download])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
  if __name__ == "__main__":
355
  demo.queue(max_size=5)
 
4
  import json
5
  import tempfile
6
  import zipfile
7
+ import traceback
8
  from huggingface_hub import hf_hub_download
 
 
9
  import base64
10
  from PIL import Image
11
  from io import BytesIO
12
 
13
+ print("=" * 50)
14
+ print("Starting VisualQuality-R1 GGUF")
15
+ print("=" * 50)
16
+
17
  # Константы
18
  REPO_ID = "mradermacher/VisualQuality-R1-7B-GGUF"
19
  MODEL_FILE = "VisualQuality-R1-7B.Q4_K_M.gguf"
 
32
  # Глобальные переменные
33
  llm = None
34
 
35
+ print("Importing llama_cpp...")
36
+ try:
37
+ from llama_cpp import Llama
38
+ print("llama_cpp imported successfully")
39
+ except Exception as e:
40
+ print(f"Error importing llama_cpp: {e}")
41
+ traceback.print_exc()
42
+
43
+ # Пробуем импортировать chat handlers
44
+ chat_handler_class = None
45
+ try:
46
+ from llama_cpp.llama_chat_format import Qwen2VLChatHandler
47
+ chat_handler_class = Qwen2VLChatHandler
48
+ print("Using Qwen2VLChatHandler")
49
+ except ImportError:
50
+ print("Qwen2VLChatHandler not found, trying Llava15ChatHandler...")
51
+ try:
52
+ from llama_cpp.llama_chat_format import Llava15ChatHandler
53
+ chat_handler_class = Llava15ChatHandler
54
+ print("Using Llava15ChatHandler")
55
+ except ImportError:
56
+ print("Llava15ChatHandler not found, trying Llava16ChatHandler...")
57
+ try:
58
+ from llama_cpp.llama_chat_format import Llava16ChatHandler
59
+ chat_handler_class = Llava16ChatHandler
60
+ print("Using Llava16ChatHandler")
61
+ except ImportError:
62
+ print("No suitable chat handler found!")
63
+ chat_handler_class = None
64
+
65
 
66
  def download_models():
67
  """Скачивание моделей"""
68
+ print(f"Downloading {MODEL_FILE}...")
69
 
70
  model_path = hf_hub_download(
71
  repo_id=REPO_ID,
 
73
  )
74
  print(f"Model downloaded: {model_path}")
75
 
76
+ print(f"Downloading {MMPROJ_FILE}...")
77
  mmproj_path = hf_hub_download(
78
  repo_id=REPO_ID,
79
  filename=MMPROJ_FILE,
 
85
 
86
  def load_model():
87
  """Загрузка модели"""
88
+ global llm, chat_handler_class
89
 
90
  if llm is not None:
91
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ try:
94
+ model_path, mmproj_path = download_models()
95
+
96
+ print("Creating chat handler...")
97
+ if chat_handler_class is not None:
98
+ chat_handler = chat_handler_class(
99
+ clip_model_path=mmproj_path,
100
+ verbose=True
101
+ )
102
+ print("Chat handler created")
103
+ else:
104
+ print("WARNING: No chat handler, trying without it")
105
+ chat_handler = None
106
+
107
+ print("Loading LLM...")
108
+ llm = Llama(
109
+ model_path=model_path,
110
+ chat_handler=chat_handler,
111
+ n_ctx=4096,
112
+ n_threads=4,
113
+ n_gpu_layers=0,
114
+ verbose=True,
115
+ )
116
+ print("Model loaded successfully!")
117
+ return True
118
+
119
+ except Exception as e:
120
+ print(f"Error loading model: {e}")
121
+ traceback.print_exc()
122
+ return False
123
 
124
 
125
  def image_to_data_uri(image):
 
130
  if image.mode != "RGB":
131
  image = image.convert("RGB")
132
 
 
133
  max_size = 768
134
  if max(image.size) > max_size:
135
  ratio = max_size / max(image.size)
 
172
  """Оценка одного изображения"""
173
  global llm
174
 
175
+ print(f"score_single_image called, use_thinking={use_thinking}")
176
 
177
  if image is None:
178
  return "❌ Upload an image first", "", ""
179
 
180
+ if not load_model():
181
+ return "❌ Failed to load model. Check logs.", "", ""
182
+
183
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
184
  prompt_text = template.format(Question=PROMPT)
185
 
186
+ print("Converting image to data URI...")
187
  image_uri = image_to_data_uri(image)
188
+ print(f"Image URI created, length: {len(image_uri)}")
189
 
190
  messages = [
191
  {
 
197
  }
198
  ]
199
 
200
+ print("Starting generation...")
201
  generated_text = ""
202
 
203
  try:
 
218
  thinking = extract_thinking(generated_text)
219
  score = extract_score(generated_text)
220
 
221
+ score_display = f"⭐ **Score: {score:.2f} / 5.00**" if score else "*Analyzing...*"
 
 
 
222
 
223
  yield generated_text, thinking, score_display
224
 
225
+ print(f"Generation complete, output length: {len(generated_text)}")
226
+
227
  final_score = extract_score(generated_text)
228
  final_thinking = extract_thinking(generated_text) if use_thinking else ""
229
 
230
  if final_score is not None:
231
  score_display = f"⭐ **Quality Score: {final_score:.2f} / 5.00**\n\n📊 **For Leaderboard:** `{final_score:.2f}`"
232
  else:
233
+ score_display = "❌ Could not extract score. Raw output shown below."
234
 
235
  yield generated_text, final_thinking, score_display
236
 
237
  except Exception as e:
238
+ error_msg = f"❌ Error: {str(e)}"
239
+ print(error_msg)
240
+ traceback.print_exc()
241
+ yield error_msg, "", ""
242
 
243
 
244
  def process_batch(files, use_thinking=True, progress=gr.Progress()):
245
  """Batch processing"""
246
  global llm
247
 
248
+ print(f"process_batch called with {len(files) if files else 0} files")
249
 
250
  if not files:
251
+ return "❌ No files uploaded", None
252
+
253
+ if not load_model():
254
+ return "❌ Failed to load model. Check logs.", None
255
 
256
  results = []
257
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
258
  prompt_text = template.format(Question=PROMPT)
259
 
260
  for i, file in enumerate(files):
261
+ filename = "unknown"
262
  try:
263
  if hasattr(file, 'name'):
264
  image = Image.open(file.name)
 
267
  image = Image.open(file)
268
  filename = f"image_{i+1}.jpg"
269
 
270
+ print(f"Processing {i+1}/{len(files)}: {filename}")
271
+
272
  image_uri = image_to_data_uri(image)
273
 
274
  messages = [
 
299
  "raw_output": generated_text
300
  })
301
 
302
+ print(f" Score: {score}")
303
+ progress((i + 1) / len(files), desc=f"Processed {i+1}/{len(files)}: {filename}")
304
 
305
  except Exception as e:
306
+ print(f" Error: {e}")
307
  results.append({
308
+ "filename": filename,
309
  "score": "ERROR",
310
  "thinking": "",
311
  "raw_output": str(e)
312
  })
313
 
314
+ # Create output files
315
+ print("Creating output files...")
316
+ try:
317
+ with tempfile.TemporaryDirectory() as tmpdir:
318
+ txt_file = os.path.join(tmpdir, "leaderboard_scores.txt")
319
+ with open(txt_file, "w") as f:
320
+ for r in results:
321
+ score_str = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
322
+ f.write(f"{r['filename']}\t{score_str}\n")
323
+
324
+ json_file = os.path.join(tmpdir, "full_results.json")
325
+ with open(json_file, "w") as f:
326
+ json.dump(results, f, indent=2, ensure_ascii=False)
327
+
328
+ csv_file = os.path.join(tmpdir, "scores.csv")
329
+ with open(csv_file, "w") as f:
330
+ f.write("filename,score\n")
331
+ for r in results:
332
+ score_str = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
333
+ f.write(f"{r['filename']},{score_str}\n")
334
+
335
+ zip_path = os.path.join(tmpdir, "results.zip")
336
+ with zipfile.ZipFile(zip_path, 'w') as zipf:
337
+ zipf.write(txt_file, "leaderboard_scores.txt")
338
+ zipf.write(json_file, "full_results.json")
339
+ zipf.write(csv_file, "scores.csv")
340
+
341
+ final_zip = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
342
+ with open(zip_path, 'rb') as f:
343
+ final_zip.write(f.read())
344
+ final_zip.close()
345
+
346
+ print(f"Results saved to {final_zip.name}")
347
+ except Exception as e:
348
+ print(f"Error saving results: {e}")
349
+ traceback.print_exc()
350
+ return f"❌ Error saving results: {e}", None
351
 
352
  # Summary
353
  valid_scores = [r['score'] for r in results if isinstance(r['score'], float)]
354
+ avg = sum(valid_scores) / len(valid_scores) if valid_scores else 0
355
 
356
+ summary = f"""## ✅ Batch Processing Complete!
357
 
358
  **Processed:** {len(results)} images
359
+ **Successful:** {len(valid_scores)}
360
  **Failed:** {len(results) - len(valid_scores)}
361
 
362
+ ### Statistics:
363
+ - **Average Score:** {avg:.2f}
364
+ - **Min Score:** {min(valid_scores):.2f if valid_scores else 'N/A'}
365
+ - **Max Score:** {max(valid_scores):.2f if valid_scores else 'N/A'}
366
 
367
+ ### Preview (first 10):
368
+ | Filename | Score |
369
+ |----------|-------|
370
+ """ + "\n".join([f"| {r['filename'][:40]} | {r['score']:.2f if isinstance(r['score'], float) else r['score']} |" for r in results[:10]])
371
 
372
  return summary, final_zip.name
373
 
374
 
375
+ # Gradio Interface
376
+ print("Creating Gradio interface...")
377
+
378
+ with gr.Blocks(title="VisualQuality-R1 GGUF") as demo:
379
  gr.Markdown("""
380
  # 🎨 VisualQuality-R1 (GGUF/CPU)
381
 
382
+ **Image Quality Assessment** | CPU Mode (~30-60 sec per image)
383
 
384
  [![Paper](https://img.shields.io/badge/arXiv-Paper-red)](https://arxiv.org/abs/2505.14460)
385
+ [![Model](https://img.shields.io/badge/🤗-Model-yellow)](https://huggingface.co/TianheWu/VisualQuality-R1-7B)
386
  """)
387
 
388
  with gr.Tabs():
389
  with gr.TabItem("📷 Single Image"):
390
  with gr.Row():
391
  with gr.Column():
392
+ img_input = gr.Image(label="📷 Upload Image", type="pil", height=350)
393
+ thinking_cb = gr.Checkbox(label="🧠 Enable Thinking Mode", value=True)
394
+ analyze_btn = gr.Button("🔍 Analyze Quality", variant="primary", size="lg")
395
 
396
  with gr.Column():
397
+ score_out = gr.Markdown(value="*Upload an image to see the score*")
398
+ thinking_out = gr.Textbox(label="🧠 Thinking Process", lines=6, interactive=False)
399
+ raw_out = gr.Textbox(label="📝 Full Output", lines=8, interactive=False)
400
 
401
+ analyze_btn.click(
402
+ score_single_image,
403
+ inputs=[img_input, thinking_cb],
404
+ outputs=[raw_out, thinking_out, score_out]
405
+ )
406
 
407
+ with gr.TabItem("📁 Batch Processing"):
408
+ gr.Markdown("""
409
+ ### Batch Processing for Leaderboard
410
+ Upload multiple images. Results in TXT, CSV, JSON formats.
411
+
412
+ ⚠️ ~30-60 seconds per image on CPU
413
+ """)
414
 
415
  with gr.Row():
416
  with gr.Column():
417
+ batch_files = gr.File(
418
+ label="📁 Upload Images",
419
+ file_count="multiple",
420
+ file_types=["image"]
421
+ )
422
+ batch_thinking = gr.Checkbox(
423
+ label="🧠 Enable Thinking (slower)",
424
+ value=False
425
+ )
426
  batch_btn = gr.Button("🚀 Process All", variant="primary", size="lg")
427
 
428
  with gr.Column():
429
+ batch_summary = gr.Markdown(value="*Upload images and click Process*")
430
+ batch_download = gr.File(label="📥 Download Results (ZIP)")
431
 
432
+ batch_btn.click(
433
+ process_batch,
434
+ inputs=[batch_files, batch_thinking],
435
+ outputs=[batch_summary, batch_download]
436
+ )
437
+
438
+ gr.Markdown("""
439
+ ---
440
+ | Score | Quality |
441
+ |-------|---------|
442
+ | 1.0 | Very poor |
443
+ | 2.0 | Poor |
444
+ | 3.0 | Fair |
445
+ | 4.0 | Good |
446
+ | 5.0 | Excellent |
447
+ """)
448
+
449
+ print("Starting server...")
450
 
451
  if __name__ == "__main__":
452
  demo.queue(max_size=5)