ó
    /j-jâo  ã                  óx  • % S r SSKJr  SSKrSSKJr  SSKJr  SSKJr  SSK	J
r
  SS	K	Jr  SS
K	Jr  SSK	Jr  SSK	Jr  SSKJr  SrSrSrSrSrSrSr\ " S S5      5       r\ " S S5      5       rS rS rS rS rS\4S jrS rS  r \ " S! S"5      5       r!0 r"S#\#S$'   / r$S%\#S&'    " S' S(5      r% " S) S*\5      r&g)+u°  Implementation of the ML (Table Transformer) table parser.

The ``flavor='ml'`` backend follows one rule: **the model supplies the
structure, the PDF supplies the text.**

A neural table-structure model (Microsoft's Table Transformer / TATR, loaded
from HuggingFace) is run on the rendered page to detect rows, columns and
spanning cells *as boxes*. Those boxes are turned into the same
column/row/spanning grid the heuristic parsers build, and every cell's text is
then filled from the PDF's own text layer via :func:`get_table_index` â€” exactly
like the other flavors. The model never emits a single character of cell text,
so it cannot hallucinate or alter a value; OCR-grade substitution errors are
impossible because no recognition of glyphs happens here at all.

The heavy dependencies (``torch`` / ``transformers`` / ``timm``) are optional
and imported lazily â€” install them with ``pip install 'camelot-py[ml]'``. The
box-to-grid post-processing and the imageâ†’PDF coordinate mapping are pure
functions (no torch), so they are unit-tested without the models present.

Text source seam
----------------
Cell text comes from :meth:`MachineLearning._text_source`, which today returns
the born-digital PDF text layer. The structure half already runs on the page
*image*, so a future OCR text source (scanned / image-only PDFs) only has to
provide ``{text fragments with bboxes}`` to the same fill path â€” see the
roadmap in the ``[ml]`` tier task.
é    )ÚannotationsN)Ú	dataclass)Úfieldé   )ÚImageConversionBackend)Úbbox_from_str)Úscale)Ú	scale_pdf)Útext_in_bbox_per_axis)Ú	translateé   )Ú
BaseParserz	table rowztable columnztable spanning cellz:microsoft/table-transformer-structure-recognition-v1.1-allz%microsoft/table-transformer-detectiong      @ç      à?c                  ó8   • \ rS rSr% SrS\S'   S\S'   S\S'   S	rg
)ÚDetectedObjecté?   a�  One box emitted by the structure model.

Coordinates are in image-pixel space with a top-left origin (y grows
downward), matching what ``transformers`` object-detection
post-processing returns.

Parameters
----------
label : str
    Class name, e.g. ``"table row"`` / ``"table column"``.
score : float
    Detection confidence in ``[0, 1]``.
bbox : tuple
    ``(x0, y0, x1, y1)`` in image pixels.
ÚstrÚlabelÚfloatÚscorez!tuple[float, float, float, float]Úbbox© N©Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__Ú__static_attributes__r   ó    ÚO/home/mande/repo/quber/.venv/lib/python3.13/site-packages/camelot/parsers/ml.pyr   r   ?   s   ‡ ñð  ƒJØƒLØ
+Ö+r!   r   c                  ó\   • \ rS rSr% Sr\" \S9rS\S'   \" \S9r	S\S'   \" \S9r
S\S'   S	rg
)ÚMLGridéV   a?  A reconstructed table grid, in image-pixel space.

Attributes
----------
col_bounds : list
    ``(x_left, x_right)`` per column, left-to-right.
row_bounds : list
    ``(y_top, y_bottom)`` per row, top-to-bottom (image y grows down).
spans : list
    ``(r0, c0, r1, c1)`` inclusive grid-index ranges for spanning cells.
)Údefault_factoryzlist[tuple[float, float]]Ú
col_boundsÚ
row_boundszlist[tuple[int, int, int, int]]Úspansr   N)r   r   r   r   r   r   Úlistr'   r   r(   r)   r    r   r!   r"   r$   r$   V   s9   ‡ ñ
ñ -2À$Ñ,G€JÐ)ÓGÙ,1À$Ñ,G€JÐ)ÓGÙ-2À4Ñ-H€EÐ*ÖHr!   r$   c                óD  ^^	• US:X  a  SOSu  m	m[        UU	4S jU  5       S S9n/ nU Hr  u  pVU(       aT  US   S   US   S   -   S	-  n[        XV-   S	-  U-
  5      U::  a)  [        US   S   U5      [        US   S   U5      4US'   M`  UR	                  XV45        Mt     U$ )
aa  Collapse boxes to merged ``(lo, hi)`` intervals along one axis.

Parameters
----------
boxes : list of DetectedObject
axis : int
    ``0`` for the x-axis (columns), ``1`` for the y-axis (rows).
tol : float
    Centre-distance under which adjacent intervals are merged.

Returns
-------
list of tuple
    Merged ``(lo, hi)`` intervals, sorted by centre.
r   )r   r   )r   é   c              3  ó^   >#   • U  H"  oR                   T   UR                   T   4v •  M$     g 7f©N)r   )Ú.0ÚbÚhi_iÚlo_is     €€r"   Ú	<genexpr>Ú"_axis_intervals.<locals>.<genexpr>{   s$   øé € Ð5ªu¨!�&‰&�‰,˜Ÿ™˜t™Õ	%ªuùs   ƒ*-c                ó   • U S   U S   -   S-  $ )Nr   r   ç       @r   )Úts    r"   Ú<lambda>Ú!_axis_intervals.<locals>.<lambda>|   s   € �q˜‘t˜a ™d‘{ cÒ)r!   )Úkeyéÿÿÿÿr   r6   )ÚsortedÚabsÚminÚmaxÚappend)
ÚboxesÚaxisÚtolÚrawÚmergedÚloÚhiÚprev_centrer1   r2   s
           @@r"   Ú_axis_intervalsrI   i   s¼   ù€ ð    1›9‘¨&�J€Dˆ$Ü
Ý5©uÓ5Ù)ñ€Cð )+€FÛ‰ˆÞØ! "™: a™=¨6°"©:°a©=Ñ8¸CÑ?ˆKÜ�B‘G˜s‘? [Ñ0Ó1°SÓ8Ü! &¨¡*¨Q¡-°Ó4´c¸&À¹*ÀQ¹-ÈÓ6LÐM��r‘
ÙØ�‰�r�hÖñ ð €Mr!   c                óD  • U (       d  / $ U S   S   /n[        [        U 5      S-
  5       H'  nUR                  X   S   XS-      S   -   S-  5        M)     UR                  U S   S   5        [        [        U5      S-
  5       Vs/ s H  o!U   XS-      4PM     sn$ s  snf )u  Turn merged band intervals into gap-free, ordered cell bounds.

Separators are placed at the midpoint of each adjacent gap; the outer
edges keep the first/last interval's extent. The result tiles the axis
with no gaps or overlaps â€” the shape :class:`camelot.core.Table` expects.
r   r   r6   r;   )ÚrangeÚlenr@   )Ú	intervalsÚsepsÚis      r"   Ú_bounds_from_intervalsrP   ‰   s¥   € ö Øˆ	Ø�a‰L˜‰OÐ€DÜ”3�y“> AÑ%Ö&ˆØ�‰�Y‘\ !‘_ y°Q±Ñ'7¸Ñ':Ñ:¸cÑAÖBñ 'à‡K�K�	˜"‘˜aÑ Ô!Ü,1´#°d³)¸a±-Ô,@ÓAÒ,@ q�!‰W�d˜q™5‘kÓ"Ñ,@ÑAÐAùÒAs   ÂBc                ól   • [        X5      [        X5      peXe::  a  gX-
  nUS:*  =(       d
    Xe-
  U-  U:¬  $ )zCTrue if ``[b0, b1]`` covers at least ``frac`` of cell ``[a0, a1]``.Fr   )r?   r>   )Úa0Úa1Úb0Úb1ÚfracrF   rG   Úcells           r"   Ú_interval_overlaprX   ™   s;   € ä�‹[œ#˜b›+ˆØ	ƒxØØ‰7€DØ�1‰9×0˜™ DÑ(¨DÑ0Ð0r!   c                ó   • / nU  Hç  nUR                   u  pVpx[        U5       V	V
Vs/ s H"  u  n	u  p«[        X«XW[        5      (       d  M   U	PM$     nn
n	n[        U5       V	V
Vs/ s H"  u  n	u  p«[        X«Xh[        5      (       d  M   U	PM$     nn
n	nU(       a  U(       d  Mš  [	        U5      [        U5      pþ[	        U5      [        U5      nnXþ:”  d  UU:”  d  MÒ  UR                  UUUU45        Mé     U$ s  snn
n	f s  snn
n	f )u¾   Map spanning boxes to inclusive ``(r0, c0, r1, c1)`` grid ranges.

A spanning box that resolves to a single cell (covers one row and one
column) is dropped â€” it adds no merge information.
)r   Ú	enumeraterX   Ú_SPAN_OVERLAP_FRACTIONr>   r?   r@   )r)   r'   r(   ÚoutÚsÚsx0Úsy0Úsx1Úsy1rO   Úar0   Úcols_hitÚrows_hitÚr0Úr1Úc0Úc1s                     r"   Ú_spans_to_cellsri   ¢   sð   € ð €CÛˆØŸV™VÑˆ�#ô ' zÔ2õ
â2‘	�‘6�AÜ   sÔ1G×H÷ Ù2ð 	ò 
ô ' zÔ2õ
â2‘	�‘6�AÜ   sÔ1G×H÷ Ù2ð 	ò 
ö
 žxÙÜ�X“¤ H£ˆBÜ�X“¤ H£ˆBˆØ‹7�b˜2•gØ�J‰J˜˜B  BÐ'Ö(ñ# ð$ €Jùô!
ùô

s   ¦C2
Á	C2
Á"C9
ÂC9
c                ó  • U  Vs/ s H  o3R                   U:¼  d  M  UPM     nnU Vs/ s H  o3R                  [        :X  d  M  UPM     nnU Vs/ s H  o3R                  [        :X  d  M  UPM     nnU Vs/ s H  o3R                  [        :X  d  M  UPM     nnU(       a  U(       d
  [        5       $ [        [        USU5      5      n[        [        USU5      5      n	[        XxU	5      n
[        X‰U
S9$ s  snf s  snf s  snf s  snf )aù  Reconstruct a table grid from structure-model detections (pure).

Parameters
----------
objects : list of DetectedObject
    Row / column / spanning-cell boxes in image-pixel space.
score_thresh : float, optional (default: 0.5)
    Drop detections below this confidence.
merge_tol : float, optional
    Centre-distance under which duplicate row/column bands merge.

Returns
-------
MLGrid
    Column/row bounds (image space) and spanning-cell index ranges.
    Empty bounds mean no usable grid was found.
r   r   )r'   r(   r)   )	r   r   ÚLABEL_COLUMNÚ	LABEL_ROWÚLABEL_SPANNINGr$   rP   rI   ri   )ÚobjectsÚscore_threshÚ	merge_tolÚoÚkeptÚcolsÚrowsr)   r'   r(   Ú
span_cellss              r"   Úobjects_to_gridrv   ¾   sÑ   € ñ$ Ó:’w�!§'¡'¨\Ñ"9�A‘w€DÐ:ÙÓ7’t�!Ÿw™w¬,Ñ6�A‘t€DÐ7ÙÓ4’t�!Ÿw™w¬)Ñ3�A‘t€DÐ4ÙÓ:š�1§¡¬>Ñ 9�Q™€EÐ:Þ–tÜ‹xˆÜ'¬¸¸aÀÓ(KÓL€JÜ'¬¸¸aÀÓ(KÓL€JÜ  °JÓ?€JÜ˜ZÀjÑQÐQùò ;ùÚ7ùÚ4ùÚ:s-   …C3œC3¨C8ÁC8ÁC=Á*C=Á6DÂDc           	     óX  • Uu  p#nU R                    VVs/ s H  u  pV[        XR5      [        Xb5      4PM     nnn/ nU R                   HW  u  pš[        [        [	        U* U	5      5      U5      n[        [        [	        U* U
5      5      U5      nUR                  X¼45        MY     Xx4$ s  snnf )u^  Map an image-space grid to PDF-space ``cols`` / ``rows`` (pure).

Mirrors :func:`camelot.utils.scale_image`'s per-coordinate transform,
including the y-flip (image origin top-left â†’ PDF origin bottom-left).

Parameters
----------
grid : MLGrid
pdf_scalers : tuple
    ``(pdf_width_scaler, pdf_height_scaler, image_height)`` â€” same shape
    Lattice builds for :func:`scale_image`.

Returns
-------
tuple
    ``(cols, rows)`` where ``cols`` is a list of ``(x_left, x_right)``
    increasing and ``rows`` a list of ``(y_top, y_bottom)`` decreasing â€”
    the order :class:`camelot.core.Table` requires.
)r'   r	   r(   r=   r   r@   )ÚgridÚpdf_scalersÚsxÚsyÚimg_hrg   rh   rs   rt   Útop_imgÚbot_imgÚy_topÚy_bottoms                r"   Úgrid_to_pdf_cols_rowsr�   Ü   s�   € ð(  �M€BˆEØ9=¿ºÔIº©v¨rŒU�2‹]œE "›MÓ*¹€DÑIØ€DØ ŸOœOÑˆÜ”cœ) U F¨GÓ4Ó5°rÓ:ˆÜœœY¨ v¨wÓ7Ó8¸"Ó=ˆØ�‰�UÐ%Ö&ñ ,ð ˆ:Ðùó Js   •"B&c                óø   • U Ht  u  p#pE[        X$S-   5       H\  n[        X5S-   5       HG  nU R                  U   U   nXs:”  a  SUl        Xu:  a  SUl        Xb:”  a  SUl        Xd:  d  M@  SUl        MI     M^     Mv     g)u¦  Open interior edges of each span block so its cells merge (pure).

Starts from a fully-edged grid (every cell bounded on all sides) and, for
each spanning block, drops the edges *interior* to the block. A cell with
an open interior edge reports ``hspan`` / ``vspan`` True, so text flows to
the block's anchor cell via :meth:`MachineLearning._reduce_index` and
``copy_text`` works â€” the same spanning model Lattice uses.
r   FN)rK   ÚcellsÚleftÚrightÚtopÚbottom)	Útabler)   re   rg   rf   rh   ÚrÚcrW   s	            r"   Úapply_spansr‹   ú   sz   € ó  ‰ˆ�Ü�r ™6Ö"ˆAÜ˜2 A™vÖ&�Ø—{‘{ 1‘~ aÑ(�Ø“6Ø %�D”IØ“6Ø!&�D”JØ“6Ø$�D”HØ•6Ø"'�D–Kó 'ó #ò  r!   c                  óL   • \ rS rSr% SrS\S'   S\S'   S\S'   S\S'   S\S'   S	rg
)Ú_LoadedModelsi  z?Holds the lazily-imported torch handle and the two TATR models.ÚobjectÚtorchÚdet_processorÚ	det_modelÚstr_processorÚ	str_modelr   Nr   r   r!   r"   r�   r�     s"   ‡ áIàƒMØÓØÓØÓØÖr!   r�   z)dict[tuple[str, str, str], _LoadedModels]Ú_MODEL_CACHEzlist[object]Ú_OCR_ENGINEc                  ó$   • \ rS rSrSrS rS rSrg)Ú_OCRWordi(  aN  A textline-like wrapper around one OCR word, in PDF coordinate space.

Exposes just what :func:`camelot.utils.get_table_index` /
:func:`text_in_bbox_per_axis` need (``x0/y0/y1/x1`` with a bottom-left
origin and ``get_text``). ``split_text`` / ``flag_size`` are *not*
supported through OCR (they need per-glyph data), so no ``_objs``.
c                ó@   • X l         X0l        X@l        XPl        Xl        g r.   )Úx0Úy0Úx1Úy1Ú_text)ÚselfÚtextr™   rš   r›   rœ   s         r"   Ú__init__Ú_OCRWord.__init__1  s   € ØŒØŒØŒØŒØ�
r!   c                ó   • U R                   $ )z Return the recognised word text.)r�   ©rž   s    r"   Úget_textÚ_OCRWord.get_text8  s   € à�z‰zÐr!   )r�   r™   r›   rš   rœ   N)r   r   r   r   r   r    r¤   r    r   r!   r"   r—   r—   (  s   † ñòõr!   r—   c                  óØ   ^ • \ rS rSrSrSSSSSSSS\\SSSSS	S
SSS4U 4S jjrS r\	S 5       r
S rSS jrU 4S jrS rS rS rS rS rS rS rS rS r\	S 5       rSS jrSrU =r$ ) ÚMachineLearningi=  uF	  Neural table-structure parser (Table Transformer / TATR).

The model detects table structure on the rendered page; cell text is
filled from the PDF's text layer. Requires the optional ML dependencies
(``pip install 'camelot-py[ml]'``).

Parameters
----------
table_regions : list, optional (default: None)
    Page regions to restrict detection to, ``"x1,y1,x2,y2"`` in PDF
    coordinate space (left-top, right-bottom).
table_areas : list, optional (default: None)
    Exact table areas, ``"x1,y1,x2,y2"`` in PDF coordinate space. When
    given, table *detection* is skipped and structure recognition runs
    directly on each area.
copy_text : list, optional (default: None)
    ``{'h', 'v'}`` â€” directions to copy text across spanning cells.
shift_text : list, optional (default: ['l', 't'])
    ``{'l', 'r', 't', 'b'}`` â€” direction text in a spanning cell flows.
split_text : bool, optional (default: False)
    Split text that spans multiple cells.
flag_size : bool, optional (default: False)
    Wrap font-size-flagged text in ``<s></s>``.
strip_text : str, optional (default: '')
    Characters stripped from a cell before assignment.
structure_model : str, optional
    HuggingFace checkpoint for structure recognition.
detection_model : str, optional
    HuggingFace checkpoint for table detection.
device : str, optional (default: 'cpu')
    Torch device, e.g. ``'cpu'`` or ``'cuda'``.
detection_threshold : float, optional (default: 0.5)
    Score threshold for the detection model.
structure_threshold : float, optional (default: 0.5)
    Score threshold for the structure model.
crop_padding : int, optional (default: 10)
    Pixels of margin added around each detected table before structure
    recognition. TATR is trained on padded crops; the margin keeps the
    outermost rows/columns from being clipped.
ocr : {'auto', True, False}, optional (default: 'auto')
    Where cell text comes from. ``'auto'`` uses the PDF's born-digital
    text layer when present and falls back to OCR on the rendered page
    when the page has none (scanned / image-only PDFs); ``True`` always
    OCRs; ``False`` never does. OCR needs the optional OCR dependencies
    (``pip install 'camelot-py[ocr]'``) and does not support
    ``split_text`` / ``flag_size`` (no per-glyph data).
resolution : int, optional (default: 300)
    DPI for rendering the page to an image.
NFÚ Úcpur   é
   Úautoi,  TÚpdfiumc                ó  >• [         TU ]  SUUUUUUU=(       d    SS/UUS9
  X�l        X l        X°l        XÀl        XÐl        Xàl        Xðl        UU l	        [        UUS9U l        S U l        S U l        S U l        S U l        S U l        S U l        g )NÚmachine_learningÚlr7   )	Útable_regionsÚtable_areasÚ	copy_textÚ
split_textÚ
strip_textÚreplace_textÚ
shift_textÚ	flag_sizeÚdebug)Úuse_fallbackÚbackend)Úsuperr    Ústructure_modelÚdetection_modelÚdeviceÚdetection_thresholdÚstructure_thresholdÚcrop_paddingÚocrÚ
resolutionr   ÚicbÚ_modelsÚ_ocr_engineÚ
_image_rgbÚ
_ocr_cacheÚ_pdf_scalersÚ_image_scalers)rž   r°   r±   r²   r¶   r³   r·   r´   rµ   r¼   r½   r¾   r¿   rÀ   rÁ   rÂ   rÃ   r¹   rº   r¸   ÚkwargsÚ	__class__s                        €r"   r    ÚMachineLearning.__init__p  sª   ø€ ô. 	‰ÑØØ'Ø#ØØ!Ø!Ø%Ø!×/ c¨3 ZØØð 	ñ 	
ð  /ÔØ.ÔØŒØ#6Ô Ø#6Ô Ø(ÔØŒØ$ˆŒÜ)°|ÈWÑUˆŒØ-1ˆŒØˆÔàˆŒØ15ˆŒØ?CˆÔØAEˆÕr!   c                óF  • U R                   b  U R                   $ U R                  U R                  U R                  4n[        R                  U5      nUb  X l         U R                   $  SSKnSSKJn  SSKJ	n  UR                  U R                  5      nUR                  U R                  5      R                  U R                  5      R                  5       nUR                  U R                  5      n	UR                  U R                  5      R                  U R                  5      R                  5       n
U R                  U5        U R                  U	5        [        X7X‰U
5      U l         U R                   [        U'   U R                   $ ! [         a  n[        S5      UeSnAff = f)z«Import torch/transformers and build the two TATR models once.

Raises
------
ImportError
    If the optional ML dependencies are not installed, with the
    install hint.
Nr   )ÚAutoImageProcessor)ÚAutoModelForObjectDetectionzbflavor='ml' requires the optional ML dependencies. Install them with: pip install 'camelot-py[ml]')rÅ   r½   r¼   r¾   r”   Úgetr�   ÚtransformersrÏ   rÐ   ÚImportErrorÚfrom_pretrainedÚtoÚevalÚ_normalize_processor_sizer�   )rž   Ú	cache_keyÚcachedr�   rÏ   rÐ   Úexcr�   r‘   r’   r“   s              r"   Ú_load_modelsÚMachineLearning._load_models§  sf  € ð �<‰<Ñ#Ø—<‘<ÐØ×)Ñ)¨4×+?Ñ+?ÀÇÁÐMˆ	Ü×!Ñ! )Ó,ˆØÑØ!ŒLØ—<‘<Ðð	ÛÝ7Ý@ð +×:Ñ:¸4×;OÑ;OÓPˆà'×7Ñ7¸×8LÑ8LÓMß‰R�—‘‹_ß‰T‹Vð 	ð
 +×:Ñ:¸4×;OÑ;OÓPˆà'×7Ñ7¸×8LÑ8LÓMß‰R�—‘‹_ß‰T‹Vð 	ð
 	×&Ñ& }Ô5Ø×&Ñ& }Ô5Ü$Ø )¸Ió
ˆŒð #'§,¡,Œ�YÑØ�|‰|Ðøô1 ó 	ÜðBóð ðûð	ús   Á(F Æ
F ÆFÆF c                óˆ   • [        U SS5      n[        U[        5      (       a   [        U5      S1:X  a  US   nX"S.U l        ggg)u  Make a ``longest_edge``-only resize config transformers accepts.

The TATR structure checkpoint ships ``size={'longest_edge': 800}`` (a
max-longest-edge resize), but DETR's image processor in transformers
>= 4.x requires ``{height, width}`` or ``{shortest_edge,
longest_edge}``. Setting both edges to the same value reproduces a
longest-edge cap exactly â€” the scale that makes the shortest edge that
big would push the longest past the cap, so it falls back to capping
the longest edge, aspect ratio preserved. No-op for valid configs.
ÚsizeNÚlongest_edge)Úshortest_edgerß   )ÚgetattrÚ
isinstanceÚdictÚsetrÞ   )Ú	processorrÞ   Úedges      r"   r×   Ú)MachineLearning._normalize_processor_sizeÕ  sJ   € ô �y &¨$Ó/ˆÜ�dœD×!Ñ!¤c¨$£i°NÐ3CÓ&CØ˜Ñ'ˆDØ/3ÑJˆI�Nð 'DÐ!r!   c                ó    • U R                  5       (       a  [        XR                  5       / 5      $ [        XR                  U R                  5      $ )u{  Return ``{direction: [textlines]}`` for the cell-fill pass.

Two sources behind one seam:

* **born-digital** â€” the PDF's own text layer clipped to ``bbox``
  (exact characters, the default).
* **OCR** â€” words recognised from the page image (scanned / image-only
  PDFs), used when :meth:`_use_ocr` resolves true. OCR words are
  horizontal-only; the vertical axis is empty.
)Ú_use_ocrr   Ú
_ocr_wordsÚhorizontal_textÚvertical_text)rž   r   s     r"   Ú_text_sourceÚMachineLearning._text_sourceé  s<   € ð �=‰=�?‰?Ü(¨¯©Ó/@À"ÓEÐEÜ$ T×+?Ñ+?À×ASÑASÓTÐTr!   c                óf   • U R                   SL a  gU R                   S:X  a  U R                  (       + $ g)z·Resolve whether this page's cells are filled from OCR.

``ocr=True`` always; ``ocr='auto'`` only when the page has no
born-digital text layer (the scanned case); ``ocr=False`` never.
Tr«   F)rÂ   rë   r£   s    r"   ré   ÚMachineLearning._use_ocrø  s1   € ð �8‰8�tÒØØ�8‰8�vÓØ×+Ñ+Ô+Ð+Ør!   c                óL   >• U R                  5       (       a  g[        TU ]	  5       $ )zóDon't bail on a text-less page when OCR can supply the text.

BaseParser short-circuits ``extract_tables`` when the page has no
text layer; with OCR enabled a scanned page is exactly what we want
to process, so override that for the OCR case.
F)ré   r»   Ú_document_has_no_text)rž   rÌ   s    €r"   rò   Ú%MachineLearning._document_has_no_text  s!   ø€ ð �=‰=�?‰?ØÜ‰wÑ,Ó.Ð.r!   c                ó6  • U R                   b  U R                   $ [        (       a  [        S   U l         U R                   $  SSKJn  U" 5       U l         [        R                  U R                   5        U R                   $ ! [         a  n[	        S5      UeSnAff = f)z0Lazily build (and process-cache) the OCR engine.Nr   )ÚRapidOCRz|flavor='ml' with OCR (scanned PDFs) requires the optional OCR dependencies. Install them with: pip install 'camelot-py[ocr]')rÆ   r•   Úrapidocr_onnxruntimerõ   rÓ   r@   )rž   rõ   rÚ   s      r"   Ú_get_ocr_engineÚMachineLearning._get_ocr_engine  s•   € à×ÑÑ'Ø×#Ñ#Ð#ßŠ;Ü*¨1™~ˆDÔØ×#Ñ#Ð#ð	Ý5ñ $›:ˆÔÜ×Ñ˜4×+Ñ+Ô,Ø×ÑÐøô ó 	ÜðQóð ðûð	ús   Á A= Á=
BÂBÂBc                ó”  • U R                   b  U R                   $ SSKnU R                  5       nU" UR                  U R                  5      5      u  p4/ nU R
                  u  pgnU=(       d    /  HÇ  u  pšnU	 Vs/ s H  oÌS   PM	     nnU	 Vs/ s H  oÌS   PM	     nn[        [        U5      U5      n[        [        U5      U5      n[        [        [        U* [        U5      5      5      U5      n[        [        [        U* [        U5      5      5      U5      nUR                  [        X¯UUU5      5        MÉ     XPl         U$ s  snf s  snf )zFOCR the page image once; return words as PDF-coord :class:`_OCRWord`s.Nr   r   )rÈ   Únumpyr÷   ÚasarrayrÇ   rÉ   r	   r>   r?   r=   r   r@   r—   )rž   ÚnpÚengineÚresultÚ_Úwordsrz   r{   r|   ÚboxrŸ   Ú_scoreÚptÚxsÚysr™   r›   r   r€   s                      r"   rê   ÚMachineLearning._ocr_words!  s  € à�?‰?Ñ&Ø—?‘?Ð"Ûà×%Ñ%Ó'ˆÙ˜2Ÿ:™: d§o¡oÓ6Ó7‰	ˆØ "ˆØ×)Ñ)‰ˆ�Ø!'§¨2¢ÑˆC�vÙ"%Ó&¢#˜B�Q”%¡#ˆBÐ&Ù"%Ó&¢#˜B�Q”%¡#ˆBÐ&ä”s˜2“w Ó#ˆBÜ”s˜2“w Ó#ˆBÜœ#œi¨¨´°B³Ó8Ó9¸2Ó>ˆEÜœS¤¨E¨6´3°r³7Ó!;Ó<¸bÓAˆHØ�L‰Lœ $¨H°b¸%Ó@ÖAñ ".ð  ŒØˆùò 'ùÚ&s   Á7E ÂEc                ó
  • SSK nSSKJn  U R                  R	                  U R
                  U R                  5      nUR                  S   UR                  S   pTUR                  UR                  X1R                  5      5      U l        SU l        U R                  [        U5      -  U R                  [        U5      -  U4U l        U[        U R                  5      -  U[        U R                  5      -  U R                  4U l        g)u>   Render the page to an RGB image and build imageâ†”PDF scalers.r   N)ÚImager   )Úcv2ÚPILr  rÄ   Úto_arrayÚfilenameÚpageÚshapeÚ	fromarrayÚcvtColorÚCOLOR_BGR2RGBrÇ   rÈ   Ú	pdf_widthr   Ú
pdf_heightrÉ   rÊ   )rž   r	  r  Ú	image_bgrr|   Úimg_ws         r"   Ú_renderÚMachineLearning._render:  sÇ   € ãÝà—H‘H×%Ñ% d§m¡m°T·Y±YÓ?ˆ	Ø —‘ qÑ)¨9¯?©?¸1Ñ+=ˆuØŸ/™/¨#¯,©,°y×BSÑBSÓ*TÓUˆŒØˆŒà�N‰NœU 5›\Ñ)Ø�O‰Oœe E›lÑ*Øð
ˆÔð ”E˜$Ÿ.™.Ó)Ñ)Ø”E˜$Ÿ/™/Ó*Ñ*Ø�O‰Oð
ˆÕr!   c                óN  • U R                   nU" USS9R                  U R                  5      nUR                  R	                  5          U" S0 UD6nSSS5        UR                  R                  UR                  SSS2   /5      nUR                  WXHS9S   n	UR                  R                  n
/ n[        U	S   U	S   U	S	   5       HK  u  pÍnU
[        U5         nS
 U 5       u  nnnnUR                  [        U[        U5      UUUU45      5        MM     U$ ! , (       d  f       NÌ= f)z~Run one object-detection model and return DetectedObjects.

Coordinates are in ``image``'s own pixel space (top-left origin).
r  )ÚimagesÚreturn_tensorsNr;   )Ú	thresholdÚtarget_sizesr   ÚscoresÚlabelsrA   c              3  ó8   #   • U  H  n[        U5      v •  M     g 7fr.   )r   )r/   Úvs     r"   r3   Ú)MachineLearning._infer.<locals>.<genexpr>d  s   é € Ð4²¨1œe AŸh˜h²ùs   ‚r   )rÅ   rÕ   r¾   r�   Úno_gradÚtensorrÞ   Úpost_process_object_detectionÚconfigÚid2labelÚzipÚintr@   r   r   )rž   Úimagerå   Úmodelr  ÚmodelsÚinputsÚoutputsÚtargetÚresultsr&  rn   ÚscÚlblr  Únamer™   rš   r›   rœ   s                       r"   Ú_inferÚMachineLearning._inferQ  s!  € ð
 —‘ˆÙ %¸Ñ=×@Ñ@ÀÇÁÓMˆØ�\‰\×!Ñ!Õ#Ù‘o˜f‘oˆG÷ $à—‘×$Ñ$ e§j¡j±°2°Ñ&6Ð%7Ó8ˆØ×9Ñ9Ø˜yð :ð 
à
ñˆð —<‘<×(Ñ(ˆØˆÜØ�HÑ˜w xÑ0°'¸'Ñ2Bö
‰LˆB�Sð œC ›HÑ%ˆDÙ4±Ó4‰NˆB��B˜Ø�N‰Nœ>¨$´°b³	¸BÀÀBÈÐ;KÓLÖMñ
ð ˆ÷ $Õ#ús   Á	DÄ
D$c                óÀ  • U R                   b:  U R                    Vs/ s H"  n[        [        U5      U R                  5      PM$     sn$ U R	                  U R
                  U R                  R                  U R                  R                  U R                  5      nU Vs/ s H0  o3R                  R                  S5      (       d  M$  UR                  PM2     sn$ s  snf s  snf )z®Return table bounding boxes in image-pixel space.

Uses the detection model unless the user pinned ``table_areas``
(then those PDF boxes are mapped straight to image space).
rˆ   )r±   r
   r   rÊ   r3  rÇ   rÅ   r�   r‘   r¿   r   Ú
startswithr   )rž   Úarearn   rq   s       r"   Ú_detect_table_regionsÚ%MachineLearning._detect_table_regionsh  s»   € ð ×ÑÑ'ð !×,Ò,óâ,�Dô œ-¨Ó-¨t×/BÑ/BÖCÙ,ñð ð —+‘+Ø�O‰OØ�L‰L×&Ñ&Ø�L‰L×"Ñ"Ø×$Ñ$ó	
ˆñ !(ÓG¢˜1¯7©7×+=Ñ+=¸g×+F“�—”¡ÑGÐGùòùò Hs   œ)CÂ#CÃCc                óÞ  • U R                   nU R                  R                  u  p4[        S[	        US   5      U-
  5      n[        S[	        US   5      U-
  5      n[        U[	        US   5      U-   5      n[        U[	        US   5      U-   5      nU R                  R                  XVXx45      n	U R                  U	U R                  R                  U R                  R                  U R                  5      n
U
 Vs/ s Hh  n[        UR                  UR                  UR                  S   U-   UR                  S   U-   UR                  S   U-   UR                  S   U-   45      PMj     sn$ s  snf )ux  Run structure recognition on one table crop.

The crop is padded by ``crop_padding`` px (clamped to the image) â€”
TATR's structure model was trained on padded table crops, and without
the margin the outermost rows/columns get clipped. Returns
DetectedObjects in *full-page* image coordinates (the padded-crop
offset is added back), so downstream geometry is page-consistent.
r   r   r   r,   )rÁ   rÇ   rÞ   r?   r(  r>   Úcropr3  rÅ   r’   r“   rÀ   r   r   r   r   )rž   ÚregionÚpadÚwidthÚheightr™   rš   r›   rœ   r;  rn   rq   s               r"   Ú_recognize_structureÚ$MachineLearning._recognize_structure{  sK  € ð ×ÑˆØŸ™×,Ñ,‰ˆÜ�”C˜˜q™	“N SÑ(Ó)ˆÜ�”C˜˜q™	“N SÑ(Ó)ˆÜ�œ˜F 1™I›¨Ñ,Ó-ˆÜ�œ˜V A™Y›¨#Ñ-Ó.ˆØ�‰×#Ñ# R¨RÐ$4Ó5ˆØ—+‘+ØØ�L‰L×&Ñ&Ø�L‰L×"Ñ"Ø×$Ñ$ó	
ˆñ ó
ò �ô Ø—‘Ø—‘Ø—‘˜‘˜R‘ §¡¨¡¨R¡°·±¸±¸R±ÀÇÁÈÁÈRÁÐPöñ
 ñ
ð 	
ùò 
s   Ã8A/E*c                óf  • U R                  5         U R                  5         U R                  5       (       aE  U R                  (       d  U R                  (       a#  [
        R                  " SSS9  SU l        SU l        0 U l        U R                  5        Hœ  n[        U R                  U5      U R                  S9nUR                  (       a  UR                  (       d  MJ  [        X R                  5      u  p4US   S   US   S   US   S   US   S   4nUUUR                   S	.U R                  U'   Mž     g
)zBDetect tables, recognise structure, store each grid in PDF coords.znflavor='ml' OCR mode does not support split_text / flag_size (no per-glyph data); ignoring them for this page.r   )Ú
stacklevelF)ro   r   r;   r   )rs   rt   r)   N)rÛ   r  ré   r³   r·   ÚwarningsÚwarnÚtable_bbox_parsesr8  rv   r@  rÀ   r'   r(   r�   rÉ   r)   )rž   r<  rx   rs   rt   Úpdf_bboxs         r"   Ú_generate_table_bboxÚ$MachineLearning._generate_table_bbox�  s  € à×ÑÔØ�‰ŒØ�=‰=�?‰? §§°4·>·>ô �MŠMðDàòð
 $ˆDŒOØ"ˆDŒNØ!#ˆÔØ×0Ñ0Ö2ˆFÜ"Ø×)Ñ)¨&Ó1Ø!×5Ñ5ñˆDð —?—?¨$¯/¯/ÙÜ.¨t×5FÑ5FÓG‰JˆDØ˜Q™ ™
 D¨¡H¨Q¡K°°b±¸!±¸dÀ1¹gÀa¹jÐIˆHàØØŸ™ñ0ˆD×"Ñ" 8Ó,ò 3r!   c                ód   • U R                   U   nU R                  U5      U l        US   US   / / 4$ )Nrs   rt   )rF  rí   Út_bbox)rž   r   Ú	user_colsÚparses       r"   Ú_generate_columns_and_rowsÚ*MachineLearning._generate_columns_and_rows»  s=   € Ø×&Ñ& tÑ,ˆØ×'Ñ'¨Ó-ˆŒð �V‰}˜e F™m¨R°Ð3Ð3r!   c                ó¼   • U R                  XX45      nUR                  5         [        X`R                  U   R	                  S/ 5      5        U R                  U5        U$ )Nr)   )Ú_initialize_new_tableÚset_all_edgesr‹   rF  rÑ   Úrecord_parse_metadata)rž   Ú	table_idxr   rs   rt   rË   rˆ   s          r"   Ú_generate_tableÚMachineLearning._generate_tableÂ  sS   € Ø×*Ñ*¨9¸DÓGˆØ×ÑÔÜ�E×1Ñ1°$Ñ7×;Ñ;¸GÀRÓHÔIØ×"Ñ" 5Ô)Øˆr!   c                ó2   • SSK Jn  UR                  XU5      $ )z@Flow text within a spanning cell to its anchor (reuses Lattice).r   )ÚLattice)ÚlatticerX  Ú_reduce_index)rˆ   Úidxr¶   rX  s       r"   rZ  ÚMachineLearning._reduce_indexÉ  s   € õ 	%à×$Ñ$ U°Ó<Ð<r!   c                ó¤   • UR                   R                  (       d&  UR                  S   S:X  d  UR                  S   S:X  a  gUR                  S:¬  $ )z?Drop empty/degenerate detections (no rows, no cols, all blank).r   r   Tg      Y@)ÚdfÚemptyr  Ú
whitespace)rž   rˆ   s     r"   Ú_reject_tableÚMachineLearning._reject_tableÐ  s?   € à�8‰8�>�>˜UŸ[™[¨™^¨qÓ0°E·K±KÀ±NÀaÓ4GØØ×Ñ 5Ñ(Ð(r!   )rÇ   rÊ   rÅ   rÈ   rÆ   rÉ   rÁ   r½   r¿   r¾   r·   rÄ   rÂ   rÃ   r³   r¼   rÀ   rK  rF  )ÚreturnÚbool)r   r   r   r   r   ÚDEFAULT_STRUCTURE_MODELÚDEFAULT_DETECTION_MODELr    rÛ   Ústaticmethodr×   rí   ré   rò   r÷   rê   r  r3  r8  r@  rH  rN  rU  rZ  ra  r    Ú__classcell__)rÌ   s   @r"   r§   r§   =  sÄ   ø† ñ0ðh ØØØØØØØØ/Ø/ØØØØØØØØØ÷)2Fòn,ð\ ñKó ðKò&Uô
õ	/ò ò$ò2
ò.ò.Hò&
òDò<4òð ñ=ó ð=÷)ò )r!   r§   )'r   Ú
__future__r   rD  Údataclassesr   r   Úbackendsr   Úutilsr   r	   r
   r   r   Úbaser   rl   rk   rm   re  rf  ÚDEFAULT_MERGE_TOLr[   r   r$   rI   rP   rX   ri   rv   r�   r‹   r�   r”   r   r•   r—   r§   r   r!   r"   Ú<module>ro     s
  ðòõ8 #ã Ý !Ý å -Ý !Ý Ý Ý )Ý Ý ð €	Ø€Ø&€ð WÐ ØAÐ ð
 Ð ð Ð ð ÷,ð ,ó ð,ð, ÷Ið Ió ðIò$ò@Bò 1òð8 +.Ð9Jô Rò<ò<(ð. ÷ð ó ðð ;=€Ð7Ó <ð
 €ˆ\Ó ÷ñ ô*W)�jõ W)r!   