| """ |
| Reading Order Base Interface |
| |
| Defines interfaces for reading order reconstruction. |
| """ |
|
|
| from abc import ABC, abstractmethod |
| from typing import List, Optional, Dict, Any, Tuple |
| from dataclasses import dataclass, field |
| from pydantic import BaseModel, Field |
|
|
| from ..schemas.core import BoundingBox, LayoutRegion, OCRRegion |
|
|
|
|
| class ReadingOrderConfig(BaseModel): |
| """Configuration for reading order reconstruction.""" |
| |
| method: str = Field( |
| default="rule_based", |
| description="Method: rule_based or model_based" |
| ) |
|
|
| |
| detect_columns: bool = Field( |
| default=True, |
| description="Attempt to detect multi-column layouts" |
| ) |
| max_columns: int = Field( |
| default=4, |
| ge=1, |
| description="Maximum number of columns to detect" |
| ) |
| column_gap_threshold: float = Field( |
| default=0.1, |
| ge=0.0, |
| le=1.0, |
| description="Minimum gap ratio between columns" |
| ) |
|
|
| |
| reading_direction: str = Field( |
| default="ltr", |
| description="Reading direction: ltr (left-to-right) or rtl" |
| ) |
| vertical_priority: bool = Field( |
| default=True, |
| description="Prioritize top-to-bottom over left-to-right" |
| ) |
|
|
| |
| respect_layout_types: bool = Field( |
| default=True, |
| description="Respect layout region boundaries" |
| ) |
| header_footer_separate: bool = Field( |
| default=True, |
| description="Keep headers/footers at start/end" |
| ) |
|
|
|
|
| @dataclass |
| class ReadingOrderResult: |
| """Result of reading order reconstruction.""" |
| |
| order: List[int] = field(default_factory=list) |
|
|
| |
| ordered_regions: List[Any] = field(default_factory=list) |
|
|
| |
| num_columns: int = 1 |
| column_assignments: Dict[int, int] = field(default_factory=dict) |
|
|
| |
| processing_time_ms: float = 0.0 |
| success: bool = True |
| error: Optional[str] = None |
|
|
| def get_ordered_text(self, regions: List[OCRRegion]) -> str: |
| """Get text in reading order.""" |
| if not self.order: |
| return "" |
| ordered_texts = [regions[i].text for i in self.order if i < len(regions)] |
| return " ".join(ordered_texts) |
|
|
|
|
| class ReadingOrderReconstructor(ABC): |
| """Abstract base class for reading order reconstruction.""" |
|
|
| def __init__(self, config: Optional[ReadingOrderConfig] = None): |
| self.config = config or ReadingOrderConfig() |
| self._initialized = False |
|
|
| @abstractmethod |
| def initialize(self): |
| """Initialize the reconstructor.""" |
| pass |
|
|
| @abstractmethod |
| def reconstruct( |
| self, |
| regions: List[Any], |
| layout_regions: Optional[List[LayoutRegion]] = None, |
| page_width: Optional[int] = None, |
| page_height: Optional[int] = None, |
| ) -> ReadingOrderResult: |
| """ |
| Reconstruct reading order for regions. |
| |
| Args: |
| regions: OCR regions or layout regions |
| layout_regions: Optional layout regions for context |
| page_width: Page width in pixels |
| page_height: Page height in pixels |
| |
| Returns: |
| ReadingOrderResult with ordered indices |
| """ |
| pass |
|
|
| @property |
| def is_initialized(self) -> bool: |
| return self._initialized |
|
|