PIL的image.paste操作降低图像/绘制文本质量
我正在Windows 7系统上开发,使用的是Python 2.7.3和PIL 1.1.7。
我想写一个Python脚本,生成一组带有文字的图片。因为我需要把文字换行并放进一个任意大小的框里,所以我写了一个方法,把文字画到一个白色的RGBA背景图上,并开启了透明度层。为了简化问题,我写了一个小的Python脚本来展示这个问题:
import Image,ImageDraw,ImageFont
import webbrowser
# sample text and font
text = "The text quality will degrade with the paste operation."
verdana_font = ImageFont.truetype("verdana.ttf", 20)
# get the line size
text_width, text_height = verdana_font.getsize(text)
# create a blank canvas with extra space between lines
blank_canvas = Image.new('RGB', (text_width + 10, text_height * 10 + 5 * 10), (255, 255, 255))
# create a blank RGBA canvas for the drawn text
text_canvas = Image.new('RGBA', (text_width, text_height), (255, 255, 255, 0))
draw = ImageDraw.Draw(text_canvas)
# draw the text onto the text canvas
draw.text((0,0), text, font = verdana_font, fill = "#000000")
# print 10 lines
for x in range(0,10):
# calculate the coordinates for the paste operation and debug
coordinates = (5, 5 + (x * (5 + text_height)))
print "x = %d | coordinates = %r" % (x, coordinates)
# paste the text onto the blank canvas
blank_canvas.paste(text_canvas, coordinates, text_canvas)
# create a temporary canvas
temp_canvas = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0))
# paste the text canvas onto the temp canvas using the png alpha layer for transparency
temp_canvas.paste(text_canvas, (0,0), text_canvas)
# swap the canvases
text_canvas = temp_canvas
# save the blank canvas to a file
blank_canvas.save("paste-degradation.png", "PNG")
# open the image up to see the paste operation's image degradation
webbrowser.open("paste-degradation.png")
每次把文字粘贴到一个新的“临时”画布上时,画出的文字质量就会越来越差。上面的代码生成的图片看起来是这样的:
我的代码有问题吗?还是说PIL本身有bug?
1 个回答
这个问题是关于draw.text()这个函数的行为有点出乎意料。
draw.text()这个函数绘制文本时,会把一些像素设置为fill
(这些像素肯定是在文本内部),而不去碰其他像素(那些肯定在外面的)。但是,如果一个像素被发现有25%是在文本里面,那么draw.text()就会把这个像素设置为25%的fill
和75%的原始值。这里的比例是独立应用于RGBA的四个部分。在这个例子中,你的背景是(255, 255, 255, 0),而fill
是(0, 0, 0, 255):所以那个25%在文本里的像素会变成(192, 192, 192, 64)。
不过我觉得这个结果并不是很直观。直观的结果应该是(0, 0, 0, 64)。如果你把这样的文本粘贴到一张完全红色的图片上,那么上面的像素仍然会贡献25%的浅灰色(192, 192, 192)。换句话说,你会看到灰色的边缘,而你本来只希望看到黑色、红色和它们之间的颜色。
(其实上面的解释有点过于简化:把背景设置为(0, 0, 0, 0)并没有帮助。我怀疑这是因为这个颜色实际上等同于(255, 255, 255, 0),也就是完全透明。此外,似乎在canvas.paste(img, (0,0), img)
调用中也使用了同样的算法。)
解决这个问题的一种方法是只使用从文本绘制的图像的透明度部分,也就是说,把temp_canvas.paste(text_canvas, (0,0), text_canvas)
替换为temp_canvas.paste("#000000", (0,0), text_canvas)
。