a3e8911deb
- 新增作息报时器独立模块,包含完整的日程管理、语音播报和待机功能 - 短链接正式前缀由/l/改为/to/,解决手机小屏下与I、i、1字形混淆问题 - 保留旧前缀/l/、/L/、/I/、/i/兼容已分发的旧短链 - 新增短码大小写兜底匹配,仅唯一匹配时生效避免歧义 - 新增防爆紧急替换功能,可轮换首页鉴权并替换短链接 - 新增多个调试和集成测试脚本,保障数据安全和功能回归
105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
# 调试:宝龙导视牌 logo 裁切 · 掩膜参数迭代
|
||
# 目标:7 行连通域计数 = [11,11,10,11,11,11,11](r2 行牌子上只有 10 格)
|
||
# 用法:python dev_test_scripts\debug\debug_mall_crop_mask.py
|
||
import os
|
||
import sys
|
||
|
||
import cv2
|
||
import numpy as np
|
||
|
||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'tools'))
|
||
from tool_crop_mall_logos import imread_u, imwrite_u, detect_board_quad, warp_board # noqa: E402
|
||
|
||
SRC = r'D:\Temp文件\baolong-mall\照片.jpg'
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
|
||
img = imread_u(SRC)
|
||
quad = detect_board_quad(img)
|
||
warped = warp_board(img, quad)
|
||
wh, ww = warped.shape[:2]
|
||
gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
|
||
hsv = cv2.cvtColor(warped, cv2.COLOR_BGR2HSV)
|
||
S = hsv[:, :, 1].astype(np.float32)
|
||
V = hsv[:, :, 2].astype(np.float32)
|
||
|
||
# 局部纹理(box filter 标准差):过曝区颜色失效时靠纹理兜底
|
||
gf = gray.astype(np.float32)
|
||
m1 = cv2.boxFilter(gf, -1, (9, 9))
|
||
m2 = cv2.boxFilter(gf * gf, -1, (9, 9))
|
||
STD = np.sqrt(np.maximum(m2 - m1 * m1, 0))
|
||
|
||
|
||
def build_mask(s_th, v_th, t_th, close_k, open_k):
|
||
mask = (((S > s_th) | (V < v_th) | (STD > t_th)).astype(np.uint8)) * 255
|
||
mask[:60, :] = 0
|
||
mask[-60:, :] = 0
|
||
mask[:, :60] = 0
|
||
mask[:, -60:] = 0
|
||
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((close_k, close_k), np.uint8))
|
||
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((open_k, open_k), np.uint8))
|
||
return mask
|
||
|
||
|
||
def row_counts(mask):
|
||
n, labels, stats, cents = cv2.connectedComponentsWithStats(mask, 8)
|
||
blocks = []
|
||
for i in range(1, n):
|
||
x, y, bw, bh, area = stats[i]
|
||
cx, cy = cents[i]
|
||
if area < 8000 or bw < 100 or bh < 80 or bw > 0.35 * ww or bh > 0.25 * wh:
|
||
continue
|
||
if cy < 0.17 * wh or cx < 0.06 * ww:
|
||
continue
|
||
blocks.append((cy, cx, x, y, bw, bh, area))
|
||
blocks.sort()
|
||
rows = []
|
||
for b in blocks:
|
||
if rows and abs(b[0] - rows[-1][-1][0]) < 60:
|
||
rows[-1].append(b)
|
||
else:
|
||
rows.append([b])
|
||
return [len(r) for r in rows], blocks, rows
|
||
|
||
|
||
combos = [
|
||
(45, 140, 999, 9, 7), # 旧参数(仅颜色)
|
||
(30, 170, 12, 21, 9),
|
||
(25, 180, 10, 25, 11),
|
||
(25, 180, 14, 25, 13),
|
||
(20, 190, 12, 29, 13),
|
||
(30, 170, 16, 21, 11),
|
||
]
|
||
best = None
|
||
for c in combos:
|
||
mask = build_mask(*c)
|
||
counts, blocks, rows = row_counts(mask)
|
||
print(c, '->', counts)
|
||
if counts == [11, 11, 10, 11, 11, 11, 11]:
|
||
best = (c, mask, rows)
|
||
break
|
||
|
||
if best:
|
||
c, mask, rows = best
|
||
scale = 1600.0 / ww
|
||
vis = cv2.resize(warped, (1600, int(wh * scale))).copy()
|
||
for r in rows:
|
||
for (cy, cx, x, y, bw, bh, area) in r:
|
||
cv2.rectangle(vis, (int(x * scale), int(y * scale)),
|
||
(int((x + bw) * scale), int((y + bh) * scale)), (0, 255, 0), 2)
|
||
imwrite_u(os.path.join(HERE, 'blocks_preview.jpg'), vis)
|
||
imwrite_u(os.path.join(HERE, 'mask_preview.jpg'), cv2.resize(mask, (1600, int(wh * scale))))
|
||
print('BEST', c)
|
||
else:
|
||
print('无完美组合,取最后一组出预览')
|
||
mask = build_mask(*combos[-1])
|
||
counts, blocks, rows = row_counts(mask)
|
||
scale = 1600.0 / ww
|
||
vis = cv2.resize(warped, (1600, int(wh * scale))).copy()
|
||
for r in rows:
|
||
for (cy, cx, x, y, bw, bh, area) in r:
|
||
cv2.rectangle(vis, (int(x * scale), int(y * scale)),
|
||
(int((x + bw) * scale), int((y + bh) * scale)), (0, 255, 0), 2)
|
||
imwrite_u(os.path.join(HERE, 'blocks_preview.jpg'), vis)
|
||
imwrite_u(os.path.join(HERE, 'mask_preview.jpg'), cv2.resize(mask, (1600, int(wh * scale))))
|