F-allahmoradi commited on
Commit
3f6b428
·
verified ·
1 Parent(s): b189993

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +223 -0
app.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==============================
2
+ # 📦 وارد کردن کتابخانه‌ها
3
+ # ==============================
4
+ import json
5
+ import re
6
+ import os
7
+ import tempfile
8
+ import torch
9
+ import pytesseract
10
+ from pdf2image import convert_from_path
11
+ from PIL import Image
12
+ from transformers import AutoTokenizer, AutoModelForMaskedLM
13
+ from groq import Groq
14
+ import gradio as gr
15
+
16
+ # ==============================
17
+ # 🌐 تنظیمات جهانی
18
+ # ==============================
19
+ ZWNJ = '\u200c'
20
+ MLM_NAME = "bert-base-multilingual-cased"
21
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
22
+
23
+ GROQ_API_KEYS = [
24
+ "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",
25
+ "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",
26
+ "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"
27
+ ]
28
+
29
+ print("⏳ بارگذاری BERT برای اصلاح املایی...")
30
+ tok = AutoTokenizer.from_pretrained(MLM_NAME)
31
+ mlm = AutoModelForMaskedLM.from_pretrained(MLM_NAME).to(DEVICE)
32
+
33
+ # ==============================
34
+ # 🧠 توابع پردازش متن (همان کد شما)
35
+ # ==============================
36
+ def is_persian(text: str) -> bool:
37
+ if not text: return True
38
+ fa_cnt = sum(1 for ch in text if '\u0600' <= ch <= '\u06FF')
39
+ return (fa_cnt / max(len(text), 1)) > 0.5
40
+
41
+ def fix_persian(text: str) -> str:
42
+ n = text
43
+ n = n.replace('ك', 'ک').replace('ي', 'ی').replace('ئ', 'ی')
44
+ n = n.replace('أ', 'ا').replace('ة', 'ه')
45
+ n = re.sub(r'\(0', '', n)
46
+ n = n.replace('پسرده', 'پرده')
47
+ n = re.sub(r'\bاینن\b', 'این', n)
48
+ n = re.sub(r'\bخلسوت\b', 'خلوت', n)
49
+ n = re.sub(r'نضورد', 'نخورد', n)
50
+ n = re.sub(r'سبصد', 'سیصد', n)
51
+ n = re.sub(r'صایون', 'صابون', n)
52
+ n = re.sub(r' +', ' ', n).strip()
53
+ n = re.sub(r'(می) (باشد|کند|شود|روم)', r'\1' + ZWNJ + r'\2', n)
54
+ return n
55
+
56
+ def mlm_correct(word: str) -> str:
57
+ if not word.isalpha(): return word
58
+ text = f"[MASK] {word[1:]}" if len(word) > 1 else "[MASK]"
59
+ inputs = tok(text, return_tensors="pt").to(DEVICE)
60
+ with torch.no_grad():
61
+ logits = mlm(**inputs).logits
62
+ mask_id = torch.where(inputs.input_ids[0] == tok.mask_token_id)[0]
63
+ if mask_id.numel() == 0: return word
64
+ probs = logits[0, mask_id].softmax(dim=-1)
65
+ best_id = probs.argmax().item()
66
+ best_token = tok.decode(best_id).strip()
67
+ if best_token.isalpha() and len(best_token) >= 2:
68
+ return best_token + word[1:] if len(word) > 1 else best_token
69
+ return word
70
+
71
+ def fix_multilingual(text: str) -> str:
72
+ tokens = re.findall(r'\b\w+\b|[^\w\s]', text, flags=re.UNICODE)
73
+ fixed = [mlm_correct(t) if t.isalpha() else t for t in tokens]
74
+ return re.sub(r'\s+', ' ', ' '.join(fixed)).strip()
75
+
76
+ def smart_correct(text: str) -> str:
77
+ return fix_persian(text) if is_persian(text) else fix_multilingual(text)
78
+
79
+ # ==============================
80
+ # 📄 OCR (7 صفحه، DPI=250)
81
+ # ==============================
82
+ def ocr_with_enhancement(input_path, max_pages=7):
83
+ extracted = ""
84
+ if input_path.lower().endswith('.pdf'):
85
+ images = convert_from_path(input_path, first_page=1, last_page=max_pages, dpi=250)
86
+ for i, img in enumerate(images, 1):
87
+ img = img.convert("L")
88
+ raw = pytesseract.image_to_string(img, lang='fas+eng')
89
+ corrected = smart_correct(raw)
90
+ extracted += f"\n--- صفحه {i} ---\n{corrected}"
91
+ else:
92
+ img = Image.open(input_path).convert("L")
93
+ raw = pytesseract.image_to_string(img, lang='fas+eng')
94
+ extracted = smart_correct(raw)
95
+ return extracted.strip()
96
+
97
+ # ==============================
98
+ # 🤖 استخراج اطلاعات کتاب با چرخش کلیدهای Groq
99
+ # ==============================
100
+ def extract_book_info(extracted_text):
101
+ system_prompt = """شما یک متخصص استخراج اطلاعات کتاب هستید. از متن زیر، اطلاعات زیر را استخراج کرده و **فقط یک JSON معتبر** برگردانید:
102
+
103
+ {
104
+ "title": "عنوان کتاب",
105
+ "author": "نام نویسنده",
106
+ "translator": "نام مترجم",
107
+ "publisher": "ناشر",
108
+ "edition": "نوبت چاپ",
109
+ "subject": "موضوع کتاب",
110
+ }
111
+
112
+ اگر فیلدی یافت نشد، مقدار آن را null قرار دهید.
113
+ هیچ توضیحی جز JSON ندهید."""
114
+
115
+ for key in GROQ_API_KEYS:
116
+ try:
117
+ client = Groq(api_key=key)
118
+ resp = client.chat.completions.create(
119
+ messages=[{"role": "system", "content": system_prompt},
120
+ {"role": "user", "content": f"متن استخراج‌شده:\n\n{extracted_text}"}],
121
+ model="llama-3.1-8b-instant",
122
+ temperature=0.1,
123
+ max_tokens=1000,
124
+ timeout=30
125
+ )
126
+ raw = resp.choices[0].message.content.strip()
127
+ json_match = re.search(r'\{.*\}', raw, re.DOTALL)
128
+ if json_match:
129
+ return json.loads(json_match.group())
130
+ except:
131
+ continue
132
+ return {"error": "همه کلیدهای Groq شکست خوردند."}
133
+
134
+ # ==============================
135
+ # 🎛 تابع اصلی برای Gradio
136
+ # ==============================
137
+ def process_file(file):
138
+ if not file:
139
+ return "❌ فایلی انتخاب نشده است.", None
140
+
141
+ full_text = ocr_with_enhancement(file.name, max_pages=7)
142
+ if not full_text:
143
+ return "❌ متنی استخراج نشد.", None
144
+
145
+ book_info = extract_book_info(full_text)
146
+ book_info["extracted_text_sample"] = full_text[:1000]
147
+
148
+ output_text = "📚 **اطلاعات استخراج‌شده:**\n\n"
149
+ fields = {
150
+ "title": "📖 عنوان",
151
+ "author": "✍️ نویسنده",
152
+ "translator": "🔤 مترجم",
153
+ "publisher": "🏢 ناشر",
154
+ "edition": "🔄 نوبت چاپ",
155
+ "subject": "🏷️ موضوع",
156
+ }
157
+ for k, label in fields.items():
158
+ v = book_info.get(k)
159
+ output_text += f"{label}: {'✅ ' + str(v) if v else '❌ یافت نشد'}\n"
160
+
161
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f:
162
+ json.dump(book_info, f, ensure_ascii=False, indent=2)
163
+ json_path = f.name
164
+ book_info.pop("isbn", None)
165
+ return output_text, json_path
166
+
167
+ # ==============================
168
+ # 🖥 رابط Gradio با تم هکری و راست‌چین کامل
169
+ # ==============================
170
+ with gr.Blocks(css="""
171
+ body {
172
+ background-color: #000 !important;
173
+ color#32CD32 !important;
174
+ direction: rtl !important;
175
+ text-align: right !important;
176
+ font-family: 'Courier New', monospace;
177
+ }
178
+ .gr-button {
179
+ background: #0a0 !important;
180
+ color: #000 !important;
181
+ border: 1px solid #32CD32 !important;
182
+ font-family: 'Courier New', monospace;
183
+ }
184
+ .gr-input, .gr-output, .gr-file {
185
+ background: #111 !important;
186
+ color:#32CD32 !important;
187
+ border: 1px solid #0a0 !important;
188
+ direction: rtl !important;
189
+ text-align: right !important;
190
+ font-family: 'Courier New', monospace;
191
+ }
192
+ .output-markdown h1 {
193
+ text-align: center !important;
194
+ direction: ltr !important;
195
+ color:#32CD32 !important;
196
+ font-family: 'Courier New', monospace;
197
+ }
198
+ .gr-textbox textarea {
199
+ text-align: right !important;
200
+ direction: rtl !important;
201
+ }
202
+ label {
203
+ color:#32CD32 !important;
204
+ font-family: 'Courier New', monospace;
205
+ }
206
+ """) as demo:
207
+ gr.Markdown("# استخراج اطلاعات کتاب ")
208
+ with gr.Row():
209
+ file_input = gr.File(label="آپلود فایل", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
210
+ json_output = gr.File(label="دانلود نتیجه (JSON)")
211
+ text_output = gr.Textbox(
212
+ label="نتایج",
213
+ lines=15,
214
+ interactive=False,
215
+ elem_classes=["gr-textbox"]
216
+ )
217
+ btn = gr.Button("پردازش", variant="primary")
218
+ btn.click(fn=process_file, inputs=file_input, outputs=[text_output, json_output])
219
+
220
+ # ==============================
221
+ # ▶️ اجرا
222
+ # ==============================
223
+ demo.launch(debug=True)