Spaces:
Runtime error
Runtime error
File size: 5,874 Bytes
4dffb27 0e8b849 4dffb27 9ecde69 8c4a20f 95c55af 9ecde69 8c4a20f 744219a 95c55af 4dffb27 8c4a20f 4dffb27 744219a 8c4a20f 0e8b849 8c4a20f 744219a 9ecde69 744219a 4dffb27 8c4a20f 4dffb27 9ecde69 4dffb27 8c4a20f 0e8b849 8c4a20f 0e8b849 744219a 0e8b849 9ecde69 4dffb27 8c4a20f 744219a 8c4a20f 744219a 4dffb27 744219a 4dffb27 744219a 4dffb27 744219a 8c4a20f 744219a 8c4a20f c8b8e71 4dffb27 8c4a20f 744219a 0e8b849 744219a 8649d54 8c4a20f 0e8b849 8c4a20f 0e8b849 c8b8e71 9ecde69 4dffb27 8c4a20f 0e8b849 9ecde69 8649d54 8c4a20f 0e8b849 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
import os, io, json, traceback
import gradio as gr
import fitz # PyMuPDF
from PIL import Image
import spaces # مطلوب ل ZeroGPU
from huggingface_hub import hf_hub_download
# ===== إعدادات النموذج =====
HOROOF_MODEL_NAME = os.environ.get("HOROOF_MODEL", "NaserNajeh/Horoof")
# تحميل كسول لتقليل زمن الإقلاع
_model = None
_tokenizer = None
_image_processor = None
def _load_clean_config(repo_id: str):
"""تحميل config.json وإزالة أي quantization_config لتفادي bitsandbytes."""
from transformers import AutoConfig
try:
cfg_path = hf_hub_download(repo_id=repo_id, filename="config.json")
with open(cfg_path, "r", encoding="utf-8") as f:
cfg_json = json.load(f)
# أزل أي مفاتيح قد تفرض bnb
cfg_json.pop("quantization_config", None)
cfg_json.pop("load_in_4bit", None)
cfg_json.pop("load_in_8bit", None)
return AutoConfig.from_dict(cfg_json)
except Exception:
# احتياطي: لو فشل، خذ الإعدادات الافتراضية
return AutoConfig.from_pretrained(repo_id)
def load_horoof():
"""تحميل نموذج Horoof (Qwen2-VL) على الـGPU عند أول استدعاء فقط، بدون bitsandbytes/torchvision."""
global _model, _tokenizer, _image_processor
if _model is not None:
return
try:
import torch
from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoImageProcessor
if not torch.cuda.is_available():
raise AssertionError("هذه النسخة تتطلب GPU (CUDA) مفعّل على الـSpace.")
# إعدادات خفيفة: نمنع أي كمّية كي لا يُستدعى bitsandbytes
cfg = _load_clean_config(HOROOF_MODEL_NAME)
_tokenizer = AutoTokenizer.from_pretrained(HOROOF_MODEL_NAME, trust_remote_code=False)
_image_processor = AutoImageProcessor.from_pretrained(HOROOF_MODEL_NAME, trust_remote_code=False)
_model = Qwen2VLForConditionalGeneration.from_pretrained(
HOROOF_MODEL_NAME,
config=cfg,
torch_dtype=torch.float16, # fp16 على الـGPU
).to("cuda")
except Exception as e:
raise RuntimeError(f"تعذّر تحميل النموذج: {e}")
def pdf_to_images(pdf_bytes: bytes, dpi: int = 220, max_pages: int = 0):
"""تحويل PDF إلى قائمة صور PIL."""
pages_imgs = []
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
total = doc.page_count
n_pages = total if (not max_pages or max_pages <= 0) else min(max_pages, total)
for i in range(n_pages):
page = doc.load_page(i)
pix = page.get_pixmap(dpi=dpi, alpha=False)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
pages_imgs.append((i + 1, img))
doc.close()
return pages_imgs
def ocr_page_with_horoof(pil_img: Image.Image, max_new_tokens: int = 1200) -> str:
"""تشغيل Horoof على صورة صفحة واحدة (بدون quantization)."""
load_horoof()
import torch
# رسالة محادثة متوافقة مع Qwen2-VL
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": pil_img},
{"type": "text", "text": "اقرأ النص العربي في الصورة كما هو دون أي تعديل أو تفسير."},
],
}
]
# قالب المحادثة كنص (بدون تقطيع) ثم نقاطّع
prompt = _tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
# مدخلات الصورة + النص
vision_inputs = _image_processor(images=pil_img, return_tensors="pt")
text_inputs = _tokenizer([prompt], return_tensors="pt")
inputs = {**vision_inputs, **text_inputs}
inputs = {k: (v.to("cuda") if hasattr(v, "to") else v) for k, v in inputs.items()}
# توليد
output_ids = _model.generate(**inputs, max_new_tokens=max_new_tokens)
text = _tokenizer.batch_decode(output_ids, skip_special_tokens=True)[0]
return (text or "").strip()
@spaces.GPU # ضروري ل ZeroGPU: يجعل الاستدعاء يحجز GPU
def ocr_pdf(pdf_file, dpi, limit_pages):
"""الدالة الرئيسة التي يستدعيها Gradio."""
if pdf_file is None:
return "لم يتم رفع ملف."
try:
pdf_bytes = pdf_file.read() if hasattr(pdf_file, "read") else pdf_file
limit = int(limit_pages) if limit_pages else 1 # صفحة واحدة افتراضًا للاختبار
pages = pdf_to_images(pdf_bytes, dpi=int(dpi), max_pages=limit)
if not pages:
return "لا توجد صفحات."
out = []
for idx, img in pages:
txt = ocr_page_with_horoof(img)
out.append(f"--- صفحة {idx} ---\n{txt}")
return "\n\n".join(out)
except AssertionError as ae:
return f"⚠️ {ae}"
except Exception as e:
traceback.print_exc()
return f"حدث خطأ: {repr(e)}"
with gr.Blocks(title="Horoof OCR (ZeroGPU)") as demo:
gr.Markdown("### Horoof OCR على ZeroGPU (Qwen2-VL) — بدون bitsandbytes/torchvision.")
pdf_in = gr.File(label="ارفع ملف PDF", file_types=[".pdf"], type="binary")
dpi = gr.Slider(150, 300, value=220, step=10, label="دقة التحويل (DPI)")
limit_pages = gr.Number(value=1, precision=0, label="عدد الصفحات (للاختبار؛ زِد لاحقًا)")
run_btn = gr.Button("بدء التحويل")
out = gr.Textbox(label="النص المستخرج", lines=24)
demo.queue()
run_btn.click(fn=ocr_pdf, inputs=[pdf_in, dpi, limit_pages], outputs=out, api_name="ocr_pdf")
if __name__ == "__main__":
demo.launch()
|