Spaces:
Sleeping
Sleeping
File size: 3,457 Bytes
fe30d6e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | """
μΈλ‘λ‘ κΈ΄ μ
보 νμ΄μ§λ₯Ό μμ€ν
(ν°λ³΄ν λ¨μ) κ²½κ³μμ μλΌ staff κ²μΆμ μμ ννλ€.
κ°λ‘ ν¬μ(μ€μ κ°μ‘°) ν μν¬κ° μ€λ λΉλ ꡬκ°μ μ°Ύμ λΆν νλ€.
"""
from __future__ import annotations
import os
from typing import List, Tuple
import cv2
import numpy as np
_DISABLE = os.environ.get("STELLA_SYSTEM_SPLIT_DISABLE", "").lower() in ("1", "true", "yes")
def horizontal_row_density(image_bgr: np.ndarray) -> np.ndarray:
gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3, 3), 0)
binary = cv2.adaptiveThreshold(
blur,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV,
31,
15,
)
h, w = binary.shape
kernel_width = max(25, w // 12)
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_width, 1))
horizontal = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_kernel, iterations=1)
return np.sum(horizontal > 0, axis=1).astype(np.float64)
def _moving_average(arr: np.ndarray, window: int) -> np.ndarray:
window = max(3, window | 1)
pad = window // 2
padded = np.pad(arr, (pad, pad), mode="edge")
cumsum = np.cumsum(np.insert(padded, 0, 0))
out = (cumsum[window:] - cumsum[:-window]) / float(window)
return out[: len(arr)]
def _propose_cut_rows(row_density: np.ndarray, h: int) -> List[int]:
"""κ° μμ€ν
μ¬μ΄μ μ»· y(λ°΄λ κ²½κ³)."""
if h < 200:
return []
win = max(21, min(71, h // 55))
sm = _moving_average(row_density, win)
mx = float(np.max(sm)) or 1.0
low_thresh = max(12.0, 0.11 * mx)
min_run = max(12, h // 100)
below = sm < low_thresh
runs: List[Tuple[int, int]] = []
i = 0
while i < len(below):
if not below[i]:
i += 1
continue
j = i
while j < len(below) and below[j]:
j += 1
if j - i >= min_run:
runs.append((i, j))
i = j
line_guess = max(10, h // 140)
min_band = max(8 * line_guess, int(h * 0.11))
cuts = [(a + b) // 2 for a, b in runs if (b - a) >= min_run * 0.35]
header = int(h * 0.06)
cuts = [c for c in cuts if c > header + min_band // 2]
merged: List[int] = []
for c in sorted(cuts):
if not merged or c - merged[-1] >= min_band:
merged.append(c)
elif (merged[-1] + c) // 2 != merged[-1]:
merged[-1] = (merged[-1] + c) // 2
filtered: List[int] = []
prev = 0
for c in merged:
if c - prev >= min_band and h - c >= min_band:
filtered.append(c)
prev = c
return filtered
def split_work_bgr_into_bands(work_bgr: np.ndarray) -> List[Tuple[np.ndarray, int, int]]:
"""
work μ’νκ³μμ (λ°΄λ μ΄λ―Έμ§, y0, x0) λͺ©λ‘. x0λ νμ 0.
λΉνμ±Β·μ»· μμΒ·λ무 μμ νμ΄μ§λ©΄ [(μ 체, 0, 0)].
"""
if _DISABLE:
return [(work_bgr, 0, 0)]
h, w = work_bgr.shape[:2]
rd = horizontal_row_density(work_bgr)
cuts = _propose_cut_rows(rd, h)
if not cuts:
return [(work_bgr, 0, 0)]
bounds = [0] + cuts + [h]
bands: List[Tuple[np.ndarray, int, int]] = []
for i in range(len(bounds) - 1):
y0, y1 = bounds[i], bounds[i + 1]
if y1 - y0 < 50:
continue
bands.append((work_bgr[y0:y1, 0:w], y0, 0))
return bands if bands else [(work_bgr, 0, 0)]
|