| |
| """复刻 PHPWind 9.x PwGDCode 渲染逻辑,生成代表性验证码。 |
| 依据 alibaba/phpwind: upload/src/library/utility/verifycode/PwGDCode.php + PwBaseCode.php |
| """ |
| import math |
| import random |
| import sys |
|
|
| import numpy as np |
| from PIL import Image, ImageDraw, ImageFont |
|
|
| W, H = 150, 60 |
| CHARSET = "1234567890" |
| FONT = "/System/Library/Fonts/Supplemental/Arial.ttf" |
|
|
|
|
| def rand_color(): |
| return (random.randint(0, 255), random.randint(0, 120), random.randint(0, 255)) |
|
|
|
|
| def rand_noise_color(): |
| return (random.randint(50, 255), random.randint(50, 200), random.randint(25, 200)) |
|
|
|
|
| def gen(out_path, code=None): |
| if code is None: |
| code = "".join(random.choice(CHARSET) for _ in range(4)) |
|
|
| |
| img = Image.new("RGB", (W, H), (255, 255, 255)) |
| d = ImageDraw.Draw(img) |
|
|
| |
| codeX = (W - 20) / 4 |
| codeY = H // 2 + random.randint(5, 10) |
| for i, ch in enumerate(code): |
| size = random.randint(14, 20) |
| angle = random.randint(-20, 10) |
| font = ImageFont.truetype(FONT, size) |
| |
| tmp = Image.new("RGBA", (40, 40), (0, 0, 0, 0)) |
| td = ImageDraw.Draw(tmp) |
| td.text((2, 2), ch, font=font, fill=rand_color() + (255,)) |
| tmp = tmp.rotate(angle, expand=True, fillcolor=(0, 0, 0, 0)) |
| x = int(codeX * i + 10) |
| y = int(codeY - tmp.height / 2) |
| img.paste(tmp.convert("RGB"), (x, y), tmp.split()[3]) |
|
|
| |
| d = ImageDraw.Draw(img) |
| mode = random.randint(1, 3) |
| if mode == 1: |
| for _ in range(random.randint(30, 40)): |
| x1, y1 = random.randint(0, W), random.randint(0, H) |
| d.line([(x1, y1), (random.randint(x1 - 10, x1 + 5), random.randint(y1 + 5, y1 + 20))], fill=rand_noise_color(), width=1) |
| elif mode == 2: |
| for _ in range(random.randint(600, 800)): |
| d.point((random.randint(0, W - 1), random.randint(0, H - 1)), fill=rand_noise_color()) |
| else: |
| for _ in range(random.randint(5, 10)): |
| d.arc([random.randint(0, W), random.randint(10, H), random.randint(10, W), random.randint(H, H * 2)], |
| random.randint(0, 90), random.randint(0, 90), fill=rand_noise_color()) |
|
|
| |
| arr = np.array(img) |
| out = np.zeros_like(arr) |
| for y in range(H): |
| shift = int(math.sin(y / H * 2 * math.pi - 0.6) * 5) |
| for x in range(W): |
| sx = x + shift |
| if 0 <= sx < W: |
| out[y, x] = arr[y, sx] |
| Image.fromarray(out).save(out_path) |
| return code |
|
|
|
|
| if __name__ == "__main__": |
| import os |
| outdir = sys.argv[1] if len(sys.argv) > 1 else "/tmp/sp_pw9" |
| os.makedirs(outdir, exist_ok=True) |
| for i in range(6): |
| code = gen(f"{outdir}/pw9_{i}.png") |
| print(f"pw9_{i}.png code={code}") |
|
|