§
    �Øj2¿  ã                   ó”  — d Z ddlZ	 ddlmZmZmZmZ nX# e$ rP dhdej	        de
dej	        fd„Zdej	        dej	        fd	„Zd
ej	        dedej	        fd„Zd
ej	        dej	        fd„ZY nw xY wg d¢ZdZdZddhZ ed„  ee¦  «        D ¦   «         ¦  «        Zd„  ee¦  «        D ¦   «         Zd„  ee¦  «        D ¦   «         Zd„ eD ¦   «         ZeZdZdZdZdej	        dej	        dedej	        fd„Zdej	        dej	        dej	        ded ed!edz  d"e
d#e
dee         fd$„Zd%ee         d&ee         d edefd'„Z G d(„ d)¦  «        Z e!d*k    �r~ddl"Z"ddl#Z#ddl$Z$ddl%Z%ddlZddl&Z'ddl(Z( e$j)        d+¬,¦  «        Z*e* +                    d-d.¬/¦  «         e* +                    d0d1¬/¦  «         e* +                    d2e
d3d4¬5¦  «         e* +                    d6edd7¬5¦  «         e* +                    d8ed9d:¬5¦  «         e* ,                    ¦   «         Z-e#j.         /                    e-j0        ¦  «        s# e1d;e-j0        › �¦  «          e"j2        d<¦  «         e#j.         3                    e-j4        ¦  «        s# e1d=e-j4        › �¦  «          e"j2        d<¦  «         ej5         6                    d>¦  «        Z7 ej8         ej9        g d?¢gej:        ¬@¦  «        e7 ;                    dAdB e<e¦  «        d<z
  dCfej:        ¬D¦  «        g¦  «        Z=didG„Z>dH„ Z?dI„ Z@dJ„ ZAdK„ ZBdL„ ZCdM„ ZD e1dNe-j0        › �¦  «          e'jE        e-j0        dOg¬P¦  «        ZF e eF¦  «        ZG e1dQdR„ eF H                    ¦   «         D ¦   «         › �¦  «          edS„  e#jI        e-j4        ¦  «        D ¦   «         ¦  «        ZJeJs# e1dTe-j4        › �¦  «          e"j2        d¦  «          e1dU e<eJ¦  «        › dV�¦  «         dWZKeJD �]àZLeKr �nÚ e(jM        eL¦  «        ZN e<eN¦  «        ZOeN P                    ¦   «          e#j.         Q                    e#j.         R                    eL¦  «        ¦  «        d         ZS e1dXeS› dYeO› dZ�¦  «          eTeO¦  «        D �]RZU e1d[eU› d\�d]d^¬_¦  «          e(jM        eL¦  «        ZNeNeU         ZV e(jW        e-jX        d`z  e-jX        d`z  ¦  «        ZYeV Z                    eYe(j[        ¬a¦  «        Z\eN P                    ¦   «           e%j]         ej^        e\j_        ej:        ¬@¦  «         `                    e\ja        e\jb        dC¦  «        e%jc        ¦  «        Zd	  eDeGede-je        e-jf        ¦  «        Zgn4# eh$ r,Zi e1dbei› �¦  «         ddljZj ejjk        ¦   «          Y dZi[i�ŒdZi[iww xY w e1dceg› dd�¦  «          e%jl        d¦  «        dez  Zmemdfk    r e1dg¦  «         d^ZK n�ŒT�Œâ e%jn        ¦   «          dS dS )jas  
ImageFeatureExtractorV2
Purpose: ONNX-based image feature extraction and FCOS-based bbox detection
         using a UNetThin2 model exported with output_name='dec_all'.

Differences from V1 (UNetThin + CCL)
--------------------------------------
Feature map :  unchanged -- 'combined' decoder output (1, 5*F, H, W) is
               forwarded to the GNN pipeline exactly as before, via
               get_feature_map(). Class logits (1, C, H, W) are exposed
               separately via get_class_logits() -- the two are no longer
               concatenated (see get_feature_map()/get_class_logits()
               docstrings for why: they have different statistical
               character and should be pooled differently downstream).

Detection   :  CCL replaced by FCOS heads.
               - coarse head (H/8) : table, picture
               - fine   head (H/4) : all remaining foreground classes
               Each head's reg/ctr outputs are decoded directly from the
               ONNX session; no morphological post-processing is needed.

ONNX output layout varies by training flags (dec_all branch):

  use_fcos=False, use_text_seg=False:
    [0] combined   -- (1, 5*F, H, W)
    [1] logits     -- (1, C,   H, W)

  use_fcos=True, use_text_seg=False:
    [0] combined   -- (1, 5*F, H, W)
    [1] reg_coarse -- (1, 4,   H/8, W/8)
    [2] ctr_coarse -- (1, 1,   H/8, W/8)
    [3] reg_fine   -- (1, 4,   H/4, W/4)
    [4] ctr_fine   -- (1, 1,   H/4, W/4)
    [5] logits     -- (1, C,   H, W)

  use_fcos=True, use_text_seg=True, use_db_thresh=False:
    [0..5] same as above
    [6] text_logits -- (1, 2, H, W)  raw bg/text class logits

  use_fcos=True, use_text_seg=True, use_db_thresh=True:
    [0..5] same as above
    [6] text_logits -- (1, 2, H, W)
    [7] thresh_map  -- (1, 1, H, W)  adaptive threshold in [0, 1]
    [8] db_map      -- (1, 1, H, W)  differentiable binary map in [0, 1]
é    Né   )Úresize_imageÚto_grayÚsoftmax_numpyÚsigmoid_numpyÚxÚaxisÚreturnc                 ó’   — | t          j        | |d¬¦  «        z
  }t          j        |¦  «        }|t          j        ||d¬¦  «        z  S )zB
        Numerically stable softmax along the given axis.
        T)r	   Úkeepdims)ÚnpÚmaxÚexpÚsum)r   r	   Ú	x_shiftedÚexp_xs       ú /Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/pymupdf/layout/onnx/ImageFeatureExtractorV2.pyr   r   4   sJ   € ð �œ˜q t°dÐ;Ñ;Ô;Ñ;ˆ	Ý”�yÑ!Ô!ˆØ•r”v˜e¨$¸Ð>Ñ>Ô>Ñ>Ð>ó    c                 ó8   — ddt          j        |  ¦  «        z   z  S )z-
        Numerically stable sigmoid.
        ç      ð?)r   r   )r   s    r   r   r   =   s   € ð �c�BœF A 2™JœJÑ&Ñ'Ð'r   ÚimageÚnew_sizec                 óŽ  — | j         dd…         \  }}|\  }}t          j        |¦  «        dz   ||z  z  dz
  }t          j        |¦  «        dz   ||z  z  dz
  }t          j        |¦  «                             t
          ¦  «        }t          j        |¦  «                             t
          ¦  «        }	t          j        |dz   d|dz
  ¦  «        }
t          j        |	dz   d|dz
  ¦  «        }||z
  ddd…f         }||	z
  dd…df         }t          j        |d|dz
  ¦  «        }t          j        |	d|dz
  ¦  «        }	| j        dk    ry| |	dd…df         |ddd…f         f         }| |	dd…df         |
ddd…f         f         }| |dd…df         |ddd…f         f         }| |dd…df         |
ddd…f         f         }nl| t          j        |	|¦  «                 }| t          j        |	|
¦  «                 }| t          j        ||¦  «                 }| t          j        ||
¦  «                 }d|z
  d|z
  z  }|d|z
  z  }d|z
  |z  }||z  }| j        dk    r/|d         |z  |d         |z  z   |d         |z  z   |d         |z  z   n||z  ||z  z   ||z  z   ||z  z   }t          j        |dd¦  «                             | j	        ¦  «        S )	a‡  
        Resize an image using bilinear interpolation with numpy (vectorized).
        Equivalent to cv2.resize(..., INTER_LINEAR).

        Parameters
        ----------
        image : np.ndarray
            Input image (H, W) or (H, W, C).
        new_size : tuple
            (new_width, new_height)

        Returns
        -------
        np.ndarray
            Resized image.
        Nr   ç      à?é   r   é   ).Néÿ   )
Úshaper   ÚarangeÚfloorÚastypeÚintÚclipÚndimÚix_Údtype)r   r   ÚhÚwÚnew_wÚnew_hr   ÚyÚx0Úy0Úx1Úy1ÚdxÚdyÚIaÚIbÚIcÚIdÚwaÚwbÚwcÚwdÚouts                          r   r   r   D   sü  € ð" Œ{˜2˜A˜2Œ‰ˆˆ1Ø‰ˆˆuõ ŒY�uÑÔ Ñ#¨¨E©	Ñ2°SÑ8ˆÝŒY�uÑÔ Ñ#¨¨E©	Ñ2°SÑ8ˆåŒX�a‰[Œ[×Ò¥Ñ$Ô$ˆÝŒX�a‰[Œ[×Ò¥Ñ$Ô$ˆÝŒW�R˜!‘V˜Q  A¡Ñ&Ô&ˆÝŒW�R˜!‘V˜Q  A¡Ñ&Ô&ˆà�"‰f�d˜A˜A˜A�gÔˆØ�"‰f�a�a�a˜�gÔˆõ ŒW�R˜˜A ™EÑ"Ô"ˆÝŒW�R˜˜A ™EÑ"Ô"ˆàŒ:˜Š?ˆ?à�r˜!˜!˜!˜T˜'”{ B t¨Q¨Q¨Q w¤KÐ/Ô0ˆBØ�r˜!˜!˜!˜T˜'”{ B t¨Q¨Q¨Q w¤KÐ/Ô0ˆBØ�r˜!˜!˜!˜T˜'”{ B t¨Q¨Q¨Q w¤KÐ/Ô0ˆBØ�r˜!˜!˜!˜T˜'”{ B t¨Q¨Q¨Q w¤KÐ/Ô0ˆBˆBà•r”v˜b "‘~”~Ô&ˆBØ•r”v˜b "‘~”~Ô&ˆBØ•r”v˜b "‘~”~Ô&ˆBØ•r”v˜b "‘~”~Ô&ˆBð �"‰f˜˜R™Ñ ˆØ�1�r‘6‰]ˆØ�"‰f˜‰]ˆØ�"‰Wˆð <A¼:Èº?¸?ð �)Œ}˜rÑ! B y¤M°BÑ$6Ñ6Ø�)Œ}˜rÑ!ñ"Ø$& y¤M°BÑ$6ñ7ð 7à�"‰W�r˜B‘wÑ  b¡Ñ(¨2°©7Ñ2ð 	õ Œw�s˜A˜sÑ#Ô#×*Ò*¨5¬;Ñ7Ô7Ð7r   c                 óè  — | j         dk    s| j        d         dk    rt          d¦  «        ‚| dd…dd…df                              t          j        ¦  «        }| dd…dd…df                              t          j        ¦  «        }| dd…dd…df                              t          j        ¦  «        }d|z  d|z  z   d	|z  z   }t	          j        |dd
¦  «                             t          j        ¦  «        S )a\  
        Convert a BGR image to grayscale using numpy only.
        Equivalent to cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).

        Parameters
        ----------
        image : np.ndarray
            Input image in BGR format, shape (H, W, 3).

        Returns
        -------
        np.ndarray
            Grayscale image, shape (H, W).
        r   r   z/Input must be a BGR image with shape (H, W, 3).Nr   r   gÉv¾Ÿ/½?gbX9´Èâ?g‰A`åÐ"Ó?r   )r$   r   Ú
ValueErrorr!   r   Úfloat32r#   Úuint8)r   ÚBÚGÚRÚgrays        r   r   r   �   sè   € ð Œ:˜Š?ˆ?˜eœk¨!œn°Ò1Ð1ÝÐNÑOÔOÐOð �!�!�!�Q�Q�Q˜�'ŒN×!Ò!¥"¤*Ñ-Ô-ˆØ�!�!�!�Q�Q�Q˜�'ŒN×!Ò!¥"¤*Ñ-Ô-ˆØ�!�!�!�Q�Q�Q˜�'ŒN×!Ò!¥"¤*Ñ-Ô-ˆð �q‰y˜5 1™9Ñ$ u¨q¡yÑ0ˆõ Œw�t˜Q Ñ$Ô$×+Ò+­B¬HÑ5Ô5Ð5r   )Ú
backgroundÚtextÚtitleÚpictureÚtablez	list-itemzpage-headerzpage-footerzsection-headerÚfootnoteÚcaptionÚformularC   rF   rG   c              #   ó2   K  — | ]\  }}|t           v ¯|V — Œd S ©N©Ú_COARSE_CLASS_NAMES©Ú.0ÚiÚnames      r   ú	<genexpr>rS   ·   s>   è è € ð ð Ù
ˆ!ˆTØÕ"Ð"Ð"ð Ø"Ð"Ð"Ð"ðð r   c                 ó6   — g | ]\  }}|d k    ¯|t           v¯|‘ŒS ©r   rM   rO   s      r   ú
<listcomp>rV   ¼   s:   € ð ð ð Ù
ˆ!ˆTØˆ1‚u€u�Õ0Ð0Ð0ð Ø0Ð0Ð0r   c                 ó.   — g | ]\  }}|t           k    ¯|‘ŒS © )Ú_PICTURE_CLASSrO   s      r   rV   rV   Á   s-   € ð ð ð Ù
ˆ!ˆT°½Ò0FÐ0F€AÐ0FÐ0FÐ0Fr   c                 ó>   — g | ]}t           |         t          k    ¯|‘ŒS rX   )Ú_CLASS_NAMESrY   )rP   rQ   s     r   rV   rV   Å   s-   € ð ð ð Ø
¥L°¤Oµ~Ò$EÐ$E€AÐ$EÐ$EÐ$Er   g333333Ó?g      Ð?ÚboxesÚscoresÚiou_thrc                 óš  — | j         d         dk    r t          j        dt          j        ¬¦  «        S | dd…df         | dd…df         | dd…df         | dd…df         f\  }}}}||z
  ||z
  z  }|                     ¦   «         ddd…         }g }	|j        �r|d         }
|	                     |
¦  «         |j        dk    rní|dd…         }t          j        ||
         ||         ¦  «        }t          j        ||
         ||         ¦  «        }t          j        ||
         ||         ¦  «        }t          j        ||
         ||         ¦  «        }t          j        d||z
  ¦  «        t          j        d||z
  ¦  «        z  }|||
         ||         z   |z
  d	z   z  }|||k             }|j        �°t          j	        |	t          j        ¬¦  «        S )
zxGreedy NMS on (N, 4) boxes sorted by descending score.

    Returns:
        keep : (K,) int array of kept indices.
    r   ©r&   Nr   r   r   éÿÿÿÿç        g�íµ ÷Æ°>)
r   r   ÚemptyÚint32ÚargsortÚsizeÚappendÚmaximumÚminimumÚarray)r\   r]   r^   r.   r/   Úx2Úy2ÚareasÚorderÚkeeprQ   ÚrestÚxx1Úyy1Úxx2Úyy2ÚinterÚious                     r   Ú
_nms_numpyrw   Õ   sÂ  € ð „{�1„~˜ÒÐÝŒx˜¥¤Ð*Ñ*Ô*Ð*à˜1˜1˜1˜a˜4”[ %¨¨¨¨1¨¤+¨u°Q°Q°Q¸°T¬{¸EÀ!À!À!ÀQÀ$¼KÐG�N€BˆˆB�Ø�2‰g˜"˜r™'Ñ"€EØ�^Š^ÑÔ˜d˜d ˜dÔ#€EØ€Dà
Œ*ñ %Ø�!ŒHˆØ�Š�A‰ŒˆØŒ:˜Š?ˆ?ØØ�Q�R�RŒyˆÝŒz˜"˜Qœ%  D¤Ñ*Ô*ˆÝŒz˜"˜Qœ%  D¤Ñ*Ô*ˆÝŒz˜"˜Qœ%  D¤Ñ*Ô*ˆÝŒz˜"˜Qœ%  D¤Ñ*Ô*ˆÝ”
˜3  c¡	Ñ*Ô*­R¬Z¸¸SÀ3¹YÑ-GÔ-GÑGˆØ˜˜qœ E¨$¤KÑ/°%Ñ7¸$Ñ>Ñ?ˆØ�S˜G’^Ô$ˆð Œ*ñ %õ Œ8�D¥¤Ð)Ñ)Ô)Ð)r   Ú
seg_logitsÚregÚctrÚ	score_thrÚnms_iouÚactive_class_indicesÚorig_hÚorig_wc                 ó  — | j         \  }}	}
}t          | d         d¬¦  «        }t          |d         ¦  «        }||z  }||
z  }t          j        |
t          j        ¬¦  «        dz   |z  }t          j        |t          j        ¬¦  «        dz   |z  }t          j        ||¦  «        \  }}|d         |z  }|d         |z  }|d         |z  }|d         |z  }|�|nt          t          d
|	¦  «        ¦  «        }g g g }}}|D �]`}|dk    s||	k    rŒ||         |z  }||k    }| 	                    ¦   «         sŒ6t          j
        ||z
  |         d|¦  «        }t          j
        ||z
  |         d|¦  «        } t          j
        ||z   |         d|¦  «        }!t          j
        ||z   |         d|¦  «        }"t          j        || |!|"gd
¬¦  «        }#||         }$t          |#|$|¦  «        }%|%j        dk    rŒñ|                     |#|%         ¦  «         |                     t          j        |%j        |t          j        ¬¦  «        ¦  «         |                     |$|%         ¦  «         �Œb|rCt          j        |d¬¦  «        t          j        |d¬¦  «        t          j        |d¬¦  «        dœgS t          j        dt          j        ¬¦  «        t          j        dt          j        ¬¦  «        t          j        dt          j        ¬¦  «        dœgS )a�  Decode one FCOS head's ONNX outputs into bbox dicts (numpy-only).

    Normalisation convention (DocumentJsonDataset):
        l_norm = l_fmap_px / fmap_w
        Decoded: l_orig = l_norm * orig_w
               = (l_fmap_px / fmap_w) * orig_w
        Grid:    grid_x_orig = (i + 0.5) * (orig_w / fmap_w)
        => x1 = grid_x_orig - l_orig  correctly recovers orig-space coordinate.

    Args:
        seg_logits : (1, C, H, W) -- raw segmentation logits at fmap resolution.
        reg        : (1, 4, H, W) -- normalised (l, t, r, b) per DocumentJsonDataset.
        ctr        : (1, 1, H, W) -- centerness logits.
        score_thr  : minimum score (seg_prob x centerness) to keep.
        nms_iou    : IoU threshold for class-wise NMS.
        active_class_indices : foreground indices this head is responsible for.
                               None -> all foreground classes.
        orig_h, orig_w : output coordinate space (page image size).

    Returns:
        List with one element (batch=1):
            {'boxes':  (N, 4) float32  -- (x1, y1, x2, y2) in orig image px,
             'labels': (N,)   int32,
             'scores': (N,)   float32}
    r   ©r	   ©r   r   r`   r   )r   r   )r   r   )r   r   Nr   ©r\   Úlabelsr]   )r   é   )r   r   r   r   r   r=   ÚmeshgridÚlistÚrangeÚanyr#   Ústackrw   rf   rg   Úfullrd   Úconcatenaterc   )&rx   ry   rz   r{   r|   r}   r~   r   Ú_ÚCÚHÚWÚ	seg_probsÚ
ctr_scoresÚscale_xÚscale_yÚysÚxsÚgrid_xÚgrid_yÚlÚtÚrÚbvÚ	cls_rangeÚ	all_boxesÚ
all_labelsÚ
all_scoresÚclsÚscoreÚmaskr.   r/   rk   rl   r\   r]   ro   s&                                         r   Ú_fcos_decode_numpyr¤   ô   s  € ðF Ô!�J€A€qˆ!ˆQõ ˜j¨œm°!Ð4Ñ4Ô4€Iå˜s 4œyÑ)Ô)€Jð �q‰j€GØ�q‰j€GÝ
Œ)�A�RœZÐ
(Ñ
(Ô
(¨3Ñ
.°'Ñ	9€BÝ
Œ)�A�RœZÐ
(Ñ
(Ô
(¨3Ñ
.°'Ñ	9€BÝ”[  RÑ(Ô(�N€FˆFð 
ˆTŒ�VÑ	€AØ	ˆTŒ�VÑ	€AØ	ˆTŒ�VÑ	€AØ	ˆTŒ�VÑ	€Bà(<Ð(HÐ$Ð$Ý�%  1™+œ+Ñ&Ô&ð ð )+¨B°˜:ˆz€Iàð (ñ (ˆØ�!Š8ˆ8�s˜a’x�xØà˜#” Ñ+ˆØ˜	Ò!ˆØ�xŠx‰zŒzð 	ØåŒW�f˜q‘j $Ô'¨¨FÑ3Ô3ˆÝŒW�f˜q‘j $Ô'¨¨FÑ3Ô3ˆÝŒW�f˜q‘j $Ô'¨¨FÑ3Ô3ˆÝŒW�f˜r‘k 4Ô(¨!¨VÑ4Ô4ˆå”˜2˜r 2 rÐ*°Ð3Ñ3Ô3ˆØ�t”ˆå˜% ¨Ñ1Ô1ˆØŒ9˜Š>ˆ>Øà×Ò˜˜tœÑ%Ô%Ð%Ø×Ò�"œ' $¤)¨S½¼ÐAÑAÔAÑBÔBÐBØ×Ò˜& œ,Ñ'Ô'Ð'Ñ'àð @Ýœ>¨)¸1Ð=Ñ=Ô=Ýœ>¨*¸1Ð=Ñ=Ô=Ýœ>¨*¸1Ð=Ñ=Ô=ð?ð ?ð @ð 	@õ ”x ­b¬jÐ9Ñ9Ô9Ý”x ­r¬xÐ8Ñ8Ô8Ý”x ­r¬zÐ:Ñ:Ô:ð<ð <ð =ð =r   Údets_coarseÚ	dets_finec                 óò  — | d         |d         }}t          j        |d         |d         gd¬¦  «        }t          j        |d         |d         gd¬¦  «        }t          j        |d         |d         gd¬¦  «        }|j        d         dk    r|||dœS g g g }
}	}t          j        |¦  «        D ]ˆ}||k    }t	          ||         ||         |¦  «        }|                     ||         |         ¦  «         |	                     ||         |         ¦  «         |
                     ||         |         ¦  «         Œ‰t          j        |d¬¦  «        t          j        |	d¬¦  «        t          j        |
d¬¦  «        dœS )a§  Concatenate coarse + fine detections and apply a per-class NMS pass.

    Class-agnostic NMS would incorrectly suppress valid detections of
    different classes that happen to overlap (e.g. a section-header and the
    text block immediately below it).  Per-class NMS only suppresses
    duplicate candidates within the same class, which matches the convention
    used in fcos_decode (PyTorch path).

    Args:
        dets_coarse, dets_fine : each a single-element list (batch=1) as
                                 returned by _fcos_decode_numpy.
        nms_iou : IoU threshold for per-class NMS.

    Returns:
        Single dict with keys 'boxes', 'labels', 'scores'.
    r   r\   r�   r„   r]   rƒ   )r   rŒ   r   Úuniquerw   rg   )r¥   r¦   r|   ÚdcÚdfrž   rŸ   r    Ú
kept_boxesÚkept_labelsÚkept_scoresr¡   r£   ro   s                 r   Ú_merge_detectionsr®   S  s‡  € ð* ˜Œ^˜Y qœ\ˆ€Bå”  G¤¨r°'¬{Ð ;À1ÐEÑEÔE€IÝ”  H¤¨r°(¬|Ð <À1ÐEÑEÔE€JÝ”  H¤¨r°(¬|Ð <À1ÐEÑEÔE€Jà„�qÔ˜QÒÐØ"¨jÀJÐOÐOÐOà+-¨r°2˜[�€JÝŒy˜Ñ$Ô$ð 3ð 3ˆØ˜sÒ"ˆÝ˜I dœO¨Z¸Ô-=¸wÑGÔGˆØ×Ò˜) Dœ/¨$Ô/Ñ0Ô0Ð0Ø×Ò˜: dÔ+¨DÔ1Ñ2Ô2Ð2Ø×Ò˜: dÔ+¨DÔ1Ñ2Ô2Ð2Ð2õ ”. °1Ð5Ñ5Ô5Ý”. °1Ð5Ñ5Ô5Ý”. °1Ð5Ñ5Ô5ðð ð r   c                   óÆ   — e Zd ZdZd„ Zd„ Zd„ Zdd„Zdeded	e	j
        fd
„Zd„ Zd„ Zd„ Zd„ Zd	efd„Zd	efd„Zd	efd„Z	 	 ddeded	efd„Z	 	 ddeded	efd„Zd	efd„ZdS )ÚImageFeatureExtractorV2a  
    Wraps a UNetThin2 ONNX session (exported with output_name='dec_all',
    use_fcos=True) to produce:

      - a combined decoder feature map  (forwarded to the GNN pipeline)
      - picture-class bbox detections   (lazy, via get_picture_detections())
      - non-picture detections          (lazy, via is_image_page())

    The public interface is fully compatible with ImageFeatureExtractorV1
    so existing callers (BoxRFDGNN, image_feature_extraction_task) require
    no changes.

    Internal changes vs. V1:
      - CCL (connected-component labelling) is replaced by FCOS decoding.
      - ONNX session produces 5 outputs instead of 1; all are stored.
      - Detection results are split by head assignment (coarse / fine) and
        merged with a final NMS pass.
    c                 óô   — || _         d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _	        d| _
        d| _        d| _        d| _        d| _        d| _        d| _        dS )ai  
        Args:
            onnx_session : onnxruntime.InferenceSession for a UNetThin2 model
                           exported with output_name='dec_all', use_fcos=True.
                           Expected output order:
                             [0] combined, [1] reg_coarse, [2] ctr_coarse,
                             [3] reg_fine, [4] ctr_fine
        NFr   )Ú_sessionÚ	_combinedÚ_reg_coarseÚ_ctr_coarseÚ	_reg_fineÚ	_ctr_fineÚ_logitsÚ_text_logitsÚ_thresh_mapÚ_db_mapÚ_cachedÚ_detections_pictureÚ_detections_non_pictureÚ_page_hÚ_page_wÚ	_target_hÚ	_target_w)ÚselfÚonnx_sessions     r   Ú__init__z ImageFeatureExtractorV2.__init__˜  s�   € ð %ˆŒð  ˆŒØˆÔØˆÔØˆŒØˆŒØˆŒð !ˆÔØ ˆÔØ ˆŒð ˆŒð (,ˆÔ Ø'+ˆÔ$ð ˆŒØˆŒØˆŒØˆŒˆˆr   c                 ó   — d| _         dS )z4Signal that predict() results should be reused once.TN©r¼   ©rÃ   s    r   Úmark_cachedz#ImageFeatureExtractorV2.mark_cachedÂ  s   € àˆŒˆˆr   c                 ó&   — | j         r	d| _         dS dS )z’
        Return True (and clear the flag) if a cached result exists.
        Called by image_feature_extraction_task() to skip predict().
        FTrÇ   rÈ   s    r   Úconsume_cachez%ImageFeatureExtractorV2.consume_cacheÆ  s   € ð
 Œ<ð 	Ø ˆDŒLØ�4Øˆur   Nc                 ó6  — |j         dd…         \  | _        | _        | j                             ¦   «         d         j         }|d         |d         c| _        | _        t          || j        | j        f¦  «        }t          |¦  «         	                    t          j        ¦  «        }|                     ¦   «         |                     ¦   «         }}||z
  ||z
  dz   z  }t          j        |d¬¦  «        }|�t          j        ||gd¬¦  «        }t          j        |d¬¦  «        }| j                             ¦   «         d         j        }	d„ | j                             ¦   «         D ¦   «         }
| j                             d|	|i¦  «        }t'          t)          |
|¦  «        ¦  «        }|                     d¦  «        | _        |                     d	¦  «        | _        |                     d
¦  «        | _        |                     d¦  «        | _        |                     d¦  «        | _        |                     d¦  «        | _        |                     d¦  «        | _        |                     d¦  «        | _        |                     d¦  «        | _        d| _        d| _         dS )a¸  
        Run ONNX inference and store all five head outputs.
        FCOS decoding is NOT performed here -- results are computed lazily.

        Calling predict() invalidates all previously cached detection results
        so that get_picture_detections() and is_image_page() always reflect
        the current page.

        Args:
            page_img   : np.ndarray (H, W, C) uint8
            aug_fetmap : optional extra channel map concatenated before inference

        Side effects:
            self._combined / _logits / _reg_coarse / _ctr_coarse /
            _reg_fine / _ctr_fine    <- set from ONNX outputs
            self._detections_picture / _non_picture <- reset to None
        Nr   r   r   g:Œ0âŽyE>r�   c                 ó   — g | ]	}|j         ‘Œ
S rX   ©rR   ©rP   Úos     r   rV   z3ImageFeatureExtractorV2.predict.<locals>.<listcomp>ù  s   € ÐDÐDÐD 1˜œÐDÐDÐDr   ÚcombinedÚ
reg_coarseÚ
ctr_coarseÚreg_fineÚctr_fineÚlogitsÚtext_logitsÚ
thresh_mapÚdb_map)!r   r¿   rÀ   r²   Ú
get_inputsrÁ   rÂ   r   r   r!   r   r=   Úminr   Úexpand_dimsrŒ   rR   Úget_outputsÚrunÚdictÚzipÚgetr³   r´   rµ   r¶   r·   r¸   r¹   rº   r»   r½   r¾   )rÃ   Úpage_imgÚ
aug_fetmapÚinput_shapeÚimg_resizedÚimg_grayÚmin_valÚmax_valÚnn_inputÚ
input_nameÚoutput_namesÚort_outputsÚout_maps                r   ÚpredictzImageFeatureExtractorV2.predictÔ  s<  € ð$ &.¤^°B°Q°BÔ%7Ñ"ˆŒ�d”là”m×.Ò.Ñ0Ô0°Ô3Ô9ˆØ)4°Q¬¸ÀQ¼Ð&ˆŒ˜œõ # 8¨d¬n¸d¼nÐ-MÑNÔNˆÝ˜kÑ*Ô*×1Ò1µ"´*Ñ=Ô=ˆà#Ÿ<š<™>œ>¨8¯<ª<©>¬>�ˆØ˜wÑ&¨7°WÑ+<¸tÑ+CÑDˆå”> (°Ð3Ñ3Ô3ˆØÐ!Ý”~ x°Ð&<À1ÐEÑEÔEˆHÝ”> (°Ð3Ñ3Ô3ˆð ”m×.Ò.Ñ0Ô0°Ô3Ô8ˆ
ØDÐD¨¬×(AÒ(AÑ(CÔ(CÐDÑDÔDˆØ”m×'Ò'¨¨z¸8Ð.DÑEÔEˆõ •s˜<¨Ñ5Ô5Ñ6Ô6ˆà"Ÿ;š; zÑ2Ô2ˆŒØ"Ÿ;š; |Ñ4Ô4ˆÔØ"Ÿ;š; |Ñ4Ô4ˆÔØ"Ÿ;š; zÑ2Ô2ˆŒØ"Ÿ;š; zÑ2Ô2ˆŒØ"Ÿ;š; xÑ0Ô0ˆŒð $ŸKšK¨Ñ6Ô6ˆÔØ#ŸKšK¨Ñ5Ô5ˆÔØ#ŸKšK¨Ñ1Ô1ˆŒð (,ˆÔ Ø'+ˆÔ$Ð$Ð$r   Úfmap_hÚfmap_wr
   c           	      óV  — | j         j        \  }}}}||k    r||k    r| j         S 	 ddl}t          j        d|||ft          j        ¬¦  «        }t          |¦  «        D ].}	 |j        | j         d|	f         ||f|j        ¬¦  «        |d|	f<   Œ/|S # t          $ r� t          j
        |¦  «        |z  |z                       t          ¦  «        }
t          j
        |¦  «        |z  |z                       t          ¦  «        }| j         dd…dd…|
dd…df         |ddd…f         f         cY S w xY w)aF  Resize the real segmentation logits to the given feature-map size.

        Uses self._logits, which is the actual seg_head output (index 5 from
        the ONNX session).  This is identical to the logits used in
        check_unethin2_result, so FCOS score computation is consistent between
        the PyTorch and ONNX inference paths.

        Args:
            fmap_h, fmap_w : target spatial size matching the reg/ctr tensors
                             (H/8 for coarse head, H/4 for fine head).

        Returns:
            np.ndarray (1, C, fmap_h, fmap_w) float32
        r   Nr   r`   ©Úinterpolation)r¸   r   Úcv2r   Úzerosr=   rˆ   ÚresizeÚINTER_LINEARÚImportErrorr   r!   r"   )rÃ   rï   rð   r�   rŽ   r�   r�   rô   r:   Úcr•   r–   s               r   Ú_logits_for_fmapz(ImageFeatureExtractorV2._logits_for_fmap  s`  € ð ”\Ô'‰
ˆˆ1ˆa�Ø�Š;ˆ;˜1 š;˜;Ø”<Ðð	@ØˆJˆJˆJÝ”(˜A˜q &¨&Ð1½¼ÐDÑDÔDˆCÝ˜1‘X”Xð ð �Ø&˜CœJØ”L  A Ô&Ø˜VÐ$Ø"%Ô"2ðñ ô ��A�q�D‘	�	ð
 ˆJøÝð 	@ð 	@ð 	@Ý”)˜FÑ#Ô# aÑ'¨&Ñ0×8Ò8½Ñ=Ô=ˆBÝ”)˜FÑ#Ô# aÑ'¨&Ñ0×8Ò8½Ñ=Ô=ˆBØ”<    1 1 1 b¨¨¨¨D¨¤k°2°d¸A¸A¸A°g´;Ð >Ô?Ð?Ð?Ð?ð	@øøøs   ¦A'B ÂBD(Ä'D(c           
      ó  — | j         �dS | j        j        d         }| j        j        d         }|                      ||¦  «        }t	          || j        | j        t          t          t          | j	        | j
        ¬¦  «        }|d         | _         dS )zCRun picture FCOS decoding if not yet computed this predict() cycle.Nr   r   ©rx   ry   rz   r{   r|   r}   r~   r   r   )r½   r´   r   rú   r¤   rµ   Ú_SCORE_COARSE_THRÚ_NMS_IOUÚ_PICTURE_CLASS_IDXr¿   rÀ   )rÃ   Úfmap_h_cÚfmap_w_cÚlogits_cÚdetss        r   Ú_ensure_picture_detectionsz2ImageFeatureExtractorV2._ensure_picture_detections5  s�   € àÔ#Ð/ØˆFàÔ#Ô)¨!Ô,ˆØÔ#Ô)¨!Ô,ˆØ×(Ò(¨°8Ñ<Ô<ˆå!ØØÔ ØÔ Ý'ÝÝ!3Ø”<Ø”<ð	
ñ 	
ô 	
ˆð $(¨¤7ˆÔ Ð Ð r   c           
      ó&  — | j         �dS | j        j        d         }| j        j        d         }|                      ||¦  «        }t	          || j        | j        t          t          t          pd| j	        | j
        ¬¦  «        }| j        j        d         }| j        j        d         }|                      ||¦  «        }t	          || j        | j        t          t          t          pd| j	        | j
        ¬¦  «        }t          ||t          ¦  «        | _         dS )a3  Run non-picture FCOS decoding if not yet computed this predict() cycle.

        Non-picture classes span both heads:
          - coarse head : table (non-picture coarse class)
          - fine   head : text, title, list-item, page-header, ...
        Both results are merged with a final NMS pass.
        Nr   r   rü   )r¾   r´   r   rú   r¤   rµ   rý   rþ   Ú_NON_PICTURE_COARSE_IDXr¿   rÀ   r¶   r·   Ú_SCORE_FINE_THRÚ_NON_PICTURE_FINE_IDXr®   )	rÃ   r   r  r  r¥   Úfmap_h_fÚfmap_w_fÚlogits_fr¦   s	            r   Ú_ensure_non_picture_detectionsz6ImageFeatureExtractorV2._ensure_non_picture_detectionsK  s  € ð Ô'Ð3ØˆFð Ô#Ô)¨!Ô,ˆØÔ#Ô)¨!Ô,ˆØ×(Ò(¨°8Ñ<Ô<ˆå(ØØÔ ØÔ Ý'ÝÝ!8Ð!@¸DØ”<Ø”<ð	
ñ 	
ô 	
ˆð ”>Ô'¨Ô*ˆØ”>Ô'¨Ô*ˆØ×(Ò(¨°8Ñ<Ô<ˆå&ØØ”Ø”Ý%ÝÝ!6Ð!>¸$Ø”<Ø”<ð	
ñ 	
ô 	
ˆ	õ (9Ø˜¥Hñ(
ô (
ˆÔ$Ð$Ð$r   c                 ó   — | j         S )aD  
        Return the combined decoder feature map from the last predict() call.
        This is the continuous embedding output only -- it no longer
        includes the class logits (see get_class_logits() for those).

        Returns:
            np.ndarray (1, 5*F, H, W) float32, or None if predict() not called.
        )r³   rÈ   s    r   Úget_feature_mapz'ImageFeatureExtractorV2.get_feature_map~  s   € ð Œ~Ðr   c                 ó   — | j         S )a‚  
        Return the per-class segmentation logits from the last predict()
        call. Raw (pre-softmax) scores; apply softmax before pooling ops
        that expect a probability distribution (e.g. entropy, margin --
        see roi_pooling.extract_bbox_features_by_roi_pooling).

        Returns:
            np.ndarray (1, C, H, W) float32, or None if predict() not called.
        )r¸   rÈ   s    r   Úget_class_logitsz(ImageFeatureExtractorV2.get_class_logits‰  s   € ð Œ|Ðr   c                 óð  — |                      ¦   «         sdS |                     d¦  «                             ¦   «         rdS |                     ¦   «         }t	          j        |j        t          j        ¬¦  «        }|                     |j	        |j
        |j        ¦  «        }|                      |¦  «         |                      ¦   «          |                      ¦   «          | j        d         j        d         dk    S )aç  
        Determine whether the page is an image-only PDF page that requires OCR.

        Returns True only when ALL three conditions hold:
          1. The page contains at least one raster image (pymupdf).
          2. The page contains no embedded selectable text (pymupdf).
          3. The FCOS model detects at least one non-picture region
             (text, table, header, etc.) with score > _SCORE_FINE_THR.

        Calls predict() internally and marks the result as cached so the
        immediately following BoxRFDGNN.predict() skips re-running inference.
        Picture detection is deferred to get_picture_detections() if needed.

        Args:
            page : PyMuPDF page object

        Returns:
            bool
        FrD   r`   r\   r   )Úget_image_infoÚget_textÚstripÚ
get_pixmapr   Ú
frombufferÚsamplesr>   ÚreshapeÚheightÚwidthÚnrî   rÉ   r  r¾   r   )rÃ   ÚpageÚpixÚ
bytes_datarâ   s        r   Úis_image_pagez%ImageFeatureExtractorV2.is_image_page•  sÝ   € ð* ×"Ò"Ñ$Ô$ð 	Ø�5ð �=Š=˜Ñ Ô ×&Ò&Ñ(Ô(ð 	Ø�5ð —_’_Ñ&Ô&ˆÝ”] 3¤;µb´hÐ?Ñ?Ô?ˆ
Ø×'Ò'¨¬
°C´I¸s¼uÑEÔEˆà�Š�XÑÔÐØ×ÒÑÔÐð 	×+Ò+Ñ-Ô-Ð-àÔ+¨GÔ4Ô:¸1Ô=ÀÒAÐAr   c                 óÊ   — | j         €t          d¦  «        ‚|                      ¦   «          | j        }|d         j        d         dk    rg S |d                              ¦   «         S )a©  
        Return picture-class detections from the last predict() call in
        page pixel coordinates.

        Runs picture FCOS decoding on the first call after each predict().
        Subsequent calls within the same predict() cycle are free (cached).

        Returns:
            list of [x1, y1, x2, y2] in page pixel space (float).

        Raises:
            RuntimeError if predict() has not been called.
        Nz8predict() must be called before get_picture_detections()r\   r   )r³   ÚRuntimeErrorr  r½   r   Útolist)rÃ   Údets     r   Úget_picture_detectionsz.ImageFeatureExtractorV2.get_picture_detections¾  so   € ð Œ>Ð!ÝØJñô ð ð 	×'Ò'Ñ)Ô)Ð)àÔ&ˆØˆwŒ<Ô˜aÔ  AÒ%Ð%ØˆIà�7Œ|×"Ò"Ñ$Ô$Ð$r   c                 ó0  — | j         €t          d¦  «        ‚|                      ¦   «          |                      ¦   «          | j        }| j        }t          j        |d         |d         gd¬¦  «        }t          j        |d         |d         gd¬¦  «        }t          j        |d         |d         gd¬¦  «        }|j        d         dk    rg g g g dœS | 	                    ¦   «         | 	                    ¦   «         | 	                    ¦   «         d	„ |D ¦   «         dœS )
ai  
        Return all FCOS layout detections (both heads, merged) from the last
        predict() call in page pixel coordinates.

        Useful for downstream consumers that need the full detection set
        rather than the picture-only subset.

        Returns:
            dict with keys:
                'boxes'  : list of [x1, y1, x2, y2]
                'labels' : list of int  (1-based foreground class indices)
                'scores' : list of float
                'names'  : list of str  (class name for each detection)

        Raises:
            RuntimeError if predict() has not been called.
        Nz7predict() must be called before get_layout_detections()r\   r   r�   r„   r]   ©r\   r„   r]   Únamesc                 óv   — g | ]6}|t          t          ¦  «        k     rt          |         nt          |¦  «        ‘Œ7S rX   )Úlenr[   Ústr)rP   Úlbls     r   rV   zAImageFeatureExtractorV2.get_layout_detections.<locals>.<listcomp>  sJ   € ð ð ð àð &)­3­|Ñ+<Ô+<Ò%<Ð%<•˜SÔ!Ð!Å#ÀcÁ(Ä(ðð ð r   )
r³   r!  r  r  r½   r¾   r   rŒ   r   r"  )rÃ   ÚpicÚnopxrž   rŸ   r    s         r   Úget_layout_detectionsz-ImageFeatureExtractorV2.get_layout_detectionsÝ  s8  € ð$ Œ>Ð!ÝØIñô ð ð 	×'Ò'Ñ)Ô)Ð)Ø×+Ò+Ñ-Ô-Ð-àÔ'ˆØÔ+ˆå”^ S¨¤\°D¸´MÐ$BÈ!ÐLÑLÔLˆ	Ý”^ S¨¤]°D¸´NÐ$CÈ!ÐLÑLÔLˆ
Ý”^ S¨¤]°D¸´NÐ$CÈ!ÐLÑLÔLˆ
àŒ?˜1Ô Ò"Ð"Ø¨2¸ÀbÐIÐIÐIð  ×&Ò&Ñ(Ô(Ø ×'Ò'Ñ)Ô)Ø ×'Ò'Ñ)Ô)ðð à%ðñ ô ð	
ð 
ð 	
r   r   é
   Ú	db_threshÚmin_areac                 óÒ  — | j         €t          d¦  «        ‚|                      ¦   «         }|d         s|S 	 |                      ||¬¦  «        }n# t          $ r |cY S w xY w|s|S t          rt          d         nd}t          d„ t          t          ¦  «        D ¦   «         d¦  «        }t          |d         ¦  «        }t          j
        |t          j        ¬	¦  «        }|dd…df         |dd…d
f         z   dz  }	|dd…df         |dd…df         z   dz  }
t          j        t          |¦  «        dt          j        ¬	¦  «        }t          t          |d         |d         ¦  «        ¦  «        D ]?\  }\  }}||k    rŒ|\  }}}}|	|k    |	|k    z  |
|k    z  |
|k    z  }||dk    z  }|||<   Œ@g }g }g }g }t          t          |d         |d         |d         |d         ¦  «        ¦  «        D �]€\  }\  }}}}||k    rU|                     |¦  «         |                     |¦  «         |                     |¦  «         |                     |¦  «         Œf|||k             }|j        d         dk    rŒ„|                     t#          |dd…df                              ¦   «         ¦  «        t#          |dd…df                              ¦   «         ¦  «        t#          |dd…d
f                              ¦   «         ¦  «        t#          |dd…df                              ¦   «         ¦  «        g¦  «         |                     |¦  «         |                     |¦  «         |                     |¦  «         �Œ‚||dk             }|D ]h}|                     |                     ¦   «         ¦  «         |                     |¦  «         |                     d¦  «         |                     d¦  «         Œi||||dœS )a¶  
        Refine layout detections by grouping text boxes into layout regions.

        Algorithm:
          1. For each text detection box, check which layout bbox contains
             its centre point.
          2. If it belongs to a layout region, add it to that region's group.
             The final bbox for that region is the union of all its text boxes.
             The original layout class and score are preserved.
             picture regions are excluded from this process (kept as-is).
          3. Text boxes whose centre does not fall inside any layout region
             are assigned as independent 'text' class detections.
          4. Layout regions that received no text boxes keep their original
             FCOS bbox unchanged.

        Requires use_text_seg=True export.  Falls back to
        get_layout_detections() when text outputs are absent.

        Args:
            db_thresh : binarisation threshold passed to get_text_detection().
            min_area  : minimum contour area passed to get_text_detection().

        Returns:
            Same dict structure as get_layout_detections().

        Raises:
            RuntimeError if predict() has not been called.
        Nz?predict() must be called before get_layout_detections_refined()r\   )r0  r1  r   ra   c              3   ó,   K  — | ]\  }}|d k    ¯|V — ŒdS )rD   NrX   )rP   rQ   r  s      r   rS   zHImageFeatureExtractorV2.get_layout_detections_refined.<locals>.<genexpr><  s7   è è € ð ,ð ,¡$ ! QØ  Fš{˜{ð Ø*˜{˜{˜{ð,ð ,r   r   r`   r   r   r   r„   r]   r'  rb   rD   r&  )r³   r!  r.  Úget_text_detectionrÿ   ÚnextÚ	enumerater[   r)  r   rj   r=   r‹   rd   rà   rg   r   ÚfloatrÛ   r   r"  )rÃ   r0  r1  ÚlayoutÚ
text_boxesÚpicture_idxÚtext_idxÚn_layoutÚtbÚcxÚcyÚ
assignmentrQ   Úboxr+  r.   r/   rk   rl   ÚinsideÚunassigned_insideÚ	out_boxesÚ
out_labelsÚ
out_scoresÚ	out_namesr¢   rR   ÚmembersÚorphansÚrows                                 r   Úget_layout_detections_refinedz5ImageFeatureExtractorV2.get_layout_detections_refined  s|  € ð< Œ>Ð!ÝØQñô ð ð ×+Ò+Ñ-Ô-ˆØ�gŒð 	ØˆMð	Ø×0Ò0¸9Ø:Bð 1ñ Dô DˆJˆJøåð 	ð 	ð 	ØˆMˆMˆMð	øøøð ð 	ØˆMå/AÐIÕ(¨Ô+Ð+ÀrˆÝð ,ð ,­)µLÑ*AÔ*Að ,ñ ,ô ,Ø-.ñ0ô 0ˆõ �v˜g”Ñ'Ô'ˆÝ”8˜J­b¬jÐ9Ñ9Ô9ˆØ�q�q�q˜!�t”H˜r ! ! ! Q $œxÑ'¨3Ñ.ˆØ�q�q�q˜!�t”H˜r ! ! ! Q $œxÑ'¨3Ñ.ˆõ ”W�S ™_œ_¨b½¼ÐAÑAÔAˆ
å&¥s¨6°'¬?¸FÀ8Ô<LÑ'MÔ'MÑNÔNð 	.ð 	.‰MˆA‰z��SØ�kÒ!Ð!ØØ ‰NˆB��B˜Ø˜B’h 2¨¢8Ñ,°°b²Ñ9¸RÀ2ºXÑFˆFà &¨*¸Ò*:Ñ ;ÐØ,-ˆJÐ(Ñ)Ð)ð ˆ	Øˆ
Øˆ
Øˆ	å*3µCØ�w” ¨Ô!1Ø�xÔ  &¨¤/ñ53ô 53ñ +4ô +4ð 	#ñ 	#Ñ&ˆAÑ&��S˜% ð �kÒ!Ð!Ø× Ò  Ñ%Ô%Ð%Ø×!Ò! #Ñ&Ô&Ð&Ø×!Ò! %Ñ(Ô(Ð(Ø× Ò  Ñ&Ô&Ð&Øà˜ qšÔ)ˆGØŒ}˜QÔ 1Ò$Ð$àð ×ÒÝ�g˜a˜a˜a ˜d”m×'Ò'Ñ)Ô)Ñ*Ô*Ý�g˜a˜a˜a ˜d”m×'Ò'Ñ)Ô)Ñ*Ô*Ý�g˜a˜a˜a ˜d”m×'Ò'Ñ)Ô)Ñ*Ô*Ý�g˜a˜a˜a ˜d”m×'Ò'Ñ)Ô)Ñ*Ô*ð	ñ ô ð ð ×Ò˜cÑ"Ô"Ð"Ø×Ò˜eÑ$Ô$Ð$Ø×Ò˜TÑ"Ô"Ð"Ñ"ð �Z 2Ò%Ô&ˆØð 	%ð 	%ˆCØ×Ò˜SŸZšZ™\œ\Ñ*Ô*Ð*Ø×Ò˜hÑ'Ô'Ð'Ø×Ò˜cÑ"Ô"Ð"Ø×Ò˜VÑ$Ô$Ð$Ð$ð  Ø Ø Øð	
ð 
ð 	
s   ¶A ÁAÁAc                 óö  — | j         €t          d¦  «        ‚| j        €t          d¦  «        ‚	 ddl}n# t          $ r t          d¦  «        ‚w xY w| j        �| j        d         }n$| j        d         }t          |d¬¦  «        d         }|j        \  }}| j        |z  }| j	        |z  }	||k     
                    t          j        ¦  «        d	z  }
|                     |
|j        |j        ¦  «        \  }}g }|D �]}|                     |¦  «        |k     rŒ|                     |¦  «        \  }}}}t%          t          j        ||z  d| j        ¦  «        ¦  «        }t%          t          j        ||	z  d| j	        ¦  «        ¦  «        }t%          t          j        ||z   |z  d| j        ¦  «        ¦  «        }t%          t          j        ||z   |	z  d| j	        ¦  «        ¦  «        }|                     ||||g¦  «         �Œ|S )
a  
        Extract text bounding boxes from the DB binary map.

        The DB map is a near-binary probability map computed in forward() as
            db_map = sigmoid(k * (text_prob - thresh_map)),  k=50
        so values are clustered near 0 or 1.  Thresholding at db_thresh and
        running contour extraction yields tight word/line-level text boxes.

        Falls back to text_prob (softmax class-1 of text_logits) when the
        model was exported without use_db_thresh (db_map is None).

        Args:
            db_thresh : binarization threshold applied to db_map or text_prob.
                        Default 0.5 works well for db_map; increase to ~0.6-0.7
                        when falling back to text_prob to reduce false positives.
            min_area  : minimum contour area in feature-map pixels.
                        Contours smaller than this are discarded as noise.

        Returns:
            list of [x1, y1, x2, y2] in page pixel coordinates (float).

        Raises:
            RuntimeError if predict() has not been called.
            RuntimeError if the model was not exported with use_text_seg=True.
        Nz4predict() must be called before get_text_detection()úlThe ONNX model was not exported with use_text_seg=True. Re-export with use_text_seg=True to use this method.r   z2opencv-python is required for get_text_detection()r‚   r�   r   r   )r³   r!  r¹   rô   rø   r»   r   r   rÀ   r¿   r!   r   r>   ÚfindContoursÚRETR_EXTERNALÚCHAIN_APPROX_SIMPLEÚcontourAreaÚboundingRectr7  r#   rg   )rÃ   r0  r1  Ú_cv2ÚprobÚtlÚfeat_hÚfeat_wr“   r”   ÚbinaryÚcontoursr�   r\   Úcntr   r+   r(   r'   r.   r/   rk   rl   s                          r   r4  z*ImageFeatureExtractorV2.get_text_detection�  s+  € ð6 Œ>Ð!ÝØFñô ð ð ÔÐ$ÝðGñô ð ð
	UØÐÐÐÐøÝð 	Uð 	Uð 	UÝÐSÑTÔTÐTð	Uøøøð Œ<Ð#Ø”< Ô%ˆDˆDàÔ$ QÔ'ˆBÝ  ¨!Ð,Ñ,Ô,¨QÔ/ˆDàœ‰ˆ�Ø”, Ñ'ˆØ”, Ñ'ˆð ˜)Ò#×+Ò+­B¬HÑ5Ô5¸Ñ;ˆØ×'Ò'Ø�DÔ&¨Ô(@ñ
ô 
‰ˆ�!ð ˆØð 	+ñ 	+ˆCØ×Ò Ñ$Ô$ xÒ/Ð/ØØ×*Ò*¨3Ñ/Ô/‰JˆAˆq�!�QÝ•r”w˜q 7™{°a¸¼ÑFÔFÑGÔGˆBÝ•r”w˜q 7™{°a¸¼ÑFÔFÑGÔGˆBÝ•r”w  A¡¨Ñ0°a¸¼ÑFÔFÑGÔGˆBÝ•r”w  A¡¨Ñ0°a¸¼ÑFÔFÑGÔGˆBØ�LŠL˜"˜b " bÐ)Ñ*Ô*Ð*Ñ*àˆs	   ®3 ³Ac                 ó  — | j         €t          d¦  «        ‚| j        €t          d¦  «        ‚| j        d         }t          |d¬¦  «        d         }| j        �| j        d         nd}| j        �| j        d         nd}|||dœS )	a  
        Return text segmentation outputs from the last predict() call.

        Available only when the model was exported with use_text_seg=True.
        Returns raw numpy arrays so the caller can apply any threshold strategy.

        Output keys:
            'text_prob'  : (H, W) float32  -- per-pixel text probability in [0, 1].
                           Derived from text_logits via softmax class-1 channel.
                           Always present when use_text_seg=True.
            'thresh_map' : (H, W) float32  -- adaptive threshold map in [0, 1].
                           Present only when use_db_thresh=True; None otherwise.
            'db_map'     : (H, W) float32  -- differentiable binary map in [0, 1].
                           Present only when use_db_thresh=True; None otherwise.

        Returns:
            dict with the keys above.

        Raises:
            RuntimeError if predict() has not been called.
            RuntimeError if the model was not exported with use_text_seg=True.
        Nz7predict() must be called before get_text_segmentation()rM  r   r�   r   r‚   )Ú	text_probrØ   rÙ   )r³   r!  r¹   r   rº   r»   )rÃ   rU  r\  rØ   rÙ   s        r   Úget_text_segmentationz-ImageFeatureExtractorV2.get_text_segmentationË  s¸   € ð. Œ>Ð!ÝØIñô ð ð ÔÐ$ÝðGñô ð ð Ô˜qÔ!ˆÝ! "¨1Ð-Ñ-Ô-¨aÔ0ˆ	à/3Ô/?Ð/K�TÔ% dÔ+Ð+ÐQUˆ
Ø/3¬|Ð/K�T”\ $Ô'Ð'ÐQUˆð $Ø$Ø ð
ð 
ð 	
r   rL   )r   r/  )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rÅ   rÉ   rË   rî   r"   r   Úndarrayrú   r  r  r  r  Úboolr  r‡   r$  rß   r.  r7  rK  r4  r]  rX   r   r   r°   r°   „  sÈ  € € € € € ðð ð&$ð $ð $ðTð ð ðð ð ð9,ð 9,ð 9,ð 9,ð| @ sð  @°Cð  @¸B¼Jð  @ð  @ð  @ð  @ðF+ð +ð +ð,-
ð -
ð -
ðf	ð 	ð 	ð
ð 
ð 
ð'B Tð 'Bð 'Bð 'Bð 'BðR%¨ð %ð %ð %ð %ð>,
 tð ,
ð ,
ð ,
ð ,
ð\ @CØ68ðs
ð s
°uð s
Ø03ðs
Ø=Aðs
ð s
ð s
ð s
ðl 58Ø+-ðHð H¨Eð HØ%(ðHØ26ðHð Hð Hð HðT,
 tð ,
ð ,
ð ,
ð ,
ð ,
ð ,
r   r°   Ú__main__z7Visualise ImageFeatureExtractorV2 results on PDF files.)ÚdescriptionÚ	onnx_pathz&Path to the exported .onnx model file.)ÚhelpÚpdf_dirz*Directory containing PDF files to process.z--dpié`   z*Rendering DPI for PDF pages (default: 96).)ÚtypeÚdefaultrg  z--score_thrz0Layout detection score threshold (default: 0.3).z--db_thrr   z-DB map binarisation threshold (default: 0.5).zERROR: ONNX file not found: r   z ERROR: PDF directory not found: é*   )é   rm  rm  r`   é<   éÜ   r   )rf   r&   é  é„  c                 óÔ   — t          |t          | j        d         d¦  «        z  |t          | j        d         d¦  «        z  d¦  «        }|dk     rt          j        | d||¬¦  «        } | S )Nr   r   r   r‚   )ÚfxÚfy)rÛ   r   r   rô   rö   )ÚimgÚmax_wÚmax_hÚscales       r   Ú_scale_dispry  &  sh   € Ý�E�C ¤	¨!¤¨aÑ0Ô0Ñ0Ø�C ¤	¨!¤¨aÑ0Ô0Ñ0°#ñ7ô 7ˆà�3Š;ˆ;Ý”*˜S &¨U°uÐ=Ñ=Ô=ˆCØˆ
r   c                 ó,  — |                       ¦   «         }t          |d         |d         |d         |d         ¦  «        D �]S\  \  }}}}}}}	t          |¦  «        t          |¦  «        t          |¦  «        t          |¦  «        f\  }}}}t          d„ t          t          |t          t          ¦  «        dz
  ¦  «                 D ¦   «         ¦  «        }
t          j        |||f||f|
d¦  «         |	› d|d	›�}t          j	        |t          j
        d
d¦  «        \  \  }}}t          |dz
  |dz   ¦  «        }t          j        ||||z
  dz
  f||z   dz   |dz   f|
d¦  «         t          j        |||dz   |ft          j
        d
ddt          j        ¦  «         �ŒU|S )z%Draw get_layout_detections() results.r\   r„   r]   r'  c              3   ó4   K  — | ]}t          |¦  «        V — Œd S rL   )r"   )rP   rù   s     r   rS   z%_draw_layout_boxes.<locals>.<genexpr>4  s@   è è € ð Kð K Q�#˜a™&œ&ð Kð Kð Kð Kð Kð Kr   r   r   ú z.2fgÍÌÌÌÌÌÜ?r…   ra   ©r   r   r   )Úcopyrà   r"   ÚtupleÚ_class_colorsrÛ   r)  rô   Ú	rectangleÚgetTextSizeÚFONT_HERSHEY_SIMPLEXr   ÚputTextÚLINE_AA)ÚcanvasÚ
detectionsr:   r.   r/   rk   rl   r+  r¢   rR   ÚcolorÚlabelÚtwÚthr�   Útys                   r   Ú_draw_layout_boxesr�  -  sÄ  € à�kŠk‰mŒmˆÝ25Ø˜7Ô# Z°Ô%9Ø˜8Ô$ j°Ô&9ñ3;ô 3;ð 	9ñ 	9Ñ.ÑˆR��R˜˜c 5¨$õ ! ™WœW¥c¨"¡g¤g­s°2©w¬w½¸B¹¼Ð?‰NˆB��B˜Ýð Kð KÝ'­¨Cµµ]Ñ1CÔ1CÀaÑ1GÑ(HÔ(HÔIðKñ Kô Kñ Kô KˆEåŒM˜#  B˜x¨"¨b¨°5¸!Ñ<Ô<Ð<ØÐ)Ð)˜eÐ)Ð)Ð)ˆEÝœ/Ø•sÔ/°°qñ:ô :‰K‰HˆR��aå�R˜!‘V˜R !™VÑ$Ô$ˆBÝŒM˜#  B¨¡G¨a¡KÐ0°2¸±7¸Q±;ÀÀQÁÐ2GØ ñ%ô %ð %åŒK˜˜U R¨!¡V¨R LÝÔ0°$Ø'¨­C¬Kñ9ô 9ð 9ñ 9ð ˆ
r   c           	      óâ   — |                       ¦   «         }|D ]W\  }}}}t          j        |t          |¦  «        t          |¦  «        ft          |¦  «        t          |¦  «        fdd¦  «         ŒX|S )z5Draw get_text_detection() results (green rectangles).)r   éÈ   r   r   )r~  rô   r�  r"   )r†  r\   r:   r.   r/   rk   rl   s          r   Ú_draw_text_boxesr�  B  sr   € à�kŠk‰mŒmˆØ %ð 	*ð 	*ÑˆR��R˜ÝŒM˜#¥ B¡¤­¨R©¬Ð1µC¸±G´G½SÀ¹W¼WÐ3EØ% qñ*ô *ð *ð *àˆ
r   c                 ó,  — t          j        |                      t          j        ¦  «        ||ft           j        ¬¦  «        }t          j        |dd¦  «        dz                       t          j        ¦  «        }t          j        |t           j	        ¦  «        S )z@Render a float32 (H,W) map in [0,1] as a JET colormap BGR image.rò   rb   r   r   )
rô   rö   r!   r   r=   r÷   r#   r>   ÚapplyColorMapÚCOLORMAP_JET)Úarrr'   r(   Úresizedr>   s        r   Ú_map_to_jetr–  J  sr   € å”*˜SŸZšZ­¬
Ñ3Ô3°a¸°VÝ+.Ô+;ð=ñ =ô =ˆå”˜ # sÑ+Ô+¨cÑ1×9Ò9½"¼(ÑCÔCˆÝÔ  ­Ô(8Ñ9Ô9Ð9r   c                 óÄ  — t          j        | d         d¬¦  «                             t           j        ¦  «        }t          j        g |j        ¢d‘R t           j        ¬¦  «        }t          t          t          ¦  «        ¦  «        D ]9}t          t          |t          t          ¦  «        dz
  ¦  «                 |||k    <   Œ:t          j        |||ft          j        ¬¦  «        S )z=Render segmentation logits (1,C,Hf,Wf) as a colour class map.r   r�   r   r`   r   rò   )r   Úargmaxr!   rd   rõ   r   r>   rˆ   r)  r[   r€  rÛ   rô   rö   ÚINTER_NEAREST)rÖ   r'   r(   Úpredr:   rù   s         r   Ú_seg_to_colorr›  Q  sº   € åŒy˜ œ¨Ð+Ñ+Ô+×2Ò2µ2´8Ñ<Ô<ˆÝŒxÐ(˜$œ*Ð( aÐ(Ð(µ´Ð9Ñ9Ô9ˆÝ•s�<Ñ(Ô(Ñ)Ô)ð 	Kð 	KˆAÝ*­3¨qµ#µmÑ2DÔ2DÀqÑ2HÑ+IÔ+IÔJˆC�˜’	‰NˆNÝŒz˜#  1˜vµSÔ5FÐGÑGÔGÐGr   c           
      óê   — |                       ¦   «         }t          j        ||dt          j        dddt          j        ¦  «         t          j        ||dt          j        dddt          j        ¦  «         |S )z%Stamp a label in the top-left corner.)é   é   ç333333ã?)r   r   r   r   r}  r   )r~  rô   r„  rƒ  r…  )ru  rD   r:   s      r   Ú_labelr   Y  sn   € à�hŠh‰jŒjˆÝŒ�C˜˜wÝÔ,¨c°9ÀÅ3Ä;ñ	Pô 	Pð 	PåŒ�C˜˜wÝÔ,¨c°?ÀAÅsÄ{ñ	Tô 	Tð 	Tàˆ
r   c                 óx  — |j         dd…         \  }}|| _        || _        |                      |¦  «         |                      |¬¦  «        }t          ||¦  «        }t          j        dt          t          |d¦  «        ¦  «        ¦  «         t          | j        ||¦  «        }t          |d¦  «        }		 |                      ¦   «         }
t          t          |
d         ||¦  «        d¦  «        }|
d	         �Kt          t          |
d	         ||¦  «        d
¦  «        }t          t          |
d         ||¦  «        d¦  «        }n“t          j        ||dft          j        ¬¦  «        }|                     ¦   «         }|                     ¦   «         }t          j        |ddt          j        ddd¦  «         t          j        |ddt          j        ddd¦  «         t          j        |	|||gd¬¦  «        }t          j        dt          |¦  «        ¦  «         |                      |¬¦  «        }t-          ||¦  «        }t          j        dt          t          |dt/          |¦  «        › �¦  «        ¦  «        ¦  «         n2# t0          $ r% t          j        dt          |	¦  «        ¦  «         Y nw xY wt/          |d         ¦  «        S )aß  Run inference and display results in separate windows per row.

        Window 1 - Layout Detection:
            [ original | layout detection ]

        Window 2 - Segmentation Maps:
            [ layout seg | text prob | thresh map | db map ]
            (text panels only when use_text_seg=True)

        Window 3 - Text Detection:
            [ original | text detection ]
            (only when use_text_seg=True)

        predict() internally resizes the image to model input size, runs ONNX,
        then decodes coordinates back into the original page pixel space using
        _page_h/_page_w.  So all returned coordinates are already in bgr_orig
        space with maximum precision.

        Returns n_layout (int).
        Nr   )r0  z1. Layout Detectionzlayout detection (refined)z
layout segr\  z	text probrØ   z
thresh maprÙ   zdb mapr   r`   zthresh: N/A)r/  rm  rŸ  )é´   r¢  r¢  r   zdb: N/Ar�   z2. Segmentation Mapsz3. Text Detectionztext detection  n=r\   )r   r  rý   rî   rK  r�  rô   Úimshowry  r   r›  r¸   r]  r–  r   rõ   r>   r~  r„  rƒ  rŒ   r4  r�  r)  r!  )Ú	extractorÚbgr_origr{   Údb_thrr~   r   Úlayout_detsÚlayout_canvasÚ	seg_colorÚ	seg_panelÚtext_segÚtp_panelÚ	thr_panelÚdb_panelÚblankÚwin2r9  Útext_canvass                     r   Ú_process_pager²  b  sá  € ð* "œ¨¨¨Ô+‰ˆ�à&/ˆ	Ô!Ø&/ˆ	Ô#Ø×Ò˜(Ñ#Ô#Ð#ð "×?Ò?È&Ð?ÑQÔQˆÝ*¨8°[ÑAÔAˆÝŒ
Ð(Ý�v mÐ5QÑRÔRÑSÔSñ	Uô 	Uð 	Uõ " )Ô"3°V¸VÑDÔDˆ	Ý˜9 lÑ3Ô3ˆ	ð	GØ!×7Ò7Ñ9Ô9ˆHÝ�{¨8°KÔ+@À&È&ÑQÔQØ*ñ,ô ,ˆHð ˜Ô%Ð1Ý"¥;¨x¸Ô/EÀvÈvÑ#VÔ#VØ#/ñ1ô 1�	å"¥;¨x¸Ô/AÀvÈvÑ#VÔ#VØ#+ñ-ô -��õ œH f¨f°aÐ%8ÅÄÐIÑIÔI�Ø!ŸJšJ™LœL�	Ø!ŸJšJ™LœL�Ý”˜I }°hÝÔ4°c¸?ÈAñOô Oð Oå”˜H y°hÝÔ4°c¸?ÈAñOô Oð Oõ ”> 9¨h¸	À8Ð"LÐSTÐUÑUÔUˆDÝŒJÐ-­{¸4Ñ/@Ô/@ÑAÔAÐAð $×6Ò6ÀÐ6ÑHÔHˆJÝ*¨8°ZÑ@Ô@ˆKÝŒJÐ*Ý"¥6¨+Ø*P½sÀ:¹¼Ð*PÐ*Pñ$Rô $Rñ Sô SñTô Tð Tð Tøõ ð 	Gð 	Gð 	GåŒJÐ-­{¸9Ñ/EÔ/EÑFÔFÐFÐFÐFð	Gøøøõ �;˜wÔ'Ñ(Ô(Ð(s   Â3GI5 É5,J$Ê#J$zLoading ONNX: ÚCPUExecutionProvider)Ú	providersz  outputs: c                 ó   — g | ]	}|j         ‘Œ
S rX   rÎ   rÏ   s     r   rV   rV   µ  s   € Ð?Ð?Ð? A˜œÐ?Ð?Ð?r   c              #   ó¸   K  — | ]U}|                      ¦   «                              d ¦  «        ¯)t          j                             t
          j        |¦  «        V — ŒVdS )z.pdfN)ÚlowerÚendswithÚosÚpathÚjoinÚargsrh  )rP   Úfs     r   rS   rS   º  sd   è è € ð ð àØ�7Š7‰9Œ9×Ò˜fÑ%Ô%ðÝ
Œ�Š•T”\ 1Ñ%Ô%ðð ð ð ð ð r   zNo PDF files found in zFound z  PDF file(s). Press ESC to stop.Fú
z  (z pages)z  page z ...r|  T)ÚendÚflushéH   )ÚmatrixÚ
colorspacezfailed: zlayout=z boxesr   é   zESC -- stopping.rU   )rp  rq  )ora  Únumpyr   Úcommon_utilr   r   r   r   rø   rb  r"   r  r[   Ú_BACKGROUND_CLASSrY   rN   Úsortedr6  Ú_COARSE_CLASS_IDXÚ_FINE_CLASS_IDXrÿ   r  r  rý   r  rþ   r7  rw   r‡   rß   r¤   r®   r°   r^  Úsysr¹  Úargparserô   ÚonnxruntimeÚortÚpymupdfÚArgumentParserÚparserÚadd_argumentÚ
parse_argsr¼  rº  Úisfilerf  ÚprintÚexitÚisdirrh  ÚrandomÚRandomStateÚ_rngÚvstackrj   r>   Úrandintr)  r€  ry  r�  r�  r–  r›  r   r²  ÚInferenceSessionÚsessionr¤  rÝ   ÚlistdirÚ	pdf_filesÚstopÚpdf_pathÚopenÚdocÚn_pagesÚcloseÚsplitextÚbasenameÚpdf_namerˆ   Úpage_idxr  ÚMatrixÚdpiÚmatr  ÚcsRGBr  ÚcvtColorr  r  r  r  r  ÚCOLOR_RGB2BGRr¥  r{   r¦  r<  Ú	ExceptionÚeÚ	tracebackÚ	print_excÚwaitKeyÚkeyÚdestroyAllWindowsrX   r   r   ú<module>rø     s{	  ðð,ð ,ð\ Ð Ð Ð ðk6ØQÐQÐQÐQÐQÐQÐQÐQÐQÐQÐQÐQÐQøØð i6ð i6ð i6ð?ð ?˜œð ?¨3ð ?°r´zð ?ð ?ð ?ð ?ð(˜œð (¨¬
ð (ð (ð (ð (ð:8˜BœJð :8°%ð :8¸B¼Jð :8ð :8ð :8ð :8ðz6�r”zð 6 b¤jð 6ð 6ð 6ð 6ð 6ð 6ð]i6øøøð`ð ð €ð !Ð Ø€ð  	Ð*Ð Ø�fð ð Ø�Y˜|Ñ,Ô,ðñ ô ñ ô Ð ð
ð Ø�Y˜|Ñ,Ô,ðñ ô €ð
ð Ø�Y˜|Ñ,Ô,ðñ ô Ð ðð Ø ðñ ô Ð ð (Ð ð Ð Ø€Ø€ð*�b”jð *¨"¬*ð *¸uð *ÈÌð *ð *ð *ð *ð>\=Ø”Jð\=à”Jð\=ð ”Jð\=ð ð	\=ð
 ð\=ð # T™kð\=ð ð\=ð ð\=ð 
ˆ$„Zð\=ð \=ð \=ð \=ð~*Ø˜$”Zð*à˜$”Zð*ð ð*ð 
ð	*ð *ð *ð *ðbs	
ð s	
ð s	
ð s	
ð s	
ñ s	
ô s	
ð s	
ðr ˆzÒÑØ€J€J€JØ€I€I€IØ€O€O€OØ€J€J€JØÐÐÐØÐÐÐØ€N€N€Nà$ˆXÔ$ØMðñ ô €Fð ×Ò˜Ð*RÐÑSÔSÐSØ
×Ò˜	Ð*VÐÑWÔWÐWØ
×Ò˜¨#°rØIð ñ Kô Kð Kà
×Ò˜¨E¸3ØOð ñ Qô Qð Qà
×Ò˜
¨E¸3ØLð ñ Nô Nð Nà×ÒÑÔ€DàŒ7�>Š>˜$œ.Ñ)Ô)ð ØˆÐ=¨T¬^Ð=Ð=Ñ>Ô>Ð>ØˆŒ�‰ŒˆØŒ7�=Š=˜œÑ&Ô&ð ØˆÐ?°´Ð?Ð?Ñ@Ô@Ð@ØˆŒ�‰Œˆð
 Œ9× Ò  Ñ$Ô$€DØ�B”IØˆŒ�,�,�,� r¤xÐ0Ñ0Ô0Ø�Š�R˜ C C¨Ñ$5Ô$5¸Ñ$9¸1Ð#=ÀRÄXˆÑNÔNðñ ô €Mðð ð ð ðð ð ð*ð ð ð:ð :ð :ðHð Hð Hðð ð ðF)ð F)ð F)ðX 
€EÐ
+˜4œ>Ð
+Ð
+Ñ,Ô,Ð,Ø$�Ô$ØŒØ)Ð*ðñ ô €Gð (Ð'¨Ñ0Ô0€Ià	€EÐ
AÐ?Ð?¨×)<Ò)<Ñ)>Ô)>Ð?Ñ?Ô?Ð
AÐ
AÑBÔBÐBð
 �ð ð à�”˜DœLÑ)Ô)ðñ ô ñ ô €Ið
 ð ØˆÐ5 t¤|Ð5Ð5Ñ6Ô6Ð6ØˆŒ�‰ŒˆØ	€EÐ
C�3�3�y‘>”>Ð
CÐ
CÐ
CÑDÔDÐDð
 €DØð %ñ %ˆØð 	Ø‰EØ�”˜hÑ'Ô'ˆØ�#�c‘(”(ˆØ�	Š	‰ŒˆØ”7×#Ò# B¤G×$4Ò$4°XÑ$>Ô$>Ñ?Ô?ÀÔBˆØˆÐ0�8Ð0Ð0 Ð0Ð0Ð0Ñ1Ô1Ð1à˜˜g™œð 	ñ 	ˆHØˆEÐ*˜HÐ*Ð*Ð*°¸4Ð@Ñ@Ô@Ð@à�7”< Ñ)Ô)ˆCØ�x”=ˆDØ!�7”> $¤(¨R¡-°´¸B±Ñ?Ô?ˆCØ—?’?¨#¸'¼-�?ÑHÔHˆCØ�IŠI‰KŒKˆKØ#�s”|Ø�”˜cœk°´Ð:Ñ:Ô:×BÒBØ”J ¤	¨1ñ.ô .àÔ!ñô ˆHðØ(˜=¨°HØ)-¬¸¼ñFô F��øàð ð ð Ø��n �n�nÑ%Ô%Ð%Ø Ð Ð Ð Ð"5 )Ô"5Ñ"7Ô"7Ð"7Ø���‘øøøøðøøøð
 ˆEÐ,˜HÐ,Ð,Ð,Ñ-Ô-Ð-à�#”+˜a‘.”. 4Ñ'ˆCØ�bŠyˆyØ�Ð(Ñ)Ô)Ð)Ø�Ø�ñ ùð
 €CÔÑÔÐÐÐðe Ðs(   ˆ •AA*Á)A*Ô/UÕU9Õ!U4Õ4U9