Merge pull request #8246 from python-pillow/tutorial-images

This commit is contained in:
Hugo van Kemenade 2024-07-24 10:48:25 +03:00 committed by GitHub
commit fddc902c35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 158 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -37,6 +37,9 @@ example, lets display the image we just loaded::
>>> im.show()
.. image:: show_hopper.webp
:align: center
.. note::
The standard version of :py:meth:`~PIL.Image.Image.show` is not very
@ -79,6 +82,9 @@ Convert files to JPEG
except OSError:
print("cannot convert", infile)
.. image:: ../../Tests/images/hopper.jpg
:align: center
A second argument can be supplied to the :py:meth:`~PIL.Image.Image.save`
method which explicitly specifies a file format. If you use a non-standard
extension, you must always specify the format this way:
@ -103,6 +109,9 @@ Create JPEG thumbnails
except OSError:
print("cannot create thumbnail for", infile)
.. image:: thumbnail_hopper.jpg
:align: center
It is important to note that the library doesnt decode or load the raster data
unless it really has to. When you open a file, the file header is read to
determine the file format and extract things like mode, size, and other
@ -140,16 +149,19 @@ Copying a subrectangle from an image
::
box = (100, 100, 400, 400)
box = (0, 0, 64, 64)
region = im.crop(box)
The region is defined by a 4-tuple, where coordinates are (left, upper, right,
lower). The Python Imaging Library uses a coordinate system with (0, 0) in the
upper left corner. Also note that coordinates refer to positions between the
pixels, so the region in the above example is exactly 300x300 pixels.
pixels, so the region in the above example is exactly 64x64 pixels.
The region could now be processed in a certain manner and pasted back.
.. image:: cropped_hopper.webp
:align: center
Processing a subrectangle, and pasting it back
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -164,6 +176,9 @@ modes of the original image and the region do not need to match. If they dont
the region is automatically converted before being pasted (see the section on
:ref:`color-transforms` below for details).
.. image:: pasted_hopper.webp
:align: center
Heres an additional example:
Rolling an image
@ -186,6 +201,9 @@ Rolling an image
return im
.. image:: rolled_hopper.webp
:align: center
Or if you would like to merge two images into a wider image:
Merging images
@ -203,6 +221,9 @@ Merging images
return im
.. image:: merged_hopper.webp
:align: center
For more advanced tricks, the paste method can also take a transparency mask as
an optional argument. In this mask, the value 255 indicates that the pasted
image is opaque in that position (that is, the pasted image should be used as
@ -229,6 +250,9 @@ Note that for a single-band image, :py:meth:`~PIL.Image.Image.split` returns
the image itself. To work with individual color bands, you may want to convert
the image to “RGB” first.
.. image:: rebanded_hopper.webp
:align: center
Geometrical transforms
----------------------
@ -245,6 +269,9 @@ Simple geometry transforms
out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise
.. image:: rotated_hopper_90.webp
:align: center
To rotate the image in 90 degree steps, you can either use the
:py:meth:`~PIL.Image.Image.rotate` method or the
:py:meth:`~PIL.Image.Image.transpose` method. The latter can also be used to
@ -256,11 +283,38 @@ Transposing an image
::
out = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
.. image:: flip_left_right_hopper.webp
:align: center
::
out = im.transpose(Image.Transpose.FLIP_TOP_BOTTOM)
.. image:: flip_top_bottom_hopper.webp
:align: center
::
out = im.transpose(Image.Transpose.ROTATE_90)
.. image:: rotated_hopper_90.webp
:align: center
::
out = im.transpose(Image.Transpose.ROTATE_180)
.. image:: rotated_hopper_180.webp
:align: center
::
out = im.transpose(Image.Transpose.ROTATE_270)
.. image:: rotated_hopper_270.webp
:align: center
``transpose(ROTATE)`` operations can also be performed identically with
:py:meth:`~PIL.Image.Image.rotate` operations, provided the ``expand`` flag is
true, to provide for the same changes to the image's size.
@ -278,7 +332,7 @@ choose to resize relative to a given size.
from PIL import Image, ImageOps
size = (100, 150)
with Image.open("Tests/images/hopper.webp") as im:
with Image.open("hopper.webp") as im:
ImageOps.contain(im, size).save("imageops_contain.webp")
ImageOps.cover(im, size).save("imageops_cover.webp")
ImageOps.fit(im, size).save("imageops_fit.webp")
@ -342,6 +396,9 @@ Applying filters
from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)
.. image:: enhanced_hopper.webp
:align: center
Point Operations
^^^^^^^^^^^^^^^^
@ -355,8 +412,11 @@ Applying point transforms
::
# multiply each pixel by 1.2
out = im.point(lambda i: i * 1.2)
# multiply each pixel by 20
out = im.point(lambda i: i * 20)
.. image:: transformed_hopper.webp
:align: center
Using the above technique, you can quickly apply any simple expression to an
image. You can also combine the :py:meth:`~PIL.Image.Image.point` and
@ -388,6 +448,9 @@ Note the syntax used to create the mask::
imout = im.point(lambda i: expression and 255)
.. image:: masked_hopper.webp
:align: center
Python only evaluates the portion of a logical expression as is necessary to
determine the outcome, and returns the last value examined as the result of the
expression. So if the expression above is false (0), Python does not look at
@ -412,6 +475,10 @@ Enhancing images
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")
.. image:: contrasted_hopper.jpg
:align: center
Image sequences
---------------
@ -444,10 +511,43 @@ Reading sequences
As seen in this example, youll get an :py:exc:`EOFError` exception when the
sequence ends.
Writing sequences
^^^^^^^^^^^^^^^^^
You can create animated GIFs with Pillow, e.g.
::
from PIL import Image
# List of image filenames
image_filenames = [
"hopper.jpg",
"rotated_hopper_270.jpg",
"rotated_hopper_180.jpg",
"rotated_hopper_90.jpg",
]
# Open images and create a list
images = [Image.open(filename) for filename in image_filenames]
# Save the images as an animated GIF
images[0].save(
"animated_hopper.gif",
save_all=True,
append_images=images[1:],
duration=500, # duration of each frame in milliseconds
loop=0, # loop forever
)
.. image:: animated_hopper.gif
:align: center
The following class lets you use the for-statement to loop over the sequence:
Using the ImageSequence Iterator class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using the :py:class:`~PIL.ImageSequence.Iterator` class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
@ -467,25 +567,61 @@ Drawing PostScript
::
from PIL import Image
from PIL import PSDraw
from PIL import Image, PSDraw
import os
with Image.open("hopper.ppm") as im:
title = "hopper"
box = (1 * 72, 2 * 72, 7 * 72, 10 * 72) # in points
# Define the PostScript file
ps_file = open("hopper.ps", "wb")
ps = PSDraw.PSDraw() # default is sys.stdout or sys.stdout.buffer
ps.begin_document(title)
# Create a PSDraw object
ps = PSDraw.PSDraw(ps_file)
# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# Start the document
ps.begin_document()
# draw title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3 * 72, 4 * 72), title)
# Set the text to be drawn
text = "Hopper"
ps.end_document()
# Define the PostScript font
font_name = "Helvetica-Narrow-Bold"
font_size = 36
# Calculate text size (approximation as PSDraw doesn't provide direct method)
# Assuming average character width as 0.6 of the font size
text_width = len(text) * font_size * 0.6
text_height = font_size
# Set the position (top-center)
page_width, page_height = 595, 842 # A4 size in points
text_x = (page_width - text_width) // 2
text_y = page_height - text_height - 50 # Distance from the top of the page
# Load the image
image_path = "hopper.ppm" # Update this with your image path
with Image.open(image_path) as im:
# Resize the image if it's too large
im.thumbnail((page_width - 100, page_height // 2))
# Define the box where the image will be placed
img_x = (page_width - im.width) // 2
img_y = text_y + text_height - 200 # 200 points below the text
# Draw the image (75 dpi)
ps.image((img_x, img_y, img_x + im.width, img_y + im.height), im, 75)
# Draw the text
ps.setfont(font_name, font_size)
ps.text((text_x, text_y), text)
# End the document
ps.end_document()
ps_file.close()
.. image:: hopper_ps.webp
.. note::
PostScript converted to PDF for display purposes
More on reading images
----------------------
@ -553,7 +689,7 @@ Reading from a tar archive
from PIL import Image, TarIO
fp = TarIO.TarIO("Tests/images/hopper.tar", "hopper.jpg")
fp = TarIO.TarIO("hopper.tar", "hopper.jpg")
im = Image.open(fp)
@ -568,7 +704,6 @@ in the current directory can be saved as JPEGs at reduced quality.
import glob
from PIL import Image
def compress_image(source_path, dest_path):
with Image.open(source_path) as img:
if img.mode != "RGB":