frknuzn/ocr-poc
0
1from __future__ import annotations2 3from typing import List, Tuple4 5import numpy as np6 7 8def load_image(path_or_img) -> np.ndarray:9 if isinstance(path_or_img, np.ndarray):10 return path_or_img11 try:12 import cv213 except Exception as e:14 raise RuntimeError("OpenCV (opencv-python) gerekli: pip install -r requirements.txt") from e15 img = cv2.imread(str(path_or_img))16 if img is None:17 raise FileNotFoundError(f"Görsel yüklenemedi veya bulunamadı: {path_or_img}")18 return img19 20 21def _preprocess_variants(img: np.ndarray) -> list:22 try:23 import cv224 except Exception:25 return [img]26 variants = [img]27 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if img.ndim == 3 else img.copy()28 try:29 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))30 eq = clahe.apply(gray)31 variants.append(eq)32 except Exception:33 pass34 try:35 th = cv2.adaptiveThreshold(36 gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 1137 )38 variants.append(th)39 except Exception:40 pass41 return variants42 43 44_ocr_instance = None45 46 47def _get_ocr():48 global _ocr_instance49 if _ocr_instance is None:50 try:51 from paddleocr import PaddleOCR52 except Exception as e:53 raise RuntimeError(54 "PaddleOCR kurulumu eksik. Lütfen şu adımları izleyin:\n"55 "1) pip install -U pip wheel setuptools\n"56 "2) pip uninstall -y paddleocr paddlex PyMuPDF pymupdf\n"57 "3) pip install \"paddleocr==2.7.0.3\" --no-deps\n"58 "4) pip install -r requirements.txt\n"59 ) from e60 _ocr_instance = PaddleOCR(lang="tr", use_angle_cls=True, show_log=False)61 return _ocr_instance62 63 64def read_text_with_paddle(image_or_path) -> List[str]:65 ocr = _get_ocr()66 # Collect OCR results from multiple variants67 variants = []68 if isinstance(image_or_path, np.ndarray):69 variants = _preprocess_variants(image_or_path)70 else:71 try:72 img = load_image(image_or_path)73 variants = _preprocess_variants(img)74 except Exception:75 variants = []76 77 results = []78 if variants:79 for v in variants:80 try:81 results.append(ocr.ocr(v, cls=True))82 except Exception:83 continue84 else:85 results.append(ocr.ocr(image_or_path, cls=True))86 87 grouped_lines: List[str] = []88 plain_tokens: List[str] = []89 90 for res in results:91 for page in res:92 items: List[Tuple[float, float, float, str]] = []93 for box, (text, conf) in page:94 text = (text or "").strip()95 if not text:96 continue97 xs = [p[0] for p in box]98 ys = [p[1] for p in box]99 x_min = float(min(xs))100 y_c = float(sum(ys) / len(ys))101 h = float(max(ys) - min(ys))102 items.append((y_c, x_min, h, text))103 plain_tokens.append(text)104 105 if not items:106 continue107 items.sort(key=lambda t: (t[0], t[1]))108 hs = [h for _, _, h, _ in items if h > 0]109 tol = (np.median(hs) * 0.6) if hs else 10.0110 111 buckets: List[List[Tuple[float, float, float, str]]] = []112 cur_y = None113 cur_bucket: List[Tuple[float, float, float, str]] = []114 for it in items:115 yc, x, h, tx = it116 if cur_y is None or abs(yc - cur_y) <= tol:117 cur_bucket.append(it)118 cur_y = yc if cur_y is None else cur_y119 else:120 buckets.append(cur_bucket)121 cur_bucket = [it]122 cur_y = yc123 if cur_bucket:124 buckets.append(cur_bucket)125 126 for bucket in buckets:127 bucket.sort(key=lambda t: t[1])128 line = " ".join(tx for _, _, _, tx in bucket).strip()129 if line:130 grouped_lines.append(line)131 132 if len(grouped_lines) < max(8, int(len(plain_tokens) * 0.5)):133 # Simple fallback: join tokens in reading order with a separator for large X jumps134 approx_lines: List[str] = []135 try:136 # Build from the first OCR pass if available137 first = results[0] if results else []138 last_x = None139 buf: List[str] = []140 for page in first:141 items: List[Tuple[float, float, float, str]] = []142 for box, (text, conf) in page:143 text = (text or "").strip()144 if not text:145 continue146 xs = [p[0] for p in box]147 ys = [p[1] for p in box]148 x_min = float(min(xs))149 y_c = float(sum(ys) / len(ys))150 items.append((y_c, x_min, 0.0, text))151 items.sort(key=lambda t: (t[0], t[1]))152 for yc, x, _, tx in items:153 if last_x is None or (x - last_x) < 60:154 buf.append(tx)155 else:156 if buf:157 approx_lines.append(" ".join(buf).strip())158 buf = [tx]159 last_x = x160 if buf:161 approx_lines.append(" ".join(buf).strip())162 except Exception:163 pass164 seen = set()165 merged: List[str] = []166 for ln in approx_lines + grouped_lines:167 if ln and ln not in seen:168 merged.append(ln)169 seen.add(ln)170 return merged171 172 return grouped_lines173 