项目场景:
turtle库画图的保存
项目场景:我们用turtle画的图,有时想保存成png或者jpg格式,该怎么处理呢?
问题描述
使用postscript函数和PIL库所遇到的问题:
在网上很容易搜索到,turtle库中有postscript函数,可以将画图内容保存成eps文件,然后使用PIL中的Image.open()的方法,就可以打开该文件,并且save成想要的格式。但在使用过程中,我遇到了如下问题:
import turtle
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
# 设置参数
turtle.screensize(3000, 3000)
turtle.color("red")
turtle.pensize(1)
turtle.speed(0)
turtle.ht()
# 画正方形,中心为原点
square = 1000
turtle.penup()
turtle.goto(square, square)
turtle.pendown()
turtle.rt(90)
for i in range(4):
turtle.forward(square*2)
turtle.rt(90)
# 保存图片,并修改格式
turtle.getscreen().getcanvas().postscript(file="1.eps")
im = Image.open("1.eps")
im.save("1.jpg")
square参数是正方形边长的一半,当我把square写为100,会得到以下图片:
分辨率704x591,图形看起来正常的,但是设置的画布长宽均为3000,背景不符合要求。
接下来把正方形半边长调整为1000,会得到如下图像:
得到了一张704x593的白图,我的猜想是postscript()函数默认保存的是一定大小的图片,我们需要设置相应的参数来使得画布上的内容全部保存。打开postscript()函数的介绍:
def postscript(self, cnf={}, **kw):
"""Print the contents of the canvas to a postscript
file. Valid options: colormap, colormode, file, fontmap,
height, pageanchor, pageheight, pagewidth, pagex, pagey,
rotate, width, x, y."""
return self.tk.call((self._w, 'postscript') +
self._options(cnf, kw))
我们可以看到有width和height的参数,我修改代码后运行。
turtle.getscreen().getcanvas().postscript(file="1.eps", width=1000, height=1000)
得到的还是一张白图,分辨率为751x752,可知width和height的设置并没有起到想要的结果。
为了解决这个问题,接下来就开始走各种各样的弯路,turtle的setup函数,以及打开eps文件看到的0.7504这串神秘数字,具体过程就不再一一赘述,也是这个过程发现网上这个方面的资料几乎没有,才想着写下这篇博客。
原因分析:
最终还是转到了postscript()这个函数本身:
turtle.getscreen().getcanvas(),这个函数返回的是canvas对象,属于tk库中的内容,找到了tk中canvas.postscrip()的相关文档(我只列了和本文相关的)。
height | Sets the height of the area to print. The default height is the Canvas height. |
width | Sets the width of the Canvas area to be printed. Defaults to the width of the Canvas |
pageheight | Sets the height of the printed page. The Canvas image will be scaled to fit. height is any valid screen distance. |
pagewidth | Sets the width of the printed page. The Canvas image will be scaled to fit. |
pagex | Sets the coordinate for the x positioning point. Can be any valid screen distance. |
pagey | Sets the coordinate for the y positioning point. Can be any valid screen distance. |
经过百度查询后得知postscript是页面描述语言,用于打印机打印文件,我们可以将postscript()函数理解为将画布上的内容打印到一张纸上,想要将内容完整打印,就要设置好画布的大小,即width和height,还要设置好这张纸的大小,即pagewidth和pageheight,turtle画图默认从中心开始画图,打印机默认从左上角开始打印,我们就需要调整坐标系,即调整x和y。
这是了解postscript函数机理后重新写的代码。
import turtle
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
canvas_width = 1000
canvas_height = 1000
turtle.screensize(canvas_width, canvas_height)
turtle.color("red")
turtle.pensize(0)
turtle.speed(0)
turtle.ht()
# 例1***************************************************************************************
# 画正方形
square = 500
turtle.penup()
turtle.goto(square, square)
turtle.pendown()
turtle.rt(90)
for i in range(4):
turtle.forward(square*2+1)
turtle.rt(90)
turtle.getscreen().getcanvas().postscript(colormode="color", file="1.eps", pagewidth=canvas_width, pageheight=canvas_height, width=canvas_width, height=canvas_height, x=-canvas_width/2, y=-(canvas_height/2))
turtle.clear()
im = Image.open("1.eps")
print(im.size)
print(im.getpixel((canvas_width/2-square, canvas_height/2-square+1)))
for i in range(square*2):
im.putpixel((canvas_width//2-square+i+1, canvas_height//2-square+i+1+1), (255, 0, 0))
# for i in range(1001):
# if(im.getpixel((i, 500))!=(255,255,255)):
# print(i)
# break
# print(im.getpixel((200, 501)))
im.save('1.jpg')
# 例2 画圆********************************************************************
turtle.clearscreen()
turtle.color("red")
turtle.pensize(0)
turtle.speed(0)
turtle.ht()
center_x = 390
center_y = 0
circle = 100
turtle.penup()
turtle.goto(center_x, center_y-circle)
turtle.pendown()
turtle.seth(0)
turtle.circle(circle)
turtle.getscreen().getcanvas().postscript(colormode="color", file="2.eps", pagewidth=canvas_width, pageheight=canvas_height, width=canvas_width, height=canvas_height, x=-canvas_width/2, y=-canvas_height/2)
im = Image.open("2.eps")
for i in range(circle*2):
im.putpixel((center_x - circle + i + 1+canvas_width//2, center_y+canvas_height//2), (255, 0, 0))
im.putpixel((center_x+canvas_width//2, center_y - circle + i + 1+canvas_height//2), (255, 0, 0))
im.save('2.jpg')
得到的图片分辨率1001x1001,图形大小和位置均基本符合要求,但整张图长宽多了一个像素。
总结:
关于为什么会多一个像素:
我的猜想是turtle库的画图语法本身是一种比较艺术的语言,例如原点是(0,0),向右移动100个像素turtle.forward(100),然后海龟的位置就变成了(3,0),这样来看海龟本身是不占像素的,但是我们储存位图的时候是按照矩阵的形式储存的,每一个点都是矩阵中的一个元素,这两种描述有冲突的地方,所以turtle库没有提供直接把画布内容转换成位图的函数,而是提供了一种转换成矢量图的方法postscript,我们最后再把矢量图转换成位图,经过了两次转换,就可能在某个环节就会出现问题。
因为我的目的只是能够画出完整且不严重失真的图片,所以就没有细究太多。(细究也没弄懂放弃了)