Исходный код manuscript.utils.page_transforms

"""Collapse text regions in the document tree without legacy Page structures."""
from typing import Optional, Sequence

from manuscript.data import Element, Page, TextRegion
from .geometry import merge_polygons


def _mean(values):
    values = [value for value in values if value is not None]
    return sum(values) / len(values) if values else None


def _text(region):
    children = [_text(child) for child in region.children]
    children = [text for text in children if text]
    if children:
        return (" " if region.type == "line" else "\n").join(children)
    return region.text or ""


[документация] def merge_text_spans(text_spans: Sequence[TextRegion], method="bbox", *, type="word") -> Optional[TextRegion]: """Merge detached text regions into one region; inputs remain unchanged.""" if not text_spans: return None if any(not isinstance(node, TextRegion) for region in text_spans for node in (region, *region.descendants)): raise TypeError("Only text regions can be collapsed") polygons = [node.polygon for region in text_spans for node in (region, *region.descendants) if node.polygon is not None] polygon = merge_polygons(polygons, method=method) if polygons else None if polygon is not None and len(polygon) == 3: # Preserve the triangular outline with four vertices for TextRegion. first, second = polygon[:2] polygon.insert(1, tuple((a + b) / 2 for a, b in zip(first, second))) elif polygon is not None and len(polygon) < 3: polygon = merge_polygons(polygons, method="bbox") texts = [_text(region) for region in text_spans] angles = {region.orientation for region in text_spans} return TextRegion(type=type, polygon=polygon, text=" ".join(text for text in texts if text) or None, detection_confidence=_mean([region.detection_confidence for region in text_spans]), recognition_confidence=_mean([region.recognition_confidence for region in text_spans]), orientation=next(iter(angles)) if len(angles) == 1 else None)
def _collapse(region, expected, method): if not isinstance(region, TextRegion) or region.type != expected: raise TypeError(f"Expected a {expected} TextRegion") merged = merge_text_spans([region], method=method, type=expected) # Keep the region's metadata and existing geometry; remove its text children. result = Element.from_tree_dict(region.to_tree_dict()) result.children = [] result.text = merged.text if result.polygon is None: result.polygon = merged.polygon return result
[документация] def collapse_line_text_spans(line, method="bbox"): return _collapse(line, "line", method)
[документация] def collapse_block_text_spans(block, method="bbox"): return _collapse(block, "block", method)
[документация] def collapse_page_text_spans(page: Page, level="line", method="bbox") -> Page: """Collapse matching text containers while preserving unrelated tree nodes.""" if not isinstance(page, Page): raise TypeError("Expected the document-tree Page") if level not in ("line", "block"): raise ValueError("level must be line or block") result = page.clone() for region in list(result.descendants): if isinstance(region, TextRegion) and region.type == level and region.root is result: collapsed = _collapse(region, level, method) region.text = collapsed.text region.polygon = collapsed.polygon region.children = [] return result
__all__ = ["merge_text_spans", "collapse_line_text_spans", "collapse_block_text_spans", "collapse_page_text_spans"]