Spaces:
Runtime error
Runtime error
| """ | |
| OCR Engine using Tesseract and OpenCV for document text extraction. | |
| Supports multiple languages and document types. | |
| """ | |
| import pytesseract | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import logging | |
| from pathlib import Path | |
| from typing import Dict, List, Tuple, Optional | |
| from dataclasses import dataclass | |
| logger = logging.getLogger(__name__) | |
| class OCRResult: | |
| """Result from OCR processing""" | |
| text: str | |
| confidence: float | |
| language: str | |
| raw_text: str | |
| bounding_boxes: List[Dict] | |
| class OCREngine: | |
| """Advanced OCR engine with preprocessing and multi-language support""" | |
| SUPPORTED_LANGUAGES = { | |
| 'eng': 'English', | |
| 'fra': 'French', | |
| 'deu': 'German', | |
| 'spa': 'Spanish', | |
| 'jpn': 'Japanese', | |
| 'chi_sim': 'Chinese (Simplified)', | |
| } | |
| def __init__(self, languages: Optional[List[str]] = None): | |
| """Initialize OCR engine""" | |
| self.languages = languages or ['eng'] | |
| self.lang_string = '+'.join(self.languages) | |
| def preprocess_image(self, image_path: str) -> np.ndarray: | |
| """Preprocess image for better OCR accuracy""" | |
| img = cv2.imread(image_path) | |
| if img is None: | |
| raise ValueError(f"Cannot read image: {image_path}") | |
| # Convert to grayscale | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Apply CLAHE (Contrast Limited Adaptive Histogram Equalization) | |
| clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) | |
| enhanced = clahe.apply(gray) | |
| # Denoise | |
| denoised = cv2.fastNlMeansDenoising(enhanced) | |
| # Threshold | |
| _, binary = cv2.threshold(denoised, 150, 255, cv2.THRESH_BINARY) | |
| # Dilate to connect nearby characters | |
| kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) | |
| dilated = cv2.dilate(binary, kernel, iterations=1) | |
| return dilated | |
| def extract_text(self, image_path: str) -> OCRResult: | |
| """Extract text from image using OCR""" | |
| try: | |
| # Preprocess image | |
| processed = self.preprocess_image(image_path) | |
| # Convert to PIL Image for pytesseract | |
| pil_image = Image.fromarray(processed) | |
| # Extract text with Tesseract | |
| raw_text = pytesseract.image_to_string( | |
| pil_image, | |
| lang=self.lang_string, | |
| config='--psm 3' | |
| ) | |
| # Get confidence scores | |
| data = pytesseract.image_to_data( | |
| pil_image, | |
| lang=self.lang_string, | |
| output_type=pytesseract.Output.DICT | |
| ) | |
| # Calculate average confidence | |
| confidences = [int(conf) for conf in data['confidence'] if int(conf) > 0] | |
| avg_confidence = np.mean(confidences) / 100 if confidences else 0.0 | |
| # Extract bounding boxes | |
| bounding_boxes = [] | |
| for i in range(len(data['text'])): | |
| if int(data['conf'][i]) > 0: | |
| bounding_boxes.append({ | |
| 'text': data['text'][i], | |
| 'x': data['left'][i], | |
| 'y': data['top'][i], | |
| 'width': data['width'][i], | |
| 'height': data['height'][i], | |
| 'confidence': int(data['conf'][i]) | |
| }) | |
| # Clean text | |
| cleaned_text = self._clean_text(raw_text) | |
| return OCRResult( | |
| text=cleaned_text, | |
| confidence=avg_confidence, | |
| language=self.lang_string, | |
| raw_text=raw_text, | |
| bounding_boxes=bounding_boxes | |
| ) | |
| except Exception as e: | |
| logger.error(f"OCR extraction failed: {str(e)}") | |
| raise | |
| def _clean_text(text: str) -> str: | |
| """Clean extracted text""" | |
| # Remove extra whitespace | |
| text = ' '.join(text.split()) | |
| # Remove special characters but keep punctuation | |
| text = text.replace('\x00', '') | |
| return text.strip() | |
| def extract_tables(self, image_path: str) -> List[Dict]: | |
| """Detect and extract table structures from image""" | |
| img = cv2.imread(image_path) | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| # Detect lines | |
| edges = cv2.Canny(gray, 100, 200) | |
| lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=100, maxLineGap=10) | |
| if lines is None: | |
| return [] | |
| # Find table cells | |
| tables = [] | |
| # This is a simplified implementation | |
| return tables | |