o
    VhH                     @   s   d dl Z d dlZd dlZd dlZd dlmZ d dlmZ d dlm	Z	 d dl
mZ d dlZd dlZd dlZd dlmZ d dlmZmZmZmZ d dlmZmZmZmZmZ d d	lmZ G d
d deZdS )    N)deepcopy)
ThreadPool)Path)Optional)Dataset)FORMATS_HELP_MSGHELP_URLIMG_FORMATScheck_file_speeds)DEFAULT_CFG
LOCAL_RANKLOGGERNUM_THREADSTQDM)imreadc                       s   e Zd ZdZdddeddddddd	d
f fdd	Zdd Zdee fddZ	d*ddZ
dd Zdd Zd+ddZd+ddZdd Zdd Zd d! Zd"d# Zd$d% Zd,d&d'Zd(d) Z  ZS )-BaseDataseta	  
    Base dataset class for loading and processing image data.

    This class provides core functionality for loading images, caching, and preparing data for training and inference
    in object detection tasks.

    Attributes:
        img_path (str): Path to the folder containing images.
        imgsz (int): Target image size for resizing.
        augment (bool): Whether to apply data augmentation.
        single_cls (bool): Whether to treat all objects as a single class.
        prefix (str): Prefix to print in log messages.
        fraction (float): Fraction of dataset to utilize.
        im_files (List[str]): List of image file paths.
        labels (List[Dict]): List of label data dictionaries.
        ni (int): Number of images in the dataset.
        rect (bool): Whether to use rectangular training.
        batch_size (int): Size of batches.
        stride (int): Stride used in the model.
        pad (float): Padding value.
        buffer (list): Buffer for mosaic images.
        max_buffer_length (int): Maximum buffer size.
        ims (list): List of loaded images.
        im_hw0 (list): List of original image dimensions (h, w).
        im_hw (list): List of resized image dimensions (h, w).
        npy_files (List[Path]): List of numpy file paths.
        cache (str): Cache images to RAM or disk during training.
        transforms (callable): Image transformation function.

    Methods:
        get_img_files: Read image files from the specified path.
        update_labels: Update labels to include only specified classes.
        load_image: Load an image from the dataset.
        cache_images: Cache images to memory or disk.
        cache_images_to_disk: Save an image as an *.npy file for faster loading.
        check_cache_disk: Check image caching requirements vs available disk space.
        check_cache_ram: Check image caching requirements vs available memory.
        set_rectangle: Set the shape of bounding boxes as rectangles.
        get_image_and_label: Get and return label information from the dataset.
        update_labels_info: Custom label format method to be implemented by subclasses.
        build_transforms: Build transformation pipeline to be implemented by subclasses.
        get_labels: Get labels method to be implemented by subclasses.
    i  FT              ?Ng      ?c                    sv  t    || _|| _|| _|| _|| _|| _| | j| _	| 
 | _| j|d t| j| _|| _|| _|	| _|
| _| jrI| jdusEJ |   g | _| jrZt| j| jd dfnd| _dg| j dg| j dg| j | _| _| _dd | j	D | _t|tr| n|du rd	nd| _| jd	kr|  r|j rt!"d
 | #  n| jdkr| $ r| #  | j%|d| _&dS )a  
        Initialize BaseDataset with given configuration and options.

        Args:
            img_path (str): Path to the folder containing images.
            imgsz (int, optional): Image size for resizing.
            cache (bool | str, optional): Cache images to RAM or disk during training.
            augment (bool, optional): If True, data augmentation is applied.
            hyp (dict, optional): Hyperparameters to apply data augmentation.
            prefix (str, optional): Prefix to print in log messages.
            rect (bool, optional): If True, rectangular training is used.
            batch_size (int, optional): Size of batches.
            stride (int, optional): Stride used in the model.
            pad (float, optional): Padding value.
            single_cls (bool, optional): If True, single class training is used.
            classes (list, optional): List of included classes.
            fraction (float, optional): Fraction of dataset to utilize.
        )include_classN   i  r   c                 S   s   g | ]	}t |d qS )z.npy)r   with_suffix).0f r   I/var/www/vscode/kcb/lib/python3.10/site-packages/ultralytics/data/base.py
<listcomp>~   s    z(BaseDataset.__init__.<locals>.<listcomp>Tramzcache='ram' may produce non-deterministic training results. Consider cache='disk' as a deterministic alternative if your disk space allows.disk)hyp)'super__init__img_pathimgszaugment
single_clsprefixfractionget_img_filesim_files
get_labelslabelsupdate_labelslennirect
batch_sizestridepadset_rectanglebufferminmax_buffer_lengthimsim_hw0im_hw	npy_files
isinstancestrlowercachecheck_cache_ramdeterministicr   warningcache_imagescheck_cache_diskbuild_transforms
transforms)selfr#   r$   r?   r%   r    r'   r0   r1   r2   r3   r&   classesr(   	__class__r   r   r"   C   s@   
"
".$
zBaseDataset.__init__c              
      sl  zg }t |tr
|n|gD ]Y}t|}| r'|tjt|d d dd7 }q| r]t|dd#}| 	 
 }t|jtj  | fdd|D 7 }W d	   n1 sWw   Y  qt| j | d
tdd |D }|sJ | j d| dt W n ty } zt| j d| dt |d	}~ww | jdk r|d	tt|| j  }t|| jd |S )aN  
        Read image files from the specified path.

        Args:
            img_path (str | List[str]): Path or list of paths to image directories or files.

        Returns:
            (List[str]): List of image file paths.

        Raises:
            FileNotFoundError: If no images are found or the path doesn't exist.
        z**z*.*T)	recursivezutf-8)encodingc                    s&   g | ]}| d r|d  n|qS )z./)
startswithreplacer   xparentr   r   r      s   & z-BaseDataset.get_img_files.<locals>.<listcomp>Nz does not existc                 s   s4    | ]}| d d  tv r|dtjV  qdS )./N)splitr>   r	   rN   osseprO   r   r   r   	<genexpr>   s   2 z,BaseDataset.get_img_files.<locals>.<genexpr>zNo images found in z. zError loading data from 
   )r'   )r<   listr   is_dirglobr=   is_fileopenreadstrip
splitlinesrR   rW   rX   FileNotFoundErrorr'   sortedr   	Exceptionr   r(   roundr.   r
   )rG   r#   r   ptr*   er   rQ   r   r)      s0    "
zBaseDataset.get_img_filesr   c                    s   t |dd}tt| jD ]j}|durj| j| d }| j| d }| j| d  | j| d }||kd}|| | j| d< || | j| d<  r] fdd	t|D | j| d< |durj|| | j| d< | jrzd
| j| d ddd
f< qdS )z
        Update labels to include only specified classes.

        Args:
            include_class (list, optional): List of classes to include. If None, all classes are included.
        r[   rT   Nclsbboxessegments	keypointsc                    s   g | ]
\}}|r | qS r   r   )r   siidxrm   r   r   r      s    z-BaseDataset.update_labels.<locals>.<listcomp>r   )	nparrayreshaperanger.   r,   any	enumerater&   )rG   r   include_class_arrayirk   rl   rn   jr   rq   r   r-      s$    zBaseDataset.update_labelsc              
   C   s(  | j | | j| | j| }}}|du r| rNzt|}W n0 tyM } z t| j	 d| d|  t
|jdd t|}W Y d}~n	d}~ww t|}|du r]td| |jdd \}}|r| jt|| }	|	dkrtt||	 | jtt||	 | j}
}tj||
|ftjd	}n||  kr| jksn tj|| j| jftjd	}| jr|||f|jdd | j |< | j|< | j|< | j| dt| j  k r| jkrn n| jd
}| jdkrd\| j |< | j|< | j|< |||f|jdd fS | j | | j| | j| fS )a  
        Load an image from dataset index 'i'.

        Args:
            i (int): Index of the image to load.
            rect_mode (bool, optional): Whether to use rectangular resizing.

        Returns:
            (np.ndarray): Loaded image as a NumPy array.
            (Tuple[int, int]): Original image dimensions in (height, width) format.
            (Tuple[int, int]): Resized image dimensions in (height, width) format.

        Raises:
            FileNotFoundError: If the image file is not found.
        Nz"Removing corrupt *.npy image file z	 due to: T)
missing_okzImage Not Found    r[   )interpolationr   r   )NNN) r8   r*   r;   existsrr   loadrf   r   rB   r'   r   unlinkr   rd   shaper$   maxr6   mathceilcv2resizeINTER_LINEARr%   r9   r:   r5   appendr.   r7   popr?   )rG   ry   	rect_modeimr   fnrj   h0w0rwhrz   r   r   r   
load_image   s@   "
.0 
zBaseDataset.load_imagec           
      C   s  d\}}| j dkr| jdfn| jdf\}}tt_}||t| j}tt	|| jt
dkd}|D ]:\}}	| j dkrE|| j|  j7 }n|	\| j|< | j|< | j|< || j| j7 }| j d|| dd	| d
|_q1|  W d   dS 1 s{w   Y  dS )z3Cache images to memory or disk for faster training.r   i   @r   DiskRAMr   )totaldisablezCaching images (.1fzGB )N)r?   cache_images_to_diskr   r   r   imapru   r/   r   rw   r   r;   statst_sizer8   r9   r:   nbytesr'   descclose)
rG   bgbfcnstoragepoolresultspbarry   rP   r   r   r   rC      s   "

"
"zBaseDataset.cache_imagesc                 C   s8   | j | }| stj| t| j| dd dS dS )z2Save an image as an *.npy file for faster loading.F)allow_pickleN)r;   r~   rr   saveas_posixr   r*   )rG   ry   r   r   r   r   r     s   
"z BaseDataset.cache_images_to_diskc                 C   s  ddl }d\}}t| jd}t|D ]/}t| j}t|}|du r#q||j7 }t	
t|jt	jsAd| _t| j d  dS q|| j | d|  }	|t| jd j\}
}}|	|krd| _t| j |	| dd	t|d
  d|| dd|
| dd	 dS dS )a  
        Check if there's enough disk space for caching images.

        Args:
            safety_margin (float, optional): Safety margin factor for disk space calculation.

        Returns:
            (bool): True if there's enough disk space, False otherwise.
        r   Nr      z8Skipping caching images to disk, directory not writeableFr[   r   zGB disk space required, with d   % safety margin but only rU   z#GB free, not caching images to diskT)shutilr6   r/   ru   randomchoicer*   r   r   rW   accessr   rR   W_OKr?   r   rB   r'   
disk_usageint)rG   safety_marginr   r   r   n_im_filer   disk_requiredr   usedfreer   r   r   rD     s:   


zBaseDataset.check_cache_diskc           
      C   s   d\}}t | jd}t|D ]&}tt| j}|du rq| jt|j	d |j	d  }||j
|d  7 }q|| j | d|  }t }	||	jkrqd| _t| j || ddt|d	  d
|	j| dd|	j| dd	 dS dS )z
        Check if there's enough RAM for caching images.

        Args:
            safety_margin (float, optional): Safety margin factor for RAM calculation.

        Returns:
            (bool): True if there's enough RAM, False otherwise.
        r   r   Nr   r[   r|   r   z%GB RAM required to cache images with r   r   rU   z GB available, not caching imagesFT)r6   r/   ru   r   r   r   r*   r$   r   r   r   psutilvirtual_memory	availabler?   r   rB   r'   r   r   )
rG   r   r   r   r   r   r   ratiomem_requiredmemr   r   r   r@   9  s.   




zBaseDataset.check_cache_ramc                    s:  t t  j j t}|d d }t dd  jD }|dddf |dddf  }|	 } fdd|D  _
 fdd|D  _|| }ddgg| }t|D ](}|||k }| | }	}
|
dk rs|
dg||< qW|	dkrdd|	 g||< qWt t | j  j  j t j  _| _dS )	zBSet the shape of bounding boxes for YOLO detections as rectangles.rT   r[   c                 S   s   g | ]}| d qS )r   )r   rO   r   r   r   r   \      z-BaseDataset.set_rectangle.<locals>.<listcomp>Nr   c                       g | ]} j | qS r   )r*   r   ry   rG   r   r   r   _  r   c                    r   r   )r,   r   r   r   r   r   `  r   )rr   flooraranger/   r1   astyper   rs   r,   argsortr*   ru   r6   r   r   r$   r2   r3   batch_shapesbatch)rG   binbsarirectshapesry   ariminimaxir   r   r   r4   W  s&    0
zBaseDataset.set_rectanglec                 C   s   |  | |S )z5Return transformed label information for given index.)rF   get_image_and_label)rG   indexr   r   r   __getitem__p  s   zBaseDataset.__getitem__c                 C   s   t | j| }|dd | |\|d< |d< |d< |d d |d d  |d d |d d  f|d< | jrB| j| j|  |d	< | |S )
z
        Get and return label information from the dataset.

        Args:
            index (int): Index of the image to retrieve.

        Returns:
            (dict): Label dictionary with image and metadata.
        r   Nimg	ori_shaperesized_shaper   r[   	ratio_pad
rect_shape)r   r,   r   r   r0   r   r   update_labels_info)rG   r   labelr   r   r   r   t  s   

zBaseDataset.get_image_and_labelc                 C   s
   t | jS )z5Return the length of the labels list for the dataset.)r.   r,   r   r   r   r   __len__  s   
zBaseDataset.__len__c                 C   s   |S )zCustom your label format here.r   )rG   r   r   r   r   r     s   zBaseDataset.update_labels_infoc                 C      t )a  
        Users can customize augmentations here.

        Examples:
            >>> if self.augment:
            ...     # Training transforms
            ...     return Compose([])
            >>> else:
            ...    # Val transforms
            ...    return Compose([])
        NotImplementedError)rG   r    r   r   r   rE     s   zBaseDataset.build_transformsc                 C   r   )a)  
        Users can customize their own format here.

        Examples:
            Ensure output is a dictionary with the following keys:
            >>> dict(
            ...     im_file=im_file,
            ...     shape=shape,  # format: (height, width)
            ...     cls=cls,
            ...     bboxes=bboxes,  # xywh
            ...     segments=segments,  # xy
            ...     keypoints=keypoints,  # xy
            ...     normalized=True,  # or False
            ...     bbox_format="xyxy",  # or xywh, ltwh
            ... )
        r   r   r   r   r   r+     s   zBaseDataset.get_labels)T)r   )N)__name__
__module____qualname____doc__r   r"   r)   r   r\   r-   r   rC   r   rD   r@   r4   r   r   r   r   rE   r+   __classcell__r   r   rI   r   r      s:    /J&
4

$
r   ) r^   r   rW   r   copyr   multiprocessing.poolr   pathlibr   typingr   r   numpyrr   r   torch.utils.datar   ultralytics.data.utilsr   r   r	   r
   ultralytics.utilsr   r   r   r   r   ultralytics.utils.patchesr   r   r   r   r   r   <module>   s    