VOIDER commited on
Commit
6eab0c4
·
verified ·
1 Parent(s): c57535e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -120
app.py CHANGED
@@ -35,32 +35,33 @@ llm = None
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():
@@ -85,24 +86,25 @@ def download_models():
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(
@@ -178,14 +180,14 @@ def score_single_image(image, use_thinking=True):
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
  {
@@ -222,7 +224,7 @@ def score_single_image(image, use_thinking=True):
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 ""
@@ -230,7 +232,7 @@ def score_single_image(image, use_thinking=True):
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
 
@@ -245,13 +247,13 @@ 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
@@ -300,7 +302,7 @@ def process_batch(files, use_thinking=True, progress=gr.Progress()):
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}")
@@ -311,17 +313,16 @@ def process_batch(files, use_thinking=True, progress=gr.Progress()):
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
 
@@ -329,122 +330,73 @@ def process_batch(files, use_thinking=True, progress=gr.Progress()):
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
 
 
35
  print("Importing llama_cpp...")
36
  try:
37
  from llama_cpp import Llama
38
+ import llama_cpp
39
+ print(f"llama_cpp version: {llama_cpp.__version__ if hasattr(llama_cpp, '__version__') else 'unknown'}")
40
  except Exception as e:
41
  print(f"Error importing llama_cpp: {e}")
42
  traceback.print_exc()
43
 
44
+ # Пробуем импортировать chat handler для Qwen2-VL
45
  chat_handler_class = None
46
+ chat_handler_name = None
47
+
48
  try:
49
  from llama_cpp.llama_chat_format import Qwen2VLChatHandler
50
  chat_handler_class = Qwen2VLChatHandler
51
+ chat_handler_name = "Qwen2VLChatHandler"
52
+ print(f"✓ Found {chat_handler_name}")
53
+ except ImportError as e:
54
+ print(f"✗ Qwen2VLChatHandler not found: {e}")
55
+
56
+ # Список доступных chat handlers
57
+ if chat_handler_class is None:
58
+ print("\nListing available chat handlers...")
59
  try:
60
+ from llama_cpp import llama_chat_format
61
+ handlers = [name for name in dir(llama_chat_format) if 'Handler' in name or 'Chat' in name]
62
+ print(f"Available handlers: {handlers}")
63
+ except Exception as e:
64
+ print(f"Could not list handlers: {e}")
 
 
 
 
 
 
 
65
 
66
 
67
  def download_models():
 
86
 
87
  def load_model():
88
  """Загрузка модели"""
89
+ global llm, chat_handler_class, chat_handler_name
90
 
91
  if llm is not None:
92
  return True
93
 
94
+ if chat_handler_class is None:
95
+ print("ERROR: No suitable chat handler found for Qwen2-VL!")
96
+ print("Please ensure llama-cpp-python >= 0.3.2 is installed")
97
+ return False
98
+
99
  try:
100
  model_path, mmproj_path = download_models()
101
 
102
+ print(f"Creating {chat_handler_name}...")
103
+ chat_handler = chat_handler_class(
104
+ clip_model_path=mmproj_path,
105
+ verbose=True
106
+ )
107
+ print("Chat handler created")
 
 
 
 
108
 
109
  print("Loading LLM...")
110
  llm = Llama(
 
180
  return "❌ Upload an image first", "", ""
181
 
182
  if not load_model():
183
+ return "❌ Failed to load model. Qwen2VLChatHandler not available. Check logs.", "", ""
184
 
185
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
186
  prompt_text = template.format(Question=PROMPT)
187
 
188
+ print("Converting image...")
189
  image_uri = image_to_data_uri(image)
190
+ print(f"Image converted, URI length: {len(image_uri)}")
191
 
192
  messages = [
193
  {
 
224
 
225
  yield generated_text, thinking, score_display
226
 
227
+ print(f"Generation complete, length: {len(generated_text)}")
228
 
229
  final_score = extract_score(generated_text)
230
  final_thinking = extract_thinking(generated_text) if use_thinking else ""
 
232
  if final_score is not None:
233
  score_display = f"⭐ **Quality Score: {final_score:.2f} / 5.00**\n\n📊 **For Leaderboard:** `{final_score:.2f}`"
234
  else:
235
+ score_display = "❌ Could not extract score"
236
 
237
  yield generated_text, final_thinking, score_display
238
 
 
247
  """Batch processing"""
248
  global llm
249
 
250
+ print(f"process_batch: {len(files) if files else 0} files")
251
 
252
  if not files:
253
+ return "❌ No files", None
254
 
255
  if not load_model():
256
+ return "❌ Failed to load model", None
257
 
258
  results = []
259
  template = QUESTION_TEMPLATE_THINKING if use_thinking else QUESTION_TEMPLATE_NO_THINKING
 
302
  })
303
 
304
  print(f" Score: {score}")
305
+ progress((i + 1) / len(files), desc=f"{i+1}/{len(files)}: {filename}")
306
 
307
  except Exception as e:
308
  print(f" Error: {e}")
 
313
  "raw_output": str(e)
314
  })
315
 
316
+ # Create files
 
317
  try:
318
  with tempfile.TemporaryDirectory() as tmpdir:
319
  txt_file = os.path.join(tmpdir, "leaderboard_scores.txt")
320
  with open(txt_file, "w") as f:
321
  for r in results:
322
+ s = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
323
+ f.write(f"{r['filename']}\t{s}\n")
324
 
325
+ json_file = os.path.join(tmpdir, "results.json")
326
  with open(json_file, "w") as f:
327
  json.dump(results, f, indent=2, ensure_ascii=False)
328
 
 
330
  with open(csv_file, "w") as f:
331
  f.write("filename,score\n")
332
  for r in results:
333
+ s = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
334
+ f.write(f"{r['filename']},{s}\n")
335
 
336
  zip_path = os.path.join(tmpdir, "results.zip")
337
  with zipfile.ZipFile(zip_path, 'w') as zipf:
338
  zipf.write(txt_file, "leaderboard_scores.txt")
339
+ zipf.write(json_file, "results.json")
340
  zipf.write(csv_file, "scores.csv")
341
 
342
  final_zip = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
343
  with open(zip_path, 'rb') as f:
344
  final_zip.write(f.read())
345
  final_zip.close()
 
 
346
  except Exception as e:
347
+ return f"Error saving: {e}", None
 
 
348
 
 
349
  valid_scores = [r['score'] for r in results if isinstance(r['score'], float)]
350
  avg = sum(valid_scores) / len(valid_scores) if valid_scores else 0
351
 
352
+ summary = f"""## ✅ Done!
353
 
354
+ **Processed:** {len(results)} | **OK:** {len(valid_scores)} | **Failed:** {len(results) - len(valid_scores)}
 
 
355
 
356
+ **Avg:** {avg:.2f} | **Min:** {min(valid_scores):.2f if valid_scores else 'N/A'} | **Max:** {max(valid_scores):.2f if valid_scores else 'N/A'}
 
 
 
357
 
358
+ | File | Score |
359
+ |------|-------|
 
360
  """ + "\n".join([f"| {r['filename'][:40]} | {r['score']:.2f if isinstance(r['score'], float) else r['score']} |" for r in results[:10]])
361
 
362
  return summary, final_zip.name
363
 
364
 
365
+ # Interface
366
+ print("Creating interface...")
367
 
368
+ with gr.Blocks(title="VisualQuality-R1") as demo:
369
  gr.Markdown("""
370
  # 🎨 VisualQuality-R1 (GGUF/CPU)
371
 
372
+ **Image Quality Assessment** | ~30-60 sec/image on CPU
373
 
374
+ [![Paper](https://img.shields.io/badge/arXiv-2505.14460-red)](https://arxiv.org/abs/2505.14460)
 
375
  """)
376
 
377
  with gr.Tabs():
378
+ with gr.TabItem("📷 Single"):
379
  with gr.Row():
380
  with gr.Column():
381
+ img = gr.Image(label="Image", type="pil", height=350)
382
+ think = gr.Checkbox(label="🧠 Thinking", value=True)
383
+ btn = gr.Button("🔍 Analyze", variant="primary", size="lg")
 
384
  with gr.Column():
385
+ score = gr.Markdown("*Upload image*")
386
+ thinking = gr.Textbox(label="Thinking", lines=6)
387
+ output = gr.Textbox(label="Output", lines=8)
388
+ btn.click(score_single_image, [img, think], [output, thinking, score])
 
 
 
 
 
389
 
390
+ with gr.TabItem("📁 Batch"):
 
 
 
 
 
 
 
391
  with gr.Row():
392
  with gr.Column():
393
+ files = gr.File(label="Images", file_count="multiple", file_types=["image"])
394
+ batch_think = gr.Checkbox(label="🧠 Thinking", value=False)
395
+ batch_btn = gr.Button("🚀 Process", variant="primary", size="lg")
 
 
 
 
 
 
 
 
396
  with gr.Column():
397
+ summary = gr.Markdown("*Upload & Process*")
398
+ download = gr.File(label="📥 Results")
399
+ batch_btn.click(process_batch, [files, batch_think], [summary, download])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
  print("Starting server...")
402