Skip to content

Commit ee2d9ec

Browse files
joyeliclaude
andcommitted
parity: 夜讀修法5——閉合格背景擢升(貼框白元件進 sticker、兩級守門)+ 預設曲線 lin8
使用者目檢曲線實驗拍板 lin 系(黑實對比大)後回饋:白區仍過多、明顯還有可塗黑的。 解剖 11 頁剩餘亮區:主力=「未列管白」16-22%/頁——被格線封閉的格內背景(天空/空白底) 不與留白連通、永遠進不了修法3 的 gutter/panel 分類,修法4 候選根本沒它。 修法5=貼框擢升:有框頁上,未列管大白元件量「貼格線長度/bbox 周長」(frame_line_mask 與修法2 共用),≥0.25 擢升進 sticker 候選。兩級守門: - 強貼框(≥0.40,背景證據極強)走放寬門——跳過 textCov(pad40 窗對大背景是鄰泡污染, ch34_014 實測 textCov 0.837 但 textOn=0)與 eaten 中段/小面積門(eaten 聚團交給 _sticker_protect 區域級保護),保留 fig/thin/chroma/eatenHARD 四道硬底線 + PROMOTED_TEXTON_MAX=0.3(真旁白框拒填、留灰)。 - 弱貼框(0.25-0.40)維持原門。前景白(臉/白衣)只點狀碰框、hug 低、天然不擢升。 11 頁回歸:亮區 ch34_014 33.5→25.3(-8.2pt)、ch34_010 -3.1、006 -2.0、011 -1.1、 demo06 -1.0;歷史危險頁目檢過——白鬍老人(006)鬍鬚完整、白洋裝/披肩(010)完整、 人物白描邊/氣泡深底白字全正常,毀畫面級 0。無框頁(demo04/05)不在此修範圍(次波)。 預設曲線 d2 → lin8(NIGHTREAD_CURVE 仍可選);對照圖 out/nr_fix5_cmp/ + OneDrive。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtwF9HNRYUY6CUdFutuMJa
1 parent d614e38 commit ee2d9ec

1 file changed

Lines changed: 67 additions & 16 deletions

File tree

parity/nightread.py

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@
126126
# 當背景寫字的語意證據(demo06 教堂 0.167 / 金髮女孩格
127127
# 0.228 手寫字直接落在背景白 ⇒ 放行;老人格 0.000、
128128
# ch34_010 披肩 0.023 ⇒ 無證據)
129+
# 修法5:閉合格背景擢升(貼框白元件 → sticker 候選)
130+
FRAME_HUG_DILATE = 5 # 元件外擴後與格線遮罩的交集算「貼框」
131+
FRAME_HUG_THICK = 3.0 # 格線名目厚度(px):交集數 → 貼框長度的除數
132+
FRAME_HUG_MIN = 0.25 # 貼框長度 / bbox 周長 下限(背景沿格框跑;前景白衣只點狀碰框)
133+
FRAME_HUG_STRONG = 0.40 # 強貼框:背景證據足夠強 → 走放寬門(見 sticker_plan 兩級制)
134+
PROMOTED_TEXTON_MAX = 0.30 # 強貼框仍拒的文字覆蓋上限:真有大量文字壓在上面=旁白框,
135+
# 填黑會把字變描邊糊 → 留灰(demo06 教堂 textOn 0.167 是可接受上界的參照)
129136
STICKER_TEXTON_PAD = 8 # textOn 的文字 bbox 外擴(語意證據要「真的壓在元件上」
130137
# ⇒ 只容 bbox 抖動的小 pad;BUBBLE_PAD 40px 窗會把鄰格
131138
# 文字掃進相鄰白元件湊假證據——ch34_010 披肩 pad40=0.207
@@ -227,17 +234,21 @@ def detect(img_bgr):
227234

228235
# ── 遮罩:白元件分類(修法2/3)+氣泡(修法1)───────────────────────
229236

230-
def page_is_frameless(g):
231-
"""修法2:長直格框線存在性。回傳 (frameless, h_px_per_k, v_px_per_k)。
232-
233-
暗像素對「長水平/垂直線」形態學開運算=只留貼直的長線(格框);
234-
無框/白背景頁(demo04/05 型)兩向都近零。
235-
"""
237+
def frame_line_mask(g):
238+
"""長直格框線遮罩:暗像素對「長水平/垂直線」形態學開運算=只留貼直的長線(格框)。
239+
回傳 (lh, lv) 兩個 0/1 uint8。修法2 的頁型判別與修法5 的貼框擢升共用。"""
236240
H, W = g.shape
237241
dark = (g < FRAME_DARK_TH).astype(np.uint8)
238242
L = max(60, min(W, H) // FRAME_LINE_L_DIV)
239243
lh = cv2.morphologyEx(dark, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (L, 1)))
240244
lv = cv2.morphologyEx(dark, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (1, L)))
245+
return lh, lv
246+
247+
248+
def page_is_frameless(g):
249+
"""修法2:長直格框線存在性。回傳 (frameless, h_px_per_k, v_px_per_k)。
250+
無框/白背景頁(demo04/05 型)兩向都近零。"""
251+
lh, lv = frame_line_mask(g)
241252
hk, vk = 1000.0 * lh.mean(), 1000.0 * lv.mean()
242253
frameless = not (min(hk, vk) >= FRAME_MIN_EACH and hk + vk >= FRAME_MIN_SUM)
243254
return frameless, hk, vk
@@ -503,20 +514,60 @@ def _rects(pad):
503514
if frameless:
504515
cand = {i for i in (gutter_ids | panel_ids)
505516
if stats[i, cv2.CC_STAT_AREA] >= STICKER_MIN_FRAC * g.size}
517+
hug = {}
506518
else:
507519
cand = set(panel_ids)
520+
# 修法5:閉合格背景擢升——未列管(非 gutter 非 panel)的大白元件,若「貼格線長度 /
521+
# bbox 周長」夠高=背景沿格框跑(閉合格內的天空/空白背景被格線封住、永遠進不了
522+
# 修法3 的 gutter/panel 分類),擢升進候選、照走下面同一套安全網。前景白(臉/白衣)
523+
# 是獨立元件、只點狀碰框 → hug 低、天然不擢升。ch34_014 型(22% 頁面積)的主修。
524+
lh, lv = frame_line_mask(g)
525+
frame = (lh | lv).astype(np.uint8)
526+
kd = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (FRAME_HUG_DILATE * 2 + 1,) * 2)
527+
hug = {}
528+
min_area = GUTTER_MIN_AREA_FRAC * g.size
529+
listed = gutter_ids | panel_ids
530+
for i in range(1, stats.shape[0]):
531+
if i in listed or stats[i, cv2.CC_STAT_AREA] < min_area:
532+
continue
533+
x, y, w_, h_ = (stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP],
534+
stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT])
535+
pad = FRAME_HUG_DILATE + 1
536+
x0, y0 = max(0, x - pad), max(0, y - pad)
537+
x1, y1 = min(g.shape[1], x + w_ + pad), min(g.shape[0], y + h_ + pad)
538+
comp = (lab[y0:y1, x0:x1] == i).astype(np.uint8)
539+
contact = int((cv2.dilate(comp, kd) & frame[y0:y1, x0:x1]).sum())
540+
hug_len = contact / FRAME_HUG_THICK
541+
frac = hug_len / max(1.0, 2.0 * (w_ + h_))
542+
hug[i] = round(float(frac), 3)
543+
if frac >= FRAME_HUG_MIN:
544+
cand.add(i)
508545
accept, audit = set(), []
509546
for i in sorted(cand):
510547
met = sticker_metrics(g, img_bgr, lab, stats, i, text_rects, text_rects_on)
511-
ok = (STICKER_FIG_MIN <= met["figFrac"] <= STICKER_FIG_MAX
512-
and met["thinFrac"] <= STICKER_THIN_MAX
513-
and met["chroma"] <= STICKER_CHROMA_MAX
514-
and met["textCov"] <= STICKER_TEXT_MAX
515-
and met["eatenFrac"] <= STICKER_EATEN_HARD
516-
and (met["eatenFrac"] <= STICKER_EATEN_MAX
517-
or met["textOn"] >= STICKER_TEXT_BG_MIN)
518-
and (met["areaFrac"] >= STICKER_SMALL_AREA
519-
or met["textOn"] >= STICKER_TEXT_BG_MIN))
548+
if i in hug:
549+
met["frameHug"] = hug[i]
550+
if hug.get(i, 0.0) >= FRAME_HUG_STRONG:
551+
# 修法5 兩級制——強貼框(背景證據極強)走放寬門:
552+
# · 跳過 textCov(pad40 窗對大背景是鄰泡污染;真文字壓上用 textOn 擋)
553+
# · 跳過 eaten 中段門與小面積門(eaten 聚團交給 paint_sticker 的
554+
# _sticker_protect 區域級保護=白鬍/髮絲局部不填、其餘照填)
555+
# · 保留 fig/thin/chroma/eatenHARD 四道硬底線
556+
ok = (STICKER_FIG_MIN <= met["figFrac"] <= STICKER_FIG_MAX
557+
and met["thinFrac"] <= STICKER_THIN_MAX
558+
and met["chroma"] <= STICKER_CHROMA_MAX
559+
and met["eatenFrac"] <= STICKER_EATEN_HARD
560+
and met["textOn"] <= PROMOTED_TEXTON_MAX)
561+
else:
562+
ok = (STICKER_FIG_MIN <= met["figFrac"] <= STICKER_FIG_MAX
563+
and met["thinFrac"] <= STICKER_THIN_MAX
564+
and met["chroma"] <= STICKER_CHROMA_MAX
565+
and met["textCov"] <= STICKER_TEXT_MAX
566+
and met["eatenFrac"] <= STICKER_EATEN_HARD
567+
and (met["eatenFrac"] <= STICKER_EATEN_MAX
568+
or met["textOn"] >= STICKER_TEXT_BG_MIN)
569+
and (met["areaFrac"] >= STICKER_SMALL_AREA
570+
or met["textOn"] >= STICKER_TEXT_BG_MIN))
520571
met["accept"] = bool(ok)
521572
audit.append(met)
522573
if ok:
@@ -603,7 +654,7 @@ def lut_knee(knee=KNEE_X, ceil=DIM_CEIL, end_slope=KNEE_END_SLOPE):
603654
"lin8": lambda: lut_linear(8), # 極暗保護:OLED 黑碎顧慮的最小抬升(8 遠小於舊 30)
604655
"knee": lambda: lut_knee(),
605656
}
606-
SCENE_CURVE = os.environ.get("NIGHTREAD_CURVE", "d2")
657+
SCENE_CURVE = os.environ.get("NIGHTREAD_CURVE", "lin8") # 2026-08-25 使用者目檢拍板 lin 系(黑實、對比大)
607658

608659

609660
def lut_scene():

0 commit comments

Comments
 (0)