YOUKKASS commited on
Commit
b1ff060
·
verified ·
1 Parent(s): ea01e9d

Update modules/ocr.py

Browse files
Files changed (1) hide show
  1. modules/ocr.py +75 -120
modules/ocr.py CHANGED
@@ -2,141 +2,96 @@ from paddleocr import PaddleOCR
2
  import cv2
3
  import numpy as np
4
  import os
5
- import uuid
6
 
7
- # تخزين محركات OCR لكل لغة لتجنّب إعادة التهيئة
8
- _OCR_ENGINES = {}
9
 
10
- def _get_engine(lang='en'):
11
- if lang not in _OCR_ENGINES:
 
 
12
  try:
13
- # استخدام الإعدادات المتوافقة مع الإصدار الحالي
14
- _OCR_ENGINES[lang] = PaddleOCR(
15
  use_angle_cls=True,
16
- lang=lang,
17
  show_log=False,
18
- det_db_thresh=0.3, # تخفيض threshold للكشف
19
- det_db_box_thresh=0.3
20
- # تم إزالة rec_thresh لأنه غير مدعوم
 
21
  )
22
- print("✅ تم تهيئة محرك PaddleOCR بنجاح")
23
  except Exception as e:
24
  print(f"❌ فشل في تهيئة PaddleOCR: {e}")
25
  raise
26
- return _OCR_ENGINES[lang]
27
-
28
- def preprocess_for_ocr(input_path, upscale=2):
29
- """
30
- يعيد مسارات لعدّة نسخ مُحسّنة من الصورة لتجربتها مع OCR.
31
- """
32
- img = cv2.imread(input_path)
33
- if img is None:
34
- return []
35
-
36
- # تكبير لزيادة وضوح الحروف
37
- h, w = img.shape[:2]
38
- img_up = cv2.resize(img, (w * upscale, h * upscale), interpolation=cv2.INTER_CUBIC)
39
-
40
- # تدرج رمادي
41
- gray = cv2.cvtColor(img_up, cv2.COLOR_BGR2GRAY)
42
-
43
- # زيادة التباين (CLAHE)
44
- clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
45
- gray = clahe.apply(gray)
46
-
47
- # إزالة ضوضاء خفيفة
48
- gray = cv2.fastNlMeansDenoising(gray, None, h=3, templateWindowSize=7, searchWindowSize=21)
49
-
50
- # تمويه خفيف + Otsu
51
- blur = cv2.GaussianBlur(gray, (3, 3), 0)
52
- _, th = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
53
-
54
- # نسخة معكوسة (في حال كان النص أبيض على خلفية داكنة)
55
- inv = cv2.bitwise_not(th)
56
-
57
- # غلق مورفولوجي لتقوية الحروف
58
- kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
59
- th = cv2.morphologyEx(th, cv2.MORPH_CLOSE, kernel, iterations=1)
60
- inv = cv2.morphologyEx(inv, cv2.MORPH_CLOSE, kernel, iterations=1)
61
-
62
- # حفظ نسخ مؤقتة
63
- out_paths = []
64
- for mat in (th, inv):
65
- temp_name = f"/tmp/ink_{uuid.uuid4().hex}.png"
66
- cv2.imwrite(temp_name, mat)
67
- out_paths.append(temp_name)
68
-
69
- return out_paths
70
 
71
- def _run_ocr(image_path, lang='en'):
72
  """
73
- يشغّل OCR ويُعيد (texts, boxes, scores)
74
  """
75
- ocr = _get_engine(lang)
76
- result = ocr.ocr(image_path, cls=True)
77
- texts, boxes, scores = [], [], []
78
-
79
- if result is not None and len(result) > 0:
80
- for line in result[0]:
81
- if line and len(line) >= 2:
82
- try:
83
- box = line[0]
 
 
 
 
 
 
 
84
  text = line[1][0]
85
- score = float(line[1][1]) if isinstance(line[1][1], (int, float)) else 0.0
86
- if text is not None and box is not None:
 
 
87
  texts.append(text)
88
- boxes.append(box)
89
- scores.append(score)
90
- except Exception as e:
91
- print(f"⚠️ خطأ في معالجة نتيجة OCR: {e}")
92
- continue
93
-
94
- return texts, boxes, scores
95
-
96
- def extract_texts(image_path: str, ocr_lang: str = 'en', preprocess: bool = True, min_score: float = 0.35):
97
- """
98
- يحاول استخراج نصوص من النسخة الأصلية والنسخ المُحسّنة ويختار أفضل نتيجة.
99
- """
100
- best = {"texts": [], "boxes": [], "scores": []}
101
-
102
- # 1) المحاولة على الصورة الأصلية
103
- t0, b0, s0 = _run_ocr(image_path, lang=ocr_lang)
104
- # فلترة حسب min_score
105
- filtered0 = [(t, b, s) for t, b, s in zip(t0, b0, s0) if s >= min_score]
106
- if filtered0:
107
- best["texts"], best["boxes"], best["scores"] = zip(*filtered0)
108
- best["texts"], best["boxes"], best["scores"] = list(best["texts"]), list(best["boxes"]), list(best["scores"])
109
- else:
110
- best["texts"], best["boxes"], best["scores"] = [], [], []
111
-
112
- print(f"📊 النتائج الأصلية: {len(best['texts'])} نصوص")
113
-
114
- # 2) المحاولات المُحسّنة
115
- if preprocess:
116
- variant_paths = preprocess_for_ocr(image_path)
117
- print(f"🔄 معالجة {len(variant_paths)} نسخة محسنة")
118
 
119
- for i, p in enumerate(variant_paths):
120
- t, b, s = _run_ocr(p, lang=ocr_lang)
121
- filtered = [(tt, bb, ss) for tt, bb, ss in zip(t, b, s) if ss >= min_score]
122
-
123
- # تنظيف الملف المؤقت
124
  try:
125
- os.remove(p)
126
  except:
127
  pass
128
-
129
- # اختر النتيجة الأفضل
130
- if filtered:
131
- current_score = np.mean([ss for _, _, ss in filtered]) if filtered else 0
132
- best_score = np.mean(best["scores"]) if best["scores"] else 0
133
-
134
- if (len(filtered) > len(best["texts"])) or (
135
- len(filtered) == len(best["texts"]) and current_score > best_score
136
- ):
137
- best["texts"], best["boxes"], best["scores"] = zip(*filtered)
138
- best["texts"], best["boxes"], best["scores"] = list(best["texts"]), list(best["boxes"]), list(best["scores"])
139
- print(f"✅ النسخة المحسنة {i+1}: وجدت {len(filtered)} نصوص بجودة أفضل")
140
-
141
- print(f"🎯 النتائج النهائية: {len(best['texts'])} نصوص")
142
- return best["texts"], best["boxes"]
 
2
  import cv2
3
  import numpy as np
4
  import os
5
+ import tempfile
6
 
7
+ # تهيئة محرك OCR مرة واحدة فقط
8
+ ocr_engine = None
9
 
10
+ def get_ocr_engine():
11
+ """الحصول على أو إنشاء محرك OCR"""
12
+ global ocr_engine
13
+ if ocr_engine is None:
14
  try:
15
+ # استخدام الإعدادات المتوافقة مع الإصدار 2.7.0
16
+ ocr_engine = PaddleOCR(
17
  use_angle_cls=True,
18
+ lang='en',
19
  show_log=False,
20
+ det_db_thresh=0.3,
21
+ det_db_box_thresh=0.3,
22
+ # rec_thresh غير مدعوم في الإصدار 2.7.0 - تمت إزالته
23
+ use_space_char=True
24
  )
25
+ print("✅ تم تهيئة محرك PaddleOCR بنجاح (الإصدار 2.7.0)")
26
  except Exception as e:
27
  print(f"❌ فشل في تهيئة PaddleOCR: {e}")
28
  raise
29
+ return ocr_engine
30
+
31
+ def preprocess_image(image_path):
32
+ """معالجة مسبقة بسيطة للصورة"""
33
+ try:
34
+ img = cv2.imread(image_path)
35
+ if img is None:
36
+ return image_path
37
+
38
+ # تحويل إلى تدرج الرمادي
39
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
40
+
41
+ # زيادة التباين البسيط
42
+ enhanced = cv2.convertScaleAbs(gray, alpha=1.3, beta=40)
43
+
44
+ # حفظ الصورة المحسنة مؤقتاً
45
+ _, temp_path = tempfile.mkstemp(suffix='.png')
46
+ cv2.imwrite(temp_path, enhanced)
47
+
48
+ return temp_path
49
+ except Exception as e:
50
+ print(f"⚠️ خطأ في معالجة الصورة: {e}")
51
+ return image_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ def extract_texts(image_path: str, preprocess: bool = True):
54
  """
55
+ استخراج النصوص من الصورة مع معالجة مسبقة اختيارية
56
  """
57
+ try:
58
+ ocr = get_ocr_engine()
59
+ processed_path = image_path
60
+
61
+ # المعالجة المسبقة إذا requested
62
+ if preprocess:
63
+ processed_path = preprocess_image(image_path)
64
+
65
+ # استخراج النصوص
66
+ result = ocr.ocr(processed_path, cls=True)
67
+ texts = []
68
+ boxes = []
69
+
70
+ if result and result[0]:
71
+ for line in result[0]:
72
+ if line and len(line) >= 2:
73
  text = line[1][0]
74
+ confidence = line[1][1] if len(line[1]) > 1 else 0.5
75
+
76
+ # فلترة النصوص ذات الثقة المنخفضة يدوياً
77
+ if confidence > 0.3 and text.strip():
78
  texts.append(text)
79
+ boxes.append(line[0])
80
+ print(f"📝 تم استخراج: '{text}' (ثقة: {confidence:.2f})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # تنظيف الملف المؤقت إذا تم إنشاؤه
83
+ if preprocess and processed_path != image_path and os.path.exists(processed_path):
 
 
 
84
  try:
85
+ os.remove(processed_path)
86
  except:
87
  pass
88
+
89
+ print(f"✅ تم استخراج {len(texts)} نصاً من الصورة")
90
+ return texts, boxes
91
+
92
+ except Exception as e:
93
+ print(f"❌ خطأ في استخراج النصوص: {e}")
94
+ import traceback
95
+ traceback.print_exc()
96
+ return [], []
97
+