mirror of
https://github.com/simon987/od-database.git
synced 2025-04-20 10:56:47 +00:00
Fix captcha part 1: more human readable (but less cool)
This commit is contained in:
parent
32c1c861ad
commit
e8965497d4
196
captcha.py
196
captcha.py
@ -1,20 +1,10 @@
|
|||||||
import os
|
|
||||||
import random
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
import numpy
|
|
||||||
import pylab
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
import mpl_toolkits.mplot3d.axes3d as axes3d
|
|
||||||
import io
|
|
||||||
from wand.image import Image as WImage
|
|
||||||
from flask import request, session
|
from flask import request, session
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from common import logger
|
|
||||||
|
|
||||||
SIZE = (60, 20)
|
|
||||||
with open("words.txt") as f:
|
|
||||||
WORDS = f.read().splitlines(keepends=False)
|
|
||||||
|
|
||||||
|
|
||||||
def get_code():
|
def get_code():
|
||||||
@ -51,41 +41,175 @@ def verify():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
cfg = {
|
||||||
|
"image": {
|
||||||
|
"size": (200, 72),
|
||||||
|
"supersampling": 2
|
||||||
|
},
|
||||||
|
"noise": {
|
||||||
|
"min": 100,
|
||||||
|
"max": 250
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"green": [(1, 51, 1), (34, 204, 34)],
|
||||||
|
"yellow": [(67, 67, 1), (221, 221, 0)],
|
||||||
|
"cyan": [(17, 51, 85), (85, 187, 254)],
|
||||||
|
"magenta": [(51, 1, 51), (254, 0, 254)],
|
||||||
|
"red": [(67, 1, 1), (254, 68, 68)],
|
||||||
|
"orange": [(68, 51, 1), (255, 153, 0)]
|
||||||
|
},
|
||||||
|
"lines": {
|
||||||
|
"back_thin": {"n": 3, "w": 5},
|
||||||
|
"back_thick": {"n": 3, "w": 6},
|
||||||
|
"back_positions": [
|
||||||
|
{
|
||||||
|
"ax": (0, 10),
|
||||||
|
"ay": (0, 36),
|
||||||
|
"bx": (150, 200),
|
||||||
|
"by": (18, 50)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ax": (0, 10),
|
||||||
|
"ay": (18, 50),
|
||||||
|
"bx": (150, 200),
|
||||||
|
"by": (0, 17)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"front_horizontal_thin": {"n": 2, "w": 3},
|
||||||
|
"front_horizontal_thick": {"n": 2, "w": 4},
|
||||||
|
"front_horizontal_positions": [
|
||||||
|
{
|
||||||
|
"ax": (0, 20),
|
||||||
|
"ay": (0, 34),
|
||||||
|
"bx": (150, 200),
|
||||||
|
"by": (18, 50)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ax": (0, 20),
|
||||||
|
"ay": (18, 72),
|
||||||
|
"bx": (140, 200),
|
||||||
|
"by": (0, 36)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"front_vertical": {"n": 2, "w": 4},
|
||||||
|
"front_vertical_positions": {
|
||||||
|
"outside": 5,
|
||||||
|
"font_width": 13,
|
||||||
|
"ay": (0, 16),
|
||||||
|
"by": (54, 72)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"font": {
|
||||||
|
"path": "static/Hack-Regular.ttf",
|
||||||
|
"size": 60,
|
||||||
|
"outline": [1, 2]
|
||||||
|
},
|
||||||
|
"letters": {
|
||||||
|
"3": {
|
||||||
|
"count": 3,
|
||||||
|
"x_min": 35,
|
||||||
|
"x_max": 50,
|
||||||
|
"y_min": -5,
|
||||||
|
"y_max": 8
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"count": 4,
|
||||||
|
"x_min": 20,
|
||||||
|
"x_max": 35,
|
||||||
|
"y_min": -5,
|
||||||
|
"y_max": 8
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"count": 5,
|
||||||
|
"x_min": 5,
|
||||||
|
"x_max": 20,
|
||||||
|
"y_min": -5,
|
||||||
|
"y_max": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size = cfg["image"]["size"]
|
||||||
|
c = cfg["image"]["supersampling"]
|
||||||
|
|
||||||
|
# Additional config
|
||||||
|
letter_count = "4"
|
||||||
|
|
||||||
|
|
||||||
|
def horizontal_lines(draw, c, line_par, line_pos, fill):
|
||||||
|
for _ in range(line_par["n"]):
|
||||||
|
pos = random.randrange(0, len(line_pos))
|
||||||
|
ax = random.randint(*line_pos[pos]["ax"])
|
||||||
|
ay = random.randint(*line_pos[pos]["ay"])
|
||||||
|
bx = random.randint(*line_pos[pos]["bx"])
|
||||||
|
by = random.randint(*line_pos[pos]["by"])
|
||||||
|
draw.line([(ax*c, ay*c), (bx*c, by*c)], width=line_par["w"]*c, fill=fill)
|
||||||
|
|
||||||
|
|
||||||
def make_captcha():
|
def make_captcha():
|
||||||
word = random.choice(WORDS)
|
|
||||||
path = get_path(word)
|
|
||||||
|
|
||||||
logger.info("generating CAPTCHA: " + word)
|
color_name, color = random.choice(list(cfg["colors"].items()))
|
||||||
|
text = ''.join(random.choices(string.ascii_uppercase + string.digits, k=cfg["text"]["letters"][letter_count]["count"]))
|
||||||
|
|
||||||
if os.path.exists(path):
|
path = get_path(text)
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
image = Image.new('L', SIZE, 255)
|
w = size[0]*c
|
||||||
image_draw = ImageDraw.Draw(image)
|
h = size[1]*c
|
||||||
font = ImageFont.truetype("static/Hack-Regular.ttf", 12)
|
|
||||||
|
|
||||||
image_draw.text((5, 3), word, font=font)
|
img = Image.new('RGB', (w, h))
|
||||||
|
pixels = img.load()
|
||||||
|
|
||||||
x, y = numpy.meshgrid(range(SIZE[0]), range(SIZE[1]))
|
# noise
|
||||||
z = 1 - numpy.asarray(image) / 255
|
for x in range(w):
|
||||||
|
for y in range(h):
|
||||||
|
rcol = random.randint(cfg["noise"]["min"], cfg["noise"]["max"])
|
||||||
|
pixels[x, y] = (rcol, rcol, rcol)
|
||||||
|
|
||||||
fig = pylab.figure()
|
# background lines
|
||||||
ax = axes3d.Axes3D(fig)
|
draw = ImageDraw.Draw(img)
|
||||||
ax.plot_wireframe(x, -y, z, rstride=1, cstride=1)
|
|
||||||
ax.set_zlim((0, 20))
|
|
||||||
ax.set_axis_off()
|
|
||||||
pylab.close(fig)
|
|
||||||
|
|
||||||
buf = io.BytesIO()
|
horizontal_lines(draw, c, cfg["lines"]["back_thin"], cfg["lines"]["back_positions"], color[0])
|
||||||
fig.savefig(buf, dpi=150)
|
horizontal_lines(draw, c, cfg["lines"]["back_thick"], cfg["lines"]["back_positions"], color[0])
|
||||||
buf.seek(0)
|
|
||||||
image.close()
|
|
||||||
|
|
||||||
with WImage(blob=buf.read()) as img:
|
# text
|
||||||
img.trim()
|
ctx = cfg["text"]["font"]
|
||||||
img.save(filename=path)
|
font = ImageFont.truetype(ctx["path"], ctx["size"]*c)
|
||||||
|
outline = random.choice(ctx["outline"])
|
||||||
|
|
||||||
return word
|
ctx = cfg["text"]["letters"][letter_count]
|
||||||
|
x = random.randint(ctx["x_min"], ctx["x_max"])
|
||||||
|
y = random.randint(ctx["y_min"], ctx["y_max"])
|
||||||
|
draw.text((x*c-outline*c, y*c-outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c-outline*c, y*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c-outline*c, y*c+outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c, y*c-outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c, y*c+outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c+outline*c, y*c-outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c+outline*c, y*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c+outline*c, y*c+outline*c), text, color[0], font=font)
|
||||||
|
draw.text((x*c, y*c), text, color[1], font=font)
|
||||||
|
|
||||||
|
# foreground lines
|
||||||
|
horizontal_lines(draw, c, cfg["lines"]["front_horizontal_thin"], cfg["lines"]["front_horizontal_positions"], color[1])
|
||||||
|
horizontal_lines(draw, c, cfg["lines"]["front_horizontal_thick"], cfg["lines"]["front_horizontal_positions"], color[1])
|
||||||
|
|
||||||
|
# vertical lines
|
||||||
|
line_par = cfg["lines"]["front_vertical"]
|
||||||
|
line_pos = cfg["lines"]["front_vertical_positions"]
|
||||||
|
|
||||||
|
for _ in range(line_par["n"]):
|
||||||
|
ax = random.randint(x-line_pos["outside"], x+line_pos["outside"] + cfg["text"]["letters"][letter_count]["count"]*line_pos["font_width"])
|
||||||
|
bx = ax + random.randint(-line_pos["font_width"], line_pos["font_width"])
|
||||||
|
ay = random.randint(*line_pos["ay"])
|
||||||
|
by = random.randint(*line_pos["by"])
|
||||||
|
draw.line([(ax*c, ay*c), (bx*c, by*c)], width=line_par["w"]*c, fill=color[1])
|
||||||
|
|
||||||
|
img.thumbnail(cfg["image"]["size"], Image.ANTIALIAS)
|
||||||
|
img.save(path, "png")
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user