docstr: Cut region from image
  Parameters
  ----------
  pageimg : page image
  coordstring : coordinates from PAGE in the form "c1_1,c_2 c2_1,c2_2 ..."
  mode :
      CutMode.BOX : cut straight rectangle around coordinates
      CutMode.POLYGON : cut polygon around coordinates
      CutMode.MBR : cut minimum bounding rectangle around coordinates
  angle :
      float : rotate angle in clockwise direction
      None : guess angle from minimum bounding rectangle
  max_auto_angle :
      float : if angle is None, try to guess angle up to boundary
  cval :
      colour : mask and fill empty regions with
      None : calculate via maximum pixel
  scale : factor to scale the coordinates with
docstr_tok: ["Cut","region","from","image","Parameters","--","--","--","--","--","pageimg",":","page","image","coordstring",":","coordinates","from","PAGE","in","the","form","``","c1_1",",","c_2","c2_1",",","c2_2","...","''","mode",":","CutMode.BOX",":","cut","straight","rectangle","around","coordinates","CutMode.POLYGON",":","cut","polygon","around","coordinates","CutMode.MBR",":","cut","minimum","bounding","rectangle","around","coordinates","angle",":","float",":","rotate","angle","in","clockwise","direction","None",":","guess","angle","from","minimum","bounding","rectangle","max_auto_angle",":","float",":","if","angle","is","None",",","try","to","guess","angle","up","to","boundary","cval",":","colour",":","mask","and","fill","empty","regions","with","None",":","calculate","via","maximum","pixel","scale",":","factor","to","scale","the","coordinates","with"]
code: def cutout(
      pageimg: np.array,
      coordstring: str,
      mode: CutMode,
      angle=0,
      max_auto_angle=0,
      cval=None,
      scale=1,
  ):
      """Cut region from image
      Parameters
      ----------
      pageimg : page image
      coordstring : coordinates from PAGE in the form "c1_1,c_2 c2_1,c2_2 ..."
      mode :
          CutMode.BOX : cut straight rectangle around coordinates
          CutMode.POLYGON : cut polygon around coordinates
          CutMode.MBR : cut minimum bounding rectangle around coordinates
      angle :
          float : rotate angle in clockwise direction
          None : guess angle from minimum bounding rectangle
      max_auto_angle :
          float : if angle is None, try to guess angle up to boundary
      cval :
          colour : mask and fill empty regions with
          None : calculate via maximum pixel
      scale : factor to scale the coordinates with
      """
  
      coords = [p.split(",") for p in coordstring.split()]
      if not coords:
          return pageimg[0:0, 0:0]
      coords = [
          (int(scale * int(c[1])), int(scale * int(c[0])))
          for c in coords
      ]
      coords = np.array(coords, np.int32).reshape((-1, 1, 2))
      maxX, maxY = np.amax(coords, 0).squeeze()
      minX, minY = np.amin(coords, 0).squeeze()
      cut = pageimg[minX : maxX + 1, minY : maxY + 1]
      if cut.size == 0:
          return cut  # empty image
      coords -= (minX, minY)
      maxX, maxY = (maxX - minX, maxY - minY)
      minX, minY = (0, 0)
  
      # calculate angle if needed
      if angle is None:
          if max_auto_angle > 0:
              mbr = cv.minAreaRect(coords)
              angle = mbr[2] - 90 if mbr[2] > 45 else mbr[2]
              if abs(angle) > max_auto_angle:
                  angle = 0
          else:
              angle = 0
  
      # set cval if needed
      if cval is None:
          if cut.ndim == 2:
              cval = np.amax(cut).item()
          else:
              x, y = np.unravel_index(
                  np.argmax(np.mean(cut, axis=2)), cut.shape[:2]
              )
              cval = cut[x, y, :].tolist()
  
      # rotate cut
      if angle:
          (h, w) = cut.shape[:2]
          (cX, cY) = (w // 2, h // 2)
          M = cv.getRotationMatrix2D((cX, cY), -angle, 1.0)
          cos = np.abs(M[0, 0])
          sin = np.abs(M[0, 1])
          # compute the new bounding dimensions of the image
          nW = np.ceil((h * sin) + (w * cos)).astype(int)
          nH = np.ceil((h * cos) + (w * sin)).astype(int)
          # adjust the rotation matrix to take into account translation
          M[0, 2] += (nW / 2) - cX
          M[1, 2] += (nH / 2) - cY
          # rotate coords
          coords = cv.transform(coords[..., ::-1], M)
          minX, minY = np.amin(coords, 0).squeeze()
          maxX, maxY = np.amax(coords, 0).squeeze()
          # rotate image
          cut = cv.warpAffine(
              cut,
              M,
              (nW, nH),
              flags=cv.INTER_LINEAR,
              borderMode=cv.BORDER_CONSTANT,
              borderValue=cval,
          )
      else:
          coords = coords[..., ::-1]
          minX, minY = minY, minX
          maxX, maxY = maxY, maxX
  
      # simplify coordinates with MBR
      if mode is CutMode.MBR:
          mbr = cv.minAreaRect(coords)
          coords = cv.boxPoints(mbr).astype(int).reshape(-1, 1, 2)
  
      # mask pixels outside coords
      if mode in (CutMode.POLYGON, CutMode.MBR):
          box = (np.ones(cut.shape) * cval).astype(cut.dtype)
          mask = np.zeros(cut.shape, dtype=np.uint8)
          mask = cv.fillPoly(mask, [coords], color=[255] * cut.ndim)
          mask_inv = cv.bitwise_not(mask)
          fg = cv.bitwise_and(cut, mask)
          bg = cv.bitwise_and(box, mask_inv)
          cut = cv.add(fg, bg)
  
      return cut[minY : maxY + 1, minX : maxX + 1]
  
imports: import os
  from copy import deepcopy
  from dataclasses import dataclass, field
  from random import shuffle
  import math
  import numpy
  from paiargparse import pai_dataclass, pai_meta
  from tfaip.data.pipeline.definitions import PipelineMode, INPUT_PROCESSOR, TARGETS_PROCESSOR
  from tqdm import tqdm
  from lxml import etree
  import cv2
  from typing import List, Generator, Optional, Iterable, Dict, Any, Tuple
  from enum import IntEnum
  from calamari_ocr.ocr.dataset.datareader.base import CalamariDataGenerator, CalamariDataGeneratorParams, InputSample, SampleMeta
  from calamari_ocr.utils import split_all_ext, filename, glob_all
  from calamari_ocr.ocr.predict.params import Prediction
  import logging
label_desc: Use image node_id 2 for calling the PageXMLReader obj's underlying member method code with example usage: obj.cutout(pageimg, coordstring, mode, angle, max_auto_angle, cval, scale) and returns: cut, pageimg, cut