[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky] [Facebook]
[Build Your Own Python Action Arcade!]

[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: Paste an image onto another image with PIL in Python

[A background image with copies of another image pasted on top with PIL in Python]

This example pastes a smiley face image onto a background showing Himeji Castle in Japan. Click and drag to select the rectangle where the image should be pasted.

If the Maintain Aspect box is checked, the program makes the pasted image as large as possible while fitting within the selected rectangle and without distortion. If the box is not checked, the program makes the smiley face fill the target area.

The key is the paste_image function

paste_image

Here's the paste_image function.

def paste_image(bg_image, overlay_image, x1, y1, x2, y2, maintain_aspect=True): '''Paste the overlay image onto the background image.''' # If we should preserve the aspect ratio, adjust the target area as needed. wid = x2 - x1 hgt = y2 - y1 if maintain_aspect: # Resize the target area to have the correct aspect ratio. image_aspect = overlay_image.width / overlay_image.height target_aspect = wid / hgt if target_aspect > image_aspect: # The target area is too wide and short. Make it skinnier. wid = int(image_aspect * hgt) else: # The target area is too tall and thin. Make it shorter. hgt = int(wid / image_aspect) # Center the new target area over the original target area. x1 = int((x1 + x2) / 2 - wid / 2) y1 = int((y1 + y2) / 2 - wid / 2) # Resize the overlay image to fit the target area. overlay_image = overlay_image.resize((wid, hgt), Image.LANCZOS) # Paste the overlay image onto the background image. bg_image.paste(overlay_image, (x1, y1))

This code first subtracts the minimum and maximum X and Y coordinates to get the width and height of the target area.

Next, if the maintain_aspect parameter is checked, the program modifies the wid and hgt values accordingly. To do that, it compares the aspect ratio (width-to-height ratio) of the overlay image and the target rectangle. If the target rectangle is too wide and short, the code recalculates the width to make the rectangle thinner. If the target rectangle is too tall and skinny, the code recalculates the height to make the rectangle shorter. The result is a rectangle that has the same aspect ratio as the overlay image but that will fit within the rectangle's original size.

The code then calculates the coordinates where the overlay image must be drawn to center it on the target rectangle. For example, the X coordinate of the overlay image's upper left corner is the rectangle's X midpoint (x1 + x2) / 2 minus half of the overlay image's width. You could change this behavior if you like. For example, you could leave x1 and y1 unchanged if you want to put the overlay image in the rectangle's upper left corner. To make the function more flexible, you could add a parameter giving the desired alignment for the image.

Having calculated an appropriate width and height (or not, if the maintain_aspect parameter was false), the function calls the overlay image's resize method to make the image have that width and height. It finishes by using the background image's paste method to paste the overlay image onto the background.

Mouse Events

When you press the mouse down over the program's canvas, the following event handler executes.

def mouse_down(self, event): # Save the starting point. self.start_point = (event.x, event.y)

This code saves the mouse's location to use as the rectangle's starting corner.

When you move the mouse while the left button is pressed, the following event handler springs into action.

def mouse_move(self, event): # If there is a current selection rectangle, remove it. if self.selection_rectangle is not None: self.canvas.delete(self.selection_rectangle) # Create a selection rectangle. self.end_point = (event.x, event.y) thickness = 3 dash_pattern = (4, 4) color = 'yellow' self.selection_rectangle = self.canvas.create_rectangle( self.start_point, self.end_point, outline=color, width=thickness, dash=dash_pattern)

First, if there is already a selection rectangle, the program removes it. It then save the mouse's current position in self.end_point and draws a selection rectangle with opposite corners at the start and end points.

Finally, when you release the left mouse button the following code executes.

def mouse_up(self, event): '''We're done selecting the tagret area.''' # Paste the smiley onto the selected area. self.paste_smiley(self.start_point, self.end_point, self.aspect_var.get()) # We are no longer creating a selection rectangle. if self.selection_rectangle is not None: self.canvas.delete(self.selection_rectangle) self.start_point = None self.end_point = None self.selection_rectangle = None

This code calls the paste_smiley method described next to draw the smiley face image on the selected rectangle. It deletes the selection rectangle and resets the various mouse variables to None.

paste_smiley

The following paste_smiley method draws the smiley face in the selected rectangle.

def paste_smiley(self, start_point, end_point, maintain_aspect): '''Paste the smiley face at this location.''' # Get the target area coordinates. x1 = min(start_point[0], end_point[0]) x2 = max(start_point[0], end_point[0]) y1 = min(start_point[1], end_point[1]) y2 = max(start_point[1], end_point[1]) rect = (x1, y1, x2, y2) # Paste the image. paste_image(self.bg_image, self.smiley_image, *rect, self.aspect_var.get()) # Draw the selection rectangle. self.dr.rectangle(rect, outline='blue') # Display the result. self.canvas.delete(self.selection_rectangle) self.bg_photo_image = ImageTk.PhotoImage(self.bg_image) self.canvas.create_image(0, 0, image=self.bg_photo_image, anchor='nw')

This code finds the minimum and maximum X and Y coordinates of the selection rectangle's start and end points. It then calls the paste_image function described at the beginning of this post to add the smiley image to the background image.

Next, the code draws the selection rectangle so you can see how the smiley fits inside it. (The variable self.dr is an ImageDraw.Draw object that lets the program draw on the background image.)

At this point, the background image holds the original Himeji Castle picture plus any smiley faces and selection rectangles that have been drawn, but they aren't visible on the program's Canvas widget. To show the current image, the code deletes all objects from the Canvas, converts the background image into a PhotoImage, and draws the image onto the Canvas. [These smiley faces do not preserve the original image's aspect ratio]

Conclusion

The key to the program is the paste_image function, which draws an overlay image (smiley) on top of a background image (Himeji Castle). If desired, that function resizes the overlay image to preserve its aspect ratio and centers the result. The picture at the top of the post shows results with aspect ratio preserved. The picture on the right shows the result when you don't preserve aspect ratios.

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.