专栏首页浊酒清味Python也能绘制艺术画?这里有一个完整教程

Python也能绘制艺术画?这里有一个完整教程

介绍

我们知道Python作为一个程序语言,讲究的是严谨和逻辑;而艺术画似乎处于另一个维度,更多是无规则和随心所欲。然而我们却可以找到两者的交汇点。今天我们将学习如何用Python制作艺术图。一旦我们知道如何用Python做基础,我们就可以免费获得Python工具库的其他部分(web框架、数据科学工具、AI+ML+CV工具等)。可以想象,拥有这些工具的我们其实没有天花板。

我们将在本python绘图教程中涉及的工具和库有:

  • Numpy + Scipy + Matplotlib
  • Jupter Noteboobk
  • 用于交互设计的Ipythonwidgets
  • 用于SVG后处理的Vpype
  • (可选)Axidraw Python客户端

代码链接:

https://github.com/zoso95/plotter_guides/tree/master/genhut

(可选)Axidraw Python客户端安装指南:

https://axidraw.com/doc/py_api/#installation

基本步骤

生成艺术的第一件作品叫做Voronoi图,如下图所示:

我们需要画一堆多边形,然后填充其中一些多边形。

初始设计

我们要做的第一件事是调整画布的大小。通常使用11x14张纸。这里的想法是,我们希望我们的多边形延伸到框架的边缘之外,因此有一个更大的区域(x/y_bounds)来创建我们的多边形,然后实际上将它们显示在一个11x14的区域内:

x_bounds = np.array([0, 13])
y_bounds = np.array([0, 16])

x_buffer, y_buffer = 1, 1

x_plot = x_bounds + np.array([x_buffer, -x_buffer])
y_plot = y_bounds + np.array([y_buffer, -y_buffer])

还记得我说过Voronoi图包含点并给出多边形吗?让我们把这些点表示出来。这段代码将生成200点:

num_points = 200
x = np.random.uniform(*x_bounds, size=num_points).reshape((num_points, 1))
y = np.random.uniform(*y_bounds, size=num_points).reshape((num_points, 1))
pts = np.hstack([x, y])
plt.scatter(*pts.transpose())

它应该是这样的:

现在我们来看看我们的图表是什么样的。先用泰森多边形法函数返回,我们过滤掉一些无效的多边形:

vor = Voronoi(pts)
verts = vor.vertices
shapes_ind = vor.regions

"""
We are doing three things here
1. filtering out any empty shapes (len(s) == 0)
2. filtering out any shapes that go out of bounds (then it has an index of -1)
3. Closing the polygon by adding the last point back (so [1,2,3]->[1,2,3,1])
"""
shapes_ind = [s+s[0:1] for s in shapes_ind if len(s)>0 and -1 not in s]
shapes = [verts[s] for s in shapes_ind]

# Plot the Diagram

fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim(*x_plot)
ax.set_ylim(*y_plot)
lc = LineCollection(shapes)
ax.add_collection(lc)

我们会得到一个像这样的图:

现在我们只缺少填充多边形。这里有一个简单的数学技巧。我们的想法是,如果我们有一个形状,集中在(0,0),然后按比例形状的年代,我们可以把所有的点乘以S .所以我们要做的就是把一个多边形,中心,规模下来很多次,然后移动多边形+填充回到起始位置。一旦我们可以在一个多边形上做,我们可以在任意多的多边形上做!完成所有这些的代码是这样的:

# let's just look at one polygon
polygon_ind = 15
polygon = shapes[polygon_ind]
center = np.mean(polygon, axis=0)

rescaled = []
n_fill_lines = 5
min_scalar = 0.1 # scale it down to 1/10th the original size

for scaler in np.linspace(min_scalar, 1, num=n_fill_lines, endpoint=False):
    scaled = scaler*(polygon - center) + center
    rescaled.append(scaled)


fig, ax = plt.subplots(figsize=(10,10))
ax.set_xlim(-1.25, 1.25)
ax.set_ylim(-1.25, 1.25)
lc = LineCollection(rescaled)
ax.add_collection(lc)

添加交互性

正如您可能已经注意到的,我们已经挑选了许多影响我们的设计外观的数字,但是我们并没有非常仔细地挑选它们!你可能会想“到目前为止,这看起来还行,但是如果我们使用更多的点会发生什么呢?”还是更少的填充线?”现在你可以改变这些数字,然后从那时开始重新运行程序,但是一遍又一遍做起来很麻烦。

制作复杂数字程序的挑战之一是,通常它们需要大量的参数作为输入,因此你不知道哪些数字工作得很好,也不知道它们是如何相互影响的。幸运的是,我们有ipywidgets。Ipywidgets可以与Jupytery一起工作,并提供了一个简单的函数包装器,它可以让您轻松地使用幻灯片、按钮等更改函数输入,它会重新运行您的函数,这样您就可以看到发生了什么。

要添加所有这些交互性,我们所要做的就是将代码封装到一个函数中,给输入变量一些默认值,(可选地)我们可以告诉包装器函数哪些值是有意义的,例如,浮点数取一个(最小值、最大值、值增量)。一旦我们这样做,库将自动生成一个交互式小部件。

为此,我们将运行代码:

def make_some_art(num_points=200, percent_to_fill = 0.5, n_fill_lines=5, min_scalar = 0.1,  debug=False, toggle_for_new=False):

    x = np.random.uniform(*x_bounds, size=num_points).reshape((num_points, 1))
    y = np.random.uniform(*y_bounds, size=num_points).reshape((num_points, 1))
    pts = np.hstack([x, y])

    vor = Voronoi(pts)
    verts = vor.vertices
    shapes_ind = vor.regions

    shapes_ind = [s+s[0:1] for s in shapes_ind if len(s)>0 and -1 not in s]
    shapes = [verts[s] for s in shapes_ind]

    n_shapes_to_fill = int(percent_to_fill*len(shapes))
    shapes_to_fill = np.random.choice(shapes, size=n_shapes_to_fill, replace=False)

    fill = []

    for s in shapes_to_fill:
        center = np.mean(s, axis=0)
        for scaler in np.linspace(min_scalar, 1, num=n_fill_lines, endpoint=False):
            scaled = scaler*(s - center) + center
            fill.append(scaled)


    fig, ax = plt.subplots(figsize=(20,20))
    ax.set_aspect('equal')

    if not debug:
        plt.grid(False)
        plt.axis('off')


    ax.set_xlim(*x_plot)
    ax.set_ylim(*y_plot)
    lc = LineCollection(shapes+fill)
    ax.add_collection(lc)

    return fig, ax

w = interactive(make_some_art,
                num_points=(10,1000,25),
                percent_to_fill=(0., 1., 0.05),
                n_fill_lines=(1, 20, 1),
                min_scalar=(0,1,0.01))
display(w)

接下来,我们将更改一些关于如何绘制的内容。我添加了一个调试选项,如果我们没有启用调试,我们会得到一个干净的图形,没有轴线、标记或类似的东西,所以我们可以继续绘制它。但是,当我们启用调试时,它会让我们看到这一点,这样我们就可以得到一个缩放的感觉,所有东西都在哪里,等等。

最后,我添加了一个名为toggle_for_new的无操作切换按钮,它不做任何事情,但将迫使函数使用相同的参数进行新的设计。这在你喜欢设计的时候很有用,你可以通过参数来影响它,但是你不喜欢特定的组合,所以你只想重新运行它。

现在您应该看到出现了一些滑块:

当你调整它们时,它会改变设计!

打印你的画作

我们只剩下保存结果、格式化和打印了。如果我们使用那个文件扩展名,Matplotlib允许我们直接保存到SVGs,所以我们将继续这样做。

fig, ax = w.result
fig.savefig('my_super_cool_art.svg', bbox_inches = 'tight', pad_inches = 0)

对于格式化+打印,我们有两个选项。

  1. 使用Inkscape。
  2. 使用Python工具。

因为这是在Python教程中绘制的,所以我们将使用选项2,但是选项1通常也是可以的。

为了进行格式化,我们将使用Vpype。Vpype是一个内置在Python中的命令行工具,它在改变大小或对齐方式、将多个svg合并为一个(例如,如果您想使用多种颜色)、简化svg以提高速度等方面非常出色!因为我们只是确保SVG的大小适合页面,所以这样做相对比较简单。我提供了两个示例命令。一个是11x14(因为我在示例中一直使用它),但是因为您可能要打印在信纸上,所以我也把它扔了进去。我通过缩放给了我们一个空白。

vpype \
read my_super_cool_art.svg \
scale --to 9.9in 12.6in \
write --page-format 11x14 --center my_super_cool_art_rescaled.svg

vpype \
read my_super_cool_art.svg \
scale --to 7.65in 9.9in \
write --page-format letter --center my_super_cool_art_rescaled.svg

现在我们只需用我们的钢笔绘图仪把它打印出来。为此,我们将使用Axidraw API。关于这部分大家有如果有兴趣请查看原文。

本文分享自微信公众号 - Python与机器学习之路(gh_39aead19f756),作者:Huangwei AI

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python进阶之Matplotlib入门(五)

    Matplotlib是Python的画图领域使用最广泛的绘图库,它能让使用者很轻松地将数据图形化以及利用它可以画出许多高质量的图像,是用Python画图的必备技...

    HuangWeiAI
  • 在Python中创建命令行界面的最佳方式

    通过创建命令行界面(CLI),可以使程序功能强大并具有交互性。CLI允许您接受命令行参数(操作系统命令行中程序名称后面的信息),以便向程序添加其他特性,使代码易...

    HuangWeiAI
  • Python进阶之Matplotlib入门(四)

    Matplotlib是Python的画图领域使用最广泛的绘图库,它能让使用者很轻松地将数据图形化以及利用它可以画出许多高质量的图像,是用Python画图的必备技...

    HuangWeiAI
  • R语言可视化——柱形图美化(簇状、堆积、百分比)

    昨天以最简单的单序列柱形图作为对象详细的讲解了关于套用主题以及图表美化的思路。 今天就我们常用的几种柱形图的衍生图表——簇状柱形图、堆积柱形图、百分比堆积柱形图...

    数据小磨坊
  • 18.【Kevin聊敏捷】敏捷项目管理之Sprint Retrospective 迭代回顾会

    迭代回顾会是在Sprint Review会议之后,下一个新的sprint开始之前。你可以理解为是当前sprint最后一个会议了,所以很多人认为是总结会,也可以这...

    开心的Kevin
  • 机器学习验证集为什么不再有新意?

    研究者们入门数据科学世界时,意识到的第一件事便是,拥有训练和验证机器学习模型的独立数据集,至关重要。但是要实现这一点,很不容易。

    AI科技评论
  • 用户活跃,指标波动该怎么分析?

    上一篇【用户流失,该怎么分析?】以后,有同学强烈呼吁写用户活跃。因为用户相关的话题很多,为了便于大家阅读,这里把各种话题做一个归类如下图,这样看着清楚一些。今天...

    接地气的陈老师
  • RustCon Asia 实录 | Rust 在国内某视频网站的应用

    hawkingrei(王维真),中间件高级开发工程师,开源爱好者,TiDB & TiKV Contributor。

    PingCAP
  • Grpc介绍 — Go-Service To PHP-Client

    笔者现在公司项目大部分是PHP进行开发,要完成整体微服务化必须要考虑PHP使用上的可行性,Grpc也是支持PHP作为client端发起Grpc请求,但是依赖的扩...

    喵了个咪233
  • 在 Quora 做机器学习「炼丹」是怎样的体验?

    AI 科技评论按:实际上,号称「美版知乎」的 Quora 也已经大量引入了机器学习技术,而 Quora 的工程师们则喜欢把自己研究机器学习、产出技术方案的过程...

    AI研习社

扫码关注云+社区

领取腾讯云代金券