phpwind-captcha-ocr / scripts /gen_phpwind9.py
FlanChanXwO's picture
Upload scripts/gen_phpwind9.py with huggingface_hub
f3fa724 verified
Raw
History Blame Contribute Delete
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}")