ImageDraw Module¶
Note
All source images in this document are derived from https://www.pexels.com (CC0 License).
class Draw¶
- doc
https://pillow.readthedocs.io/en/latest/reference/ImageDraw.html, http://effbot.org/imagingbook/imagedraw.htm
Note
The graphics interface uses the same coordinate system as PIL itself, with (0, 0) in the upper left corner.
Warning
Almost all of methods takes xy argument for sequence of points, and official documents says like this:
xy - Sequence of either 2-tuples like
[(x, y), (x, y), ...]
or numeric values like[x, y, x, y, ...]
.
But it seems passing [x, y, x, y, ...]
does not work,
at least PIL 1.1.7 of Pillow 2.9.0 on
CPython 2.7. (Furthermore, note that this is NOT
[[x, y], [x, y], ...]
. Passing [[x, y], [x, y], ...]
causes exception.)
arc, chord, ellipse, pieslice, rectangle¶
import math
from PIL import Image, ImageDraw
#
w, h = 120, 90
bbox = [(10, 10), (w - 10, h - 10)]
# ----------------------------------------------------------
# rectangle
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.rectangle(bbox, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_rectangle_01.jpg")
# ----------------------------------------------------------
# ellipse
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.ellipse(bbox, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_ellipse_01.jpg")
# ----------------------------------------------------------
# arc (end=130)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.arc(bbox, start=20, end=130, fill="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_arc_01.jpg")
# chord (end=130)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.chord(bbox, start=20, end=130, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_chord_01.jpg")
# pieslice (end=130)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.pieslice(bbox, start=20, end=130, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_pieslice_01.jpg")
# ----------------------------------------------------------
# arc (end=300)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.arc(bbox, start=20, end=300, fill="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_arc_02.jpg")
# chord (end=300)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.chord(bbox, start=20, end=300, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_chord_02.jpg")
# pieslice (end=300)
img = Image.new("RGB", (w, h), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.pieslice(bbox, start=20, end=300, fill="#ddddff", outline="blue")
del dctx # destroy drawing context
img.save("result/ImageDraw_pieslice_02.jpg")
|
rectangle
arc(start=20, end=130)
chord(start=20, end=130)
pieslice(start=20, end=130)
|
ellipse
arc(start=20, end=300)
chord(start=20, end=300)
pieslice(start=20, end=300)
|
bitmap¶
from PIL import Image, ImageDraw
from PIL import ImageColor
img = Image.open("data/srcimg07.jpg") # load base image
dctx = ImageDraw.Draw(img) # create drawing context
bmsz = (img.width // 16 - 10, img.height // 16 - 10)
#
# NOTE: ImageColor.colormap is undocumented attribute.
colors = list(ImageColor.colormap.keys())
for y in range(16):
for x in range(16):
bm = Image.new("L", bmsz)
dctx_inner = ImageDraw.Draw(bm)
dctx_inner.ellipse(
[(0, 0), bm.size],
fill=y * 16 + x # (y * 16 + x) varies in range(0, 256)
)
del dctx_inner
pos = [
((bmsz[0] + 10) * x + 10,
(bmsz[1] + 10) * y + 10)]
dctx.bitmap(
pos,
# pixel values of bm is used as mask to fill.
bm,
fill=colors[(y * 16 + x) % len(colors)])
#
del dctx # destroy drawing context
img.save("result/ImageDraw_bitmap_01.png")
|
line, polygon¶
import math
from PIL import Image, ImageDraw
from PIL import ImagePath # for calculating bounding box
#
ptc = 7
xy = [
((math.cos(th) + 1) * 120,
(math.sin(th) + 1) * 90)
for th in [i * (2 * math.pi) / ptc for i in range(ptc)]
] # not closed (i.e., end != start)
bbox = ImagePath.Path(xy).getbbox() # calculate bounding box
size = list(map(int, map(math.ceil, bbox[2:])))
# demo 1 - polygon with outline
img = Image.new("RGB", size, "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.polygon(xy, fill="#eeeeff", outline="blue") # draw polygon with outline
del dctx # destroy drawing context
img.save("result/ImageDraw_polygon_line_01.jpg")
# demo 2 - polygon and line with the same xy
img = Image.new("RGB", size, "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.polygon(xy, fill="#eeeeff") # draw polygon without outline
dctx.line(xy, fill="blue", width=5) # draw line
del dctx # destroy drawing context
img.save("result/ImageDraw_polygon_line_02.jpg")
|
polygon with outline
polygon and line with the same xy
|
point¶
from PIL import Image, ImageDraw
# drawing single point on large canvas is not visible for human's eye,
# so this demo use very small canvas and later resize it.
img = Image.new("RGB", (16, 16), "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.point([(2, 3)], fill="blue") # draw points
dctx.point([(5, 8)], fill="red") # draw points
del dctx # destroy drawing context
img.resize((128, 128)).save("result/ImageDraw_point_01.jpg")
|
draw single point |
import math
from PIL import Image, ImageDraw
from PIL import ImagePath # for calculating bounding box
#
xy = [
(i, (math.sin(i / 64. * math.pi) + 1) * 128)
for i in range(256)
]
bbox = ImagePath.Path(xy).getbbox() # calculate bounding box
size = list(map(int, map(math.ceil, bbox[2:])))
img = Image.new("RGB", size, "#f9f9f9") # create new Image
dctx = ImageDraw.Draw(img) # create drawing context
dctx.point(xy, fill="blue") # draw points
del dctx # destroy drawing context
img.save("result/ImageDraw_point_02.jpg")
|
draw points |
text, textsize¶
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
# get a font
# This example is for Windows (7, etc.).
# If you use Unix-like system, fonts are found at
# for example "/usr/share/fonts".
#fnt = ImageFont.truetype('c:/Windows/Fonts/msmincho.ttc', 30)
fnt = ImageFont.truetype('msmincho.ttc', 30)
img = Image.open("data/srcimg12.jpg") # open base image
dctx = ImageDraw.Draw(img) # create drawing context
# text to draw
txt = u"東京タワーと三縁山増上寺" # Tokyo tower and San'en-zan Zōjō-ji
# calculate text size
txtsz = dctx.textsize(txt, fnt)
# draw text
dctx.text(
# draw text at near (right, top)
(img.width - txtsz[0] - 20, 20),
txt,
font=fnt,
fill="#eeeeff"
)
del dctx # destroy drawing context
img.save("result/ImageDraw_text_01.jpg")
|
|
# -*- coding: utf-8 -*-
# simple banner.
#
from __future__ import unicode_literals
from PIL import Image, ImageDraw, ImageFont
fnt = ImageFont.truetype('CENTURY.TTF', 64)
txt = "Readability counts."
img = Image.new("L", (1, 1)) # temp to calculate size
draw = ImageDraw.Draw(img)
img = img.resize(draw.textsize(txt, fnt)) # re-create
draw = ImageDraw.Draw(img)
draw.text((0, 0), txt, font=fnt, fill=255)
img = img.rotate(-90, expand=True)
# values varies 0 to 255 => 0 or 1 => mapping to char => join
_TAB = [" ", "##"]
_NLT = ["\n", ""]
print("".join([
(_TAB[v > 0] + _NLT[bool((i + 1) % img.width)])
for i, v in enumerate(img.getdata())]))
|
-> result |
# -*- coding: utf-8 -*-
# simple "text to xpm" converter
#
from __future__ import unicode_literals
import sys
from PIL import Image, ImageDraw, ImageFont
if __name__ == '__main__':
fnt = ImageFont.truetype('cour.ttf', 24)
txt = sys.argv[1]
name = sys.argv[1].lower() # FIXME: more safely
img = Image.new("L", (1, 1)) # temp to calculate size
draw = ImageDraw.Draw(img)
img = img.resize(draw.textsize(txt, fnt)) # re-create
draw = ImageDraw.Draw(img)
draw.text((0, 0), txt, font=fnt, fill=255)
# values varies 0 to 255 => 0 to 2 => mapping to char => join
_PAL = [(".", "#ffffff"), ("o", "#7f7f7f"), ("#", "#000000")]
_NLT = ["\n", ""]
palette = "\n".join(['"{} c {}",'.format(ch, cl) for ch, cl in _PAL])
databody = "".join([
(_PAL[int(v / 127)][0] + _NLT[bool((i + 1) % img.width)])
for i, v in enumerate(img.getdata())]).strip().split("\n")
databody = "\n".join(['"{}",'.format(d) for d in databody])
print("""\
/* XPM */
static char * {}_xpm[] = {{
"{:d} {:d} {:d} {:d}",
{}
{}
}};
""".format(name, img.width, img.height, len(_PAL), 1, palette, databody))
|
-> result |
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
# get a font
# This example is for Windows (7, etc.).
# If you use Unix-like system, fonts are found at
# for example "/usr/share/fonts".
#fnt = ImageFont.truetype('c:/Windows/Fonts/msmincho.ttc', 30)
fnt = ImageFont.truetype('msmincho.ttc', 30)
img = Image.open("data/srcimg12.jpg") # open base image
txt = u"東京タワーと三縁山増上寺" # Tokyo tower and San'en-zan Zōjō-ji
tmpdctx = ImageDraw.Draw(img) # create drawing context
txtsz = tmpdctx.textsize(txt, fnt) # calculate text size
del tmpdctx
# create image for blending
osd = Image.new("RGB", (txtsz[0] + 10, txtsz[1] + 10), "skyblue")
dctx = ImageDraw.Draw(osd) # create drawing context
dctx.text((5, 5), txt, font=fnt, fill="black") # draw text to osd
del dctx # destroy drawing context
# blend osd image
img.paste(
osd,
box=(20, 420, osd.size[0] + 20, osd.size[1] + 420),
mask=Image.new("L", osd.size, 192))
img.save("result/ImageDraw_text_03.jpg")
|
multiline_text, multiline_textsize¶
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
# get a font
# This example is for Windows (7, etc.).
# If you use Unix-like system, fonts are found at
# for example "/usr/share/fonts".
#fnt = ImageFont.truetype('c:/Windows/Fonts/msmincho.ttc', 30)
fnt = ImageFont.truetype('msmincho.ttc', 30)
img = Image.open("data/srcimg12.jpg") # open base image
dctx = ImageDraw.Draw(img) # create drawing context
# multiline text to draw
txt = u"""\
東京タワーと三縁山増上寺
(Tokyo tower and San'en-zan Zōjō-ji)"""
# calculate text size
spacing = 2
txtsz = dctx.multiline_textsize(txt, fnt, spacing=spacing)
# draw text
dctx.multiline_text(
# draw text at near (left, bottom)
(20, img.height - txtsz[1] - 20),
txt,
font=fnt,
fill="#eeeeff",
spacing=spacing,
align="center"
)
del dctx # destroy drawing context
img.save("result/ImageDraw_multiline_text_01.jpg")
|
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont
from PIL import ImageFilter
# get a font
fnt = ImageFont.truetype('msmincho.ttc', 300)
img = Image.open("data/srcimg12.jpg") # open base image
# text to draw
txt = u"""東京タワーと
三縁山増上寺""" # Tokyo tower and San'en-zan Zōjō-ji
# calculate text size
dctx = ImageDraw.Draw(img) # create drawing context (of img)
txtsz = dctx.multiline_textsize(txt, fnt)
del dctx
# draw text to mask
mask = Image.new("L", (txtsz[0], txtsz[1]), 64)
dctx = ImageDraw.Draw(mask) # create drawing context (of mask)
dctx.multiline_text(
(0, 0),
txt,
font=fnt,
fill=255 # full opacity
)
del dctx # destroy drawing context
# add some effect
for i in range(10):
mask = mask.filter(ImageFilter.BLUR)
# putmask to base image
img.putalpha(mask.resize(img.size))
img.save("result/ImageDraw_multiline_text_02.png")
|