docstr: Evaluate a single pair of data
  
  Parameters
  ----------
  _sentinel : None
      Sentinel to force to specify gt and pred manually
  gt : str
      ground truth
  pred : str
      prediction
  skip_empty_gt : bool
      skip gt text lines that are empty
  
  Returns
  -------
  int
      length of ground truth
  int
      number of errors
  int
      number of synchronisation errors
  dict
      confusions dictionary
  tuple(str, str)
      ground_truth, prediction (same as input)
docstr_tok: ["Evaluate","a","single","pair","of","data","Parameters","--","--","--","--","--","_sentinel",":","None","Sentinel","to","force","to","specify","gt","and","pred","manually","gt",":","str","ground","truth","pred",":","str","prediction","skip_empty_gt",":","bool","skip","gt","text","lines","that","are","empty","Returns","--","--","--","-","int","length","of","ground","truth","int","number","of","errors","int","number","of","synchronisation","errors","dict","confusions","dictionary","tuple","(","str",",","str",")","ground_truth",",","prediction","(","same","as","input",")"]
code: def evaluate_single(
      _sentinel=None, gt="", pred="", skip_empty_gt=False
  ):
      """Evaluate a single pair of data
  
      Parameters
      ----------
      _sentinel : None
          Sentinel to force to specify gt and pred manually
      gt : str
          ground truth
      pred : str
          prediction
      skip_empty_gt : bool
          skip gt text lines that are empty
  
      Returns
      -------
      int
          length of ground truth
      int
          number of errors
      int
          number of synchronisation errors
      dict
          confusions dictionary
      tuple(str, str)
          ground_truth, prediction (same as input)
  
      """
      confusion = {}
      total_sync_errs = 0
  
      if len(gt) == 0 and skip_empty_gt:
          return 0, 0, 0, confusion, (gt, pred)
  
      errs, trues = edit_distance(gt, pred)
      synclist = synchronize([gt, pred])
      for sync in synclist:
          gt_str, pred_str = sync.get_text()
          if gt_str != pred_str:
              key = (gt_str, pred_str)
              total_sync_errs += max(len(gt_str), len(pred_str))
              if key not in confusion:
                  confusion[key] = 1
              else:
                  confusion[key] += 1
  
      return len(gt), errs, total_sync_errs, confusion, (gt, pred)
  
file_path: calamari/calamari_ocr/ocr/evaluator.py
imports: from dataclasses import dataclass, field
  from typing import Dict, TYPE_CHECKING
  import logging
  from edit_distance import edit_distance
  from collections import namedtuple
  from paiargparse import pai_dataclass, pai_meta
  from tfaip.data.databaseparams import DataPipelineParams
  from tfaip.data.pipeline.definitions import PipelineMode
  from tfaip.util.multiprocessing.parallelmap import parallel_map, tqdm_wrapper
  from calamari_ocr.ocr.dataset.datareader.base import CalamariDataGeneratorParams
  from calamari_ocr.ocr.dataset.textprocessors import synchronize
label_desc: Use image node_id 5 for calling the Evaluator obj's underlying member method code with example usage: obj.evaluate_single(_sentinel, gt, pred, skip_empty_gt) and returns: len,  errs,  total_sync_errs,  confusion,  gt,  pred, int,  int,  int,  confusion,  gt,  pred