F-allahmoradi commited on
Commit
52268f5
·
verified ·
1 Parent(s): 94479bd

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -0
app.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import tempfile
4
+ import streamlit as st
5
+ from PIL import Image
6
+ from pdf2image import convert_from_path, pdfinfo_from_path
7
+ import pytesseract
8
+
9
+ # ----------------------------------------------------------------------
10
+ # تنظیمات اولیه
11
+ # ----------------------------------------------------------------------
12
+ ZWNJ = '\u200c'
13
+
14
+ # ----------------------------------------------------------------------
15
+ # تابع نرمال‌سازی متن فارسی
16
+ # ----------------------------------------------------------------------
17
+ def normalize_text(text: str) -> str:
18
+ if not text:
19
+ return ""
20
+
21
+ arabic_to_persian = {
22
+ 'ك': 'ک', 'ي': 'ی', 'ئ': 'ی', 'أ': 'ا', 'ة': 'ه',
23
+ 'ؤ': 'و', 'إ': 'ا', 'ٰ': '', 'ٔ': '', 'ّ': ''
24
+ }
25
+ for ar, pr in arabic_to_persian.items():
26
+ text = text.replace(ar, pr)
27
+
28
+ corrections = {
29
+ 'پسرده': 'پرده', 'اینن': 'این', 'خلسوت': 'خلوت',
30
+ 'نضورد': 'نخورد', 'سبصد': 'سیصد', 'صایون': 'صابون',
31
+ 'وشد': 'شد', 'میکنند': 'می‌کنند', 'میشود': 'می‌شود',
32
+ 'میکرد': 'می‌کرد', 'میکنم': 'می‌کنم', 'میکنی': 'می‌کنی',
33
+ 'میکسرد': 'می‌کرد', 'میکسند': 'می‌کنند', 'اِقتصاد': 'اقتصاد',
34
+ 'اِجتماع': 'اجتماع', 'اِنسان': 'انسان', 'اِمکان': 'امکان'
35
+ }
36
+ for wrong, correct in corrections.items():
37
+ text = text.replace(wrong, correct)
38
+
39
+ # حذف کلمات غیرمعنی‌دار یا عدد/حرف لاتین وسط متن
40
+ text = re.sub(r'\b[0-9a-zA-Z\-]+\b', '', text)
41
+ text = re.sub(r'[^\w\s\u200c\u200d\u200e\u200f\u0600-\u06FF]', ' ', text)
42
+ text = re.sub(r'\s+', ' ', text).strip()
43
+ text = re.sub(r'(می|نمی)\s+(باشد|کند|شود|روم|کنم|کنی|کنید|کنیم|رسد|گردد|دهد)',
44
+ lambda m: m.group(1) + ZWNJ + m.group(2), text)
45
+ text = re.sub(r'(ها)\s+', r'\1' + ZWNJ + ' ', text)
46
+ text = re.sub(r'\s+([؟؟،،.:!;])', r'\1', text)
47
+ return text
48
+
49
+ # ----------------------------------------------------------------------
50
+ # حذف نویز و اعداد وسط متن
51
+ # ----------------------------------------------------------------------
52
+ def remove_noise(text: str) -> str:
53
+ # حذف اعداد فارسی و انگلیسی وسط متن
54
+ text = re.sub(r'\b[0-9۰-۹]+\b', '', text)
55
+ text = re.sub(r'\s+', ' ', text).strip()
56
+ return text
57
+
58
+ # ----------------------------------------------------------------------
59
+ # فرمت شبیه کتاب (حذف شکست خط وسط جمله)
60
+ # ----------------------------------------------------------------------
61
+ def format_as_book(text: str) -> str:
62
+ text = re.sub(r'\n+', '\n', text) # چند خط پشت سر هم → ۱ خط
63
+ text = re.sub(r'(?<![.؟!])\n\s*', ' ', text) # خط وسط جمله → فاصله
64
+ text = re.sub(r'\s+', ' ', text).strip() # حذف فاصله اضافی
65
+ text = re.sub(r'([.؟!])\s+', r'\1\n', text) # بعد از پایان جمله، خط جدید
66
+ return text
67
+
68
+ # ----------------------------------------------------------------------
69
+ # OCR از تصویر
70
+ # ----------------------------------------------------------------------
71
+ def ocr_from_image(image: Image.Image) -> str:
72
+ image = image.convert('L')
73
+ config = r'--oem 3 --psm 6 -c preserve_interword_spaces=1'
74
+ raw = pytesseract.image_to_string(image, lang='fas+eng', config=config)
75
+ text = normalize_text(raw)
76
+ text = remove_noise(text)
77
+ text = format_as_book(text)
78
+ return text
79
+
80
+ # ----------------------------------------------------------------------
81
+ # OCR از PDF
82
+ # ----------------------------------------------------------------------
83
+ def ocr_from_pdf(pdf_path: str, start_page: int, end_page: int) -> str:
84
+ try:
85
+ info = pdfinfo_from_path(pdf_path)
86
+ total_pages = int(info["Pages"])
87
+ end_page = min(end_page, total_pages)
88
+ start_page = max(1, start_page)
89
+
90
+ if start_page > total_pages:
91
+ return "❌ شماره صفحه شروع از تعداد کل صفحات بیشتر است."
92
+
93
+ images = convert_from_path(pdf_path, dpi=300, first_page=start_page, last_page=end_page)
94
+
95
+ all_text = f"📊 استخراج صفحات {start_page} تا {end_page} از {total_pages} صفحه:\n\n"
96
+ for i, img in enumerate(images):
97
+ page_num = start_page + i
98
+ text = ocr_from_image(img)
99
+ all_text += f"--- صفحه {page_num} ---\n{text}\n\n"
100
+
101
+ return all_text.strip()
102
+
103
+ except Exception as e:
104
+ return f"❌ خطا در پردازش PDF: {str(e)}"
105
+
106
+ # ----------------------------------------------------------------------
107
+ # رابط کاربری Streamlit
108
+ # ----------------------------------------------------------------------
109
+ def main():
110
+ st.set_page_config(page_title="OCR فارسی - کتابی", layout="wide")
111
+ st.markdown(
112
+ """
113
+ <style>
114
+ body {direction: rtl; text-align: right; font-family: "Vazir", "Tahoma", sans-serif;}
115
+ textarea {
116
+ direction: rtl !important;
117
+ text-align: justify !important;
118
+ font-family: "Vazir", "Tahoma", sans-serif !important;
119
+ line-height: 1.8 !important;
120
+ white-space: pre-wrap !important;
121
+ }
122
+ </style>
123
+ """, unsafe_allow_html=True
124
+ )
125
+
126
+ st.title(" OCR فارسی - خروجی شبیه کتاب")
127
+ st.markdown("استخراج متن از **PDF** یا **تصویر** بدون اعداد و نویز وسط متن")
128
+
129
+ with st.sidebar:
130
+ st.header("⚙️ تنظیمات استخراج")
131
+ uploaded_file = st.file_uploader("📁 فایل خود را آپلود کنید (PDF، JPG، PNG)", type=["pdf","jpg","jpeg","png"])
132
+ start_page, end_page = 1, 1
133
+ tmp_path = None
134
+
135
+ if uploaded_file is not None:
136
+ file_ext = uploaded_file.name.lower().split('.')[-1]
137
+ with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file_ext}") as tmp_file:
138
+ tmp_file.write(uploaded_file.getvalue())
139
+ tmp_path = tmp_file.name
140
+
141
+ if file_ext == "pdf":
142
+ try:
143
+ info = pdfinfo_from_path(tmp_path)
144
+ total_pages = int(info["Pages"])
145
+ st.success(f"📄 فایل PDF با **{total_pages} صفحه** شناسایی شد.")
146
+ extract_all = st.checkbox("✅ استخراج همه صفحات", value=True)
147
+ if not extract_all:
148
+ start_page = st.number_input("صفحه شروع", 1, total_pages, 1)
149
+ end_page = st.number_input("صفحه پایان", 1, total_pages, total_pages)
150
+ else:
151
+ start_page, end_page = 1, total_pages
152
+ except Exception as e:
153
+ st.error(f"❌ خطا در خواندن PDF: {e}")
154
+ else:
155
+ st.info("📷 فایل تصویری — تمام محتوا پردازش خواهد شد.")
156
+
157
+ # Main
158
+ if uploaded_file is not None and tmp_path:
159
+ file_ext = uploaded_file.name.lower().split('.')[-1]
160
+
161
+ if file_ext == "pdf":
162
+ if st.button("🚀 استخراج متن از PDF", use_container_width=True):
163
+ with st.spinner("در حال پردازش..."):
164
+ result = ocr_from_pdf(tmp_path, start_page, end_page)
165
+ st.markdown("### 📝 متن استخراج‌شده (فرمت کتابی)")
166
+ st.text_area("📘 خروجی OCR", result, height=600)
167
+ st.download_button("📥 دانلود متن", result, file_name="extracted_text.txt")
168
+
169
+ else:
170
+ st.image(uploaded_file, caption="تصویر آپلود شده", use_column_width=True)
171
+ if st.button("🚀 استخراج متن از تصویر", use_container_width=True):
172
+ with st.spinner("در حال پردازش..."):
173
+ image = Image.open(tmp_path)
174
+ result = ocr_from_image(image)
175
+ st.markdown("### 📝 متن استخراج‌شده (فرمت کتابی)")
176
+ st.text_area("📘 خروجی OCR", result, height=600)
177
+ st.download_button("📥 دانلود متن", result, file_name="extracted_text.txt")
178
+
179
+ if os.path.exists(tmp_path):
180
+ os.unlink(tmp_path)
181
+
182
+ else:
183
+ st.info("📁 لطفاً یک فایل آپلود کنید.")
184
+
185
+ if __name__ == "__main__":
186
+ main()