def from_pascal_voc(
      cls,
      images_dir: str,
      annotations_dir: str,
      label_map: Union[List[str], Dict[int, str], str],
      annotation_filenames: Optional[Collection[str]] = None,
      ignore_difficult_instances: bool = False,
      num_shards: int = 100,
      max_num_images: Optional[int] = None,
      cache_dir: Optional[str] = None,
      cache_prefix_filename: Optional[str] = None) -> DetectorDataLoader:
    """Loads from dataset with PASCAL VOC format.

    Refer to
    https://towardsdatascience.com/coco-data-format-for-object-detection-a4c5eaf518c5
    for the description of PASCAL VOC data format.

    LabelImg Tool (https://github.com/tzutalin/labelImg) can annotate the image
    and save annotations as XML files in PASCAL VOC data format.

    Annotations are in the folder: `annotations_dir`.
    Raw images are in the foloder: `images_dir`.

    Args:
      images_dir: Path to directory that store raw images.
      annotations_dir: Path to the annotations directory.
      label_map: Variable shows mapping label integers ids to string label
        names. 0 is the reserved key for `background`. Label names can't be
        duplicated. Supported format: 1. Dict, map label integers ids to string
          label names, e.g.
           {1: 'person', 2: 'notperson'}. 2. List, a list of label names. e.g.
             ['person', 'notperson'] which is
           the same as setting label_map={1: 'person', 2: 'notperson'}.
        3. String, name for certain dataset. Accepted values are: 'coco', 'voc'
          and 'waymo'. 4. String, yaml filename that stores label_map.
      annotation_filenames: Collection of annotation filenames (strings) to be
        loaded. For instance, if there're 3 annotation files [0.xml, 1.xml,
        2.xml] in `annotations_dir`, setting annotation_filenames=['0', '1']
        makes this method only load [0.xml, 1.xml].
      ignore_difficult_instances: Whether to ignore difficult instances.
        `difficult` can be set inside `object` item in the annotation xml file.
      num_shards: Number of shards for output file.
      max_num_images: Max number of imags to process.
      cache_dir: The cache directory to save TFRecord, metadata and json file.
        When cache_dir is not set, a temporary folder will be created and will
        not be removed automatically after training which makes it can be used
        later.
      cache_prefix_filename: The cache prefix filename. If not set, will
        automatically generate it based on `image_dir`, `annotations_dir` and
        `annotation_filenames`.

    Returns:
      ObjectDetectorDataLoader object.
    """
    label_map = _get_label_map(label_map)

    # If `cache_prefix_filename` is None, automatically generates a hash value.
    if cache_prefix_filename is None:
      cache_prefix_filename = util.get_cache_prefix_filename_from_pascal(
          images_dir=images_dir,
          annotations_dir=annotations_dir,
          annotation_filenames=annotation_filenames,
          num_shards=num_shards)

    cache_files = util.get_cache_files(
        cache_dir=cache_dir,
        cache_prefix_filename=cache_prefix_filename,
        num_shards=num_shards)

    # If not cached, writes data into tfrecord_file_paths and
    # annotations_json_file_path.
    # If `num_shards` differs, it's still not cached.
    if not util.is_cached(cache_files):
      cache_writer = util.PascalVocCacheFilesWriter(
          label_map=label_map,
          images_dir=images_dir,
          num_shards=num_shards,
          max_num_images=max_num_images,
          ignore_difficult_instances=ignore_difficult_instances)
      cache_writer.write_files(
          cache_files=cache_files,
          annotations_dir=annotations_dir,
          annotation_filenames=annotation_filenames)

    return cls.from_cache(cache_files.cache_prefix)