python - 使用不同的字体创建 3D 验证码

标签 python arrays numpy matplotlib

我对制作 3D 验证码很感兴趣,我让它使用一种字体,如下所示:

import string
from matplotlib.font_manager import findSystemFonts
import random
from PIL import ImageFont, Image, ImageDraw

def rand_font(fonts=[], fontpaths=None, fontext='ttf', font_encoding='', min_size=24, max_size=36):
    if fonts == []:
        fonts = findSystemFonts(fontpaths, fontext)
    requested_font = fonts[random.randint(0, len(fonts)-1)]
    font_size = random.randint(min_size, max_size)
    return ImageFont.truetype(requested_font, font_size, encoding=font_encoding)

def create_captcha(text):
    def _rand_color():
        colors = ['red', 'orange', 'white', 'purple', 'green', 'yellow']
        return colors[random.randint(0, len(colors)-1)]

    width = random.randint(400, 700)
    height = random.randint(150, 200)
    angle = angle if angle else uniform(-20, 20)


    font = rand_font()

    text_width, text_height = font.getsize(text)

    img = Image.new("L", (text_width * 3, text_height * 3), "white")
    draw = ImageDraw.Draw(img)
    draw.text((text_width, text_height), text, font=font)

    fig = pylab.figure(figsize=(width/100.0, height/100.0), dpi=4000)
    axes = Axes3D(fig)
    X, Y = numpy.meshgrid(range(img.size[0]), range(img.size[1]))
    Z = 1 - numpy.asarray(img) / 255


    func = Axes3D.plot_surface if random.randint(0,1) == 0 else Axes3D.plot_wireframe

    func(axes, X, -Y, Z, rstride=1, cstride=1, color=_rand_color())

    axes.set_zlim((-3, 3))
    axes.set_xlim((text_width * 1.1, text_width * 1.9))
    axes.set_ylim((-text_height * 1.9, -text_height* 1.1))
    axes.set_axis_off()
    axes.view_init(elev=60, azim=-90)

这很好,它可以让我创造这样的东西:
http://puu.sh/dfxcW/d9fc3f5c4e.jpg
http://puu.sh/dft1a/1d35f5c99a.png

不过,我想做的是创建一个验证码,为每个字符使用不同的字体和大小,并为每个字符稍微偏移 y

因为它是基于 numpy 数组绘制的,所以我尝试像这样遍历文本中的每个字符:

prev_x = 0
x = []
y = []
z = []
for character in text:
    X, Y = numpy.meshgrid(range(prev_x, prev_x + img.size[0]), range(img.size[1]))
    for v in X.tolist():
        x.append(v)
    for v in Y.tolist():
        y.append(v)
    # same for z
prev_x += 40 # trying to offset the characters by an x value so they dont overlap
x = numpy.array(x)
# same for y and z to convert back to numpy array
Axes3D.plot_wireframe(x, -y, z, rstride=1, cstride=1)

这会导致形状不匹配:两个或多个数组在轴 1 上具有不兼容的尺寸。 这让我感到困惑,因为我认为尺寸会完全相同,因为我对每个数组都进行了相同的调用一。我是 numpy 和 3D 的新手,所以如果有人有建议请告诉我!

最佳答案

您如何开始这件事肯定有很多很酷的因素,而且大部分都在那里;这是我为获得随机字体和每个字符可能的颜色而制作的模组。我确实更改了一些尺寸以更好地适合我的屏幕。

from matplotlib.font_manager import findSystemFonts
import random
from PIL import ImageFont, Image, ImageDraw
import numpy
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.pyplot as plt

def rand_font(fonts=[], fontpaths=None, fontext='ttf', font_encoding='', min_size=24, max_size=36):
    if fonts == []:
        fonts = findSystemFonts(fontpaths, fontext)
    requested_font = fonts[random.randint(0, len(fonts)-1)]
    font_size = random.randint(min_size, max_size)
    return ImageFont.truetype(requested_font, font_size, encoding=font_encoding)

def create_captcha(text):
    def _rand_color():
        colors = ['red', 'orange', 'purple', 'green', 'yellow']
        return colors[random.randint(0, len(colors)-1)]

    # First font just gets the general offsets
    font = rand_font()
    text_width, text_height = font.getsize(text)

    # Dont draw text here first
    img = Image.new("L", (text_width * 3, text_height * 3), "white")
    draw = ImageDraw.Draw(img)

    fig = plt.figure(figsize=(12, 8))
    axes = Axes3D(fig)

    # Do this way if you want random fonts AND colors
    #=================
    prev_x = 0
    for character in text:
        cfont = rand_font()
        char_wid, char_height = cfont.getsize(character)
        draw.text((prev_x+text_width, text_height), character, font=cfont)

        X, Y = numpy.meshgrid(range(prev_x+text_width, prev_x+text_width+char_wid),
                              range(text_height, text_height+char_height))
        Z = 1 - numpy.asarray(img.crop((prev_x+text_width, text_height,
                                        prev_x+text_width+char_wid,
                                        text_height+char_height))) / 255
        axes.plot_wireframe(X, -Y, Z, rstride=1, cstride=1, color=_rand_color())

        prev_x += char_wid # trying to offset the characters by an x value so they dont overlap
    #=================

    # Do this way if you want just random fonts on each letter all one color
    #=================
    prev_x = 0
    for character in text:
        cfont = rand_font()
        char_wid, char_height = cfont.getsize(character)
        draw.text((prev_x+text_width, text_height), character, font=cfont)

        prev_x += char_wid # trying to offset the characters by an x value so they dont overlap

    X, Y = numpy.meshgrid(range(img.size[0]), range(img.size[1]))
    Z = 1 - numpy.asarray(img) / 255
    axes.plot_wireframe(X, -Y, Z, rstride=1, cstride=1, color=_rand_color())
    #=================

    axes.set_zlim((-3, 3))
    axes.set_xlim((text_width * 1.1, text_width * 1.9))
    axes.set_ylim((-text_height * 1.9, -text_height* 1.1))
    axes.set_axis_off()
    axes.view_init(elev=60, azim=-90)
    plt.show()

create_captcha('TEST')

多字体单色版本看起来像这样: test image all one color

多字体多颜色版本看起来像这样: test image multiple colors

如果你把它变成一个表面并改变视角/高度以避免背景,它可能看起来更好......也许像这样: test image multiple colors with raised surface

至少应该是一个起点!

关于python - 使用不同的字体创建 3D 验证码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27281031/

相关文章:

Pythonic 字符串操作

arrays - 如何正确地将 bash 数组扩展为参数

char 数组上的 C/C++ 开关大小写

python - 将图像划分为 block

python - 如何确定 PyObject 是否为 numpy PyArrayObject

python - 用零替换 numpy 数组中低于阈值 # 的数字

python - 值错误 : too many values to unpack (expected 2)

javascript - python markdown哪个版本有mark方法

python - 使用 Werkzeug 解析原始 HTTP 请求

ruby - 为什么 hex -> base64 与 base64 -> hex 使用 pack 和 unpack 如此不同?