Python 数据分析与展示笔记2 – 图像手绘效果
Python 数据分析与展示系列笔记是笔者学习、实践Python 数据分析与展示的相关笔记
课程链接: Python 数据分析与展示
参考文档:
Numpy 官方文档(英文)
Numpy 官方文档(中文)
PIL 官方文档
一、PIL 库
1、安装与导入
# 安装 PIL 库
pip install pillow
# 导入 PIL 库
import PIL
# 导入 PIL 库的 Image模块/类(用读取显示图像)
from PIL import Image
2、简单使用
# 打开图像
Image.open('图片的路径')
# 转为灰度图像
.convert('L')
# 保存图像
.save('保存路径')
二、图像手绘效果
1、总体思路
- 图像中物体的边缘用黑色画出,而大片相同的区域用白色画出
- 图像梯度可以求出物体的边缘,边缘越明显,梯度越大
- 为了用黑色表色边缘,图片的像素值应该与梯度值成反比关系,而代码中的核心思想也是为了求得这个关系
2、几点理解
- depth参数:设置 x,y 梯度占总的梯度的比例,使梯度值缩小,因为我们把z向的梯度设成了1,如果不压缩x,y向梯度那么这个1就起不到什么作用
- z 方向的梯度(即梯度归一化中的1):为了表示像素值与梯度的反比关系,当像素的 x,y 向梯度值都很小时,在归一化时uni_z值就会区于1从而得到趋于255的像素值
- 图像梯度归一化:主要为了得到 uni_z 的值
- 光源设置:设置光源的角度只是为了得到 dx,dy,dz 三个可调系数而已,个人认为跟光源一点关系都没有,反而不好理解
- dx,dy,dz:uni_x,uni_y,uni_z的系数,更深入的说其实目的是把 dz 设的很大,dx,dy很小,完全可以不要dx,dy 把dz设为1,效果几乎一样
3、代码
from PIL import Image
import numpy as np
def hand_draw_image(img_path, show=True, save_path=None):
"""
把彩色图片变为手绘风格的灰度图片
:param img_path: 原图片的路径
:param show: 是否要显示手绘图片
:param save_path: 是否要保存手绘图片
:return:
"""
# 打开图片转为灰度图,并转换为 float 类型的 ndarray
img = np.asarray(Image.open(img_path).convert('L')).astype('float')
# 求图像的梯度
depth = 10
grad_x, grad_y = np.gradient(img)
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.
# 归一化图像的梯度
img_grad = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1)
uni_x = grad_x / img_grad
uni_y = grad_y / img_grad
uni_z = 1 / img_grad
# 光源对图像明暗的影响
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 轴的影响(投影)
img_res = 255 * (dx * uni_x + dy * uni_y + dz * uni_z) # 光源归一化
img_res = img_res.clip(0, 255) # 裁剪0-255外的像素值
im = Image.fromarray(img_res.astype(np.uint8)) # 重构图像
if show:
im.show()
if save_path:
im.save(save_path)
if __name__ == '__main__':
print('running hand_painted:')
hand_draw_image('图片路径')
原图:
效果:
GOOD LUCK!