pageparse.ai / src /pageparse /ocr /printed.py
Varun2007's picture
initial clean deployment commit with compilers
8c3e275
Raw
History Blame Contribute Delete
3.99 kB
from __future__ import annotations
import shutil
import subprocess
import tempfile
from pathlib import Path
import cv2
import numpy as np
TESSERACT_CANDIDATES = [
r"C:\Program Files\Tesseract-OCR\tesseract.exe",
r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",
]
class PrintedOCR:
def __init__(self, tesseract_cmd: str | None = None) -> None:
if tesseract_cmd is not None:
resolved = tesseract_cmd
else:
resolved = shutil.which("tesseract")
if resolved is None:
for c in TESSERACT_CANDIDATES:
if Path(c).exists():
resolved = c
break
if resolved is None:
raise RuntimeError(
"Tesseract not found. Install Tesseract OCR from "
"https://github.com/UB-Mannheim/tesseract/wiki "
"or ensure it is in your PATH."
)
self.tesseract_cmd = resolved
def recognize(self, image: np.ndarray) -> str:
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
tmp_path = tmp.name
cv2.imwrite(tmp_path, image)
try:
result = subprocess.run(
[self.tesseract_cmd, tmp_path, "stdout", "-l", "eng"],
capture_output=True,
text=True,
timeout=30,
)
if result.returncode != 0:
raise RuntimeError(
f"Tesseract failed (code {result.returncode}): {result.stderr.strip()}"
)
return result.stdout.strip()
finally:
Path(tmp_path).unlink(missing_ok=True)
def detect_barcodes(self, image: np.ndarray) -> list[dict]:
results = []
try:
from pyzbar.pyzbar import decode as pyzbar_decode
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if image.ndim == 3 else image
barcodes = pyzbar_decode(gray)
for barcode in barcodes:
results.append({
"data": barcode.data.decode("utf-8"),
"type": barcode.type,
"rect": {
"x": barcode.rect.left,
"y": barcode.rect.top,
"w": barcode.rect.width,
"h": barcode.rect.height,
},
})
except ImportError:
pass
except Exception as e:
print(f"Barcode detection failed: {e}")
return results
def detect_tables(self, image: np.ndarray) -> list[list[str]]:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if image.ndim == 3 else image
binary = cv2.adaptiveThreshold(
~gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, -2,
)
horizontal = binary.copy()
h_size = horizontal.shape[1] // 30
h_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (h_size, 1))
horizontal = cv2.erode(horizontal, h_kernel)
horizontal = cv2.dilate(horizontal, h_kernel)
vertical = binary.copy()
v_size = vertical.shape[0] // 30
v_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, v_size))
vertical = cv2.erode(vertical, v_kernel)
vertical = cv2.dilate(vertical, v_kernel)
table_mask = cv2.add(horizontal, vertical)
contours, _ = cv2.findContours(table_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
tables = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
if w > 50 and h > 50:
cell = gray[y:y+h, x:x+w]
try:
text = self.recognize(cell)
if text.strip():
tables.append(text.strip().split("\n"))
except Exception:
pass
return tables