用python给女朋友画张素描,网友:程序员有女朋友?

写在前面


如何使用python给心爱的女朋友画一张素描?热心网友:程序员有女朋友?

用python给女朋友画张素描,网友:程序员有女朋友?

莫慌,等我学python 一年、两年、三年、四年、五年...... 代码它不香吗,陪你熬夜,给你创造对象。言归正传,会写代码的男人是最帅的,让我们开启python之旅吧。


  • 环境配置

python版本: 3.6.0

项目所需要的环境安装包

ps: 每一步都有代码和排版截图,方便学习

<code>pip install pillow/<code>


  • 代码目录结构:


用python给女朋友画张素描,网友:程序员有女朋友?


第一步:导入相关的python包


<code># encoding:utf-8


import os
from PIL import Image, ImageFilter, ImageOps
import imghdr/<code>


使用python包的作用:


os: 本项目只用到了对文件、文件夹的操作。

PIL: Python Imaging Library,是Python平台的图像处理标准库。PIL功能非常强大,API也非常简单易用。

imghdr: 是一个用来检测图片类型的模块,传递给它的可以是一个文件对象,也可以是一个字节流。


用python给女朋友画张素描,网友:程序员有女朋友?


第二步:参数配置类


<code>class CONF:
input_path = "input_img" # 待处理的图片
output_path = "output_img" # 处理后的图片
pen_size = 3 # 定义画笔的大小 (素描画风格生效)
color_diff = 6 # 色差扩散器 (素描画风格生效)
blur = 25 # 模糊度 (水墨画风格生效)
alpha = 1.0 # 融合度 (水墨画风格生效)
picture_type = 0 # 0 表示素描风格, 1 表示水墨画风格
picture_name = ["素描风格", "水墨画风格"]
is_log = True # 是否打印日志信息/<code>


这里是个人编程的习惯,我习惯把一些配置,例如:文件路径、模型存放路径、模型参数统一放在一个类中。当然,实际项目开发的时候,是用config 文本文件存放,不会直接写在代码里,这里为了演示方便,就写在一起,也方便运行。这块代码放在代码文件的开头也方便查看和修改。


用python给女朋友画张素描,网友:程序员有女朋友?


第三步:类的初始化


<code>class SketchPic:


def __init__(self, input_path, output_path, pen_size, color_diff, blur, alpha, picture_type, picture_name, is_log):
self.input_path = input_path
self.output_path = output_path
self.pen_size = pen_size
self.color_diff = color_diff

self.blur = blur
self.alpha = alpha
self.picture_type = picture_type
self.picture_name = picture_name
self.is_log = is_log


""" 初始化 """
@classmethod
def initialize(cls, config):
input_path = config.input_path
output_path = config.output_path
pen_size = config.pen_size
color_diff = config.color_diff
blur = config.blur
alpha = config.alpha
picture_type = config.picture_type
picture_name = config.picture_name
is_log = config.is_log
return cls(input_path, output_path, pen_size, color_diff, blur, alpha, picture_type, picture_name, is_log/<code>


initialize() 函数和 __init__() 函数 是对象初始化和实例化,其中包括基本参数的赋值、最后返回用户一个对象。这里作为一个类的基本操作,是属于一个通用模板,在大多数项目中,都可以这么去写。为了养成良好的编程习惯,大家可以把这个模板记下来,后续直接套用,修改部分参数就可以了。


用python给女朋友画张素描,网友:程序员有女朋友?


第四步: 类的主流程函数


<code>""" 主流程 """
def main_process(self, ):
if os.path.exists(self.input_path) and os.path.isdir(self.output_path):
self.__visit_dir_files(self.input_path, self.output_path, self.input_path)
if self.is_log:
print(u'完成!所有图片已保存至路径' + self.output_path)
else:
print(u'待处理的图片存放的位置 %s, 如果没有请新建目录 %s' % (self.input_path, self.input_path))
print(u'生成素描后的图片存放位置 %s, 如果没有请新建目录 %s' % (self.output_path, self.output_path))


""" 图片处理 """
def __img_deal(self, img_path, save_path):
# todo:...
try:
if self.picture_type == 0:
self.__draw_gray_img(img_path, save_path) # 素描风格
elif self.picture_type == 1:
self.__water_draw(img_path, save_path) # 水墨画风格
else:
print("picture_type 值只能为 0 或 1")
return
if self.is_log:
print(u'图片[' + img_path + u']处理完毕')
except :
print(u'图片[' + img_path + u']出错')/<code>


在写代码的时候,一定要抓住主线,就是代码运行的主流程。因为一个完整可靠的项目,它是有很多细枝末节考虑,很多步骤是要分模块来写。主流程就是把主心干确定好,各个模块的入口确定好。这样开发的时候,思路会比较清晰,不会被细节吸引住。这里主心干只有个函数 __visit_dir_files() 的调用,但是它的外围都是一些边界条件的判定,不重要,但是没有它们程序会出现BUG。


__img_deal() 函数是根据配置参数,调用指定绘画函数,起到一个转发器的作用。


用python给女朋友画张素描,网友:程序员有女朋友?


第五步: 生成水墨画风格的函数


<code>"""水墨画风格"""
def __water_draw(self, img_path, save_path):
input_img = Image.open(img_path)
gray_image = input_img.convert('L') # 图片转换成灰色
gray_image_2 = gray_image.copy()
gray_image_2 = ImageOps.invert(gray_image_2)
width, height = gray_image.size
for i in range(self.blur): # 模糊度
gray_image_2 = gray_image_2.filter(ImageFilter.BLUR)
for x in range(width):
for y in range(height):
a = gray_image.getpixel((x, y))
b = gray_image_2.getpixel((x, y))
gray_image.putpixel((x, y), min(int(a * 255 / (256 - b * self.alpha)), 255))
gray_image.save(save_path)/<code>


根据模糊度和融合度,对图片的各个像素点进行运行,最后生成水墨画风格的图片。


用python给女朋友画张素描,网友:程序员有女朋友?


第六步: 生成素描画风格的函数


<code>"""素描画风格"""
def __draw_gray_img(self, img_path, save_path):
"""
素描画风格
:param in_img_name:
:param out_img_name:
:param pen_size: 定义画笔的大小
:param color_diff: 色差扩散器
:return:
"""
# 图像组成:红绿蓝 (RGB)三原色组成 亮度(255,255,255)
img = Image.open(img_path)
new = Image.new("L", img.size, 255)
width, height = img.size
img = img.convert("L")


# 逐个像素点绘制
for i in range(self.pen_size + 1, width - self.pen_size - 1):
for j in range(self.pen_size + 1, height - self.pen_size - 1):
original_color = 255 # 原始的颜色
# 逐个灰度计算
original_color = self.__pix_color_pick(img, new, i, j, original_color, -1, 0, 1, 0)
original_color = self.__pix_color_pick(img, new, i, j, original_color, 0, -1, 0, 1)
original_color = self.__pix_color_pick(img, new, i, j, original_color, -1, -1, 1, 1)
self.__pix_color_pick(img, new, i, j, original_color, 1, -1, -1, 1)
new.save(save_path) # 保存图片


""" 素描风格 每个像素点的灰度计算 """
def __pix_color_pick(self, img, new_img, i, j, original_color, a_ir, a_jr, b_ir, b_jr):

a_color = sum([img.getpixel((i + r * a_ir, j + r * a_jr)) for r in range(self.pen_size)]) // self.pen_size
b_color = sum([img.getpixel((i + r * b_ir, j + r * b_jr)) for r in range(self.pen_size)]) // self.pen_size
if abs(a_color - b_color) > self.color_diff:
original_color -= (255 - img.getpixel((i, j))) // 4
new_img.putpixel((i, j), original_color)
return original_colo/<code>


根据画笔大小和色差扩散器大小,对图片的各个像素点进行运行,最后生成素描画风格的图片。


用python给女朋友画张素描,网友:程序员有女朋友?


第七步: 递归访问文件


<code>""" 创建文件夹 """
def __mkdir(self, path):
path = path.strip().rstrip("\\\")
is_exists = os.path.exists(path)
if not is_exists:
os.makedirs(path)
return True
else:
return False


""" 递归访问文件/文件夹 """
def __visit_dir_files(self, org_input_dir, org_output_dir, recursion_dir):
single_file = False
if os.path.isdir(recursion_dir):
dir_list = os.listdir(recursion_dir)
else:
dir_list = [recursion_dir]
single_file = True
for i in range(0, len(dir_list)):
path = os.path.join(recursion_dir, dir_list[i])
if os.path.isdir(path):
self.__visit_dir_files(org_input_dir, org_output_dir, path)
else:
if imghdr.what(path):
abs_output_dir = org_output_dir + recursion_dir[len(org_input_dir):]
target_path = os.path.join(abs_output_dir, self.picture_name[self.picture_type] + dir_list[i])
if single_file:
target_path = os.path.join(org_output_dir, self.picture_name[self.picture_type] + os.path.basename(dir_list[i]))
target_dir_name = os.path.dirname(target_path)
if not os.path.exists(target_dir_name):
self.__mkdir(target_dir_name)
self.__img_deal(path, target_path)/<code>


这里也有一个难点,递归访问文件/文件夹。递归,就是自己调用自己。可以把它当成“分治法

”,打个比方,如果你想解决一个很大的难题,直接计算是非常困难的,可以把它拆解成多个小问题,一个一个来解决。而递归,就是起到一个“分治”的作用。它调用的过程,就是数据结构里面的“栈”(先进后出)。

我当时开始学习算法的时候,递归算法也是研究了一个星期才懂它的原理。所以大家学习的时候,不要着急,先在纸上模拟调用过程,慢慢就会懂了。


用python给女朋友画张素描,网友:程序员有女朋友?


第八步: 主函数入口


<code>if __name__ == '__main__':


sketch_pic = SketchPic.initialize(config=CONF)
sketch_pic.main_process()/<code>


用python给女朋友画张素描,网友:程序员有女朋友?


输入输出:

用python给女朋友画张素描,网友:程序员有女朋友?


最后,给一点点学习建议,不懂的时候,先弄明白它的功能以及会使用它,让代码先运行起来。等有时间就一个一个细节去攻破它,编程和写文章一样,需要慢慢积累,加油。



如果有疑问想获取源码,可以关注后,在后台私信我,回复:python素描。 我把源码发你。最后,感谢大家的阅读,祝大家工作生活愉快!


分享到:


相關文章: