连续看了快一个月的spark源码,吃饭脑子里蹦出来的都是rpc私有协议、DAGschedule人、TASKscheduler、调度、
资源申请.....总之一句话就是看的快吐了,该换换脑子再做打算。那怎么换脑子呢,做些啥好玩有意思的事呢;看电影、看小说
觉得有点浪费时间还没意思。灵光一现想到前一阵子和朋友吃饭,聊到程序员怎么样才浪漫。谈到MIT大学生黑了一栋宿舍楼做灯光秀,控制楼层显示”** ilove you“、黑进她前男友加密的QQ空间,看一看然后在乱做评论到此一游然后录制视频图片发给他前男友.....
从技术实现角度讲呢,这两件有趣的事到也不难实现:
1)灯光秀,现在电网控制基本都是单片机数字化的管理控制,安全防护不高只要知道控制板在哪,电脑编个程序替换现有控制板就可以任你控制了;无非就是把整座楼当成事一个LED阵列,控制它显示你要的字符;难度系数和可执行系数都不算大;无奈这事情是不合法的,抓到要里面待两周以上,就此作罢
2)黑进某人网站,然后撒泡尿到此一游难度系数稍微大些,因为QQ毕竟是商业公司安全也做了这么多年,不过人社工程+网络技术要做到也不是完全不可能,然而这个呢也是不合法的啊....
所以另辟蹊径找点有意思的玩玩,对于我这种和平人士,创造性的玩法估计比较适合我,那具体可以做些啥呢。寻思了一阵画个画什么的可好。可艺术细胞稀缺画画这种事不适合程序员啊....唉,别急,我是不会画,我让机器画估计还是可以的嘛。
有了这主意,马上谷歌百度“机器作画”发现没啥有创意的,换了个词程序动画...还是比较严肃没啥有意思的;在继续换换“ python 游戏”有点意思了,有些兄弟用python开发了不少好玩简单游戏,但还是和我想要的有些差距,继续“python 动画”、“python 动漫”慢慢有点意思了。找到一个不错的玩法——给程序一张图片,程序可以按顺序把图片给一笔一画的画出来。听起来还是挺有趣的,总比深度学习风格转化、换脸有意思,你可以看到机器是怎么一笔一画的画画还是可以的嘛。
首先到github找到项目,代码拉下来看了看,分析下作者实现思路:
1、把png、jpg图片矢量化,转化成SVG格式
2、有了SVG格式就知道图画的边界,如果可以有个软件可以按一定速度沿边界移动不就有作画效果了
3、做完边界然后把每个区域颜色填充,这样一幅画就画好了
A.因为图片的颜色会比较多,在这里作者通过opencv对图像先做聚类,用更少颜色去表示更多颜色
通过opencv读入图片:
bitmap = cv2.imread(args.filename)
drawBitmap(bitmap)
通过opencv对图片做颜色聚类&Potrace对图片做矢量图转化:
def drawBitmap(w_image):
print('Reducing the colors...')
Z = w_image.reshape((-1, 3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS, 10, 1.0)
global K
ret, label, center = cv2.kmeans(
Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
print(center)
res = center[label.flatten()]
res = res.reshape(w_image.shape)
no = 1
for i in center:
sys.stdout.write('\rDrawing: %.2f%% [' % (
no / K * 100) + '#' * no + ' ' * (K - no) + ']')
no += 1
res2 = cv2.inRange(res, i, i)
res2 = cv2.bitwise_not(res2)
#print(i)
res2 = res
cv2.imwrite('.tmp.bmp', res2)
os.system('potrace .tmp.bmp -s --flat')
print(i)
drawSVG('.tmp.svg', '#%02x%02x%02x' % (i[2], i[1], i[0]))
#os.remove('.tmp.bmp')
#os.remove('.tmp.svg')
print('\n\rFinished, close the window to exit.')
te.done()
按矢量图轨迹作画,利用了python turtle库:
def drawSVG(filename, w_color):
global first
SVGFile = open(filename, 'r')
SVG = BeautifulSoup(SVGFile.read(), 'lxml')
Height = float(SVG.svg.attrs['height'][0: -2])
Width = float(SVG.svg.attrs['width'][0: -2])
transform(SVG.g.attrs['transform'])
if first:
te.setup(height=Height, width=Width)
te.setworldcoordinates(-Width / 2, 300, Width -
Width / 2, -Height + 300)
first = False
te.tracer(100)
te.pensize(1)
te.speed(Speed)
te.penup()
te.color(w_color)
for i in SVG.find_all('path'):
attr = i.attrs['d'].replace('\n', ' ')
f = readPathAttrD(attr)
lastI = ''
for i in f:
if i == 'M':
te.end_fill()
Moveto(next(f) * scale[0], next(f) * scale[1])
te.begin_fill()
elif i == 'm':
te.end_fill()
Moveto_r(next(f) * scale[0], next(f) * scale[1])
te.begin_fill()
elif i == 'C':
Curveto(next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
lastI = i
elif i == 'c':
Curveto_r(next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
lastI = i
elif i == 'L':
Lineto(next(f) * scale[0], next(f) * scale[1])
elif i == 'l':
Lineto_r(next(f) * scale[0], next(f) * scale[1])
lastI = i
elif lastI == 'C':
Curveto(i * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
elif lastI == 'c':
Curveto_r(i * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
elif lastI == 'L':
Lineto(i * scale[0], next(f) * scale[1])
elif lastI == 'l':
Lineto_r(i * scale[0], next(f) * scale[1])
te.penup()
te.hideturtle()
te.update()
SVGFile.close()
具体代码:
# -*- coding: utf-8 -*-
# Author: tfx2001
# License: GNU GPLv3
# Time: 2018-08-09 18:27
import turtle as te
from bs4 import BeautifulSoup
import argparse
import sys
import numpy as np
import cv2
import os
WriteStep = 15 # 贝塞尔函数的取样次数
Speed = 1000
Width = 600 # 界面宽度
Height = 600 # 界面高度
Xh = 0 # 记录前一个贝塞尔函数的手柄
Yh = 0
scale = (1, 1)
first = True
K = 32
def Bezier(p1, p2, t): # 一阶贝塞尔函数
return p1 * (1 - t) + p2 * t
def Bezier_2(x1, y1, x2, y2, x3, y3): # 二阶贝塞尔函数
te.goto(x1, y1)
te.pendown()
for t in range(0, WriteStep + 1):
x = Bezier(Bezier(x1, x2, t / WriteStep),
Bezier(x2, x3, t / WriteStep), t / WriteStep)
y = Bezier(Bezier(y1, y2, t / WriteStep),
Bezier(y2, y3, t / WriteStep), t / WriteStep)
te.goto(x, y)
te.penup()
def Bezier_3(x1, y1, x2, y2, x3, y3, x4, y4): # 三阶贝塞尔函数
x1 = -Width / 2 + x1
y1 = Height / 2 - y1
x2 = -Width / 2 + x2
y2 = Height / 2 - y2
x3 = -Width / 2 + x3
y3 = Height / 2 - y3
x4 = -Width / 2 + x4
y4 = Height / 2 - y4 # 坐标变换
te.goto(x1, y1)
te.pendown()
for t in range(0, WriteStep + 1):
x = Bezier(Bezier(Bezier(x1, x2, t / WriteStep), Bezier(x2, x3, t / WriteStep), t / WriteStep),
Bezier(Bezier(x2, x3, t / WriteStep), Bezier(x3, x4, t / WriteStep), t / WriteStep), t / WriteStep)
y = Bezier(Bezier(Bezier(y1, y2, t / WriteStep), Bezier(y2, y3, t / WriteStep), t / WriteStep),
Bezier(Bezier(y2, y3, t / WriteStep), Bezier(y3, y4, t / WriteStep), t / WriteStep), t / WriteStep)
te.goto(x, y)
te.penup()
def Moveto(x, y): # 移动到svg坐标下(x,y)
te.penup()
te.goto(-Width / 2 + x, Height / 2 - y)
te.pendown()
def Moveto_r(dx, dy):
te.penup()
te.goto(te.xcor() + dx, te.ycor() - dy)
te.pendown()
def line(x1, y1, x2, y2): # 连接svg坐标下两点
te.penup()
te.goto(-Width / 2 + x1, Height / 2 - y1)
te.pendown()
te.goto(-Width / 2 + x2, Height / 2 - y2)
te.penup()
def Lineto_r(dx, dy): # 连接当前点和相对坐标(dx,dy)的点
te.pendown()
te.goto(te.xcor() + dx, te.ycor() - dy)
te.penup()
def Lineto(x, y): # 连接当前点和svg坐标下(x,y)
te.pendown()
te.goto(-Width / 2 + x, Height / 2 - y)
te.penup()
def Curveto(x1, y1, x2, y2, x, y): # 三阶贝塞尔曲线到(x,y)
te.penup()
X_now = te.xcor() + Width / 2
Y_now = Height / 2 - te.ycor()
Bezier_3(X_now, Y_now, x1, y1, x2, y2, x, y)
global Xh
global Yh
Xh = x - x2
Yh = y - y2
def Curveto_r(x1, y1, x2, y2, x, y): # 三阶贝塞尔曲线到相对坐标(x,y)
te.penup()
X_now = te.xcor() + Width / 2
Y_now = Height / 2 - te.ycor()
Bezier_3(X_now, Y_now, X_now + x1, Y_now + y1,
X_now + x2, Y_now + y2, X_now + x, Y_now + y)
global Xh
global Yh
Xh = x - x2
Yh = y - y2
def transform(w_attr):
funcs = w_attr.split(' ')
for func in funcs:
func_name = func[0: func.find('(')]
if func_name == 'scale':
global scale
scale = (float(func[func.find('(') + 1: -1].split(',')[0]),
-float(func[func.find('(') + 1: -1].split(',')[1]))
def readPathAttrD(w_attr):
ulist = w_attr.split(' ')
for i in ulist:
# print("now cmd:", i)
if i.isdigit() or i.isalpha():
yield float(i)
elif i[0].isalpha():
yield i[0]
yield float(i[1:])
elif i[-1].isalpha():
yield float(i[0: -1])
elif i[0] == '-':
yield float(i)
def drawSVG(filename, w_color):
global first
SVGFile = open(filename, 'r')
SVG = BeautifulSoup(SVGFile.read(), 'lxml')
Height = float(SVG.svg.attrs['height'][0: -2])
Width = float(SVG.svg.attrs['width'][0: -2])
transform(SVG.g.attrs['transform'])
if first:
te.setup(height=Height, width=Width)
te.setworldcoordinates(-Width / 2, 300, Width -
Width / 2, -Height + 300)
first = False
te.tracer(100)
te.pensize(1)
te.speed(Speed)
te.penup()
te.color(w_color)
for i in SVG.find_all('path'):
attr = i.attrs['d'].replace('\n', ' ')
f = readPathAttrD(attr)
lastI = ''
for i in f:
if i == 'M':
te.end_fill()
Moveto(next(f) * scale[0], next(f) * scale[1])
te.begin_fill()
elif i == 'm':
te.end_fill()
Moveto_r(next(f) * scale[0], next(f) * scale[1])
te.begin_fill()
elif i == 'C':
Curveto(next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
lastI = i
elif i == 'c':
Curveto_r(next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
lastI = i
elif i == 'L':
Lineto(next(f) * scale[0], next(f) * scale[1])
elif i == 'l':
Lineto_r(next(f) * scale[0], next(f) * scale[1])
lastI = i
elif lastI == 'C':
Curveto(i * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
elif lastI == 'c':
Curveto_r(i * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1],
next(f) * scale[0], next(f) * scale[1])
elif lastI == 'L':
Lineto(i * scale[0], next(f) * scale[1])
elif lastI == 'l':
Lineto_r(i * scale[0], next(f) * scale[1])
te.penup()
te.hideturtle()
te.update()
SVGFile.close()
def drawBitmap(w_image):
print('Reducing the colors...')
Z = w_image.reshape((-1, 3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS, 10, 1.0)
global K
ret, label, center = cv2.kmeans(
Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
print(center)
res = center[label.flatten()]
res = res.reshape(w_image.shape)
no = 1
for i in center:
sys.stdout.write('\rDrawing: %.2f%% [' % (
no / K * 100) + '#' * no + ' ' * (K - no) + ']')
no += 1
res2 = cv2.inRange(res, i, i)
res2 = cv2.bitwise_not(res2)
#print(i)
res2 = res
cv2.imwrite('.tmp.bmp', res2)
#转成矢量图,保存成.svg格式
os.system('potrace .tmp.bmp -s --flat')
print(i)
drawSVG('.tmp.svg', '#%02x%02x%02x' % (i[2], i[1], i[0]))
#os.remove('.tmp.bmp')
#os.remove('.tmp.svg')
print('\n\rFinished, close the window to exit.')
te.done()
if __name__ == '__main__':
paser = argparse.ArgumentParser(
description="Convert an bitmap to SVG and use turtle libray to draw it.")
paser.add_argument('filename', type=str,
help='The file(*.jpg, *.png, *.bmp) name of the file you want to convert.')
paser.add_argument(
"-c", "--color", help="How many colors you want to draw.(If the number is too large that the program may be very slow.)", type=int, default=32)
args = paser.parse_args()
K = args.color
try:
bitmapFile = open(args.filename, mode='r')
except FileNotFoundError:
print(__file__ + ': error: The file is not exists.')
quit()
if os.path.splitext(args.filename)[1].lower() not in ['.jpg', '.bmp', '.png']:
print(__file__ + ': error: The file is not a bitmap file.')
quit()
bitmap = cv2.imread(args.filename)
#if bitmap.shape[0] > 240:
# bitmap = cv2.resize(bitmap,(500,200))
# bitmap = cv2.resize(bitmap, (int(bitmap.shape[1] * (
# (80 - 50) / bitmap.shape[0])), 80 - 50))
#print(bitmap.shape[1])
drawBitmap(bitmap)
环境搭建:
1、git clone https://github.com/tfx2001/python-turtle-draw-svg.git
2、安装potrace,参考:https://pypi.org/project/pypotrace/
3、利用requirement.txt安装依赖库:
4、linux或者mac系统把下载的git项目中main.py删除,用上面👆代码新建main.py
5、测试执行:python main.py -c=32 60960684_p0.png,有用虚拟环境的需切换到虚拟环境后执行前面命令