Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tutorial images #8246

Merged
merged 12 commits into from
Jul 24, 2024
Binary file added docs/handbook/animated_hopper.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/handbook/contrasted_hopper.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/handbook/cropped_hopper.webp
Binary file not shown.
Binary file added docs/handbook/enhanced_hopper.webp
Binary file not shown.
Binary file added docs/handbook/flip_left_right_hopper.webp
Binary file not shown.
Binary file added docs/handbook/flip_top_bottom_hopper.webp
Binary file not shown.
Binary file added docs/handbook/hopper_ps.webp
Binary file not shown.
Binary file added docs/handbook/masked_hopper.webp
Binary file not shown.
Binary file added docs/handbook/merged_hopper.webp
Binary file not shown.
Binary file added docs/handbook/pasted_hopper.webp
Binary file not shown.
Binary file added docs/handbook/rebanded_hopper.webp
Binary file not shown.
Binary file added docs/handbook/rolled_hopper.webp
Binary file not shown.
Binary file added docs/handbook/rotated_hopper_180.webp
Binary file not shown.
Binary file added docs/handbook/rotated_hopper_270.webp
Binary file not shown.
Binary file added docs/handbook/rotated_hopper_90.webp
Binary file not shown.
Binary file added docs/handbook/show_hopper.webp
Binary file not shown.
Binary file added docs/handbook/thumbnail_hopper.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/handbook/transformed_hopper.webp
Binary file not shown.
181 changes: 158 additions & 23 deletions docs/handbook/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ example, let’s 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
Expand Down Expand Up @@ -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:
Expand All @@ -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 doesn’t 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
Expand Down Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand All @@ -164,6 +176,9 @@ modes of the original image and the region do not need to match. If they don’t
the region is automatically converted before being pasted (see the section on
:ref:`color-transforms` below for details).

.. image:: pasted_hopper.webp
:align: center

Here’s an additional example:

Rolling an image
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
----------------------

Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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")
Expand Down Expand Up @@ -342,6 +396,9 @@ Applying filters
from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)

.. image:: enhanced_hopper.webp
:align: center

Point Operations
^^^^^^^^^^^^^^^^

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
---------------

Expand Down Expand Up @@ -444,10 +511,43 @@ Reading sequences
As seen in this example, you’ll 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

::

Expand All @@ -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")

# Create a PSDraw object
ps = PSDraw.PSDraw(ps_file)

# Start the document
ps.begin_document()

# Set the text to be drawn
text = "Hopper"

# 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

ps = PSDraw.PSDraw() # default is sys.stdout or sys.stdout.buffer
ps.begin_document(title)
# 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

# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# 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))

# draw title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3 * 72, 4 * 72), title)
# 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

ps.end_document()
# 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
----------------------
Expand Down Expand Up @@ -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)


Expand All @@ -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":
Expand Down