丨budboool · 2022年07月22日

仅用5000行代码,在V853上AI渲染出一亿幅山水画

近日,一位社区大佬将一个AI画山水画的开源项目移植到全志V853开发板上。这个项目仅用不到5000行代码,就实现了一个可以自动作画的“人工智能”,并且不需要调用任何第三方库。开源项目链接:https://bbs.aw-ol.com/topic/1715/

9315D34F-FBCE-4ee2-86C5-C234B051E9D4.png

只要输入任意字符串,这个AI便会生成一幅全新的画作,每一幅都是世间独一无二的,就像是开山水画盲盒。

并且这些作品并不是简单的元素堆积——项目中没有用到任何一张图片素材,所有的景物都是代码生成的。

我们可以看到画中的山石错落有致。

lQLPJxaE0F1JuAvNAl_NBZCwJDgteDcdzmEC2qsrdgAzAA_1424_607.png

树木各具神态——或苍劲有力,或郁郁葱葱,或新芽初发,或垂垂老矣。

216B9DF3-6F05-40dc-BC52-3651CC78FF26.png

远山和近景,都做了不同层次的处理。

lQLPJxaE0F1Jt_nNAl3NBVCwzieyAwuA9aIC2qsrdkCEAA_1360_605.png

对饮的文人、垂钓的老者各具神态,山上的宝塔、田间的茅草屋各有特点。偶尔还藏了一个电线杆和Pizza Hut的彩蛋,让你迷失在画里,不知身处何处。

D9B2230F-6A21-4679-B50F-C35FC1F9DAC8.png

而完成这些的核心代码都是H5纯手写的,没有用到第三方库,整个文件大小不过100多K。这样一个“简简单单”的程序,再加上一块V853开发板,就也能轻松渲染出一幅独一无二山水画,若是再心灵手巧一点,把它做成一个电子相框裱起来挂在工位上,陶冶情操,岂不美哉。

可以看出,作者不仅对AI算法有很深的理解,在国画上也有很高的造诣,才能实现科技与人文的深度结合。

那么,这些神奇是如何实现的呢?知乎AI大佬 【胡虎护弧呼】给我们介绍了一些简单的原理。

宛若手绘的山

山水画里的树叶与小树是用多边形表示的。从简单的开始,一个三角形代表山的形状,直线作为阴影。现在从视觉上看来,这些代表阴影的直线还是太过生硬,完全不像可以生成“山水画”的感觉,我们来把分立的直线变成下图这样连续的曲线。

1.jpg

有时候简单的方法的也有惊人的效果。我们可以用一根斜率稍微不相同的线的向下切,并在垂直边和水平边选择几个采样点。然后在这些有序的点之间连接曲线,比如贝塞尔曲线或Catmull-Rom曲线。

v2-3a8f205f230b8f1caa1a6a14405fe333_1440w.png

生成曲线时,如果是标准的曲线,那么肯定太规则了不好看。那么再加控制点,再加噪音弄得更随机一点。

v2-7ea7a27a7d28b642f8c77e276ae28996_1440w.png

这些阴影的边界是三角形,还是太规则,于是把这些边界也多弄些控制点多弄些噪音。

v2-ff2fe7ca49eec06302bc48882bf43380_1440w.png

再把阴影线条的粗细变成随机形式,以及表示山的形状的直线变成多段折线,最终效果如下。记得阴影和有光照的地方需要描边。现在山的受到光照的地方太秃了,那么再来加上一些变换。第一种如下图,下图左加了一道小山沟,下图中为小山沟加上一些阴影,最后效果如下图右。

v2-1f73ec8ada55f77558bc72f181bc6f1a_1440w.png

似乎还是有些不自然...山的边缘真的只是首尾相连的折线嘛?我们可以把这些折线稍微延长一些,然后再加上第二座山。

v2-9546ade8e00d54c4f7cdc26e95b2b1c4_1440w.jpg

目前只是绘出了单座山,如何确定连片的山脉的位置呢?一种容易想到的方法是让山都生成在一根直线附近。

v2-4fa56dd17e56e122c004f3026d87741c_1440w.jpg

另一种方法是生成首尾相连的折线段,用这些折线段作为山的轮廓。

v2-fdab97c00f90fb74ab6591d0d1285355_1440w.jpg

也可以先用多边形限制山的生成范围,然后在多边形内放置线段,用于生成山脉。

v2-be407c3ebe7d3bf6a9cc6802b6af5214_1440w.png
v2-9112185e2d30a1ba8ec322460f801d82_1440w.jpg

有了山,也就相当于给山脉打下了坚实的地基,接下来要做的就是给山水画添上其他的元素:树木、亭子、古人.......

树木的随机生成——数值奇异值分解

从源码角度切入树木的随机生成,繁多的树木其实也是由类似的噪点和数学函数的运算进行了大小、形态以及位置的确定。

tree02生成的部分源码:
68B09BB5-1773-4833-A7B7-6D11E62705D6.png

以 ‘tree02’ 为例子,树木的随机生成方式与山体随机生成的方式在原理上是大致相同的,两者应该都是使用了大量的噪点和数学函数从头开始建模,并随机分配坐标位置的。

那随机生成的树木又是如何做到棵棵都不一样的呢,这里就有可能使用到了渲染编程中的数值奇异值分解原理了(不代表作者源码是基于该原理随机生成)。

奇异值分解表达如下:

431FB190-0AC8-4078-AEB7-FCB75D2DC789.png

其中A 是原矩阵。V 和 U都是旋转矩阵,Sigma 是对角矩阵,代表伸缩矩阵。网上资料很多,这里不解释。

这里仅仅讨论它的物理意义。比如对于某个向量矩阵变换A,如果它是纯伸缩矩阵,那么左右奇异矩阵都是单位矩阵

D645D59E-841A-464a-9DB5-FBA608B8DC70.png

经过矩阵变换后,为了数据降维,找出变换最明显的向量,需要选择正x 轴和正y 轴,如下图。蓝色轴和黄色轴即为在本次矩阵变化中,变化最为明显的向量。

263FBB67-904D-4d8f-9A78-DCE9B448EFE7.png

注意向量并不局限于边,三角形内的任意一段向量都可以,端点不固定,方向不固定。只是说蓝色向量,黄色向量在此次矩阵变换中,变长或变短的程度最大。它们变长或变短的倍数,就是奇异值。

如果这是个剪切矩阵,那么可能如下:

8C093E1E-998B-49b6-AA6D-C1BAF8962F86.png

那么我们需要旋转一下黄色或蓝色向量,来保证黄色向量和蓝色向量仍然是所有向量中长度变化最明显的。比如黄色向量变长为原来的1.3倍。但是与之前的纯伸缩矩阵相比,也就是与上图相比,它还旋转了135度,这是矩阵U 的功劳。而蓝色向量缩短为原来的0.7倍,同样旋转了135度,这是矩阵V的功劳。

2C6E6694-3E7B-4c3f-B679-7A709CF8E424.png

噪点可能在经过如上原理的函数运算后,最终生成了各式各样的树木、山峦、建筑等等等,该种函数运算的随机生成方式同时也体现于其他类型树木生成的源码之中。

tree07生成的部分源码:
EA37D99B-52AA-4064-9019-E90D5B115A42.png

作者Huang Lingdong

作者Huang Lingdong,本科毕业于卡耐基梅隆大学,现于母校的Studio for Creative Inquiry做研究助理,为博物馆和学校等组织开发交互媒体项目。

作者本人对中华文化也是颇有研究,除了山水画之外,之前也曾开发过一款文言文编程语言,在该语言中他用《易经》的阴和阳代表布尔变量中的0和1,感兴趣的同学也可以去探索一下此语言蕴含的中华文字之美。

v2-7a3fcfda1d1de1256a72e36e2aec014d_1440w.jpg

-传送门-

V853开发板山水画demo:https://bbs.aw-ol.com/topic/1715/
山水画线上IDE试玩:http://shan-shui-inf.lingdong.works/
作者Github主页:https://github.com/LingDong-
作者个人项目主页:https://lingdong.works/

v853shi2.jpg

推荐阅读
关注数
17
文章数
95
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息