Skip to content

How to Add Frames to Images in Python (via PIL Library)

Image collage created with PIL

Introduction

You might need to add borders or frames to your images for presentations or other decorative reasons. Frames are a good way to improve photographic presentation and they bring more focus to the image being presented. Photo frames or borders can even be useful in science publications or consulting reports.

In this tutorial, we will learn how to achieve photo / image frames using Python very quickly and intuitively. We will also provide multiple methods so you can choose the most suitable code for your image frame application.

We are going to be using Image, ImageDraw and ImageOps modules from the PIL library mainly so let’s import those first.

from PIL import Image, ImageDraw, ImageOps

Method 1: Adding Photo Frames with ImageOps

You can create a new image using PIL’s new method as below. It’s important to pay attention to color modes here as they can significantly impact the image manipulations that can be done on them as well as image saving options.

Here is a new image example with .new() method. First parameter is the color mode of the image while the second parameter in a tuple is the dimensions of the image.

Open or Create an Image


img = Image.open('/home/usa/Downloads/Wallpaperz/nick-perez-duvq92-VCZ4-unsplash.jpg')

Apply the .expand method from ImageOps module of PIL

new_img = ImageOps.expand(img, border=100)
new_img.show()
400x600 pixel RGBA image in darkorange color opened with PIL's Image module.

Nice and easy. ImageOps.expand method is probably the most suitable for simple outer border type frames.

Method 2: Adding Image Borders with Image.new & .paste

Alternatively, we can create a new image that’s slightly larger than our existing image which we desire to apply borders to and then we can paste the existing image to this new empty canvas that’s slightly larger which would yield a nice border effect.

The code below can be analyzed in 3 steps:

  1. An image is opened. This is the image that needs to be framed
  2. A new blank image is created. The existing image will be pasted over this one. So, the size of this image is the size of the opened image plus some margin for borders. Smart part of this code is that the base image dimensions are calculated relative to the existing image. Instead of hard coding the size parameter, this dynamic programming technique makes it more reusable and more convenient. Additionally, a suitable color is chosen which will end up being the border colors of the final composition.
  3. Final part is the opened image is pasted on the new base layer: .paste method takes a tuple argument. These are the coordinates where pasting originates from. It’s relative to the upper-left corner. So, as the new image is bigger in both dimensions with a margin of 300 (pad) and border margin applies to two sides, we are pasting starting from 150th pixel (pad//2).

* It’s beneficial to use “Floor Division Operator //” instead of “Float Division Operator /” at 3rd step because floor division rounds down the division to the next integer while float division yields a result with decimals which may confuse the program as pixels don’t exist in decimal state. (You can revisit our Python Operator Tips and Arithmetic Python Operators lesson for more insight.)

Voila! A little bit of creativity and we have a new image with borders.
Border thickness can easily be adjusted by increasing or decreasing the pad variable.
from PIL import Image

img = Image.open('/home/usa/Downloads/Wallpaperz/nick-perez-duvq92-vcz4-unsplash.jpg')
width, height= img.size
pad = 300
base_img = Image.new("rgba", (width+pad, height+pad), color='papayawhip')

base_img.paste(img,(pad//2, pad//2))
base_img.show()

Alternatively, we can create a new image that’s slightly larger than our existing image which we desire to apply borders to and then we can paste the existing image to this new empty canvas that’s slightly larger which would yield a nice border effect.

Method 3: Adding Image Frames with ImageDraw.draw.line

We can use “draw.lines” method to improve the aesthetics of an existing border or frame as well as create new borders by drawing each side separately.

Here is an example where 4 lines are drawn for the outer borders and 4 thinner lines are drawn for the inner borders as well resulting in an improved frame look for the existing image.

We have a dedicated Python tutorial with examples for ImageDraw module. Feel free to visit it for more ideas below:

Here is the image we have and full Python code below it:
img = Image.open('/home/usa/Downloads/Wallpaperz/nick-perez-duvq92-VCZ4-unsplash.jpg')
width, height= img.size
pad = 400

base_img = Image.new("RGBA", (width+pad, height+pad), color='papayawhip')
draw=ImageDraw.Draw(base_img)
base_img.paste(img,(pad//2, pad//2))

#Frame 1
new_width, new_height = base_img.size
xleft, xright = 100, new_width-100
yhigh, ylow = 100, new_height-100
frame1_line_width=4
frame1_line_color='gray'

draw.line((xleft, yhigh, xright, yhigh), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, ylow, xright, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, yhigh, xleft, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xright, yhigh, xright, ylow), fill=frame1_line_color, width=frame1_line_width)

#Frame 2
new_width, new_height = base_img.size
xleft, xright = 80, new_width-80
yhigh, ylow = 80, new_height-80
frame1_line_width=4
frame1_line_color='gray'

draw.line((xleft, yhigh, xright, yhigh), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, ylow, xright, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, yhigh, xleft, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xright, yhigh, xright, ylow), fill=frame1_line_color, width=frame1_line_width)

base_img.show()

I just copy pasted the Frame 2 part and only changed the pad variable slightly to line up the frames. Although it’s pretty straightforward and demonstrable this way this code would benefit greatly from writing a user defined Python function to avoid repetitiveness.

Method 4: Adding Image Frames with ImageDraw.draw.rectangle

We can achieve similar results with draw.rectangle function and the code will be a lot tidier compared to drawing each line separately.

Here is a cool example mixed with text on image feature.

* This code only demonstrates the image frame and excludes the text parts for simplicity.

You can refer to our dedicated tutorial for adding text on images with Python.

x,y = img.size

#Frame 1
xleft, xright = 100, x-100
yhigh, ylow = 100, y-100
frame1_line_width=1
frame1_line_color='gray'

draw.line((xleft, yhigh, xright, yhigh), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, ylow, xright, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xleft, yhigh, xleft, ylow), fill=frame1_line_color, width=frame1_line_width)
draw.line((xright, yhigh, xright, ylow), fill=frame1_line_color, width=frame1_line_width)

#Frame 2
xleft, xright = 50, x-50
yhigh, ylow = 50, y-50
frame2_line_width=9
frame2_line_color='white'

draw.line((xleft, yhigh, xright, yhigh), fill=frame2_line_color, width=frame2_line_width)
draw.line((xleft, ylow, xright, ylow), fill=frame2_line_color, width=frame2_line_width)
draw.line((xleft, yhigh, xleft, ylow), fill=frame2_line_color, width=frame2_line_width)
draw.line((xright, yhigh, xright, ylow), fill=frame2_line_color, width=frame2_line_width)
400x600 pixel RGBA image in darkorange color opened with PIL's Image module.

Method 5: Adding Image Borders with Opencv's copyMakeBorder

Another interesting option we have is from the opencv library. Opencv has a function called copyMakeBorder which can be used to add borders very conveniently.

Additionally, copyMakeBorder has some interesting features that are hard to find elsewhere.

In this section we will take a look at those options and demonstrate them.

Borders with the BORDER_CONSTANT parameter

import cv2
img = cv2.imread('/home/usa/Downloads/Wallpaperz/nick-perez-duvq92-VCZ4-unsplash.jpg')

new_img = cv2.copyMakeBorder(img, 280, 280, 280, 280, cv2.BORDER_CONSTANT, value=[100,128,128])

cv2.imwrite('new_img.png', new_img)

Borders with the BORDER_REFLECT parameter

You can also use a bunch of specific methods to create a reflection effect along the frame using opencv.

Borders with the BORDER_WRAP parameter

import cv2
img = cv2.imread('/home/usa/Downloads/Wallpaperz/nick-perez-duvq92-VCZ4-unsplash.jpg')

new_img = cv2.copyMakeBorder(img, 280, 280, 280, 280, cv2.BORDER_WRAP)
#new_img = cv2.copyMakeBorder(img, 280, 280, 280, 280, cv2.BORDER_CONSTANT, value=[100,128,128])

cv2.imwrite('new_img.png', new_img)


Borders with the BORDER_REPLICATE parameter

Border replicate also repeats the edges of the images and can be used for a different effect as well.

Borders with the BORDER_DEFAULT parameter

There is also a border_default option which you can choose as the border type.

400x600 pixel RGBA image in darkorange color opened with PIL's Image module.

Summary

In this Python Tutorial we learned how to create borders and frames around images using Python’s PIL library as well as the OpenCV library.

We used various image editing methods such as .paste(), .line(), .draw(), .new() and a few others.

We have a few powerful tutorials regarding image editing with Python examples via the PIL library. You are welcome to review those for getting some inspiration and technical new Python skills.