Image Module - class Image¶
Note
All source images in this document are derived from https://www.pexels.com (CC0 License).
Methods¶
convert¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.convert, http://effbot.org/imagingbook/image.htm#tag-Image.Image.convert
convert(mode)
from PIL import Image
img = Image.open('data/srcimg03.jpg')
img.convert("L").save(
"result/im_convert_m_L_01.jpg")
img.convert("1").save(
"result/im_convert_m_1_01.jpg")
|
im_convert_m_L_01.jpg |
im_convert_m_1_01.jpg |
from PIL import Image
# create RGBA image
img = Image.open('data/srcimg03.jpg')
r, g, b = img.split()
a = Image.new("L", r.size, color=127)
withalpha = Image.merge("RGBA", (r, b, g, a))
withalpha.save("result/im_convert_m_in_01.png")
# convert from RGBA to RGB
withalpha.convert("RGB").save(
"result/im_convert_m_RGB_01.jpg")
|
im_convert_m_in_01.png |
im_convert_m_RGB_01.jpg |
convert(“P”, **options)
from PIL import Image
img = Image.open('data/srcimg04.jpg')
img = img.convert(
"P",
dither=Image.FLOYDSTEINBERG, # default
palette=Image.WEB # default
#, colors=216 # standard 216-color "web palette"
)
img.save(
"result/im_convert_P_01.png")
|
|
from PIL import Image
img = Image.open('data/srcimg04.jpg')
img = img.convert(
"P",
dither=Image.NONE, # disable dithering
palette=Image.WEB # default
#, colors=216 # standard 216-color "web palette"
)
img.save(
"result/im_convert_P_02.png")
|
|
from PIL import Image
img = Image.open('data/srcimg04.jpg')
# When palette is ADAPTIVE, the parameter dither makes no sense (ignored).
img = img.convert(
"P",
palette=Image.ADAPTIVE, # an optimized palette
colors=32
)
img.save(
"result/im_convert_P_03.png")
|
convert(mode, matrix)
from PIL import Image
img = Image.open('data/srcimg03.jpg')
# rgb2xyz
mat = (
0.412453, 0.357580, 0.180423, 0,
0.212671, 0.715160, 0.072169, 0,
0.019334, 0.119193, 0.950227, 0 )
#
img.convert("RGB", mat).save(
"result/im_convert_mm_01.jpg")
|
copy¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.copy, http://effbot.org/imagingbook/image.htm#tag-Image.Image.copy
>>> from PIL import Image
>>>
>>> srcimg = Image.new("L", (2, 2), 128)
>>> cloned = srcimg.copy()
>>> cloned.putdata((0, 64, 128, 192))
>>>
>>> srcimg.tobytes("raw") # not changed
b'\x80\x80\x80\x80'
>>> cloned.tobytes("raw")
b'\x00@\x80\xc0'
crop¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.crop, http://effbot.org/imagingbook/image.htm#tag-Image.Image.crop
from PIL import Image
img = Image.open('data/srcimg11.png')
bbox = (
img.size[0] // 4, # left
img.size[1] // 4, # top
img.size[0] // 4 * 3, # right
img.size[1] // 4 * 3 # bottom
)
img.crop(bbox).save("result/im_crop_01.jpg")
|
draft¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.draft, http://effbot.org/imagingbook/image.htm#tag-Image.Image.draft
The official documents says: “For example, you can use this method to convert a color JPEG to greyscale while loading it”:
>>> from PIL import Image
>>>
>>> img = Image.open("data/srcimg01.jpg")
>>> img
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=670x445 at 0x144A048>
>>> img.decoderconfig, img.mode, img.size, img.tile
((), 'RGB', (670, 445), [('jpeg', (0, 0, 670, 445), 0, ('RGB', ''))])
>>> img.draft("L", (img.width // 4, img.height // 4))
<PIL.JpegImagePlugin.JpegImageFile image mode=L size=168x112 at 0x144A048>
>>> img.decoderconfig, img.mode, img.size, img.tile
((4, 0), 'L', (168, 112), [('jpeg', (0, 0, 168, 112), 0, ('L', ''))])
>>> img.show()
but normally you should not depend on this method very much, because:
This method is not implemented for most images. It is currently implemented only for JPEG and PCD images.
To convert mode, simply use convert(mode), and to resize image, simply use resize.
filter¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.filter, http://effbot.org/imagingbook/image.htm#tag-Image.Image.filter
See ImageFilter Module.
getbands¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getbands, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getbands
>>> from glob import iglob
>>> from PIL import Image
>>>
>>> for fn in iglob("data/*.[jp][pn]g"):
... img = Image.open(fn)
... print("{}: {}".format(fn, img.getbands()))
...
data/mask_circle_01.jpg: ('L',)
- snip -
data/srcimg08.jpg: ('R', 'G', 'B')
data/srcimg09.png: ('R', 'G', 'B', 'A')
data/srcimg10.png: ('R', 'G', 'B')
- snip -
getbbox¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getbbox, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getbbox
>>> from glob import iglob
>>> from PIL import Image
>>>
>>> for fn in iglob("data/*.[jp][pn]g"):
... img = Image.open(fn)
... print("{}: {}".format(fn, img.getbbox()))
...
data/mask_circle_01.jpg: (0, 0, 256, 256)
- snip -
data/srcimg16.jpg: (0, 0, 675, 500)
data/srcimg17.jpg: (0, 0, 967, 750)
getcolors, getpalette¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getcolors, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getcolors, https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getpalette
getcolors and getpalette with “P” mode:
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg')
>>> img = img.convert("P", palette=Image.ADAPTIVE, colors=16)
>>> #
>>> img.width * img.height
280350
>>> # pixel values are represented by indexes:
>>> data = list(img.getdata())
>>> len(data) # == img.width * img.height, that is, these are not [(r, g, b), ...]
280350
>>> data[50:70]
[7, 7, 7, 7, 7, 7, 7, 7, 13, 7, 7, 7, 7, 7, 7, 13, 13, 13, 7, 7]
>>> # getcolors returns unsorted list of (count, color) tuples.
>>> # If the maxcolors value is exceeded, the method stops counting and returns None.
>>> clrs = img.getcolors()
>>> len(clrs)
16
>>> clrs.sort(key=lambda x: x[1]) # sort by color
>>>
>>> # show up colors and palette
>>> p_nzero = img.getpalette()[:len(clrs) * 3]
>>> for v in [(clrs[i // 3], (p_nzero[i], p_nzero[i + 1], p_nzero[i + 2]))
... for i in range(0, len(p_nzero), 3)]:
... print(v)
...
((24270, 0), (227, 230, 229))
((8860, 1), (212, 210, 208))
((24091, 2), (190, 201, 214))
((25088, 3), (167, 188, 206))
((15363, 4), (199, 157, 148))
((6508, 5), (143, 163, 172))
((4955, 6), (118, 150, 170))
((27667, 7), (88, 140, 199))
((20889, 8), (224, 101, 150))
((23184, 9), (136, 102, 92))
((16386, 10), (188, 50, 112))
((16948, 11), (133, 34, 70))
((17653, 12), (82, 106, 93))
((11588, 13), (44, 99, 157))
((20856, 14), (51, 63, 49))
((16044, 15), (25, 21, 13))
getcolors and getpalette with “L” mode:
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg').convert("L")
>>> # getcolors returns unsorted list of (count, color) tuples.
>>> # If the maxcolors value is exceeded, the method stops counting and returns None.
>>> clrs = img.getcolors()
>>> len(clrs)
256
>>> print("\n".join(map(str, clrs)))
(826, 0)
(537, 1)
(450, 2)
- snip -
(106, 253)
(62, 254)
(1, 255)
>>> img.getpalette() is None
True
getdata¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getdata, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getdata
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg')
>>> grn = img.getdata(band=1) # get green
>>> upth, lwth = 0, 0
>>> for c in grn: # grn is a sequence-like object.
... if c < 128:
... lwth += 1
... else:
... upth += 1
...
>>> print("upper={}, lower={}".format(upth, lwth))
upper=144495, lower=135855
>>> # pixel data to numpy array
>>> import numpy as np
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg')
>>> red = np.array(img.getdata(band=0)) # get red
>>> grn = np.array(img.getdata(band=1)) # get green
>>> blu = np.array(img.getdata(band=2)) # get blur
>>>
>>> red
array([ 14, 25, 28, ..., 96, 118, 124])
>>> red[red < 50] = 0
>>> red
array([ 0, 0, 0, ..., 96, 118, 124])
>>> # pixel data to numpy array
>>> import numpy as np
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg')
>>> rawpb1 = np.array(img.getdata(), dtype=np.uint8).reshape((img.height, img.width, 3))
>>> rawpb1.shape
(450, 623, 3)
>>> rawpb1
array([[[ 14, 74, 160],
[ 25, 85, 171],
[ 28, 90, 175],
...,
[ 6, 52, 130],
[ 10, 53, 131],
[ 7, 49, 125]],
--- snip ---
[[151, 67, 82],
[102, 32, 43],
[146, 95, 100],
...,
[ 96, 92, 55],
[118, 112, 78],
[124, 120, 85]]], dtype=uint8)
>>>
>>> # you can use numpy.asarray:
>>> rawpb2 = np.asarray(img)
>>> rawpb2.shape
(450, 623, 3)
>>> rawpb2
array([[[ 14, 74, 160],
[ 25, 85, 171],
[ 28, 90, 175],
...,
[ 6, 52, 130],
[ 10, 53, 131],
[ 7, 49, 125]],
--- snip ---
[[151, 67, 82],
[102, 32, 43],
[146, 95, 100],
...,
[ 96, 92, 55],
[118, 112, 78],
[124, 120, 85]]], dtype=uint8)
>>>
>>> # identical?
>>> np.all(rawpb1 == rawpb2)
True
>>> # pixel data to numpy array
>>> import numpy as np
>>> from PIL import Image
>>> img = Image.open('data/srcimg06.jpg').convert("L") # as grayscale
>>> gray = np.array(img.getdata())
>>>
>>> gray
array([ 65, 76, 81, ..., 88, 109, 117])
>>> gray[gray < 70] = 0
>>> gray
array([ 0, 76, 81, ..., 88, 109, 117])
See also: putdata.
getextrema¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getextrema, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getextrema
>>> from PIL import Image
>>> img = Image.open('data/mask_grad_01.jpg') # L (i.e., single band)
>>> img.getextrema()
(0, 255)
>>> img = Image.open('data/srcimg02.jpg') # RBG (i.e., multiple bands)
>>> img.getextrema()
((0, 255), (0, 236), (0, 245))
>>> img = Image.open('data/srcimg04.jpg') # RBG
>>> img.getextrema()
((3, 255), (0, 255), (0, 255))
>>> img = Image.open('data/srcimg06.jpg') # RBG
>>> img.getextrema()
((0, 255), (0, 255), (0, 255))
getpixel¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.getpixel, http://effbot.org/imagingbook/image.htm#tag-Image.Image.getpixel
>>> from PIL import Image
>>> img1 = Image.open('data/mask_grad_01.jpg') # L
>>> img1.getpixel((10, 20))
20
>>> img2 = Image.open('data/srcimg06.jpg') # RBG
>>> img2.getpixel((10, 20))
(127, 144, 198)
histogram¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.histogram, http://effbot.org/imagingbook/image.htm#tag-Image.Image.histogram
>>> from PIL import Image
>>> fn = 'data/srcimg03.jpg'
>>> simg = Image.open(fn)
>>> r, g, b = simg.split()
>>> len(r.histogram())
256
>>> r.histogram()
[2402, 236, 214, 258, 234, ...(snip)..., 16366]
To visualize this list, for example:
#
# PIL histogram to graph with matplotlib (https://matplotlib.org)
#
from PIL import Image
import matplotlib.pyplot as plt
#
fn = 'data/srcimg03.jpg'
simg = Image.open(fn)
r, g, b = simg.split()
bins = list(range(256))
plt.plot(bins, r.histogram(), 'r')
plt.plot(bins, g.histogram(), 'g')
plt.plot(bins, b.histogram(), 'b')
plt.xlabel('Pixel value')
plt.ylabel('Frequency')
plt.title(fn)
plt.grid(True)
plt.savefig("result/im_histogram_01.jpg")
|
offset¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.offset, http://effbot.org/imagingbook/image.htm#tag-Image.Image.offset
This method that have been marked as deprecated for many releases have been removed at Pillow 3.0.0 (actually is still defined but raise NotImplementedError). Use ImageChops.offset() instead.
paste¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.paste, http://effbot.org/imagingbook/image.htm#tag-Image.Image.paste
from PIL import Image
img1 = Image.open('data/srcimg12.jpg')
img2 = Image.open('data/srcimg13.jpg')
img2 = img2.resize((img2.size[0] // 4, img2.size[1] // 4))
# paste at (0, 0)
res = img1.copy()
res.paste(img2, box=(0, 0, img2.size[0], img2.size[1]))
res.save("result/im_paste_01.jpg")
# paste at (50, 50)
res = img1.copy()
res.paste(img2, box=(50, 50, img2.size[0] + 50, img2.size[1] + 50))
res.save("result/im_paste_02.jpg")
# paste at (50, 50) with mask
res = img1.copy()
mask = Image.new("L", img2.size, 128)
res.paste(
img2, box=(50, 50, img2.size[0] + 50, img2.size[1] + 50), mask=mask)
res.save("result/im_paste_03.jpg")
# paste at (50, 50) with mask (circle)
res = img1.copy()
mask = Image.open('data/mask_circle_01.jpg')
res.paste(
img2, box=(50, 50, img2.size[0] + 50, img2.size[1] + 50),
mask=mask.resize(img2.size))
res.save("result/im_paste_04.jpg")
(0, 0) |
(50, 50) |
(50, 50) with mask |
(50, 50) with mask (circle) |
point¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.point, http://effbot.org/imagingbook/image.htm#tag-Image.Image.point
from itertools import chain
from PIL import Image
#
simg = Image.open('data/srcimg04.jpg')
# --- lookup table by function
#
dimg = simg.point(lambda i: i if i < 220 else 255)
dimg.save("result/im_point_01.jpg")
# --- lookup table by list
#
# all bands share same lookup table
lut = [i if i < 220 else 255
for i in range(256)] * len(simg.getbands())
dimg = simg.point(lut)
dimg.save("result/im_point_02.jpg")
# each band has individual lookup table
lut = list(chain.from_iterable((
(i if i < 220 else 255
for i in range(256)), # R
(tuple(range(256))), # G
(tuple(range(256))), # B
)))
dimg = simg.point(lut)
dimg.save("result/im_point_03.jpg")
|
putalpha¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.putalpha, http://effbot.org/imagingbook/image.htm#tag-Image.Image.putalpha
from PIL import Image
img = Image.open('data/srcimg12.jpg')
img.putalpha(127) # with color value
img.save(
"result/im_putalpha_01.png")
|
import numpy as np
from PIL import Image
img = Image.open('data/srcimg12.jpg')
r = np.array(img.getdata(0))
g = np.array(img.getdata(1))
b = np.array(img.getdata(2))
a = np.ones(r.shape) * 255
a[np.logical_and(r < 50, r < 50, g < 50)] = 0
alpha = Image.new("L", img.size)
alpha.putdata(a.flatten())
# alpha cab be an "L" or "1" image having the same size as this image
img.putalpha(alpha)
img.save(
"result/im_putalpha_02.png")
|
putdata¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.putdata, http://effbot.org/imagingbook/image.htm#tag-Image.Image.putdata
# from pure python list data
from PIL import Image
img = Image.new("L", (64, 64)) # single band
graddata = list(range(0, 256, 4)) * 64
img.putdata(graddata)
img.save(
"result/im_putdata_01.jpg")
|
|
# from pure python list data
from PIL import Image
img = Image.new("RGB", (64, 64)) # multiple bands
gr_r = [0] * 64 * 64
gr_g = list(range(0, 256, 4)) * 64
gr_b = list(range(0, 256, 4)) * 64
img.putdata(list(zip(gr_r, gr_g, gr_b)))
img.save(
"result/im_putdata_02.jpg")
|
|
# from numpy array
import numpy as np
from PIL import Image
img = Image.new("L", (64, 64)) # single band
graddata = np.ndarray((64, 64))
for i in range(64):
graddata[:, i] = i * 4
img.putdata(graddata.flatten())
img.save(
"result/im_putdata_03.jpg")
|
|
# from numpy array
import numpy as np
from PIL import Image
img = Image.new("RGB", (64, 64)) # multiple bands
graddata = np.zeros((3, 64, 64), dtype=np.int32)
for i in range(64):
graddata[1:, :, i] = i * 4
img.putdata(
list(
zip(graddata[0].flatten(),
graddata[1].flatten(),
graddata[2].flatten())))
img.save(
"result/im_putdata_04.jpg")
|
>>> # from bytes
>>> from PIL import Image
>>>
>>> rawpixelbytes = b'\xa0\xfe\xfe\xa0'
>>>
>>> # with frombytes
>>> img1 = Image.frombytes("L", (2, 2), rawpixelbytes)
>>>
>>> # putdata after new
>>> img2 = Image.new("L", (2, 2))
>>> img2.putdata(rawpixelbytes)
>>>
>>> # identical?
>>> img1.tobytes("raw"), img2.tobytes("raw"), img1.tobytes("raw") == img2.tobytes("raw")
(b'\xa0\xfe\xfe\xa0', b'\xa0\xfe\xfe\xa0', True)
>>>
>>> # putdata after new with scale=0.5
>>> hex(0xa0 // 2), chr(0xa0 // 2), hex(0xfe // 2)
('0x50', 'P', '0x7f')
>>> img3 = Image.new("L", (2, 2))
>>> img3.putdata(rawpixelbytes, scale=1/2.)
>>> img3.tobytes("raw")
b'P\x7f\x7fP'
>>>
>>> # putdata after new with offset=-0x10
>>> hex(0xa0 - 0x10), hex(0xfe - 0x10)
('0x90', '0xee')
>>> img4 = Image.new("L", (2, 2))
>>> img4.putdata(rawpixelbytes, offset=-0x10)
>>> img4.tobytes("raw")
b'\x90\xee\xee\x90'
See also: frombytes, frombuffer, fromarray.
putpalette, remap_palette¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.putpalette, https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.remap_palette
Note
See also: getcolors, getpalette.
remap_palette will change both pixel value and palette (in other words, indexes as pixel values will follow new palette table):
>>> import os
>>> from PIL import Image
>>>
>>> img = Image.open('data/srcimg06.jpg')
>>> img = img.convert("P", palette=Image.ADAPTIVE, colors=16)
>>> img.save('result/p_adaptive16_orig.png')
>>> os.stat('result/p_adaptive16_orig.png').st_size
76348
>>> # pixel values are represented by indexes:
>>> data = list(img.getdata())
>>> data[50:70]
[7, 7, 7, 7, 7, 7, 7, 7, 13, 7, 7, 7, 7, 7, 7, 13, 13, 13, 7, 7]
>>> clrs = img.getcolors()
>>> len(clrs)
16
>>>
>>> # show up colors and palette
>>> srcpalette = img.getpalette()
>>> p_nzero = srcpalette[:len(clrs) * 3]
>>> clrs.sort(key=lambda x: x[1]) # sort by color, for showing
>>> for v in [(clrs[i // 3], (p_nzero[i], p_nzero[i + 1], p_nzero[i + 2]))
... for i in range(0, len(p_nzero), 3)]:
... print(v)
...
((24270, 0), (227, 230, 229))
((8860, 1), (212, 210, 208))
((24091, 2), (190, 201, 214))
((25088, 3), (167, 188, 206))
((15363, 4), (199, 157, 148))
((6508, 5), (143, 163, 172))
((4955, 6), (118, 150, 170))
((27667, 7), (88, 140, 199))
((20889, 8), (224, 101, 150))
((23184, 9), (136, 102, 92))
((16386, 10), (188, 50, 112))
((16948, 11), (133, 34, 70))
((17653, 12), (82, 106, 93))
((11588, 13), (44, 99, 157))
((20856, 14), (51, 63, 49))
((16044, 15), (25, 21, 13))
>>> # build new palette ordered by occurrence, and create new image with it
>>> clrs.sort(key=lambda x: -x[0]) # sort by occurrence
>>> img = img.remap_palette([c for oc, c in clrs])
>>> img.save('result/p_adaptive16_remapped.png')
>>> os.stat('result/p_adaptive16_remapped.png').st_size
72098
>>>
>>> # --- show up again for new image
>>> data = list(img.getdata())
>>> data[50:70]
[0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0]
>>> clrs = img.getcolors()
>>> p_nzero = img.getpalette()[:len(clrs) * 3]
>>> clrs.sort(key=lambda x: x[1]) # sort by color, for showing
>>> for v in [(clrs[i // 3], (p_nzero[i], p_nzero[i + 1], p_nzero[i + 2]))
... for i in range(0, len(p_nzero), 3)]:
... print(v)
...
((27667, 0), (88, 140, 199))
((25088, 1), (167, 188, 206))
((24270, 2), (227, 230, 229))
((24091, 3), (190, 201, 214))
((23184, 4), (136, 102, 92))
((20889, 5), (224, 101, 150))
((20856, 6), (51, 63, 49))
((17653, 7), (82, 106, 93))
((16948, 8), (133, 34, 70))
((16386, 9), (188, 50, 112))
((16044, 10), (25, 21, 13))
((15363, 11), (199, 157, 148))
((11588, 12), (44, 99, 157))
((8860, 13), (212, 210, 208))
((6508, 14), (143, 163, 172))
((4955, 15), (118, 150, 170))
original
|
convert(“P”)
|
remap_palette
|
Meanwhile, putpalette replace only the colors of the palette:
>>> from PIL import Image
>>>
>>> img = Image.open('data/srcimg06.jpg')
>>> img = img.convert("P", palette=Image.ADAPTIVE, colors=16)
>>> data = list(img.getdata())
>>> data[50:70]
[7, 7, 7, 7, 7, 7, 7, 7, 13, 7, 7, 7, 7, 7, 7, 13, 13, 13, 7, 7]
>>> clrs = img.getcolors()
>>> len(clrs)
16
>>>
>>> # sort the colors of the palette ordered by occurrence
>>> # (Of course, this is nonsence. Just an example.)
>>> srcpalette = img.getpalette()
>>> newpalette = []
>>> for oc, c in sorted(clrs, key=lambda x: -x[0]):
... newpalette.extend([srcpalette[c * 3], srcpalette[c * 3 + 1], srcpalette[c * 3 + 2]])
...
>>> newpalette.extend((768 - len(newpalette)) * [0])
>>>
>>> # result image is no longer the same photo.
>>> img.putpalette(newpalette)
>>> data = list(img.getdata())
>>> data[50:70] # not changed
[7, 7, 7, 7, 7, 7, 7, 7, 13, 7, 7, 7, 7, 7, 7, 13, 13, 13, 7, 7]
>>> img.save('result/p_adaptive16_newpalette.png')
original
|
convert(“P”)
|
putpalette
|
putpixel¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.putpixel, http://effbot.org/imagingbook/image.htm#tag-Image.Image.putpixel
Note
This method is relatively slow. For more extensive changes, use paste() or the ImageDraw module instead. And also, if you want to put data as a whole image, see putdata().
For single band:
>>> from PIL import Image
>>>
>>> img = Image.new("L", (4, 4)) # single band
>>> for y in range(4):
... for x in range(4):
... img.putpixel((x, y), y * 64)
...
>>> list(img.getdata())
[0, 0, 0, 0, 64, 64, 64, 64, 128, 128, 128, 128, 192, 192, 192, 192]
You can also use PixelAccess (but fairly slow, too):
>>> from PIL import Image
>>>
>>> img = Image.new("L", (4, 4)) # single band
>>> px = img.load()
>>> for y in range(4):
... for x in range(4):
... px[(x, y)] = y * 64
...
>>> list(img.getdata())
[0, 0, 0, 0, 64, 64, 64, 64, 128, 128, 128, 128, 192, 192, 192, 192]
For multiple bands:
>>> from PIL import Image
>>>
>>> img = Image.new("RGB", (4, 4)) # multiple bands
>>> for y in range(4):
... for x in range(4):
... img.putpixel(
... (x, y),
... ((y + 1) * 64, y * 64, 255 - (y * 64)))
...
>>> print("\n".join(list(map(str, img.getdata()))))
(64, 0, 255)
(64, 0, 255)
(64, 0, 255)
(64, 0, 255)
(128, 64, 191)
(128, 64, 191)
(128, 64, 191)
(128, 64, 191)
(192, 128, 127)
(192, 128, 127)
(192, 128, 127)
(192, 128, 127)
(255, 192, 63)
(255, 192, 63)
(255, 192, 63)
(255, 192, 63)
You can also use PixelAccess (but fairly slow, too):
>>> from PIL import Image
>>>
>>> img = Image.new("RGB", (4, 4)) # multiple bands
>>> px = img.load()
>>> for y in range(4):
... for x in range(4):
... px[(x, y)] = ((y + 1) * 64, y * 64, 255 - (y * 64))
...
>>> print("\n".join(list(map(str, img.getdata()))))
(64, 0, 255)
(64, 0, 255)
(64, 0, 255)
(64, 0, 255)
(128, 64, 191)
(128, 64, 191)
(128, 64, 191)
(128, 64, 191)
(192, 128, 127)
(192, 128, 127)
(192, 128, 127)
(192, 128, 127)
(255, 192, 63)
(255, 192, 63)
(255, 192, 63)
(255, 192, 63)
quantize¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.quantize, http://effbot.org/imagingbook/image.htm#tag-Image.Image.quantize
Note
This method of original PIL is deprecated because this is identical to convert(“P”, **options), but in the case of Pillow, these are not the same each other. Pillow version is for color quantization, as its name claims so that. (Strictly speaking, convert(“P”, **options) will also do color quantization, so the differences are those scope. For example, you can’t select quantizer method if you are using convert(“P”, **options).)
Note
It seems that the argument method can be:
method=0: median cut
method=1: maximum coverage
method=2: octree
method=3: libpngquant
, but official documents (which is the same as the docstring of python source code) and C source code comment doesn’t say so. I can’t say for sure.
The argument kmeans seems that it is for k-means clustering, and I can’t say for sure about this, too. kmeans is likely used only by mode=0(median cut), 1(maximum coverage), and it seems k-means logic is applied after processing “median cut” or “maximum coverage” while changes <= (kmeans - 1). Sorry, I can’t understand at all about these. At least, zero kmeans value is no effect, larger kmeans increase the times of call of k-means logic. And I can’t sure these spec is frozen or not.
>>> from PIL import Image
>>>
>>> img = Image.open('data/srcimg06.jpg')
>>> dimg = img.quantize(
... colors=32,
... method=0, # median cut
... kmeans=5)
>>> len(dimg.getcolors())
32
>>> dimg.save("result/im_quantize_32_0_5.png")
>>> dimg = img.quantize(
... colors=32,
... method=1, # maximum coverage
... kmeans=5)
>>> len(dimg.getcolors())
32
>>> dimg.save("result/im_quantize_32_1_5.png")
>>> dimg = img.quantize(
... colors=32,
... method=2) # octree
>>> len(dimg.getcolors())
32
>>> dimg.save("result/im_quantize_32_2.png")
original
|
32, 0, 5
|
32, 1, 5
|
32, 2
|
resize¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.resize, http://effbot.org/imagingbook/image.htm#tag-Image.Image.resize
About resample, see Filters.
from PIL import Image
img = Image.open('data/srcimg13.jpg')
# * "resize" returns new image.
# * "resize" streches image without considering aspect ratio.
img = img.resize((img.width // 4, img.height * 2))
rotate¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.rotate, http://effbot.org/imagingbook/image.htm#tag-Image.Image.rotate
About resample, see Filters.
from PIL import Image
#
simg = Image.open('data/srcimg14.jpg')
# angle, resample, expand
# resample can be one of Image.NEAREST (default),
# Image.BILINEAR, Image.BICUBIC
dimg = simg.rotate(-10)
dimg.save("result/im_rotate_r-10.jpg")
#
dimg = simg.rotate(-10, expand=True)
dimg.save("result/im_rotate_r-10_expand.jpg")
rotate=-10 => result size=(912, 684) |
rotate=-10, expand=True => result size=(1017, 833) |
# collaborate with putalpha
from PIL import Image
#
simg = Image.open('data/srcimg14.jpg')
dimg = simg.rotate(-10)
mask = Image.new("L", simg.size, 255)
mask = mask.rotate(-10)
dimg.putalpha(mask)
dimg.save("result/im_rotate_r-10m.png")
#
dimg = simg.rotate(-10, expand=True)
mask = Image.new("L", simg.size, 255)
mask = mask.rotate(-10, expand=True)
dimg.putalpha(mask)
dimg.save("result/im_rotate_r-10m_expand.png")
rotate=-10 => result size=(912, 684) |
rotate=-10, expand=True => result size=(1017, 833) |
save¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.save, http://effbot.org/imagingbook/image.htm#tag-Image.Image.save
Saves this image under the given filename:
from PIL import Image
img = Image.new("RGB", (16, 16))
img.save("out.jpg")
If the filename extension is not a standard, you can pass format argument explicitly:
from PIL import Image
img = Image.new("RGB", (16, 16))
img.save("out.thumbnail", "JPEG")
Possible formats are described in Image file formats. Each format writer sometimes takes additional keyword arguments:
from PIL import Image
img = Image.new("RGB", (16, 16))
img.save("out.thumbnail", "JPEG", quality=75)
Those specifications are also described in Image file formats.
You can use a file object instead of a filename:
from PIL import Image
img = Image.new("RGB", (16, 16))
with open("out.thumbnail", "wb") as fo:
img.save(fo, "JPEG", quality=75)
The file object must implement the seek, tell, and write methods, and be opened in binary mode. In other words, you can also use file-like object like io.BytesIO
:
from io import BytesIO
import base64
from PIL import Image
img = Image.new("L", (256, 256))
img.putdata([i // 256 for i in range(256*256)])
# You can use a file object instead of a filename.
dst = BytesIO()
# In this case, you must always specify the format
img.save(dst, "JPEG")
# In case of using BytesIO, you can get the written data
# by getvalue() method:
b64ed = base64.standard_b64encode(dst.getvalue())
encoded_image = b"data:image/jpeg;base64," + b64ed
with open("result/encoded_image_01.html", "wb") as fo:
fo.write(b'<html><body><img src="')
fo.write(encoded_image)
fo.write(b'"/></body>\n')
|
seek, tell¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.seek, https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.tell, http://effbot.org/imagingbook/image.htm#tag-Image.Image.seek, http://effbot.org/imagingbook/image.htm#tag-Image.Image.tell
ImageChops_blend_02.gif
: see blend.
>>> from PIL import Image
>>> # ImageChops_blend_02.gif has 33 frames.
>>> img = Image.open("result/ImageChops_blend_02.gif")
>>> img.tell()
0
>>> img.seek(img.tell() + 1)
>>> img.tell()
1
>>> img.show()
>>> # convert RGB before save as jpeg to avoid "IOError: cannot write mode P as JPEG"
>>> img.convert("RGB").save("frame1.jpg")
>>> for i in range(31): img.seek(img.tell() + 1)
...
>>> img.seek(img.tell() + 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\Python27\lib\site-packages\PIL\GifImagePlugin.py", line 132, in seek
raise EOFError("no more images in GIF file")
EOFError: no more images in GIF file
See also: ImageSequence.
show¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.show, http://effbot.org/imagingbook/image.htm#tag-Image.Image.show
Displays this image. This method is mainly intended for debugging purposes.
>>> from PIL import Image, ImageDraw, ImageChops
>>>
>>> dia = 128
>>> circle = Image.new("L", (dia * 4, dia * 4), 0)
>>> dctx = ImageDraw.Draw(circle)
>>> dctx.ellipse([dia, dia, dia * 3, dia * 3], fill=255)
>>> del dctx
>>>
>>> offset = dia // 2
>>> r = ImageChops.offset(circle, offset, offset)
>>> #r.show()
>>> g = ImageChops.offset(circle, -offset, offset)
>>> #g.show()
>>> b = ImageChops.offset(circle, 0, -offset)
>>> #b.show()
>>>
>>> dimg = Image.merge("RGB", (r, g, b))
>>> #dimg.show()
>>> mask = Image.eval(dimg.convert("L"), lambda p: 255 if p > 0 else 0)
>>> #mask.show()
>>> dimg.putalpha(mask)
>>> dimg.show()
If you use Windows, and you encounter the trouble like:
Windows Photo Viewer can’t open this picture because either the picture is deleted, or it’s in a location that isn’t available.
or in Japanese edition:
この画像が削除されたか、利用出来ない場所にあるため、Windows フォトビューワーでこの画像を開けません。
, you can replace the viewer as you like with inserting the following codes in your debuggee code:
from PIL import ImageShow
class _WindowsViewer_NoDelTemp(ImageShow.Viewer):
format = "BMP"
def get_command(self, file, **options):
# defined as the following in original WindowsViewer
# (of PIL version '1.1.7', Pillow version = '4.1.1'):
#
# return ('start "Pillow" /WAIT "%s" '
# '&& ping -n 2 127.0.0.1 >NUL '
# '&& del /f "%s"' % (file, file))
return ("""start "Pillow" /WAIT "%s" """ % (file, ))
# you have to cleanup temporary files manually...
ImageShow.register(_WindowsViewer_NoDelTemp, order=-1)
|
from PIL import ImageShow
class _WindowsViewer2(ImageShow.Viewer):
format = "BMP"
def get_command(self, file, **options):
return ('start "Pillow" /WAIT "%s" '
'&& ping -n 5 127.0.0.1 >NUL ' # "-n 2" -> "-n 5"
'&& del /f "%s"' % (file, file))
ImageShow.register(_WindowsViewer2, order=-1)
|
from PIL import ImageShow
class _WindowsViewer3(ImageShow.Viewer):
format = "BMP"
def get_command(self, file, **options):
import os, subprocess, atexit, win32process
atexit.register(
subprocess.Popen,
["python",
"-c",
"import time, os ; time.sleep(5); os.remove('%s')" % \
file.replace(os.sep, "/")],
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | \
win32process.DETACHED_PROCESS,
close_fds=True)
return ("""start "Pillow" /WAIT "%s" """ % (file, ))
ImageShow.register(_WindowsViewer3, order=-1)
|
split¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.split, http://effbot.org/imagingbook/image.htm#tag-Image.Image.split
from PIL import Image
#
img = Image.open('data/srcimg24.jpg')
red, grn, blu = img.split()
red.save("result/im_split_01_rL.jpg")
grn.save("result/im_split_01_gL.jpg")
blu.save("result/im_split_01_bL.jpg")
blk = Image.new("L", red.size, "black")
Image.merge("RGB", (red, blk, blk)).save(
"result/im_split_01_r.jpg")
Image.merge("RGB", (blk, grn, blk)).save(
"result/im_split_01_g.jpg")
Image.merge("RGB", (blk, blk, blu)).save(
"result/im_split_01_b.jpg")
im_split_01_rL.jpg |
im_split_01_gL.jpg |
im_split_01_bL.jpg |
im_split_01_r.jpg |
im_split_01_g.jpg |
im_split_01_b.jpg |
thumbnail¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.thumbnail, http://effbot.org/imagingbook/image.htm#tag-Image.Image.thumbnail
About resample, see Filters.
# -*- coding: utf-8 -*-
#
# very simple script for creating thumbnails.
#
import argparse
import os
from glob import iglob
from PIL import Image
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input_dir", default=".")
parser.add_argument("-d", "--dest_dir", default=".")
parser.add_argument("-s", "--size", type=int, default=128)
parser.add_argument(
"-e", "--extentension",
choices=["thumbnail", "jpg", "jpeg"],
default="jpg")
args = parser.parse_args()
size = (args.size, args.size)
for pat in ("*.jpg", "*.jpeg", "*.png"):
for fpath in iglob(os.path.join(args.input_dir, pat)):
fn = os.path.basename(fpath)
bn, ext = os.path.splitext(fn)
#
img = Image.open(fpath)
# NOTE:
# * Aspect ratio is preserved.
# * This function modifies Image object in place.
# * The parameter 'resample' differs between PIL
# (or pillow) versions.
img.thumbnail(size)
#
if not os.path.exists(args.dest_dir):
os.mkdir(args.dest_dir)
img.save(
os.path.join(
args.dest_dir,
"{}.{}".format(bn, args.extentension)),
"JPEG")
tobitmap¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.tobitmap, http://effbot.org/imagingbook/image.htm#tag-Image.Image.tobitmap
>>> from io import BytesIO
>>> from PIL import Image
>>>
>>> img = Image.frombytes("L", (16, 16), b'''\
... \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\
... \xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\
... \xff\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\xff\
... \xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\
... \xff\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\xff\x00\x00\x00\x00\xff\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\x00\xff\x00\x00\x00\x00\xff\x00\x00\x00\x00\xff\
... \xff\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\xff\
... \xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\
... \xff\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\xff\
... \xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\
... \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff''')
>>>
>>> xbm = img.convert("1").tobitmap("fubar") # "xbm" format (X11 bitmap)
>>> print(xbm.decode('ascii'))
#define fubar_width 16
#define fubar_height 16
static char fubar_bits[] = {
0xff,0xff,0x03,0xc0,0x05,0xa0,0x09,0x90,0x11,0x88,0x21,0x84,0x41,0x82,0x81,
0x81,0x81,0x81,0x41,0x82,0x21,0x84,0x11,0x88,0x09,0x90,0x05,0xa0,0x03,0xc0,
0xff,0xff
};
>>>
>>> # xbm can be read by XbmImagePlugin
>>> dimg = Image.open(BytesIO(xbm))
>>> _TAB = [" ", "O"]
>>> _NLT = ["\n", ""]
>>> print("".join([
... (_TAB[v > 0] + _NLT[bool((i + 1) % dimg.width)])
... for i, v in enumerate(dimg.getdata())]))
OOOOOOOOOOOOOOOO
OO OO
O O O O
O O O O
O O O O
O O O O
O O O O
O OO O
O OO O
O O O O
O O O O
O O O O
O O O O
O O O O
OO OO
OOOOOOOOOOOOOOOO
tobytes¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.tobytes, http://effbot.org/imagingbook/image.htm#tag-Image.Image.tobytes
Note
Basically, you should regard this method as just for use by plugin authors, or just for debugging, or testing purpose. This method just returns the raw image data from the internal storage. See save, or getdata, they might be what you want.
>>> from PIL import Image
>>>
>>> img = Image.frombytes("L", (2, 2), b'\xff\x00\xff\x00')
>>> mode = "L"
>>> img = img.convert(mode)
>>>
>>> img.tobytes("hex", mode)
b'ff00ff00'
>>> img.tobytes("eps", mode)
b'ff00ff00'
>>> img.tobytes("xbm", mode)
b'0x01,0x01\n'
>>>
>>> img.tobytes("raw", mode)
b'\xff\x00\xff\x00'
>>>
>>> img.tobytes("gif", mode)
b'\x07\x00\xff\x01\xf8\x07 '
>>> img.tobytes("jpeg", mode)
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f\x14\x1d\x1a\x1f\x1e\x1d\x1a\x1c\x1c $.\' ",#\x1c\x1c(7),01444\x1f\'9=82<.342\xff\xc0\x00\x0b\x08\x00\x02\x00\x02\x01\x01\x11\x00\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05\x04\x04\x00\x00\x01}\x01\x02\x03\x00\x04\x11\x05\x12!1A\x06\x13Qa\x07"q\x142\x81\x91\xa1\x08#B\xb1\xc1\x15R\xd1\xf0$3br\x82\t\n\x16\x17\x18\x19\x1a%&\'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x08\x01\x01\x00\x00?\x00\xf1\xff\x00\x1d\xff\x00\xc9C\xf1/\xfd\x85n\xbf\xf4kW\xff\xd9'
>>> img.tobytes("pcx", mode)
b'\xc1\xff\x00\xc1\xff\x00'
>>> img.tobytes("zip", mode)
b'x\x9cc\xf8\xcf\xc0\xc4\xc0\x00\x00\x05\x07\x01\x02'
tostring¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.tostring, http://effbot.org/imagingbook/image.htm#tag-Image.Image.tostring
This method that have been marked as deprecated for many releases have been removed at Pillow 3.0.0 (actually is still defined but raise NotImplementedError). Use tobytes() instead. (But also note that original PIL has no tobytes
but has only tostring
.)
transform¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.transform, http://effbot.org/imagingbook/image.htm#tag-Image.Image.transform
About resample, see Filters.
transform(size, EXTENT, data)¶
from PIL import Image
simg = Image.open('data/srcimg16.jpg')
#
x0, y0 = 10, 0
x1, y1 = 10 + simg.width // 4, simg.height // 3
dimg = simg.transform(
simg.size, # expand to original size
method=Image.EXTENT,
data=[x0, y0, x1, y1])
dimg.save(
"result/im_transform_EXTENT_01.jpg")
#
x0, y0 = 10, 0
x1, y1 = 10 + simg.width // 4, simg.height // 3
dimg = simg.transform(
(x1 - x0, y1 - y0), # to cropped size
method=Image.EXTENT,
data=[x0, y0, x1, y1])
dimg.save(
"result/im_transform_EXTENT_02.jpg")
|
transform(size, AFFINE, data)¶
import math
from PIL import Image
simg = Image.open('data/srcimg15.jpg')
#
# shear
#
cx, cy = 0.1, 0
dimg = simg.transform(
simg.size,
method=Image.AFFINE,
data=[1, cx, 0,
cy, 1, 0,])
dimg.save(
"result/im_transform_AFFINE__shear1.jpg")
#
cx, cy = 0, 0.1
dimg = simg.transform(
simg.size,
method=Image.AFFINE,
data=[1, cx, 0,
cy, 1, 0,])
dimg.save(
"result/im_transform_AFFINE__shear2.jpg")
#
# rotate
#
theta = math.radians(-5)
dimg = simg.transform(
simg.size,
method=Image.AFFINE,
data=[math.cos(theta), math.sin(theta), 0,
-math.sin(theta), math.cos(theta), 0,])
dimg.save(
"result/im_transform_AFFINE__rotate.jpg")
|
transform(size, QUAD, data)¶
# quadrilateral warp. data specifies the four corners
# given as NW, SW, SE, and NE.
from itertools import chain
from PIL import Image
simg = Image.open('data/srcimg17.jpg')
_nw = (0, 190)
_sw = (140, simg.height - 150)
_se = (simg.width - 160, simg.height - 70)
_ne = (simg.width, 0)
dimg = simg.transform(
(simg.width, simg.height),
method=Image.QUAD,
data=list(chain.from_iterable((_nw, _sw, _se, _ne))))
dimg.save(
"result/im_transform_QUAD_01.jpg")
|
transform(size, MESH, data)¶
from PIL import Image
simg = Image.open('data/srcimg17.jpg')
(w, h) = simg.size
dimg = simg.transform(
simg.size,
method=Image.MESH,
# data is a list of target rectangles
# and corresponding source quadrilaterals.
data=[(
# target rectangle (1)
(0, 0, w // 2, h // 2),
# corresponding source quadrilateral (1)
# (NW, SW, SE, and NE. see method=QUAD)
(0, 0, 0, h, w, h, w - 100, 0)
),
(
# target rectangle (2)
(w // 2, h // 2, w, h),
# corresponding source quadrilateral (2)
# (NW, SW, SE, and NE. see method=QUAD)
(0, 0, 0, h, w, h, w - 100, 0)
),
]
)
dimg.save(
"result/im_transform_MESH_01.jpg")
|
transform(size, PERSPECTIVE, data)¶
Data is a 8-tuple (a, b, c, d, e, f, g, h) which contains the coefficients for a perspective transform.
But how do we get these coefficients? Don’t worry, because above equation can be solved if we know four relationships between \(\begin{pmatrix} x' \\ y' \end{pmatrix}\) and \(\begin{pmatrix} x \\ y \end{pmatrix}\):
That is:
import numpy as np
from numpy import linalg
from PIL import Image
def _create_coeff(
xyA1, xyA2, xyA3, xyA4,
xyB1, xyB2, xyB3, xyB4):
A = np.array([
[xyA1[0], xyA1[1], 1, 0, 0, 0, -xyB1[0] * xyA1[0], -xyB1[0] * xyA1[1]],
[0, 0, 0, xyA1[0], xyA1[1], 1, -xyB1[1] * xyA1[0], -xyB1[1] * xyA1[1]],
[xyA2[0], xyA2[1], 1, 0, 0, 0, -xyB2[0] * xyA2[0], -xyB2[0] * xyA2[1]],
[0, 0, 0, xyA2[0], xyA2[1], 1, -xyB2[1] * xyA2[0], -xyB2[1] * xyA2[1]],
[xyA3[0], xyA3[1], 1, 0, 0, 0, -xyB3[0] * xyA3[0], -xyB3[0] * xyA3[1]],
[0, 0, 0, xyA3[0], xyA3[1], 1, -xyB3[1] * xyA3[0], -xyB3[1] * xyA3[1]],
[xyA4[0], xyA4[1], 1, 0, 0, 0, -xyB4[0] * xyA4[0], -xyB4[0] * xyA4[1]],
[0, 0, 0, xyA4[0], xyA4[1], 1, -xyB4[1] * xyA4[0], -xyB4[1] * xyA4[1]],
], dtype=np.float32)
B = np.array([
xyB1[0],
xyB1[1],
xyB2[0],
xyB2[1],
xyB3[0],
xyB3[1],
xyB4[0],
xyB4[1],
], dtype=np.float32)
return linalg.solve(A, B)
#
simg = Image.open('data/srcimg17.jpg')
coeff = _create_coeff(
(0, 0),
(simg.width, 0),
(simg.width, simg.height),
(0, simg.height),
# =>
(-500, 0),
(simg.width + 500, 0),
(simg.width, simg.height),
(0, simg.height),
)
dimg = simg.transform(
(simg.width, simg.height),
method=Image.PERSPECTIVE,
data=coeff)
dimg.save(
"result/im_transform_PERSPECTIVE_01.jpg")
|
transpose¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.transpose, http://effbot.org/imagingbook/image.htm#tag-Image.Image.transpose
from PIL import Image
#
img = Image.open('data/srcimg14.jpg')
img.transpose(Image.FLIP_LEFT_RIGHT).save(
"result/im_transpose_FLIP_LEFT_RIGHT.jpg")
img.transpose(Image.FLIP_TOP_BOTTOM).save(
"result/im_transpose_FLIP_TOP_BOTTOM.jpg")
img.transpose(Image.ROTATE_90).save(
"result/im_transpose_ROTATE_90.jpg")
img.transpose(Image.ROTATE_180).save(
"result/im_transpose_ROTATE_180.jpg")
img.transpose(Image.ROTATE_270).save(
"result/im_transpose_ROTATE_270.jpg")
img.transpose(Image.TRANSPOSE).save(
"result/im_transpose_TRANSPOSE.jpg")
FLIP_LEFT_RIGHT |
FLIP_TOP_BOTTOM |
ROTATE_90 |
ROTATE_180 |
ROTATE_270 |
TRANSPOSE |
verify¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.verify, http://effbot.org/imagingbook/image.htm#tag-Image.Image.verify
>>> from io import BytesIO
>>> from PIL import Image
>>>
>>> simg = Image.open("data/srcimg09.png")
>>> dimg = BytesIO()
>>> simg.save(dimg, "PNG")
>>>
>>> # make dimg be malformed
>>> dimgrawbytes = dimg.getvalue()
>>> dimg = BytesIO(dimgrawbytes[:len(dimgrawbytes) // 2])
>>> # open brokened
>>> brokenimg = Image.open(dimg) # => success
>>> # call verify
>>> brokenimg.verify()
Traceback (most recent call last):
File "c:\Python35\lib\site-packages\PIL\PngImagePlugin.py", line 144, in crc
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
File "c:\Python35\lib\site-packages\PIL\_binary.py", line 70, in i16be
return unpack(">H", c[o:o+2])[0]
struct.error: unpack requires a bytes object of length 2
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "c:\Python35\lib\site-packages\PIL\PngImagePlugin.py", line 570, in verify
self.png.verify()
File "c:\Python35\lib\site-packages\PIL\PngImagePlugin.py", line 172, in verify
self.crc(cid, ImageFile._safe_read(self.fp, length))
File "c:\Python35\lib\site-packages\PIL\PngImagePlugin.py", line 150, in crc
% cid)
File "<string>", line None
SyntaxError: broken PNG file (incomplete checksum in b'IDAT')
Note
If you need to load the image after using this method, you must reopen the image file.
Attributes¶
- doc
https://pillow.readthedocs.io/en/latest/reference/Image.html#attributes, http://effbot.org/imagingbook/image.htm#attributes
>>> from PIL import Image
>>> img1 = Image.open("data/srcimg01.jpg")
>>> img1.format, img1.mode, img1.size
('JPEG', 'RGB', (670, 445))
>>> img1.width, img1.height # == img1.size
(670, 445)
>>> img1.info
{'jfif_version': (1, 1), 'jfif': 257, 'jfif_unit': 0, 'jfif_density': (1, 1)}
>>> img2 = Image.open("data/srcimg09.png")
>>> img2.format, img2.mode, img2.size
('PNG', 'RGBA', (540, 432))
>>> img2.width, img2.height # == img2.size
(540, 432)
>>> img2.info
{}
>>> img2.palette
>>> img2.convert("P", palette=Image.WEB).palette
<PIL.ImagePalette.ImagePalette object at 0x0000000002F430F0>