Skip to content

Key Information Extractor

Key Information Extraction (KIE) heuristics for the vision module of Otary.

HeuristicKeyInformationExtractor

Key Information Extraction (KIE) heuristic class

Source code in otary/vision/kie/heuristic.py
class HeuristicKeyInformationExtractor:
    """Key Information Extraction (KIE) heuristic class"""

    # pylint: disable=too-few-public-methods

    @staticmethod
    def extract(
        ocr_outputs: OcrMultiOutput,
        key: str,
        closest_word_dist_thresh: float,
        levenshtein_threshold: float = 0.9,
        exact_key_match: bool = False,
    ) -> list[OcrSingleOutput]:
        """
        Extracts key-value pairs from OCR outputs by matching keys heuristically
        using Levenshtein distance.

        Parameters:
            ocr_outputs (List[OCRSingleOutput]): OCR results, each containing a value
                and access to closest_word(to="left").
            key (str): expected keys to match.
            closest_word_dist_thresh (float): Maximum distance between the OCR key
                bounding box and the closest word bounding box for the value.
            levenshtein_threshold (float): Minimum normalized distance for a valid match
            exact_key_match (bool): Whether to match keys exactly

        Returns:
            Dict[str, str]: Dictionary mapping expected keys to matched values.
        """

        def is_valid_ocrso_and_key(ocrso: OcrSingleOutput) -> bool:
            if ocrso.text is None:
                return False

            if exact_key_match:
                return ocrso.text.lower() == key.lower()

            levenshtein_score = Levenshtein.similarity(key.lower(), ocrso.text.lower())
            return levenshtein_score >= levenshtein_threshold

        # find the good candidates for the key
        candidates = [
            ocrso for ocrso in ocr_outputs.ocrsos if is_valid_ocrso_and_key(ocrso)
        ]

        # find the value associated for each candidate
        result: list[OcrSingleOutput] = []
        for candidate in candidates:
            ocrso = ocr_outputs.closest_word(
                word=candidate, _to="right", dist_thresh=closest_word_dist_thresh
            )
            if ocrso is not None:
                result.append(ocrso)

        return result

extract(ocr_outputs, key, closest_word_dist_thresh, levenshtein_threshold=0.9, exact_key_match=False) staticmethod

Extracts key-value pairs from OCR outputs by matching keys heuristically using Levenshtein distance.

Parameters:

Name Type Description Default
ocr_outputs List[OCRSingleOutput]

OCR results, each containing a value and access to closest_word(to="left").

required
key str

expected keys to match.

required
closest_word_dist_thresh float

Maximum distance between the OCR key bounding box and the closest word bounding box for the value.

required
levenshtein_threshold float

Minimum normalized distance for a valid match

0.9
exact_key_match bool

Whether to match keys exactly

False

Returns:

Type Description
list[OcrSingleOutput]

Dict[str, str]: Dictionary mapping expected keys to matched values.

Source code in otary/vision/kie/heuristic.py
@staticmethod
def extract(
    ocr_outputs: OcrMultiOutput,
    key: str,
    closest_word_dist_thresh: float,
    levenshtein_threshold: float = 0.9,
    exact_key_match: bool = False,
) -> list[OcrSingleOutput]:
    """
    Extracts key-value pairs from OCR outputs by matching keys heuristically
    using Levenshtein distance.

    Parameters:
        ocr_outputs (List[OCRSingleOutput]): OCR results, each containing a value
            and access to closest_word(to="left").
        key (str): expected keys to match.
        closest_word_dist_thresh (float): Maximum distance between the OCR key
            bounding box and the closest word bounding box for the value.
        levenshtein_threshold (float): Minimum normalized distance for a valid match
        exact_key_match (bool): Whether to match keys exactly

    Returns:
        Dict[str, str]: Dictionary mapping expected keys to matched values.
    """

    def is_valid_ocrso_and_key(ocrso: OcrSingleOutput) -> bool:
        if ocrso.text is None:
            return False

        if exact_key_match:
            return ocrso.text.lower() == key.lower()

        levenshtein_score = Levenshtein.similarity(key.lower(), ocrso.text.lower())
        return levenshtein_score >= levenshtein_threshold

    # find the good candidates for the key
    candidates = [
        ocrso for ocrso in ocr_outputs.ocrsos if is_valid_ocrso_and_key(ocrso)
    ]

    # find the value associated for each candidate
    result: list[OcrSingleOutput] = []
    for candidate in candidates:
        ocrso = ocr_outputs.closest_word(
            word=candidate, _to="right", dist_thresh=closest_word_dist_thresh
        )
        if ocrso is not None:
            result.append(ocrso)

    return result