Download scripts/gen_phpwind9.py from FlanChanXwO/phpwind-captcha-ocr: direct link, hf CLI and curl.
- Browser
- Download file 3.11 kB
-
https://huggingface.co/FlanChanXwO/phpwind-captcha-ocr/resolve/main/scripts/gen_phpwind9.py
- Command line
-
hf download hf://FlanChanXwO/phpwind-captcha-ocr/scripts/gen_phpwind9.py
-
curl -L -o gen_phpwind9.py https://huggingface.co/FlanChanXwO/phpwind-captcha-ocr/resolve/main/scripts/gen_phpwind9.py
3.11 kB
| #!/usr/bin/env python3 | |
| """复刻 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" # PwBaseCode verifyType=1 | |
| 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)) | |
| # 背景:白底 (PwGDCode isRandBackground=false -> 255) | |
| img = Image.new("RGB", (W, H), (255, 255, 255)) | |
| d = ImageDraw.Draw(img) | |
| # 逐字符绘制:随机字号14-20、随机角度-20~10、随机色 | |
| 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]) | |
| # 噪声:随机 线/点/弧 (PwGDCode _setRandGraph) | |
| d = ImageDraw.Draw(img) | |
| mode = random.randint(1, 3) | |
| if mode == 1: # 30-40 条随机线 | |
| 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: # 600-800 随机点 | |
| for _ in range(random.randint(600, 800)): | |
| d.point((random.randint(0, W - 1), random.randint(0, H - 1)), fill=rand_noise_color()) | |
| else: # 5-10 条弧 | |
| 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()) | |
| # 正弦扭曲:x += sin(y/height*2π-0.6)*5 | |
| 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}") | |