import math import numpy as np def _percentiles(b: np.ndarray, qs=(0.02, 0.5, 0.98)) -> list[float]: """np.percentile(b, 100 * qs) (linear) of a uint8 array, from its histogram: no sort of the pixels.""" n = b.size cum = np.cumsum(np.bincount(b.ravel(), minlength=256)) out = [] for q in qs: x = q * (n - 1) i = math.floor(x) g = x - i lo = float(np.searchsorted(cum, i, side="right")) hi = float(np.searchsorted(cum, min(i + 1, n - 1), side="right")) d = hi - lo # numpy's lerp: from the nearer end out.append(hi - d * (1 - g) if g >= 0.5 else lo + d * g) return out def is_dark_line(gray: np.ndarray, box) -> bool: """Light text on a dark ground (a banner, a dark table head, a slide): the ground (the median of the line box) lies nearer the dark end than the text does, so the bright tail is the longer one. A shadowed line keeps dark text on a dimmer ground. The recognizers read dark text on light paper, so a light-on-dark line is read inverted.""" H, W = gray.shape x0, y0, x1, y1 = (float(v) for v in box[:4]) b = gray[ max(0, int(y0)) : min(H, int(y1) + 1), max(0, int(x0)) : min(W, int(x1) + 1) ] if not b.size: return False lo, med, hi = ( _percentiles(b) if b.dtype == np.uint8 else np.percentile(b, (2, 50, 98)) ) return bool(hi - med > 2 * (med - lo) + 8)