
    9i"o                     t    S r SSKrSSKrSSKrSSKJr  SSKrSSKr	SSK
r
SSKrSSKrSSKJr   " S S5      rg)z
Created by Jaided AI
Released Date: 18/08/2022
Description:
DBNet text detection module. 
Many parts of the codes are adapted from https://github.com/MhLiao/DB
    N)Polygon   )Configurablec                       \ rS rSr       SS jrS rS rS rS rS r	SS	 jr
S
 rS rSS jrSS jr     SS jrS r   SS jr   SS jrSS jrS rS rS r       SS jrSrg)DBNet   Nc                 f   X`l         [        R                  R                  [        R                  R	                  [
        5      SS5      n[        US5       n	[        R                  " U	5      U l	        SSS5        Ub!  U R                  U R                  U5      U l	        XR                  R                  5       ;   a  Xl        OA[        SR                  SR                  U R                  R                  5       5      5      5      eUb  X l        OF[        R                  R                  [        R                  R	                  [
        5      S5      U l        U(       a  X0R                  U   S   R                  5       ;   a@  [        R                  R                  U R                  U R                  U   S   U   5      n
S	nO,[        R                  R                  U R                  U5      n
S
n[        R                  R!                  U
5      (       d  [#        UR                  X:5      5      eU R%                  U R                  U   S   U
5        OSU l        [(        R*                  " U R                  S   5      U l        U R                  S   U l        U R                  S   U l        g! , (       d  f       GN7= f)a  
DBNet text detector class

Parameters
----------
backbone : str, optional
    Backbone to use. Options are "resnet18" and "resnet50". The default is "resnet18".
weight_dir : str, optional
    Path to directory that contains weight files. If set to None, the path will be set
    to "../weights/". The default is None.
weight_name : str, optional
    Name of the weight to use as specified in DBNet_inference.yaml or a filename 
    in weight_dir. The default is 'pretrained'.
initialize_model : Boolean, optional
    If True, construct the model and load weight at class initialization.
    Otherwise, only initial the class without constructing the model.
    The default is True.
dynamic_import_relative_path : str, optional
    Relative path to 'model/detector.py'. This option is for supporting
    integrating this module into other modules. For example, easyocr/DBNet
    This should be left as None when calling this module as a standalone. 
    The default is None.
device : str, optional
    Device to use. Options are "cuda" and "cpu". The default is 'cuda'.
verbose : int, optional
    Verbosity level. The default is 0.

Raises
------
ValueError
    Raised when backbone is invalid.
FileNotFoundError
    Raised when weight file is not found.

Returns
-------
None.
configszDBNet_inference.yamlrNz2Invalid backbone. Current support backbone are {}.,weightsweightzUA weight with a name {} is found in DBNet_inference.yaml but cannot be find file: {}.zYA weight with a name {} is not found in DBNet_inference.yaml and cannot be find file: {}.modelBGR_MEANmin_detection_sizemax_detection_size)deviceospathjoindirname__file__openyaml	safe_loadr
   set_relative_import_pathkeysbackbone
ValueErrorformat
weight_dirisfileFileNotFoundErrorinitialize_modelr   nparrayr   r   r   )selfr   r!   weight_namer$   dynamic_import_relative_pathr   verboseconfig_pathfidweight_patherror_messages               S/var/www/html/land-doc-ocr/venv/lib/python3.13/site-packages/easyocr/DBNet/DBNet.py__init__DBNet.__init__   s   \ ggll277??8#<iI_`+s#s>>#.DL $ (388GcdDL||((**$MQXXY\YaYabfbnbnbsbsbuYvwxx!(O ggll277??8+DiPDOll84X>CCEE ggll4??DLL<RS[<\]h<ij w ggll4??KH {77>>+..'(<(<[(VWW!!$,,x"8"A;O DJj!9:"&,,/C"D"&,,/C"DE $#s   J!!
J0c           
      F   Uc   eUR                  [        R                  5      nUR                  5        Hh  u  pEUS:X  a5  UR	                  USR                  X5R                  S5      -   5      05        M@  [        U[        5      (       a  U R                  XR5      nMh  Mj     U$ )a'  
Create relative import paths for modules specified in class. This method
is recursive.

Parameters
----------
configs : dict
    Configuration dictionary from .yaml file.
dynamic_import_relative_path : str, optional
    Relative path to 'model/detector/'. This option is for supporting
    integrating this module into other modules. For example, easyocr/DBNet
    This should be left as None when calling this module as a standalone. 
    The default is None.

Returns
-------
configs : dict
    Configuration dictionary with correct relative path.
class.)	splitr   sepitemsupdater   
isinstancedictr   )r'   r
   r)   preficeskeyvalues         r/   r   DBNet.set_relative_import_pathj   s    ( ,777/55bff= ICg~SXXhS9I.I%JKLeT** 99%^E )     c                     U R                   c  [        S5      eU R                   R                  [        R                  " XR
                  S9SS9  U R                   R                  5         g)z
Load weight to model.

Parameters
----------
weight_path : str
    Path to trained weight.

Raises
------
RuntimeError
    Raised when the model has not yet been contructed.

Returns
-------
None.
Nz#model has not yet been constructed.)map_locationF)strict)r   RuntimeErrorload_state_dicttorchloadr   eval)r'   r-   s     r/   load_weightDBNet.load_weight   sM    $ ::DEE

""5::k#T]b"c

r?   c                     [         R                  " U5      R                  R                  R	                  U R
                  5      U l        g)z
Contruct text detection model based on the configuration in .yaml file.

Parameters
----------
config : dict
    Configuration dictionary.

Returns
-------
None.
N)r   construct_class_from_config	structurebuilderbuildr   r   )r'   configs     r/   construct_modelDBNet.construct_model   s4     "==fEOOWW]]^b^i^ij
r?   c                 v   U R                  U5        U R                  U5        [        U R                  R                  [        R
                  R                  5      (       aZ  U R                  S:X  aI  U R                  R                  R                  R                  U R                  5      U R                  l        ggg)z
Wrapper to initialize text detection model. This model includes contructing
and weight loading.

Parameters
----------
model_config : dict
    Configuration dictionary.
weight_path : str
    Path to trained weight.

Returns
-------
None.
cpuN)
rP   rH   r9   r   rE   nnDataParallelr   moduleto)r'   model_configr-   s      r/   r$   DBNet.initialize_model   s      	\*%djj&&(=(=>>4;;RWCW#zz//6699$++FDJJ DX>r?   c                 *   [        U[        5      (       at  [        R                  R	                  U5      (       a6  [
        R                  " U[
        R                  5      R                  S5      nU$ [        SR                  U5      5      e[        U[        R                  5      (       a  UR                  S5      nU$ [        U[        R                  R                  5      (       a%  [        R                  " U5      SS2SS2SSS24   nU$ [!        S5      e)a  
Load or convert input to OpenCV BGR image numpy array.

Parameters
----------
image : str, PIL.Image, or np.ndarray
    Image to load or convert.

Raises
------
FileNotFoundError
    Raised when the input is a path to file (str), but the file is not found.
TypeError
    Raised when the data type of the input is not supported.

Returns
-------
image : np.ndarray
    OpenCV BGR image.
float32zCannot find {}NzYUnsupport image format. Only path-to-file, opencv BGR image, and PIL image are supported.)r9   strr   r   r"   cv2imreadIMREAD_COLORastyper#   r    r%   ndarrayPILImageasarray	TypeErrorr'   images     r/   get_cv2_imageDBNet.get_cv2_image   s    * eS!!ww~~e$$

5#*:*:;BB9M  ((8(?(?(FGGrzz**LL+E  syy//JJu%aDbDj1E  wxxr?   c                    UR                   u  p4nUc*  [        U R                  [        X4U R                  5      5      nX4:  aP  [        [        R                  " US-  5      S-  5      n[        [        R                  " Xc-  U-  S-  5      S-  5      nOO[        [        R                  " US-  5      S-  5      n[        [        R                  " Xt-  U-  S-  5      S-  5      n[        R                  " XU45      nXU44$ )ar  
Resize image such that the shorter side of the image is equal to the 
closest multiple of 32 to the provided detection_size. If detection_size
is not provided, it will be resized to the closest multiple of 32 each
side. If the original size exceeds the min-/max-detection sizes 
(specified in configs.yaml), it will be resized to be within the 
min-/max-sizes.

Parameters
----------
img : np.ndarray
    OpenCV BGR image.
detection_size : int, optional
    Target detection size. The default is None.

Returns
-------
np.ndarray
    Resized OpenCV BGR image. The width and height of this image should
    be multiple of 32.
    )
shapemaxr   minr   intmathceilr^   resize)	r'   imgdetection_sizeheightwidth_
new_height	new_widthresized_imgs	            r/   resize_imageDBNet.resize_image   s    , 99q! !8!8#fTMdMd:efN>TYY~':;b@AJDIIj&9E&AB&FG"LMIDIInr&9:R?@ITYYy'86'AB'FG"LMJjj*&=>UO++r?   c                     [         R                  " U5      R                  SSS5      R                  5       R	                  S5      $ )z
Convert image array (assuming OpenCV BGR format) to image tensor.

Parameters
----------
image : np.ndarray
    OpenCV BGR image.

Returns
-------
torch.tensor
    Tensor image with 4 dimension [batch, channel, width, height].
   r   r   )rE   
from_numpypermutefloat	unsqueezerg   s     r/   image_array2tensorDBNet.image_array2tensor  s8     &..q!Q7==?II!LLr?   c                 $    XR                   -
  S-  $ )z
Normalize image by substracting BGR mean and divided by 255

Parameters
----------
image : np.ndarray
    OpenCV BGR image.

Returns
-------
np.ndarray
    OpenCV BGR image.
g     o@)r   rg   s     r/   normalize_imageDBNet.normalize_image  s     %u,,r?   c                     U R                  U5      nU R                  X2S9u  p4U R                  U5      nU R                  U5      nX44$ )a  
Wrapper to load and convert an image to an image tensor

Parameters
----------
image : path-to-file, PIL.Image, or np.ndarray
    Image to load or convert.
detection_size : int, optional
    Target detection size. The default is None.

Returns
-------
img : torch.tensor
    Tensor image with 4 dimension [batch, channel, width, height]..
original_shape : tuple
    A tuple (height, width) of the original input image before resizing.
ru   )ri   r|   r   r   )r'   
image_pathru   rt   original_shapes        r/   
load_imageDBNet.load_image,  sS    $ 
+"///U""3'%%c*""r?   c           
          [        U Vs/ s H  nU R                  X2S9PM     sn6 u  p[        R                  " USS9U4$ s  snf )a(  
Wrapper to load or convert list of multiple images to a single image 
tensor. Multiple images are concatenated together on the first dimension.

Parameters
----------
images : a list of path-to-file, PIL.Image, or np.ndarray
    Image to load or convert.
detection_size : int, optional
    Target detection size. The default is None.

Returns
-------
img : torch.tensor
    A single tensor image with 4 dimension [batch, channel, width, height].
original_shape : tuple
    A list of tuples (height, width) of the original input image before resizing.
r   r   )dim)zipr   rE   cat)r'   imagesru   rh   original_shapess        r/   load_imagesDBNet.load_imagesE  sR    & #&5;(=5;E )-(_5;(= #>yyq)?::(=s   A c	                 D   U R                  X4S9n	/ n
/ n[        UR                  S5      5       Hk  nX,   u  pU(       a  U R                  X<   X   UUUUUS9u  nnOU R	                  X<   X   UUUUUS9u  nnU
R                  U5        UR                  U5        Mm     [        [        X5       VVVVs/ s HO  u  nn[        US:  5      (       a2  [        [        UU5       VVs/ s H  u  nnUS:  d  M  UU4PM     snn6 OSS/PMQ     snnnn6 u  pX4$ s  snnf s  snnnnf )a  
Translate probability heatmap tensor to text region boudning boxes.

Parameters
----------
image_tensor : torch.tensor
    Image tensor.
original_shapes : tuple
    Original size of the image (height, width) of the input image (before
    rounded to the closest multiple of 32).
hmap : torch.tensor
    Probability heatmap tensor.
text_threshold : float, optional
    Minimum probability for each pixel of heatmap tensor to be considered
    as a valid text pixel. The default is 0.2.
bbox_min_score : float, optional
    Minimum score for each detected bounding box to be considered as a
    valid text bounding box. The default is 0.2.
bbox_min_size : int, optional
    Minimum size for each detected bounding box to be considered as a
    valid text bounding box. The default is 3.
max_candidates : int, optional
    Maximum number of detected bounding boxes to be considered as 
    candidates for valid text bounding box. Setting it to 0 implies
    no maximum. The default is 0.
as_polygon : boolean, optional
    If True, return the bounding box as polygon (fine vertrices), 
    otherwise return as rectangular. The default is False.

Returns
-------
boxes_batch : list of lists
    Bounding boxes of each text box.
scores_batch : list of floats
    Confidence scores of each text box.

)	thresholdr   )bbox_min_scorebbox_min_sizemax_candidates )binarizerangesizepolygons_from_bitmapboxes_from_bitmapappendr   any)r'   image_tensorr   hmaptext_thresholdr   r   r   
as_polygonsegmentationboxes_batchscores_batchbatch_indexrv   rw   boxesscoresboxscores                      r/   	hmap2bboxDBNet.hmap2bbox\  s   \ }}T}F !2!21!56K+8MF $ 9 9(,(9(4(A(-(.9G8E9G !: !Iv !% 6 6(,(9(4(A(-(.9G8E9G !7 !Iv u%'- 70 %( ADK@^*` A__eV 36fqj// +.CFufCU0dCUKSY^abYb 1=eCU0d +/HJ2w+O A_*` %)! ((0d *`s   20D"D3D;DDc                 
    X:  $ )z
Apply threshold to return boolean tensor.

Parameters
----------
tensor : torch.tensor
    input tensor.
threshold : float
    Threshold.

Returns
-------
torch.tensor
    Boolean tensor.

r   )r'   tensorr   s      r/   r   DBNet.binarize  s    " !!r?   c                    UR                  S5      S:X  d   eUR                  5       R                  5       S   nUR                  5       R                  5       R                  5       S   nUR                  u  p/ n/ n[
        R                  " US-  R                  [        R                  5      [
        R                  [
        R                  5      u  pUS:  a  USU nU GH  nS[
        R                  " US5      -  n[
        R                  " UUS5      nUR                  S5      nUR                  S   S:  a  M\  U R                  UUR                  S	S
5      5      nUU:  a  M  UR                  S   S
:  a"  U R!                  USS9n[#        U5      S:  a  M  OM  UR                  S	S
5      nU R%                  UR                  S5      5      u  nnUUS
-   :  a  M  ['        U[(        5      (       d   UR+                  5       nUR+                  5       n[        R,                  " [        R.                  " USS2S4   U
-  U-  5      SU5      USS2S4'   [        R,                  " [        R.                  " USS2S4   U	-  U-  5      SU5      USS2S4'   UR1                  UR3                  5       5        UR1                  U5        GM     X4$ )a  
Translate boolean tensor to fine polygon indicating text bounding boxes

Parameters
----------
hmap : torch.tensor
    Probability heatmap tensor.
segmentation : torch.tensor
    Segmentataion tensor.
dest_width : TYPE
    target width of the output.
dest_height : TYPE
    target width of the output.
bbox_min_score : float, optional
    Minimum score for each detected bounding box to be considered as a
    valid text bounding box. The default is 0.2.
bbox_min_size : int, optional
    Minimum size for each detected bounding box to be considered as a
    valid text bounding box. The default is 3.
max_candidates : int, optional
    Maximum number of detected bounding boxes to be considered as 
    candidates for valid text bounding box. Setting it to 0 implies
    no maximum. The default is 0.

Returns
-------
boxes_batch : list of lists
    Polygon bounding boxes of each text box.
scores_batch : list of floats
    Confidence scores of each text box.

r   r      NgMb`?T)r\   r      r\   r   g       @)unclip_ratio)r\   r   r   )r   rS   numpydetachrm   r^   findContoursra   r%   uint8	RETR_LISTCHAIN_APPROX_SIMPLE	arcLengthapproxPolyDPreshapebox_score_fastuncliplenget_mini_boxesr9   rp   itemcliproundr   tolist)r'   r   r   
dest_widthdest_heightr   r   r   bitmaprv   rw   r   r   contoursrx   contourepsilonapproxpointsr   r   ssides                         r/   r   DBNet.polygons_from_bitmap  st   P   #q(((!!#))+A.xxz  "((*1-&&CZ)MM3224 A0HGcmmGT::G%%gw=F^^G,F||A"''fnnR.CDE~%||A"kk&sk;s8a<   ++b!$C**3;;z+BCHAu}q((j#..'__.
)..0QTU*Z78!ZIC1IQTV+k9:A{LC1ILL&MM% C  F }r?   c                    UR                  S5      S:X  d   eUR                  5       R                  5       S   nUR                  5       R                  5       R                  5       S   nUR                  u  p[
        R                  " US-  R                  [        R                  5      [
        R                  [
        R                  5      u  pUS:  a  [        [        U5      U5      nO[        U5      n[        R                  " USS4[        R                  S9n[        R                  " U4[        R                   S9n[#        U5       GH  nUU   nU R%                  U5      u  nnUU:  a  M%  [        R&                  " U5      nU R)                  UUR+                  SS5      5      nUU:  a  Me  U R-                  U5      R+                  SSS5      nU R%                  U5      u  nnUUS-   :  a  M  [        R&                  " U5      n[/        U[0        5      (       d   UR3                  5       nUR3                  5       n[        R4                  " [        R6                  " USS2S4   U
-  U-  5      SU5      USS2S4'   [        R4                  " [        R6                  " USS2S4   U	-  U-  5      SU5      USS2S4'   UR                  [        R                  5      UUSS2SS24'   UUU'   GM     UR9                  5       U4$ )	a  
Translate boolean tensor to fine polygon indicating text bounding boxes

Parameters
----------
hmap : torch.tensor
    Probability heatmap tensor.
segmentation : torch.tensor
    Segmentataion tensor.
dest_width : TYPE
    target width of the output.
dest_height : TYPE
    target width of the output.
bbox_min_score : float, optional
    Minimum score for each detected bounding box to be considered as a
    valid text bounding box. The default is 0.2.
bbox_min_size : int, optional
    Minimum size for each detected bounding box to be considered as a
    valid text bounding box. The default is 3.
max_candidates : int, optional
    Maximum number of detected bounding boxes to be considered as 
    candidates for valid text bounding box. Setting it to 0 implies
    no maximum. The default is 0.

Returns
-------
boxes_batch : list of lists
    Polygon bounding boxes of each text box.
scores_batch : list of floats
    Confidence scores of each text box.
r   r   r   r   r   dtyper\   N)r   rS   r   r   rm   r^   r   ra   r%   r   r   r   ro   r   zerosint16r[   r   r   r&   r   r   r   r9   rp   r   r   r   r   )r'   r   r   r   r   r   r   r   r   rv   rw   r   rx   num_contoursr   r   indexr   r   r   r   r   s                         r/   r   DBNet.boxes_from_bitmap  s   N   #q(((!!#))+A.xxz  "((*1-&&#CZ//9MM3+B+BD As8}n=Lx=L,1-RXX></<<(EuoG //8MFE}$XXf%F''fnnR.CDE~%++f%--b!Q7C,,S1JC}q((((3-Cj#..'__.
)..0QTU*Z78!ZIC1IQTV+k9:A{LC1I!$BHH!5E%A+!F5M5 )8 ||~v%%r?   c                 &   [        U5      nUR                  U-  UR                  -  n[        R                  " 5       nUR                  U[        R                  [        R                  5        [        R                  " UR                  U5      5      nU$ N)r   arealength	pyclipperPyclipperOffsetAddPathJT_ROUNDET_CLOSEDPOLYGONr%   r&   Execute)r'   r   r   polydistanceoffsetexpandeds          r/   r   DBNet.unclipo  sh    s|99|+dkk9**,sI..	0J0JK88FNN845r?   c                 0   [         R                  " U5      n[        [        [         R                  " U5      5      S S9nSu  pEpgUS   S   US   S   :  a  SnSnOSnSnUS   S   US   S   :  a  SnSnOSnSnX4   X5   X6   X7   /nU[        US   5      4$ )Nc                     U S   $ )Nr   r   )xs    r/   <lambda>&DBNet.get_mini_boxes.<locals>.<lambda>z  s    1r?   )r<   )r   r   r      r   r   r   r   )r^   minAreaRectsortedlist	boxPointsro   )	r'   r   bounding_boxr   index_1index_2index_3index_4r   s	            r/   r   DBNet.get_mini_boxesx  s    w/S]]<89~N-7*'!9Q<&)A,&GGGG!9Q<&)A,&GGGG1 CQ(((r?   c                    UR                   SS u  p4UR                  5       n[        R                  " [        R                  " USS2S4   R                  5       5      R                  [        R                  5      SUS-
  5      n[        R                  " [        R                  " USS2S4   R                  5       5      R                  [        R                  5      SUS-
  5      n[        R                  " [        R                  " USS2S4   R                  5       5      R                  [        R                  5      SUS-
  5      n[        R                  " [        R                  " USS2S4   R                  5       5      R                  [        R                  5      SUS-
  5      n	[        R                  " X-
  S-   Xv-
  S-   4[        R                  S9n
USS2S4   U-
  USS2S4'   USS2S4   U-
  USS2S4'   [        R                  " XR                  SSS5      R                  [        R                  5      S5        [        R                  " XU	S-   2XgS-   24   U
5      S   $ )z
Calculate total score of each bounding box

Parameters
----------
hmap : torch.tensor
    Probability heatmap tensor.
box_ : list
    Rectanguar bounding box.

Returns
-------
float
    Confidence score.
Nr   r   r   r   r\   )rm   copyr%   r   floorro   ra   int32rr   rn   r   r   r^   fillPolyr   mean)r'   r   box_hwr   xminxmaxyminymaxmasks              r/   r   DBNet.box_score_fast  s     zz"1~iikwwrxxAqD	077A1a!eLwwrwws1a4y}}/66rxx@!QUKwwrxxAqD	077A1a!eLwwrwws1a4y}}/66rxx@!QUKxxq$+/:"((K1I$AqD	1I$AqD	T;;q"a077A1Exx$q&[$Av+56=a@@r?   c                 6    U R                   R                  USS9$ )a  
Run the model to obtain a heatmap tensor from a image tensor. The heatmap
tensor indicates the probability of each pixel being a part of text area.

Parameters
----------
image_tensor : torch.tensor
    Image tensor.

Returns
-------
torch.tensor
    Probability heatmap tensor.
F)training)r   forward)r'   r   s     r/   
image2hmapDBNet.image2hmap  s     zz!!,!??r?   c	                    [        U[        5      (       d  U/nU R                  XS9u  p[        R                  " 5          U R                  U	5      nU R                  U	U
UUUUUUS9u  pSSS5        U(       a  WW4$ W$ ! , (       d  f       N= f)ar  
Wrapper to run the model on an input image to get text bounding boxes.

Parameters
----------
image : path-to-file, PIL.Image, or np.ndarray
    Image to load or convert.
text_threshold : float, optional
    Minimum probability for each pixel of heatmap tensor to be considered
    as a valid text pixel. The default is 0.2.
bbox_min_score : float, optional
    Minimum score for each detected bounding box to be considered as a
    valid text bounding box. The default is 0.2.
bbox_min_size : int, optional
    Minimum size for each detected bounding box to be considered as a
    valid text bounding box. The default is 3.
max_candidates : int, optional
    Maximum number of detected bounding boxes to be considered as 
    candidates for valid text bounding box. Setting it to 0 implies
    no maximum. The default is 0.
detection_size : int, optional
    Target detection size. Please see docstring under method resize_image()
    for explanation. The default is None.
as_polygon : boolean, optional
    If true, return the bounding boxes as find polygons, otherwise, return
    as rectagular. The default is False.
return_scores : boolean, optional
    If true, return confidence score along with the text bounding boxes.
    The default is False.

Returns
-------
list of lists
    Text bounding boxes. If return_scores is set to true, another list
    of lists will also be returned.

r   )r   r   r   r   r   N)r9   r   r   rE   no_gradr  r   )r'   rh   r   r   r   r   ru   r   return_scoresr   r   r   batch_boxesbatch_scoress                 r/   	inferenceDBNet.inference  s    \ %&&GE(,(8(8(8(`%]]_??<0D(,|7F7;HVHVGTHVBL )7 )N%K  ,, _s   *A>>
B)r   r   r
   r   r   r   r   r!   )resnet18N
pretrainedTNcudar   r   )r   )皙?r  r   r   F)r  r   r   )g      ?)r  r  r   r   NFF)__name__
__module____qualname____firstlineno__r0   r   rH   rP   r$   ri   r|   r   r   r   r   r   r   r   r   r   r   r   r  r  __static_attributes__r   r?   r/   r   r      s    &"+$(04 SEj@.kG*!F",HM - #2;6 $'#&"##$"O)b"0 /2-../Y@ ,/*++,R&h).A<@& $'#&"##$#'$"'@r?   r   )__doc__r   rq   r   shapely.geometryr   	PIL.Imagerc   r   r%   r^   r   rE   model.constructorr   r   r   r?   r/   <module>r#     s6    
   $   
   +j jr?   