code: def estimate_orientation(
      img: np.ndarray,
      n_ct: int = 50,
      ratio_threshold_for_lines: float = 5,
  ) -> int:
      """Estimate the angle of the general document orientation based on the
       lines of the document and the assumption that they should be horizontal.
  
      Args:
      ----
          img: the img or bitmap to analyze (H, W, C)
          n_ct: the number of contours used for the orientation estimation
          ratio_threshold_for_lines: this is the ratio w/h used to discriminates lines
  
      Returns:
      -------
          the angle of the general document orientation
      """
      assert len(img.shape) == 3 and img.shape[-1] in [
          1,
          3,
      ], f"Image shape {img.shape} not supported"
      max_value = np.max(img)
      min_value = np.min(img)
      if (
          max_value <= 1
          and min_value >= 0
          or (
              max_value <= 255 and min_value >= 0 and img.shape[-1] == 1
          )
      ):
          thresh = img.astype(np.uint8)
      if max_value <= 255 and min_value >= 0 and img.shape[-1] == 3:
          gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
          gray_img = cv2.medianBlur(gray_img, 5)
          thresh = cv2.threshold(gray_img, thresh=0, maxval=255, type=cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]  # type: ignore[assignment]
  
      # try to merge words in lines
      (h, w) = img.shape[:2]
      k_x = max(1, (floor(w / 100)))
      k_y = max(1, (floor(h / 100)))
      kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (k_x, k_y))
      thresh = cv2.dilate(thresh, kernel, iterations=1)  # type: ignore[assignment]
  
      # extract contours
      contours, _ = cv2.findContours(
          thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE
      )
  
      # Sort contours
      contours = sorted(
          contours, key=get_max_width_length_ratio, reverse=True
      )
  
      angles = []
      for contour in contours[:n_ct]:
          _, (w, h), angle = cv2.minAreaRect(contour)
          if (
              w / h > ratio_threshold_for_lines
          ):  # select only contours with ratio like lines
              angles.append(angle)
          elif (
              w / h < 1 / ratio_threshold_for_lines
          ):  # if lines are vertical, substract 90 degree
              angles.append(angle - 90)
  
      if len(angles) == 0:
          return 0  # in case no angles is found
      else:
          median = -median_low(angles)
          return round(median) if abs(median) != 0 else 0
  
file_path: doctr/doctr/models/_utils.py
imports: from math import floor
  from statistics import median_low
  from typing import Any, Dict, List, Optional, Tuple, Union
  import cv2
  import numpy
  from langdetect import LangDetectException, detect_langs
label_desc: Use image node_id 2 for calling a global function with example usage: estimate_orientation(img, n_ct, ratio_threshold_for_lines) and returns: int, unknown