o
    á,Ëh"o  ã                   @   sn   d Z ddlZddlZddlZddlmZ ddlZddlZ	ddl
Z
ddlZddlZddlmZ G dd„ dƒZdS )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                   @   sð   e Zd Z							d3dd„Zd	d
„ Zdd„ Zdd„ Zdd„ Zdd„ Zd4dd„Z	dd„ Z
dd„ Zd5dd„Zd4dd„Z					d6d d!„Zd"d#„ Z			d7d$d%„Z			d7d&d'„Zd8d)d*„Zd+d,„ Zd-d.„ Zd/d0„ Z							d9d1d2„ZdS ):ÚDBNetÚresnet18NÚ
pretrainedTÚcudar   c                 C   s~  || _ tj tj t¡dd¡}t|dƒ}	t |	¡| _	W d  ƒ n1 s%w   Y  |dur6|  
| j	|¡| _	|| j	 ¡ v rA|| _ntd d | j	 ¡ ¡¡ƒ‚|durV|| _ntj tj t¡d¡| _|r¥|| j	| d  ¡ v rtj | j| j	| d | ¡}
d	}n
tj | j|¡}
d
}tj |
¡s™t| ||
¡ƒ‚|  | j	| d |
¡ nd| _t | j	d ¡| _| j	d | _| j	d | _dS )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_message© r.   úN/var/www/html/scripts/venv/lib/python3.10/site-packages/easyocr/DBNet/DBNet.pyÚ__init__   s4   .ÿzDBNet.__init__c              
   C   sp   |dusJ ‚|  tj¡}| ¡ D ]%\}}|dkr(| |d ||  d¡ ¡i¡ qt|tƒr4|  ||¡}q	 q|S )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.
        NÚclassÚ.)	Úsplitr   ÚsepÚitemsÚupdater   Ú
isinstanceÚdictr   )r&   r	   r(   ÚpreficesÚkeyÚvaluer.   r.   r/   r   j   s    
zDBNet.set_relative_import_pathc                 C   s<   | j du r	tdƒ‚| j jtj|| jddd | j  ¡  dS )a.  
        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,   r.   r.   r/   Úload_weightŠ   s   
zDBNet.load_weightc                 C   s   t  |¡jj | j¡| _dS )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&   Úconfigr.   r.   r/   Úconstruct_model¡   s   zDBNet.construct_modelc                 C   sR   |   |¡ |  |¡ t| jjtjjƒr%| jdkr'| jjj 	| j¡| j_dS dS dS )aO  
        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)
rI   rC   r7   r   r@   ÚnnÚDataParallelr   ÚmoduleÚto)r&   Úmodel_configr,   r.   r.   r/   r#   °   s
   

ÿzDBNet.initialize_modelc                 C   s’   t |tƒrtj |¡rt |tj¡ d¡}|S t	d 
|¡ƒ‚t |tjƒr+| d¡}|S t |tjjƒrEt |¡dd…dd…ddd…f }|S tdƒ‚)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 {}NéÿÿÿÿzYUnsupport image format. Only path-to-file, opencv BGR image, and PIL image are supported.)r7   Ústrr   r   r!   Úcv2ÚimreadÚIMREAD_COLORÚastyper"   r   r$   ÚndarrayÚPILÚImageÚasarrayÚ	TypeError©r&   Úimager.   r.   r/   Úget_cv2_imageÅ   s   

ø
û"þzDBNet.get_cv2_imagec           	      C   s¸   |j \}}}|du rt| jt||| jƒƒ}||k r4tt |d ¡d ƒ}tt || | d ¡d ƒ}ntt |d ¡d ƒ}tt || | d ¡d ƒ}t 	|||f¡}|||ffS )a  
        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.
        Né    )
ÚshapeÚmaxr   Úminr   ÚintÚmathÚceilrS   Úresize)	r&   ÚimgÚdetection_sizeÚheightÚwidthÚ_Ú
new_heightÚ	new_widthÚresized_imgr.   r.   r/   Úresize_imageè   s    zDBNet.resize_imagec                 C   s   t  |¡ ddd¡ ¡  d¡S )a4  
        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   )r@   Ú
from_numpyÚpermuteÚfloatÚ	unsqueezer\   r.   r.   r/   Úimage_array2tensor  s   zDBNet.image_array2tensorc                 C   s   || j  d 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   r\   r.   r.   r/   Únormalize_image  s   zDBNet.normalize_imagec                 C   s8   |   |¡}| j||d\}}|  |¡}|  |¡}||fS )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.
        ©rh   )r^   ro   rv   ru   )r&   Ú
image_pathrh   rg   Úoriginal_shaper.   r.   r/   Ú
load_image,  s
   


zDBNet.load_imagec                    s.   t ‡ ‡fdd„|D ƒŽ \}}tj|dd|fS )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.
        c                    s   g | ]	}ˆj |ˆ d ‘qS )rw   )rz   )Ú.0r]   ©rh   r&   r.   r/   Ú
<listcomp>X  s    ÿz%DBNet.load_images.<locals>.<listcomp>r   )Údim)Úzipr@   Úcat)r&   Úimagesrh   Úoriginal_shapesr.   r|   r/   Úload_imagesE  s   ÿzDBNet.load_imagesçš™™™™™É?é   Fc	              
   C   s¼   | j ||d}	g }
g }t| d¡ƒD ]9}|| \}}|r/| j|| |	| |||||d\}}n| j|| |	| |||||d\}}|
 |¡ | |¡ qtdd„ t|
|ƒD ƒŽ \}
}|
|fS )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_candidatesc                 S   s<   g | ]\}}t |d kƒrtdd„ t||ƒD ƒŽ nddg‘qS )r   c                 S   s    g | ]\}}|d kr||f‘qS ©r   r.   )r{   ÚboxÚscorer.   r.   r/   r}   ¥  s    ÿz.DBNet.hmap2bbox.<locals>.<listcomp>.<listcomp>r.   )Úanyr   )r{   ÚboxesÚscoresr.   r.   r/   r}   ¥  s    
ÿ
þÿþz#DBNet.hmap2bbox.<locals>.<listcomp>)ÚbinarizeÚrangeÚsizeÚpolygons_from_bitmapÚboxes_from_bitmapÚappendr   )r&   Úimage_tensorr‚   ÚhmapÚtext_thresholdr‡   rˆ   r‰   Ú
as_polygonÚsegmentationÚboxes_batchÚscores_batchÚbatch_indexri   rj   rŽ   r   r.   r.   r/   Ú	hmap2bbox\  s<   .ù	
ù
	ýzDBNet.hmap2bboxc                 C   s   ||kS )a  
        Apply threshold to return boolean tensor.

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

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

        r.   )r&   Útensorr†   r.   r.   r/   r   ­  s   zDBNet.binarizec                 C   sÜ  |  d¡dks	J ‚| ¡  ¡ d }| ¡  ¡  ¡ d }|j\}	}
g }g }t |d  tj	¡tj
tj¡\}}|dkr?|d|… }|D ]¨}dt |d¡ }t ||d¡}| d¡}|jd dk r_qA|  || d	d
¡¡}||k rnqA|jd d
kr„| j|dd}t|ƒdkrƒqAnqA| d	d
¡}|  | d¡¡\}}||d
 k rœqAt|tƒs©| ¡ }| ¡ }t t |dd…df |
 | ¡d|¡|dd…df< t t |dd…df |	 | ¡d|¡|dd…df< | | ¡ ¡ | |¡ qA||fS )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   éÿ   Ngü©ñÒMb`?T)rQ   rp   é   rQ   rp   g       @)Úunclip_ratio)rQ   r   rp   )r’   rJ   ÚnumpyÚdetachr`   rS   ÚfindContoursrV   r$   Úuint8Ú	RETR_LISTÚCHAIN_APPROX_SIMPLEÚ	arcLengthÚapproxPolyDPÚreshapeÚbox_score_fastÚunclipÚlenÚget_mini_boxesr7   rc   ÚitemÚclipÚroundr•   Útolist)r&   r—   rš   Ú
dest_widthÚdest_heightr‡   rˆ   r‰   Úbitmapri   rj   rŽ   r   Úcontoursrk   ÚcontourÚepsilonÚapproxÚpointsrŒ   r‹   Ússider.   r.   r/   r“   À  sV   (
þ
ÿ
 ÿ ÿzDBNet.polygons_from_bitmapc                 C   sî  |  d¡dks	J ‚| ¡  ¡ d }| ¡  ¡  ¡ d }|j\}	}
t |d  tj	¡tj
tj¡\}}|dkr=tt|ƒ|ƒ}nt|ƒ}tj|ddftjd}tj|ftjd}t|ƒD ]—}|| }|  |¡\}}||k rkqYt |¡}|  || dd¡¡}||k rqY|  |¡ ddd¡}|  |¡\}}||d k r—qYt |¡}t|tƒs©| ¡ }| ¡ }t t |dd…df |
 | ¡d|¡|dd…df< t t |dd…df |	 | ¡d|¡|dd…df< | tj¡||dd…dd…f< |||< qY| ¡ |fS )	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¡   rp   ©ÚdtyperQ   N)r’   rJ   r£   r¤   r`   rS   r¥   rV   r$   r¦   r§   r¨   rb   r®   ÚzerosÚint16rP   r‘   r¯   r%   r¬   r«   r­   r7   rc   r°   r±   r²   r³   )r&   r—   rš   r´   rµ   r‡   rˆ   r‰   r¶   ri   rj   r·   rk   Únum_contoursrŽ   r   Úindexr¸   r»   r¼   rŒ   r‹   r.   r.   r/   r”     sN   '
þ


 ÿ ÿ
zDBNet.boxes_from_bitmapç      ø?c                 C   sF   t |ƒ}|j| |j }t ¡ }| |tjtj¡ t 	| 
|¡¡}|S ©N)r   ÚareaÚlengthÚ	pyclipperÚPyclipperOffsetÚAddPathÚJT_ROUNDÚET_CLOSEDPOLYGONr$   r%   ÚExecute)r&   r‹   r¢   ÚpolyÚdistanceÚoffsetÚexpandedr.   r.   r/   r­   o  s   zDBNet.unclipc           	      C   s°   t  |¡}ttt  |¡ƒdd„ d}d\}}}}|d d |d d kr)d}d}nd}d}|d d |d d kr>d}d}nd}d}|| || || || g}|t|d ƒfS )	Nc                 S   s   | d S )Nr   r.   )Úxr.   r.   r/   Ú<lambda>z  s    z&DBNet.get_mini_boxes.<locals>.<lambda>)r:   )r   r   rp   r…   r   r   r…   rp   )rS   ÚminAreaRectÚsortedÚlistÚ	boxPointsrb   )	r&   r¸   Úbounding_boxr»   Úindex_1Úindex_2Úindex_3Úindex_4r‹   r.   r.   r/   r¯   x  s"   
ÿzDBNet.get_mini_boxesc                 C   s†  |j dd… \}}| ¡ }t t |dd…df  ¡ ¡ tj¡d|d ¡}t t |dd…df  	¡ ¡ tj¡d|d ¡}t t |dd…df  ¡ ¡ tj¡d|d ¡}t t |dd…df  	¡ ¡ tj¡d|d ¡}	tj
|	| d || d ftjd}
|dd…df | |dd…df< |dd…df | |dd…df< t |
| ddd¡ tj¡d¡ t |||	d …||d …f |
¡d S )a-  
        Calculate total score of each bounding box

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

        Returns
        -------
        float
            Confidence score.
        Nrp   r   r   r½   rQ   )r`   Úcopyr$   r±   Úfloorrb   rV   Úint32re   ra   r¿   r¦   rS   ÚfillPolyr«   Úmean)r&   r—   Úbox_ÚhÚwr‹   ÚxminÚxmaxÚyminÚymaxÚmaskr.   r.   r/   r¬     s   0000$   (zDBNet.box_score_fastc                 C   s   | j j|ddS )ap  
        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–   r.   r.   r/   Ú
image2hmap­  s   zDBNet.image2hmapc	                 C   s‚   t |tƒs|g}| j||d\}	}
t ¡  |  |	¡}| j|	|
||||||d\}}W d  ƒ n1 s4w   Y  |r?||fS |S )az  
        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.

        rw   )r˜   r‡   rˆ   r‰   r™   N)r7   rÕ   rƒ   r@   Úno_gradrë   rž   )r&   r]   r˜   r‡   rˆ   r‰   rh   r™   Úreturn_scoresr–   r‚   r—   Úbatch_boxesÚbatch_scoresr.   r.   r/   Ú	inference¾  s$   
.

ùþzDBNet.inference)r   Nr   TNr   r   rÄ   rŠ   )r„   r„   r…   r   F)r„   r…   r   )rÃ   )r„   r„   r…   r   NFF)Ú__name__Ú
__module__Ú__qualname__r0   r   rC   rI   r#   r^   ro   ru   rv   rz   rƒ   rž   r   r“   r”   r­   r¯   r¬   rë   rð   r.   r.   r.   r/   r      s\    
ùU 
#$


øQ
ù`
ù
T	ør   )Ú__doc__r   rd   r   Úshapely.geometryr   Ú	PIL.ImagerX   r£   r$   rS   rÇ   r@   Úmodel.constructorr   r   r.   r.   r.   r/   Ú<module>   s    