Repository: roboflow/supervision
File: supervision/dataset/formats/pascal_voc.py (Python)

Code before:
    Returns:
        Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]: A tuple containing a list of class names, a dictionary with image names as keys and images as values, and a dictionary with image names as keys and corresponding Detections instances as values.
    """

    image_paths = list_files_with_extensions(
        directory=images_directory_path, extensions=["jpg", "jpeg", "png"]
    )

    classes = []
    images = {}
    annotations = {}

    for image_path in image_paths:
        image_name = Path(image_path).stem
        image = cv2.imread(str(image_path))

        annotation_path = os.path.join(annotations_directory_path, f"{image_name}.xml")
        if not os.path.exists(annotation_path):
            images[image_path.name] = image
            annotations[image_path.name] = Detections.empty()
            continue

        tree = parse(annotation_path)
        root = tree.getroot()

        resolution_wh = (image.shape[0], image.shape[1])
        annotation, classes = detections_from_xml_obj(
            root, classes, resolution_wh, force_masks
        )

        images[image_path.name] = image
        annotations[image_path.name] = annotation

    return classes, images, annotations


def detections_from_xml_obj(
    root: Element, classes: List[str], resolution_wh, force_masks: bool = False
) -> Tuple[Detections, List[str]]:
    """
    Converts an XML object in Pascal VOC format to a Detections object.
    Expected XML format:
    <annotation>
        ...
        <object>
            <name>dog</name>
            <bndbox>
                <xmin>48</xmin>
                <ymin>240</ymin>
                <xmax>195</xmax>
                <ymax>371</ymax>

Review comment (bug):
The order in `image.shape` is `(H, W, C)`. And that is why we get the incorrect shape of generated masks, as `resolution_wh` is in the incorrect order. 

Code after:
    Returns:
        Tuple[List[str], Dict[str, np.ndarray], Dict[str, Detections]]: A tuple containing a list of class names, a dictionary with image names as keys and images as values, and a dictionary with image names as keys and corresponding Detections instances as values.
    """

    image_paths = list_files_with_extensions(
        directory=images_directory_path, extensions=["jpg", "jpeg", "png"]
    )

    classes = []
    images = {}
    annotations = {}

    for image_path in image_paths:
        image_name = Path(image_path).stem
        image = cv2.imread(str(image_path))

        annotation_path = os.path.join(annotations_directory_path, f"{image_name}.xml")
        if not os.path.exists(annotation_path):
            images[image_path.name] = image
            annotations[image_path.name] = Detections.empty()
            continue

        tree = parse(annotation_path)
        root = tree.getroot()

        resolution_wh = (image.shape[1], image.shape[0])
        annotation, classes = detections_from_xml_obj(
            root, classes, resolution_wh, force_masks
        )

        images[image_path.name] = image
        annotations[image_path.name] = annotation

    return classes, images, annotations


def detections_from_xml_obj(
    root: Element, classes: List[str], resolution_wh, force_masks: bool = False
) -> Tuple[Detections, List[str]]:
    """
    Converts an XML object in Pascal VOC format to a Detections object.
    Expected XML format:
    <annotation>
        ...
        <object>
            <name>dog</name>
            <bndbox>
                <xmin>48</xmin>
                <ymin>240</ymin>
                <xmax>195</xmax>
                <ymax>371</ymax>