NaserNajeh commited on
Commit
744219a
·
verified ·
1 Parent(s): 32d960b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -24
app.py CHANGED
@@ -3,26 +3,29 @@ import gradio as gr
3
  import fitz # PyMuPDF
4
  from PIL import Image
5
 
6
- import spaces # ضروري لـ ZeroGPU
7
 
8
  # ===== إعدادات النموذج =====
9
  HOROOF_MODEL_NAME = os.environ.get("HOROOF_MODEL", "NaserNajeh/Horoof")
10
 
11
  # تحميل كسول لتقليل زمن الإقلاع
12
  _model = None
13
- _processor = None
 
14
 
15
  def load_horoof():
16
- """تحميل نموذج Horoof (Qwen2-VL) على الـGPU عند أول استدعاء فقط."""
17
- global _model, _processor
18
  if _model is not None:
19
  return
20
  try:
21
  import torch
22
- from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
23
  if not torch.cuda.is_available():
24
  raise AssertionError("هذه النسخة تتطلب GPU (CUDA) مفعّل على الـSpace.")
25
- _processor = AutoProcessor.from_pretrained(HOROOF_MODEL_NAME)
 
 
26
  _model = Qwen2VLForConditionalGeneration.from_pretrained(
27
  HOROOF_MODEL_NAME, torch_dtype="auto"
28
  ).to("cuda")
@@ -34,7 +37,7 @@ def pdf_to_images(pdf_bytes: bytes, dpi: int = 220, max_pages: int = 0):
34
  pages_imgs = []
35
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
36
  total = doc.page_count
37
- n_pages = total if (max_pages in [0, None]) else min(max_pages, total)
38
  for i in range(n_pages):
39
  page = doc.load_page(i)
40
  pix = page.get_pixmap(dpi=dpi, alpha=False)
@@ -44,9 +47,11 @@ def pdf_to_images(pdf_bytes: bytes, dpi: int = 220, max_pages: int = 0):
44
  return pages_imgs
45
 
46
  def ocr_page_with_horoof(pil_img: Image.Image, max_new_tokens: int = 1200) -> str:
47
- """تشغيل Horoof على صورة صفحة واحدة."""
48
  load_horoof()
49
- # نبني رسالة محادثة متوافقة مع Qwen2-VL
 
 
50
  messages = [
51
  {
52
  "role": "user",
@@ -56,26 +61,32 @@ def ocr_page_with_horoof(pil_img: Image.Image, max_new_tokens: int = 1200) -> st
56
  ],
57
  }
58
  ]
59
- # تحويل الرسالة إلى قالب محادثة داخلي
60
- prompt = _processor.apply_chat_template(messages, add_generation_prompt=True)
61
- # تجهيز المدخلات (نمرر النص والصورة معًا)
62
- inputs = _processor(text=[prompt], images=[pil_img], return_tensors="pt").to("cuda")
63
- # التوليد
 
 
 
 
 
 
 
 
 
64
  output_ids = _model.generate(**inputs, max_new_tokens=max_new_tokens)
65
- text = _processor.batch_decode(output_ids, skip_special_tokens=True)[0]
66
  return (text or "").strip()
67
 
68
- @spaces.GPU # مهم ل ZeroGPU: هذا يجعل الدالة تُخصص GPU عند الاستدعاء
69
  def ocr_pdf(pdf_file, dpi, limit_pages):
70
- """الدالة الرئيسة التي يستدعيها Gradio (مزيّنة لـ ZeroGPU)."""
71
  if pdf_file is None:
72
  return "لم يتم رفع ملف."
73
  try:
74
  pdf_bytes = pdf_file.read() if hasattr(pdf_file, "read") else pdf_file
75
- # افتراضيًا: صفحة واحدة للاختبار إذا limit_pages == 0
76
- limit = int(limit_pages)
77
- if limit == 0:
78
- limit = 1
79
  pages = pdf_to_images(pdf_bytes, dpi=int(dpi), max_pages=limit)
80
  if not pages:
81
  return "لا توجد صفحات."
@@ -91,16 +102,15 @@ def ocr_pdf(pdf_file, dpi, limit_pages):
91
  return f"حدث خطأ: {repr(e)}"
92
 
93
  with gr.Blocks(title="Horoof OCR (ZeroGPU)") as demo:
94
- gr.Markdown("### Horoof OCR على ZeroGPU (Qwen2-VL).")
95
  pdf_in = gr.File(label="ارفع ملف PDF", file_types=[".pdf"], type="binary")
96
  dpi = gr.Slider(150, 300, value=220, step=10, label="دقة التحويل (DPI)")
97
  limit_pages = gr.Number(value=1, precision=0, label="عدد الصفحات (للاختبار؛ زِد لاحقًا)")
98
  run_btn = gr.Button("بدء التحويل")
99
  out = gr.Textbox(label="النص المستخرج", lines=24)
100
 
101
- # ملاحظة: لا نمرر وسطاء لـ queue() لتجنّب أخطاء التوافق
102
  demo.queue()
103
-
104
  run_btn.click(fn=ocr_pdf, inputs=[pdf_in, dpi, limit_pages], outputs=out, api_name="ocr_pdf")
105
 
106
  if __name__ == "__main__":
 
3
  import fitz # PyMuPDF
4
  from PIL import Image
5
 
6
+ import spaces # لازم ل ZeroGPU
7
 
8
  # ===== إعدادات النموذج =====
9
  HOROOF_MODEL_NAME = os.environ.get("HOROOF_MODEL", "NaserNajeh/Horoof")
10
 
11
  # تحميل كسول لتقليل زمن الإقلاع
12
  _model = None
13
+ _tokenizer = None
14
+ _image_processor = None
15
 
16
  def load_horoof():
17
+ """تحميل نموذج Horoof (Qwen2-VL) على الـGPU عند أول استدعاء فقط، بدون torchvision."""
18
+ global _model, _tokenizer, _image_processor
19
  if _model is not None:
20
  return
21
  try:
22
  import torch
23
+ from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoImageProcessor
24
  if not torch.cuda.is_available():
25
  raise AssertionError("هذه النسخة تتطلب GPU (CUDA) مفعّل على الـSpace.")
26
+
27
+ _tokenizer = AutoTokenizer.from_pretrained(HOROOF_MODEL_NAME)
28
+ _image_processor = AutoImageProcessor.from_pretrained(HOROOF_MODEL_NAME)
29
  _model = Qwen2VLForConditionalGeneration.from_pretrained(
30
  HOROOF_MODEL_NAME, torch_dtype="auto"
31
  ).to("cuda")
 
37
  pages_imgs = []
38
  doc = fitz.open(stream=pdf_bytes, filetype="pdf")
39
  total = doc.page_count
40
+ n_pages = total if (not max_pages or max_pages <= 0) else min(max_pages, total)
41
  for i in range(n_pages):
42
  page = doc.load_page(i)
43
  pix = page.get_pixmap(dpi=dpi, alpha=False)
 
47
  return pages_imgs
48
 
49
  def ocr_page_with_horoof(pil_img: Image.Image, max_new_tokens: int = 1200) -> str:
50
+ """تشغيل Horoof على صورة صفحة واحدة (بدون torchvision)."""
51
  load_horoof()
52
+ import torch
53
+
54
+ # رسالة محادثة متوافقة مع Qwen2-VL
55
  messages = [
56
  {
57
  "role": "user",
 
61
  ],
62
  }
63
  ]
64
+
65
+ # نبني نص المحادثة عبر tokenizer (بدون تقطيع) ثم نقاطّع لاحقًا
66
+ prompt = _tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
67
+
68
+ # مدخلات الصورة
69
+ vision_inputs = _image_processor(images=pil_img, return_tensors="pt")
70
+ # مدخلات النص
71
+ text_inputs = _tokenizer([prompt], return_tensors="pt")
72
+
73
+ # دمج المدخلات وإرسالها إلى الـGPU
74
+ inputs = {**vision_inputs, **text_inputs}
75
+ inputs = {k: v.to("cuda") if isinstance(v, torch.Tensor) else v for k, v in inputs.items()}
76
+
77
+ # توليد
78
  output_ids = _model.generate(**inputs, max_new_tokens=max_new_tokens)
79
+ text = _tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0]
80
  return (text or "").strip()
81
 
82
+ @spaces.GPU # مهم ل ZeroGPU: يجعل الاستدعاء يحجز GPU
83
  def ocr_pdf(pdf_file, dpi, limit_pages):
84
+ """الدالة الرئيسة التي يستدعيها Gradio."""
85
  if pdf_file is None:
86
  return "لم يتم رفع ملف."
87
  try:
88
  pdf_bytes = pdf_file.read() if hasattr(pdf_file, "read") else pdf_file
89
+ limit = int(limit_pages) if limit_pages else 1 # صفحة واحدة افتراضًا للاختبار
 
 
 
90
  pages = pdf_to_images(pdf_bytes, dpi=int(dpi), max_pages=limit)
91
  if not pages:
92
  return "لا توجد صفحات."
 
102
  return f"حدث خطأ: {repr(e)}"
103
 
104
  with gr.Blocks(title="Horoof OCR (ZeroGPU)") as demo:
105
+ gr.Markdown("### Horoof OCR على ZeroGPU (Qwen2-VL) — بدون torchvision.")
106
  pdf_in = gr.File(label="ارفع ملف PDF", file_types=[".pdf"], type="binary")
107
  dpi = gr.Slider(150, 300, value=220, step=10, label="دقة التحويل (DPI)")
108
  limit_pages = gr.Number(value=1, precision=0, label="عدد الصفحات (للاختبار؛ زِد لاحقًا)")
109
  run_btn = gr.Button("بدء التحويل")
110
  out = gr.Textbox(label="النص المستخرج", lines=24)
111
 
112
+ # Queue الافتراضي كافٍ؛ لا نمرر باراميترات تجنّبًا لأخطاء التوافق
113
  demo.queue()
 
114
  run_btn.click(fn=ocr_pdf, inputs=[pdf_in, dpi, limit_pages], outputs=out, api_name="ocr_pdf")
115
 
116
  if __name__ == "__main__":