chore: 文件夹更名 HDMI-Tool -> DLPilot(路径与文档此前已对齐)
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
生成 640x360 黑白测试图, 供 HDMI 源端工具的自定义栏位使用
|
||||
================================================================
|
||||
输出到 ../assets/(仓库资产目录, 发布打包时随 zip 分发); 预置栏位配置
|
||||
release/pattern_slots.json 指向前 3 张, 其余两张可用 "⚙" 换上。
|
||||
|
||||
图案清单(均为 640x360 黑白, 1:1 投屏无缩放):
|
||||
bw_star.png 西门子星: 60 楔放射星, 看极限分辨率与中心偏移
|
||||
bw_sweep.png 频率扫描条纹: 周期 24px 渐减到 3px(上竖线/下横线)
|
||||
bw_checker_sweep.png 渐变棋盘: 格子尺寸 2px 递增到 32px 共 9 段
|
||||
bw_zoneplate.png 同心波带片: 环距向中心递减, 看各向混叠
|
||||
bw_testcard.png 综合测试卡: 灰阶/刻度/细棋盘/粗细线/标记
|
||||
|
||||
用法: python gen_bw_patterns.py
|
||||
"""
|
||||
|
||||
import math
|
||||
import os
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
W, H = 640, 360
|
||||
SRC = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(os.path.dirname(SRC), 'assets')
|
||||
|
||||
|
||||
def save(img, name):
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
path = os.path.join(OUT, name)
|
||||
img.save(path)
|
||||
print(f'写出 {path}')
|
||||
|
||||
|
||||
def gen_star():
|
||||
"""西门子星: 60 个黑白交替楔形, 边缘楔宽约 17px, 向中心无限加密"""
|
||||
img = Image.new('L', (W, H), 255)
|
||||
d = ImageDraw.Draw(img)
|
||||
cx, cy, R = W // 2, H // 2, 165
|
||||
n = 60
|
||||
for i in range(0, n, 2): # 黑楔在偶数位, 底为白
|
||||
d.pieslice([cx - R, cy - R, cx + R, cy + R],
|
||||
i * 360.0 / n, (i + 1) * 360.0 / n, fill=0)
|
||||
d.ellipse([cx - R - 6, cy - R - 6, cx + R + 6, cy + R + 6],
|
||||
outline=0, width=3)
|
||||
d.rectangle([0, 0, W - 1, H - 1], outline=0, width=2)
|
||||
save(img, 'bw_star.png')
|
||||
|
||||
|
||||
def gen_sweep():
|
||||
"""频率扫描条纹: 左到右黑白条纹周期 24px 渐减到 3px; 上半竖线, 下半横线"""
|
||||
img = Image.new('L', (W, H), 255)
|
||||
px = img.load()
|
||||
# 上半: 竖条纹
|
||||
ph = 0.0
|
||||
for x in range(W):
|
||||
t = x / (W - 1)
|
||||
ph += 1.0 / (24 - 21 * t)
|
||||
if (ph % 1.0) < 0.5:
|
||||
for y in range(H // 2):
|
||||
px[x, y] = 0
|
||||
# 下半: 横条纹
|
||||
ph = 0.0
|
||||
for y in range(H // 2, H):
|
||||
t = (y - H // 2) / (H // 2 - 1)
|
||||
ph += 1.0 / (24 - 21 * t)
|
||||
if (ph % 1.0) < 0.5:
|
||||
for x in range(W):
|
||||
px[x, y] = 0
|
||||
d = ImageDraw.Draw(img)
|
||||
d.line([0, H // 2, W, H // 2], fill=0, width=2)
|
||||
d.rectangle([0, 0, W - 1, H - 1], outline=0, width=2)
|
||||
d.text((8, 6), 'H sweep 24px->3px', fill=0)
|
||||
d.text((8, H // 2 + 6), 'V sweep 24px->3px', fill=0)
|
||||
save(img, 'bw_sweep.png')
|
||||
|
||||
|
||||
def gen_checker_sweep():
|
||||
"""渐变棋盘: 格子尺寸从 2px 逐段加倍到 32px, 看多大格子开始可分辨"""
|
||||
img = Image.new('L', (W, H), 0)
|
||||
d = ImageDraw.Draw(img)
|
||||
sizes = [2, 3, 4, 6, 8, 12, 16, 24, 32]
|
||||
seg = W / len(sizes)
|
||||
for i, s in enumerate(sizes):
|
||||
x0, x1 = int(round(i * seg)), int(round((i + 1) * seg))
|
||||
for yy in range(0, H, s):
|
||||
for xx in range(x0, x1, s):
|
||||
if ((xx // s) + (yy // s)) % 2 == 0:
|
||||
d.rectangle([xx, yy, min(xx + s, x1) - 1, min(yy + s, H) - 1],
|
||||
fill=255)
|
||||
d.line([x1, 0, x1, H], fill=255, width=2)
|
||||
d.text((4, 4), 'checker 2..32px', fill=255)
|
||||
save(img, 'bw_checker_sweep.png')
|
||||
|
||||
|
||||
def gen_zoneplate():
|
||||
"""同心波带片: 环距中心处约 31px、边缘约 3.5px, 观察各方向混叠"""
|
||||
img = Image.new('L', (W, H), 255)
|
||||
px = img.load()
|
||||
cx, cy, k = W / 2, H / 2, 0.0008
|
||||
for y in range(H):
|
||||
dy = y - cy
|
||||
for x in range(W):
|
||||
dx = x - cx
|
||||
if math.cos(k * (dx * dx + dy * dy)) > 0:
|
||||
px[x, y] = 0
|
||||
d = ImageDraw.Draw(img)
|
||||
d.rectangle([0, 0, W - 1, H - 1], outline=0, width=2)
|
||||
save(img, 'bw_zoneplate.png')
|
||||
|
||||
|
||||
def gen_testcard():
|
||||
"""综合测试卡: 灰阶条 + 刻度 + 细棋盘 + 粗细线 + 角标"""
|
||||
img = Image.new('L', (W, H), 0)
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# 外框与 40px 刻度(边缘短刻线)
|
||||
d.rectangle([0, 0, W - 1, H - 1], outline=255, width=2)
|
||||
for x in range(40, W, 40):
|
||||
d.line([x, 0, x, 10], fill=255, width=1)
|
||||
d.line([x, H, x, H - 10], fill=255, width=1)
|
||||
for y in range(40, H, 40):
|
||||
d.line([0, y, 10, y], fill=255, width=1)
|
||||
d.line([W, y, W - 10, y], fill=255, width=1)
|
||||
|
||||
# 顶部 16 级灰阶条
|
||||
gw = W // 16
|
||||
for i in range(16):
|
||||
v = round(i * 255 / 15)
|
||||
d.rectangle([i * gw, 20, (i + 1) * gw - 1, 44], fill=v, outline=255)
|
||||
|
||||
# 左下: 1px 棋盘块 (640/4=160 宽 x 72 高)
|
||||
bx, by, bs = 16, H - 96, 2
|
||||
for yy in range(by, by + 72, bs):
|
||||
for xx in range(bx, bx + 160, bs):
|
||||
if ((xx // bs) + (yy // bs)) % 2 == 0:
|
||||
d.rectangle([xx, yy, xx + bs - 1, yy + bs - 1], fill=255)
|
||||
d.text((bx, by - 14), '2px checker', fill=255)
|
||||
|
||||
# 右下: 1/2/4px 白线组(横线), 看线条再现
|
||||
lx = W - 210
|
||||
for row, lw in enumerate((1, 2, 4)):
|
||||
y0 = H - 100 + row * 26
|
||||
for off in range(0, 13, lw * 2):
|
||||
d.line([lx, y0 + off, lx + 190, y0 + off], fill=255, width=lw)
|
||||
d.text((lx, H - 114), '1/2/4px lines', fill=255)
|
||||
|
||||
# 中心十字(1px)
|
||||
cx, cy = W // 2, H // 2
|
||||
d.line([cx, 60, cx, H - 60], fill=255, width=1)
|
||||
d.line([W // 4, cy, W - W // 4, cy], fill=255, width=1)
|
||||
|
||||
# 四角 L 标记
|
||||
m, L = 16, 28
|
||||
for sx, sy in ((m, m), (W - 1 - m, m), (m, H - 1 - m), (W - 1 - m, H - 1 - m)):
|
||||
dx = -1 if sx > cx else 1
|
||||
dy = -1 if sy > cy else 1
|
||||
d.line([sx, sy, sx + dx * L, sy], fill=255, width=2)
|
||||
d.line([sx, sy, sx, sy + dy * L], fill=255, width=2)
|
||||
|
||||
d.text((W // 2 - 60, 52), '640x360 TEST', fill=255)
|
||||
save(img, 'bw_testcard.png')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_star()
|
||||
gen_sweep()
|
||||
gen_checker_sweep()
|
||||
gen_zoneplate()
|
||||
gen_testcard()
|
||||
Reference in New Issue
Block a user