使用Python实现图像的手绘风格效果

使用Python实现图像的手绘风格效果

  • 一、引言
  • 二、代码详细解释与示例
  • 三、完整框架流程
  • 四、运行
  • 五、结论
  • 附:完整代码

一、引言

在数字图像处理领域,模拟手绘风格是一项有趣且具有挑战性的任务。手绘风格图像通常具有独特的纹理和深浅变化,给人一种亲切和艺术感。本文旨在通过计算图像的梯度并模拟光照效果,实现一种简单而有效的手绘风格图像生成方法。

二、代码详细解释与示例

  1. 导入必要的库

    from PIL import Image
    import numpy as np
    import os
    
    • PIL(Pillow):用于图像的打开、转换和保存。
    • NumPy:用于数组和矩阵操作,以及数学计算。
    • os:用于文件和目录操作。
  2. 计算图像的阴影效果

     def calculate_shading(img, depth, vec_el, vec_az):# 计算图像在x和y方向上的梯度grad = np.gradient(img)grad_x, grad_y = grad  # grad_x是x方向的梯度,grad_y是y方向的梯度# 根据深度因子调整梯度大小grad_x *= depth / 100.grad_y *= depth / 100.# 计算梯度的模(大小),并添加一个小常数以避免除以零A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1e-7)  # 1e-7是为了数值稳定性# 计算单位梯度向量(在x, y, z方向上的分量)# 这里我们假设z方向上的分量为1/A,因为它代表图像表面的“法线”方向uni_x = grad_x / Auni_y = grad_y / Auni_z = 1. / A# 计算光源方向向量(在x, y, z方向上的分量)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方向(垂直于图像平面)的影响# 计算光源方向与单位梯度向量的点积,模拟光照效果# 点积越大,表示该点越亮;点积越小(越接近-1),表示该点越暗(但由于我们取绝对值并映射到0-255,所以实际上表现为更深的阴影)# 但由于我们是模拟手绘风格,所以直接乘以255并裁剪到0-255范围内b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)# 裁剪结果到0-255范围内,确保图像数据的有效性b = b.clip(0, 255)return b
    
    • 函数calculate_shading接收一个灰度图像数组img、深淡程度depth、光源的俯视角度vec_el和方位角度vec_az作为参数。
    • 使用np.gradient计算图像在x和y方向上的梯度,得到grad_xgrad_y
    • 根据depth因子调整梯度大小,然后计算梯度的模A,并添加一个小常数以避免除以零的错误。
    • 计算单位梯度向量uni_xuni_yuni_z(假设z方向上的分量为1/A)。
    • 计算光源方向向量dxdydz
    • 通过计算光源方向与单位梯度向量的点积,模拟光照效果,得到带有阴影效果的图像数组b
    • 使用clip函数将结果裁剪到0-255范围内,确保图像数据的有效性。

    示例
    假设我们有一个灰度图像img_array,我们可以调用这个函数来计算阴影效果:

    shaded_img = calculate_shading(img_array, depth=50.0, vec_el=np.pi / 2.2, vec_az=np.pi / 4.0)
    
  3. 将输入图片处理成手绘风格并保存

     # 将输入图片处理成手绘风格并保存为新文件def img_handpainted(input_img_path, depth=20.0, vec_el=np.pi / 2.2, vec_az=np.pi / 4.0):"""将指定路径的灰度图像转换成手绘风格的图像并保存。参数:input_img_path (str): 输入图片的路径。depth (float): 深淡程度控制因子,默认为20.0。vec_el (float): 光源的俯视角度(俯仰角),默认为np.pi/2.2。vec_az (float): 光源的方位角度(方位角),默认为np.pi/4.0。"""# 检查输入文件是否存在if not os.path.exists(input_img_path):raise FileNotFoundError(f"The file {input_img_path} does not exist.")# 打开并转换图像为灰度图,然后转换为NumPy数组img = np.array(Image.open(input_img_path).convert('L')).astype('float')# 从文件路径中提取图像名称(不包括扩展名)img_name = os.path.basename(input_img_path)[:-4]# 计算阴影效果shaded_img = calculate_shading(img, depth, vec_el, vec_az)# 将结果转换回图像格式并保存img_handwrite = Image.fromarray(shaded_img.astype('uint8'))outname = './' + img_name + '_handpainted.jpg'img_handwrite.save(outname)print(f"Saved image as {outname}")
    • 函数img_handpainted接收输入图片的路径input_img_path、深淡程度depth、光源的俯视角度vec_el和方位角度vec_az作为参数。
    • 检查输入文件是否存在,如果不存在则抛出FileNotFoundError异常。
    • 打开并转换图像为灰度图,然后转换为NumPy数组。
    • 从文件路径中提取图像名称(不包括扩展名)。
    • 调用calculate_shading函数计算阴影效果。
    • 将结果转换回图像格式,并保存为新的JPEG文件。
    • 打印保存的文件名。

    示例
    假设我们有一个灰度图像文件example.jpg,我们可以调用这个函数来生成手绘风格的图像:

    img_handpainted('./example.jpg', depth=30.0)
    

三、完整框架流程

  1. 准备阶段

    • 安装必要的Python库:Pillow(PIL)和NumPy。
    • 准备一张灰度图像作为输入。
  2. 计算阴影效果

    • 读取输入图像并转换为灰度图。
    • 将灰度图转换为NumPy数组,以便进行数学计算。
    • 调用calculate_shading函数,根据光源方向和深淡程度计算阴影效果。
  3. 生成手绘风格图像

    • 将计算得到的阴影效果应用到原始灰度图像上。
    • 将结果转换回图像格式。
    • 保存为新的JPEG文件。
  4. 运行脚本

    • 执行脚本,查看生成的手绘风格图像。

四、运行

原图片:
请添加图片描述
执行代码后生成的图片:
请添加图片描述

五、结论

通过本文介绍的方法,我们可以轻松地将普通灰度图像转换成具有手绘风格效果的图像。这种方法不仅简单易行,而且生成的图像具有独特的手绘质感,非常适合用于艺术创作和图像处理领域。希望本文能够帮助读者理解和实现这一有趣的技术。

附:完整代码

# -*- coding: utf-8 -*-
# @Time : 2021/7/4 22:30
# @Author : Leuanghing Chen
# @Blog : https://blog.csdn.net/weixin_46153372?spm=1010.2135.3001.5421
# @File : 手绘效果图.py
# @Software : PyCharmfrom PIL import Image
import numpy as np
import os# 计算图像的阴影效果,模拟手绘风格的深浅变化
def calculate_shading(img, depth, vec_el, vec_az):"""计算并返回带有阴影效果的灰度图像。参数:img (numpy.ndarray): 输入的灰度图像,应该是一个二维数组。depth (float): 深淡程度控制因子,范围0-100。值越大,阴影效果越明显。vec_el (float): 光源的俯视角度(俯仰角),以弧度为单位。控制光源相对于图像平面的垂直角度。vec_az (float): 光源的方位角度(方位角),以弧度为单位。控制光源在图像平面上的水平位置。返回:numpy.ndarray: 带有阴影效果的灰度图像,与输入图像尺寸相同。"""# 计算图像在x和y方向上的梯度grad = np.gradient(img)grad_x, grad_y = grad  # grad_x是x方向的梯度,grad_y是y方向的梯度# 根据深度因子调整梯度大小grad_x *= depth / 100.grad_y *= depth / 100.# 计算梯度的模(大小),并添加一个小常数以避免除以零A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1e-7)  # 1e-7是为了数值稳定性# 计算单位梯度向量(在x, y, z方向上的分量)# 这里我们假设z方向上的分量为1/A,因为它代表图像表面的“法线”方向uni_x = grad_x / Auni_y = grad_y / Auni_z = 1. / A# 计算光源方向向量(在x, y, z方向上的分量)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方向(垂直于图像平面)的影响# 计算光源方向与单位梯度向量的点积,模拟光照效果# 点积越大,表示该点越亮;点积越小(越接近-1),表示该点越暗(但由于我们取绝对值并映射到0-255,所以实际上表现为更深的阴影)# 但由于我们是模拟手绘风格,所以直接乘以255并裁剪到0-255范围内b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)# 裁剪结果到0-255范围内,确保图像数据的有效性b = b.clip(0, 255)return b# 将输入图片处理成手绘风格并保存为新文件
def img_handpainted(input_img_path, depth=20.0, vec_el=np.pi / 2.2, vec_az=np.pi / 4.0):"""将指定路径的灰度图像转换成手绘风格的图像并保存。参数:input_img_path (str): 输入图片的路径。depth (float): 深淡程度控制因子,默认为20.0。vec_el (float): 光源的俯视角度(俯仰角),默认为np.pi/2.2。vec_az (float): 光源的方位角度(方位角),默认为np.pi/4.0。"""# 检查输入文件是否存在if not os.path.exists(input_img_path):raise FileNotFoundError(f"The file {input_img_path} does not exist.")# 打开并转换图像为灰度图,然后转换为NumPy数组img = np.array(Image.open(input_img_path).convert('L')).astype('float')# 从文件路径中提取图像名称(不包括扩展名)img_name = os.path.basename(input_img_path)[:-4]# 计算阴影效果shaded_img = calculate_shading(img, depth, vec_el, vec_az)# 将结果转换回图像格式并保存img_handwrite = Image.fromarray(shaded_img.astype('uint8'))outname = './' + img_name + '_handpainted.jpg'img_handwrite.save(outname)print(f"Saved image as {outname}")# 当脚本作为主程序运行时,调用img_handpainted函数
if __name__ == '__main__':img_handpainted('./1.jpg')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/58843.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle Select语句

SELECT语句使用方法 在Oracle中,表是由列和行组成。 例如,示例数据库中的customers表具有以下列:customer_id,name,address,website和credit_limit。customers表中这些列中也有对应的数据。 要从表的一个或…

Scala的集合。

定义:set表示没有重复元素的集合 特点:唯一,无序 Set有可变mutable和不可变immutable 两种类型。不可变set创建后元素不能修改,可变set可对元素进行添加,删除等操作,这两种类型能满足不同场景需求。 pack…

w~大模型~合集21

我自己的原文哦~ https://blog.51cto.com/whaosoft/12459590 #大模型~微调~用带反馈的自训练 面对当前微调大模型主要依赖人类生成数据的普遍做法,谷歌 DeepMind 探索出了一种减少这种依赖的更高效方法。大模型微调非得依赖人类数据吗?用带反馈的自训…

opencv 中 threshold 函数作用

在 OpenCV 中,threshold 函数用于将图像转换为二值图像,它通过设置一个阈值来将像素值分类为两类:低于阈值的像素设置为 0(或黑色),高于阈值的像素设置为最大值(通常是 255 或白色)。…

ctfshow(316,317,318)--XSS漏洞--反射性XSS

反射型XSS相关知识 Web316 进入界面: 审计 显示是关于反射性XSS的题目。 思路 首先想到利用XSS平台解题,看其他师傅的wp提示flag是在cookie中。 当前页面的cookie是flagyou%20are%20not%20admin%20no%20flag。 但是这里我使用XSS平台,…

java 容器的快速失败(fast-fail)机制

Java容器的快速失败(fail-fast)机制是Java集合框架中的一种重要特性,它主要用于在迭代过程中检测并处理集合的并发修改。以下是对该机制的详细解释: 一、定义与原理 快速失败机制的核心思想是在迭代过程中,一旦检测到…

【案例】Excel使用宏来批量插入图片

一、场景介绍 我有一个excel文件,需要通过一列的文件名称,按照规则给批量上传图片附件。 原始文件: 成功后文件: 二、实现方法 1. 使用【wps】工具打开Excel文件,将其保存为启用宏的文件。 2.找到编辑宏的【VB编辑器…

ENSP OSPF和BGP引入

路由协议分为:内部网关协议和外部网关协议。内部网关协议用于自治系统内部的路由,包括:RIP和OSPF。外部网关协议用于自治系统之间的路由,包括BGP。内部网关协议和外部网关协议配合来共同完成网络的路由。 BGP:边界网关路由协议(b…

Linux磁盘存储

磁盘存储 设备文件 设备文件是类Unix操作系统(包括Linux)中一种特殊的文件类型,它代表了设备接口,使得用户空间的程序可以通过标准的文件操作来访问和控制硬件设备。设备文件为周边设备提供了简单的接口,如打印机、硬…

xcode更新完最新版本无法运行调试

‌Xcode更新后无法运行调试的原因可能包括以下几个方面‌: 1.‌版本兼容性问题‌:Xcode更新后,某些旧版本的代码可能不再兼容新版本的Xcode,导致出现错误。解决方法是根据错误提示逐个修复代码,或者尝试使用兼容新版本…

Windows下Python环境安装GDAL

最近发现在Windows环境下安装gdal,rasterio等都会出现头文件缺失的问题,比如: pip install gdal -i https://pypi.tuna.tsinghua.edu.cn/simple pip install rasterio -i https://pypi.tuna.tsinghua.edu.cn/simple 安装会下载tar.gz格式的gdal包,回报一些类似找不到头文件…

vscode生成项目目录结构

在博客中经常看到目录结构如下: project-tree ├─ .git ├─ .gitignore ├─ .vscodeignore ├─ images ├─ node_modules ├─ src │ ├─ config.ts │ ├─ index.ts │ └─ utils.ts ├─ tsconfig.json ├─ tslint.json └─ webpack.config.js 应该如何…

阿里云ECS服务器使用限制及不允许做的事情

阿里云ECS(Elastic Compute Service)是一种高性能的弹性计算服务,允许用户在云端创建和管理虚拟服务器。尽管ECS提供了强大的功能,但在使用过程中,阿里云有一些限制和不允许的行为。以下是一些主要的使用限制和禁止行为…

el-date-picker 设置开始时间和结束时间

<el-date-picker v-model"ruleForm.RECORDDATE" type"date" placeholder"日期" format"YYYY/M/D" value-format"YYYY/M/D" style"width: 100%;" :disabled-date"publishDateAfter" > </el-dat…

【LeetCode】【算法】240. 搜索二维矩阵II

LeetCode 240. 搜索二维矩阵II 题目描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 思路 思路&#xff1a;K神真强啊240.搜索二维矩阵II&#xff0…

【手撕排序4】计数排序+快速排序(非递归)

> &#x1f343; 本系列包括常见的各种排序算法&#xff0c;如果感兴趣&#xff0c;欢迎订阅&#x1f6a9; > &#x1f38a;个人主页:[小编的个人主页])小编的个人主页 > &#x1f380; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 > ✌️ &#x1f91e;…

FPGA实现以太网(一)、以太网基础知识

系列文章目录 FPGA实现以太网&#xff08;二&#xff09;、初始化和配置PHY芯片 文章目录 系列文章目录一、以太网简介二、OSI七层模型三、TCP/IP五层模型四、MAC-PHY接口五、MAC帧格式六、IP帧格式6.1 IP首部校验和计算6.2 IP首部校验和校验 七、UDP帧格式7.1 UDP头部校验和…

Linux平台C99与C++11获取系统时间

源码: #include <iostream> #include <chrono> #include <ctime> #include <thread>using namespace std; int main() {cout << "===使用C99方式获取系统时间===" << endl;time_t now = time(nullptr);struct tm *tm_c99 = lo…

QT版发送邮件程序

简单的TCP邮箱程序 **教学与实践目的&#xff1a;**学会网络邮件发送的程序设计技术。 1.SMTP协议 邮件传输协议包括 SMTP&#xff08;简单邮件传输协议&#xff0c;RFC821&#xff09;及其扩充协议 MIME&#xff1b; 邮件接收协议包括 POP3 和功能更强大的 IMAP 协议。 服务…

设计模式学习总结(一)

设计模式学习笔记 面向对象、设计原则、设计模式、编程规范、重构之间的关系 面向对象、设计原则、设计模式、编程规范、重构之间的关系 面向对象 现在&#xff0c;主流的编程范式或者是编程风格有三种&#xff1a;面向过程、面向对象和函数式编程。 需要掌握七大知识点&#…