故宫下雪了!我花了45秒,用Python给它画了一组手绘图

论坛 期权论坛 期权     
Python开发者   2019-2-20 00:05   6525   0
(给Python开发者加星标,提升Python技能)
作者:恋习Python /丁彦军 (本文来自作者投稿)
这几天,许多城市,迎来了2019年的第一场雪
13日早晨,当北京市民拉开窗帘时发现,窗外雪花纷纷扬扬在空中飘落
而且越下越大,树上、草地、屋顶、道路上...都落满雪花
京城银装素裹,这是今冬以来北京迎来的第二场降雪

一下雪,北京就变成了北平,故宫就变成了紫禁城

八万张门票在雪花飘下来之前,便早已预订一空

[iframe]https://v.qq.com/iframe/preview.html?width=500&height=375&auto=0&vid=o0837086i8e[/iframe]
看着朋友圈、微博好友都在纷纷晒图,小编只能羡慕不已。

不过,恋习Python突然想到,可以通过Python将故宫的建筑物图片,转化为手绘图(素描效果)。效果图如下:




一、概念与原理

我们都知道手绘图效果的特征主要有:
  • 黑白灰色;边界线条较重;相同或相近色彩趋于白色;略有光源效果

核心原理:利用像素之间的梯度值和虚拟深度值对图像进行重构,根据灰度变化来模拟人类视觉的模拟程度

把图像看成二维离散函数,灰度梯度其实就是这个二维离散函数的求导,用差分代替微分,求取图像的灰度梯度。常用的一些灰度梯度模板有:Roberts 梯度、Sobel 梯度、Prewitt 梯度、Laplacian 梯度。

以Sobel 梯度计算来解释:

首先计算出

,然后计算梯度角

梯度方向及图像灰度增大的方向,其中梯度方向的梯度夹角大于平坦区域的梯度夹角。如下图所示,灰度值增加的方向梯度夹角大,此时梯度夹角大的方向为梯度方向。对应在图像中寻找某一点的梯度方向即通过计算该点与其8邻域点的梯度角,梯度角最大即为梯度方向。




二、图像的数组形式与变换





其中,需要用到的方法:
  • Image.open( ):打开图片
  • np.array( ): 将图像转化为数组
  • convert("L"):将图片转换成二维灰度图片
  • Image.fromarray( ):将数组还原成图像uint8格式



代码如下:
  1. fromPILimportImage
  2. importnumpyasnp
  3. im=Image.open(r"C:\Users\Administrator\Desktop\gugong\微信图片_20190216152248.jpg").convert('L')
  4. a=np.asarray(im).astype('float')
  5. print(a.shape,a.dtype)
  6. (1080,608)float64
  7. #(1080,608)分别表示高度,宽度
复制代码

三、图像的手绘效果处理


实现思路步骤:

1、梯度的重构
numpy的梯度函数的介绍
np.gradient(a) : 计算数组a中元素的梯度,f为多维时,返回每个维度的梯度
离散梯度: xy坐标轴连续三个x轴坐标对应的y轴值:a, b, c 其中b的梯度是(c-a)/2
而c的梯度是: (c-b)/1


当为二维数组时,np.gradient(a) 得出两个数组,第一个数组对应最外层维度的梯度,第二个数组对应第二层维度的梯度。

代码如下:
  1. grad=np.gradient(a)
  2. grad_x,grad_y=grad
  3. grad_x=grad_x*depth/100.#对grad_x值进行归一化
  4. grad_y=grad_y*depth/100.#对grad_y值进行归一化
复制代码

2、构造guan光源效果

设计一个位于图像斜上方的虚拟光源
光源相对于图像的视角为Elevation,方位角为Azimuth
建立光源对各点梯度值的影响函数
运算出各点的新像素值


其中:
np.cos(evc.el) :单位光线在地平面上的投射长度
dx,dy,dz :光源对x,y,z三方向的影响程度

3、梯度归一化
  • 构造x和y轴梯度的三维归一化单位坐标系;
  • 梯度与光源相互作用,将梯度转化为灰度。


4、图像生成

具体详情代码如下:
  1. fromPILimportImage
  2. importnumpyasnp
  3. importos
  4. importjoin
  5. importtime
  6. defimage(sta,end,depths=10):
  7. a=np.asarray(Image.open(sta).convert('L')).astype('float')
  8. depth=depths#深度的取值范围(0-100),标准取10
  9. grad=np.gradient(a)#取图像灰度的梯度值
  10. grad_x,grad_y=grad#分别取横纵图像梯度值
  11. grad_x=grad_x*depth/100.#对grad_x值进行归一化
  12. grad_y=grad_y*depth/100.#对grad_y值进行归一化
  13. A=np.sqrt(grad_x**2+grad_y**2+1.)
  14. uni_x=grad_x/A
  15. uni_y=grad_y/A
  16. uni_z=1./A
  17. vec_el=np.pi/2.2#光源的俯视角度,弧度值
  18. vec_az=np.pi/4.#光源的方位角度,弧度值
  19. dx=np.cos(vec_el)*np.cos(vec_az)#光源对x轴的影响
  20. dy=np.cos(vec_el)*np.sin(vec_az)#光源对y轴的影响
  21. dz=np.sin(vec_el)#光源对z轴的影响
  22. b=255*(dx*uni_x+dy*uni_y+dz*uni_z)#光源归一化
  23. b=b.clip(0,255)
  24. im=Image.fromarray(b.astype('uint8'))#重构图像
  25. im.save(end)
  26. defmain():
  27. xs=10
  28. start_time=time.clock()
  29. startss=os.listdir(r"C:\Users\Administrator\Desktop\gugong")
  30. time.sleep(2)
  31. forstartsinstartss:
  32. start=''.join(starts)
  33. sta='C:/Users/Administrator/Desktop/gugong/'+start
  34. end='C:/Users/Administrator/Desktop/gugong/'+'HD_'+start
  35. image(sta=sta,end=end,depths=xs)
  36. end_time=time.clock()
  37. print('程序运行了----'+str(end_time-start_time)+'秒')
  38. time.sleep(3)
  39. main()
  40. 程序运行了----43.01828205879955秒#一共35张图片
复制代码

最终效果图对比:


其他图片就不一一列举;你也可以通过此代码为自己画一张手绘图;也可以为自己的家乡或母校画。

参考资料:北京理工大学的嵩天老师的网络课程
http://www.icourse163.org/learn/BIT-1001870002?tid=1001963001#/learn/announce


【本文作者】


丁彦军:一名痴恋于 Python 的码农,个人公号:「恋习Python」,在这里我们一起用Python 做些有意义的事。


推荐阅读
(点击标题可跳转阅读)
啥是佩奇?用 Python 画给你看
用 Python 全自动下载抖音小姐姐视频


觉得本文对你有帮助?请分享给更多人
关注「Python开发者」加星标,提升Python技能

喜欢就点一下「好看」呗~
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:835
帖子:167
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP