python 归一化_只需 45 秒,Python 给故宫画一组手绘图!

作者 | 丁彦军
责编 | 伍杏玲
13日早晨,当北京市民拉开窗帘时发现,窗外雪花纷纷扬扬在空中飘落,而且越下越大,树上、草地、屋顶、道路上,都落满雪花。京城银装素裹,这是今冬以来北京迎来的第二场降雪。
一下雪,北京就变成了北平,故宫就变成了紫禁城。八万张门票在雪花飘下来之前,便早已预订一空。
看着朋友圈、微博好友都在纷纷晒图,作为程序员的我只能坐在办公室里羡慕不已。
不过普通人得去故宫赏雪,程序员可以直接坐在家里动动手指便可以欣赏到故宫的美丽雪景手绘图了。如何操作呢?可通过Python将故宫的建筑物图片,转化为手绘图(素描效果)。效果图如下:

概念与原理
我们都知道手绘图效果的特征主要有:
- 黑白灰色
- 边界线条较重
- 相同或相近色彩趋于白色
- 略有光源效果
核心原理:利用像素之间的梯度值和虚拟深度值对图像进行重构,根据灰度变化来模拟人类视觉的模拟程度。
把图像看成二维离散函数,灰度梯度其实就是这个二维离散函数的求导,用差分代替微分,求取图像的灰度梯度。常用的一些灰度梯度模板有:Roberts 梯度、Sobel 梯度、Prewitt 梯度、Laplacian 梯度。
以Sobel 梯度计算来解释:
首先计算出

、

,然后计算梯度角

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

图像的数组形式与变换

其中,需要用到的方法:
- Image.open( ): 打开图片
- np.array( ) : 将图像转化为数组
- convert("L"): 将图片转换成二维灰度图片
- Image.fromarray( ): 将数组还原成图像uint8格式
代码如下:
from PIL import Imageimport numpy as npim = Image.open(r"C:甥敳獲AdministratorDesktopgugong微信图片_20190216152248.jpg").convert('L')a=np.asarray(im).astype('float')print(a.shape,a.dtype)(1080, 608) float64#(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) 得出两个数组,第一个数组对应最外层维度的梯度,第二个数组对应第二层维度的梯度。
代码如下:
grad=np.gradient(a)grad_x,grad_y=gradgrad_x = grad_x * depth / 100.#对grad_x值进行归一化grad_y = grad_y * depth / 100.#对grad_y值进行归一化
2、构造guan光源效果
设计一个位于图像斜上方的虚拟光源,光源相对于图像的视角为Elevation,方位角为Azimuth,建立光源对各点梯度值的影响函数,运算出各点的新像素值:

其中:
np.cos(evc.el) : 单位光线在地平面上的投射长度。
dx、dy、dz :光源对xyz三方向的影响程度。
3、梯度归一化
- 构造x和y轴梯度的三维归一化单位坐标系;
- 梯度与光源相互作用,将梯度转化为灰度。
4、图像生成
具体详情代码如下:
from PIL import Imageimport numpy as npimport osimport joinimport timedef image(sta,end,depths=10): a = np.asarray(Image.open(sta).convert('L')).astype('float') depth = depths # 深度的取值范围(0-100),标准取10 grad = np.gradient(a) # 取图像灰度的梯度值 grad_x, grad_y = grad # 分别取横纵图像梯度值 grad_x = grad_x * depth / 100.#对grad_x值进行归一化 grad_y = grad_y * depth / 100.#对grad_y值进行归一化 A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.) uni_x = grad_x / A uni_y = grad_y / A uni_z = 1. / A vec_el = np.pi / 2.2 # 光源的俯视角度,弧度值 vec_az = np.pi / 4. # 光源的方位角度,弧度值 dx = np.cos(vec_el) * np.cos(vec_az) # 光源对x 轴的影响 dy = np.cos(vec_el) * np.sin(vec_az) # 光源对y 轴的影响 dz = np.sin(vec_el) # 光源对z 轴的影响 b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z) # 光源归一化 b = b.clip(0, 255) im = Image.fromarray(b.astype('uint8')) # 重构图像 im.save(end)def main(): xs=10 start_time = time.clock() startss = os.listdir(r"C:甥敳獲AdministratorDesktopgugong") time.sleep(2) for starts in startss: start = ''.join(starts) sta = 'C:/Users/Administrator/Desktop/gugong/' + start end = 'C:/Users/Administrator/Desktop/gugong/' + 'HD_' + start image(sta=sta,end=end,depths=xs) end_time = time.clock() print('程序运行了 ----' + str(end_time - start_time) + ' 秒') time.sleep(3)main()程序运行了 ----43.01828205879955 秒 #一共35张图片
最终效果图对比:

最后,你自己动手试试吧?通过此代码为自己画一张手绘图,也可以为自己的家乡或母校画。
作者:丁彦军,一名痴恋于Python的码农
公众号:恋习Python,在这里我们一起用Python做些有意义的事。
参考资料:
http://www.icourse163.org/learn/BIT-1001870002?tid=1001963001#/learn/announce
代码链接:https://pan.baidu.com/s/1E_aZTRQWOzGV-2GV_iH43w
提取码:64z9
声明:本文为作者投稿,版权归其个人所有。
相关文章:

Windows平台下程序打包流程
Windows平台下程序打包流程 1、所有测试完成之后、程序release编译完成 2、依赖库打包 执行deploy.bat 脚本打包最新的程序以及依赖库 3、可执行程序打包 打开打包工程文件.evb, 使用 enigma virtual Box 打包可执行程序 点击“执行封包”,开始打包 …

一个apk多个ICON执行入口
一个工程对应一个AndroidManifest.xml文件,这个文件中包含有该项目的一些设置,如权限、SDk版Activity、Service信息等。一般而言,这个文件中会有且仅有一个application节点,这个节点表示这是一个应用程序,不管它下面还…

vbs之CurrentDirectory
为什么80%的码农都做不了架构师?>>> 最近要用一下Oracle instantclient的ODBC,由于配置有点繁琐,于是打算用vbs写一脚本来自动化一下,刚开始是这样的: Set ws CreateObject("WScript.Shell") w…

详解javascript: void(0);
原文 简书原文:https://www.jianshu.com/p/08ae8cbeb3be 什么是javascript: void(0); 我们经常会使用到 javascript:void(0) 这样的代码,那么在 JavaScript 中 javascript:void(0) 代表的是什么意思呢? javascript:void(0) 中最关键的是 v…

读书笔记:编写高质量代码--web前端开发修炼之道(二:5章)
读书笔记:编写高质量代码--web前端开发修炼之道 这本书看得断断续续,不连贯,笔记也是有些马虎了,想了解这本书内容的童鞋可以借鉴我的这篇笔记,希望对大家有帮助。 笔记有点长,所以分为一,二两个…

# Qt程序版本号使用
Qt程序版本号使用 在pro文件中添加(版本号可以3段或者4段) VERSION1.0.1.2DEFINES APP_VERSION\\\"$${VERSION}\\\"实际代码中使用 QString version APP_VERSION;qInfo()<<"版本信息:"<< version;

python traceback安装_Python错误:Traceback (most recent call last):感觉是软件出了问题
安装了pytest后,运行了一下,看不懂了,不过你前面的说对了 Testing started at 10:15 ... C:\Users\Administrator\PycharmProjects\untitled4\venv\Scripts\python.exe "D:\Program Files (x86)\PyCharm Community Edition 2018.2.4\hel…

Directx11教程(6) 画一个简单的三角形(2)
在上篇教程中,我们实现了在D3D11中画一个简单的三角形,但是,当我们改变窗口大小时候,三角形形状却随着窗口高宽比例改变而改变,如下图所示: 这是因为我们改变了窗口大小,但后缓冲大小在程序初始…

【单调栈 前缀和 异或】7.21序列求和
还要再细细思考的奇妙思路 题目描述 小A最近喜欢上了关于区间max的问题。她定义一个区间的价值是max(ai)(l<i<r)∗(alxoral1xor...xorar)max(ai)(l<i<r)∗(alxoral1xor...xorar)她想要知道,一个序列所有的连续子序列价值之和是多少。 输入格式 第一行一…

hibernate 复合主键 根据主键删除_hibernate封装Utils工具类
一:封装Session对象1、获取全新的Session的对象 2、获取与线程绑定的的Session的对象二:什么是持久化类1、Hlbernate是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。 …

Linux+Qt 下同一数据空间vfork多进程间通信的一种高效便捷方式(信号槽直接调用)
LinuxQt 下同一数据空间vfork多进程间通信的一种高效便捷方式(信号槽直接调用) 概述 传统的多进程间通信往往非常麻烦,采用的方法比如管道,共享内存,socket,文件等,大都非常繁琐, …

Eclipse 调试器(引用IT168)
Eclipse 调试器:零距离接触实战技巧 2011年11月25日01:29IT168字号:T|T调试的方法虽然千千万万,但归根结底,就是找到引发错误的代码。Eclipse调试器的目标是让程序员能对本地或远程程序进行错误侦测与诊断。该调试器提供所有标准调…

Cisco交换机与路由器的密码恢复_路由交换
站长原创:歪歪IT技术网 首发:迷你兔 来51cto记录一下我们net人最不喜欢记的路由器和交换机的密码恢复问题,虽然很简单的几个步骤,但是我却总是记不住,应该不是记不住,就觉得用处不大,但工作中…

投影转换_即插即用,办公投影不用愁:毕亚兹Mini DP转HDMIVGA转换器
日常办公的时候一些办公小件也很有用的,就比如说HDMI,VGA的转接头,不起眼但是很实用。去客户那里汇报工作,笔记本没有VGA接口,结果会很尴尬,到处借,没有转接头就是接不了,所以索性还…

事件绑定在IE下this是window的问题
昨天写一个函数的时候,后来用了事件绑定,开始没在IE下测试,在chrome下都是没问题的。后来在IE下测试发现出错。 后来修改一下,发现oBox.οnclickfunction(){}没问题,而addEven(oBox, "click", function(){})…

nvidia-jetson系列硬件平台上安装Qt
nvidia-jetson系列硬件平台上安装Qt 目标平台: Jetson Nano、Jetson TX2、etson Xavier NX、Jetson AGX Xavier 概述: 系统环境: 我的设备是下列环境,其实只要是L4T版本的应该都是可以的 镜像烧录方式:SDKManager 系统镜像版本:L4T-32.…

以后在这里安家
以后在这里安家,希望在这里学到更多的知识,分享更多的快乐与汗水,希望大家共同成长转载于:https://blog.51cto.com/heyangfan88/804542
如何用git命令行上传本地代码到github
如何用git命令行上传本地代码到github 2016年09月19日 16:10:36 阅读数:9337注意:安装的前提条件是配置好git的相关环境或者安装好git.exe,此处不再重点提及 上传的步骤:(本文采用git 命令界面进行操作) ( git config …

C# 的快捷键汇总(一)
全局快捷键 ——〉下列快捷组合键可用于集成开发环境 (IDE) 中的不同位置 命令名 快捷键 说明 关系图.属性 Alt Enter 将焦点从关系图切换到“属性”窗口。 编辑.复制 Ctrl C 将选定项复制到剪贴板。 编辑.剪切 Ctrl X 从…

git修改远程仓库地址
原文连接: https://blog.csdn.net/u012852597/article/details/79241548 内容: 方法有三种: 1.修改命令 git remote set-url origin [url] 例如: git remote set-url origin gitlabgitlab.chumob.com:php/hasoffer.git2.先删后加 git remote rm or…

ftp主动和被动模式_【扫盲】FTP基础知识详解
关注我,你的眼睛会怀孕本文主要介绍FTP的工作原理,FTP主动与被动两种工作模式。FTP 简介FTP协议就是文件传输控制协议。它可以使文件通过网络从一台主机传送到同一网络的另一台主机上,而不受计算机类型和操作系统类型的限制。服务器、大型机&…

单页面与多页面的区别及优缺点
单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段)&…

git使用手册
git使用手册 由 赵庆鹏创建, 最后修改于十二月 14, 2018 一、文件比较 1. 新建两个文件hello/world,内容可自定义,两个文件的内容,需要不相同,进行文件比对。2. 使用diff -u hello world > diff.txt,进行文件比…

python 乒乓球_python乒乓球
这是我用python编的一个小游戏,需要下载simpleaudio库,喜欢的可以玩。 以下是源代码: import turtle as t import simpleaudio as sa yeahsa.WaveObject.from_wave_file(‘bounce.wav’) #创建背景 game t.Screen() game.title(‘双人乒乓球…

《深入浅出Windows Phone 8应用开发》
章节 第1章 概述第2章 开发环境第3章XAML简介第4章 常用控件第5章 布局管理第6章 数据存储第7章 图形动画第8章 多媒体 第9章 启动器与选择器 第10章 手机感应编程第11章 MVVM模式第12章 Silverlight Toolkit组件第13章 网络编程第14章 异步编程与并行编程第15章 联系人和日程安…

CentOS7.4到Elasticsearch一路坑(五)
来来,zookeeper我们聊聊 zookeeper我是搭建了一个集群的,但是搭建完发现,bin/zkServer.sh status一直是不正常的 看了一下日志,的确有问题(有问题你还起来了?) 从这篇文章参考了一下:…

轻量级git服务器 Gogs git 服务器搭建
gogs搭建教程: 原文链接: https://garthwaite.org/docker-gogs.html 内容: Dockerized Gogs git server and alpine postgres in 20 minutes or less // under docker I’ve babysat gitlab omnibus before and it wasn’t any fun. So when a group of volunteer…

akaze特征匹配怎么去掉不合适的点_SIFT特征点
SIFT特征点图像特征点检测一直是研究的热点,从早期的harris角点检测开始,一直有很多人关注图像特征点的检测。最早人们关注图像中的角点,主要是因为角点能够代表图像中的一些特征。比如,通过检测两幅图像中的角点,可以…

fopen 中 按文本读写与按二进制读写 实例
参考:http://blog.csdn.net/hinyunsin/article/details/6401854 #include <stdio.h>int main(int argc, char *argv[]) {char he[20] "hello world\n";FILE *outfile fopen("t.txt", "wt");fwrite(he, sizeof(char), 20, out…
狼奔代码生成工具使用心得
狼奔代码生成工具(http://ltfwan.d33140.jit8.cn)是一款为程序员设计的代码生成器,更是一款软件项目智能开发平台,它可以自动生成ASP.NET页面及后台代码,采用了面向服务的架构(SOA)。那么,要如何通过狼奔代…