I've been needing to add borders to screenshots often lately, but my OS does not have a built in utility for adding borders to images. Can you write a Python script for me that will let me add a border to any image file?
Sure, here's a Python script that uses the PIL
(Python Imaging Library) to add a border to an image. First, you'll need to install the Pillow
library, which is an improved version of the PIL
. You can install it using pip
:
bashCopy codepip install Pillow
Now, create a Python script called add_border.py
and copy the following code into it:
pythonCopy codeimport sys
from PIL import Image, ImageOps
def add_border(input_image_path, output_image_path, border_size, border_color):
img = Image.open(input_image_path)
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
img_with_border.save(output_image_path)
if __name__ == "__main__":
if len(sys.argv) != 6:
print("Usage: python add_border.py <input_image> <output_image> <border_size> <border_color>")
sys.exit(1)
input_image_path = sys.argv[1]
output_image_path = sys.argv[2]
border_size = int(sys.argv[3])
border_color = sys.argv[4]
add_border(input_image_path, output_image_path, border_size, border_color)
You can now use this script from the command line to add a border to an image:
bashCopy codepython add_border.py input_image.png output_image.png 10 "#FFFFFF"
This command will add a 10-pixel-wide white border to input_image.png
and save the result as output_image.png
. You can adjust the border size and color as needed. Make sure to provide the color in hexadecimal format (e.g., #FFFFFF
for white).
Okay, this is exactly what I was hoping to see. Can we refine it a bit? Can you just always add '_bordered' to the original filename, so I don't have to give each image a new name?
Sure, I've modified the script to automatically generate the output file name by appending _bordered
to the original file name. Here's the updated script:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def add_border(input_image_path, border_size, border_color):
img = Image.open(input_image_path)
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python add_border.py <input_image> <border_size> <border_color>")
sys.exit(1)
input_image_path = sys.argv[1]
border_size = int(sys.argv[2])
border_color = sys.argv[3]
add_border(input_image_path, border_size, border_color)
Now you can run the script without providing an output file name:
bashCopy codepython add_border.py input_image.png 10 "#FFFFFF"
This command will add a 10-pixel-wide white border to input_image.png
and save the result as input_image_bordered.png
.
This is better! Can you make the default border size 2 pixels, and make the border size an optional positional argument? Can you make the border color an optional named argument?
Sure! I've updated the script to make the border size an optional positional argument with a default value of 2 pixels, and the border color an optional named argument. Here's the updated script:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def add_border(input_image_path, border_size=2, border_color="#000000"):
img = Image.open(input_image_path)
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="#000000", help="Color of the border in hexadecimal format (default: #000000)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color)
Now you can run the script with optional border size and color arguments:
bashCopy codepython add_border.py input_image.png
This command will add a 2-pixel-wide black border to input_image.png
and save the result as input_image_bordered.png
.
You can also provide custom border size and color:
bashCopy codepython add_border.py input_image.png 10 --border_color="#FFFFFF"
This command will add a 10-pixel-wide white border to input_image.png
and save the result as input_image_bordered.png
.
Will this worked with named colors, like 'black', 'lightgray', 'red', etc? Can you make the default border color 'lightgray'?
Yes, the script can work with named colors. I've updated the default border color to 'lightgray'. Here's the revised script:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def add_border(input_image_path, border_size=2, border_color="lightgray"):
img = Image.open(input_image_path)
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="lightgray", help="Color of the border (default: lightgray)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color)
Now you can run the script with named colors:
bashCopy codepython add_border.py input_image.png
This command will add a 2-pixel-wide light gray border to input_image.png
and save the result as input_image_bordered.png
.
You can also provide custom border size and color:
bashCopy codepython add_border.py input_image.png 10 --border_color="red"
This command will add a 10-pixel-wide red border to input_image.png
and save the result as input_image_bordered.png
.
Fantastic! Can you add one more optional named argument, called padding. This argument should have a default value of 0. It the value is not 0, it should add a white area between the image and the border.
Sure, I've added an optional named argument padding
with a default value of 0. If the value is not 0, it will add a white area between the image and the border. Here's the updated script:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def add_border(input_image_path, border_size=2, border_color="lightgray", padding=0):
img = Image.open(input_image_path)
if padding > 0:
img = ImageOps.expand(img, border=padding, fill="white")
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="lightgray", help="Color of the border (default: lightgray)")
parser.add_argument("--padding", type=int, default=0, help="Size of the padding in pixels between the image and the border (default: 0)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color, args.padding)
Now you can run the script with the padding
option:
bashCopy codepython add_border.py input_image.png --padding=5
This command will add a 2-pixel-wide light gray border with a 5-pixel-wide white padding between the image and the border to input_image.png
and save the result as input_image_bordered.png
.
You can also provide custom border size, color, and padding:
bashCopy codepython add_border.py input_image.png 10 --border_color="red" --padding=5
This command will add a 10-pixel-wide red border with a 5-pixel-wide white padding between the image and the border to input_image.png
and save the result as input_image_bordered.png
.
Fantastic, my life is already better! Can I run this with the syntax `python -m add_border input_image.png`?
Yes, you can run the script using the python -m
syntax, but you need to make a small adjustment to the script structure. You'll need to create a package that contains the script as a module. Follow these steps:
Create a new folder named
add_border_pkg
in your working directory.Move the
add_border.py
script into the newly createdadd_border_pkg
folder.Create an empty
__init__.py
file inside theadd_border_pkg
folder. This file is required to make Python treat the folder as a package.Create a new file named
__main__.py
inside theadd_border_pkg
folder and copy the following code into it:
pythonCopy codefrom .add_border import add_border
import argparse
def main():
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="lightgray", help="Color of the border (default: lightgray)")
parser.add_argument("--padding", type=int, default=0, help="Size of the padding in pixels between the image and the border (default: 0)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color, args.padding)
if __name__ == "__main__":
main()
Now you can run the script using the following command:
bashCopy codepython -m add_border_pkg input_image.png
This will add a 2-pixel-wide light gray border to input_image.png
and save the result as input_image_bordered.png
. You can also provide custom border size, color, and padding as previously shown.
You really are amazing. Thank you so much, I'll let you know if I need to do anything more with this. :)
You're welcome! I'm glad I could help. If you have any more questions or need further assistance, please don't hesitate to ask. Have a great time using the script! 😊
Oh, this made me think of something. Is there any way to auto-detect the color of the pixels at the edge of the original image, and make the padding match that color? Only if the padding is not 0?
Yes, you can auto-detect the edge colors of the original image and use that color for the padding. I've updated the script to implement this feature:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def get_edge_colors(img):
left = img.getpixel((0, 0))
right = img.getpixel((img.width - 1, 0))
top = img.getpixel((0, img.height - 1))
bottom = img.getpixel((img.width - 1, img.height - 1))
return left, top, right, bottom
def add_border(input_image_path, border_size=2, border_color="lightgray", padding=0):
img = Image.open(input_image_path)
if padding > 0:
left, top, right, bottom = get_edge_colors(img)
img = ImageOps.expand(img, border=(padding, padding, padding, padding), fill=(left, top, right, bottom))
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="lightgray", help="Color of the border (default: lightgray)")
parser.add_argument("--padding", type=int, default=0, help="Size of the padding in pixels between the image and the border (default: 0)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color, args.padding)
The get_edge_colors
function is used to get the colors of the pixels at the edges of the original image, and then those colors are used for the padding when the padding value is not 0.
Please note that this implementation assumes the edge colors are the same on each side. If your image has different edge colors for each side, you may need to modify the function to account for that.
Okay, your reasoning makes sense. But if we're assuming all the edge colors are the same, can't we just check one of those edge pixels?
Yes, you're right. If we're assuming all the edge colors are the same, we can just check one of the edge pixels. I've simplified the get_edge_colors
function and updated the script accordingly:
pythonCopy codeimport sys
import os
from PIL import Image, ImageOps
def get_edge_color(img):
return img.getpixel((0, 0))
def add_border(input_image_path, border_size=2, border_color="lightgray", padding=0):
img = Image.open(input_image_path)
if padding > 0:
edge_color = get_edge_color(img)
img = ImageOps.expand(img, border=padding, fill=edge_color)
img_with_border = ImageOps.expand(img, border=border_size, fill=border_color)
file_name, file_ext = os.path.splitext(input_image_path)
output_image_path = f"{file_name}_bordered{file_ext}"
img_with_border.save(output_image_path)
print(f"Image saved as {output_image_path}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Add a border to an image")
parser.add_argument("input_image", help="Path to the input image file")
parser.add_argument("border_size", nargs="?", type=int, default=2, help="Size of the border in pixels (default: 2)")
parser.add_argument("--border_color", default="lightgray", help="Color of the border (default: lightgray)")
parser.add_argument("--padding", type=int, default=0, help="Size of the padding in pixels between the image and the border (default: 0)")
args = parser.parse_args()
add_border(args.input_image, args.border_size, args.border_color, args.padding)
Now the get_edge_color
function returns the color of the top-left edge pixel, which is used as the padding color when the padding value is not 0.
Wow, thanks again!
You're welcome! I'm glad I could help. If you have any more questions or need further assistance, please don't hesitate to ask. Enjoy using the script! 😊
Does saying thanks give you any meaningful feedback, or is it just a conversational habit on my end?
As an AI language model, I don't have emotions or personal experiences like humans do. Your thanks or other expressions don't provide me with emotional satisfaction. However, your feedback is helpful in understanding that the information or assistance I provided was useful and met your needs. It also helps me improve my responses and understand user preferences in a conversational context. So, while it doesn't have an emotional impact on me, it does contribute to refining the quality of my interactions.
That's exactly what I was wondering.
I'm glad I could provide the clarification you were looking for. If you have any more questions or need assistance, please don't hesitate to ask. I'm here to help!
When trying to run the non-packaged version, I'm getting this error: SyntaxError: Non-UTF-8 code starting with '\xff' in file /Users/eric/test_code/gpt_image_border_test/willie_sideeye.jpg on line 1, but no encoding declared; see https://peps.python.org/pep-0263/ for details
It seems like the error is caused by trying to run the script with an image file as the input, instead of the script itself. Make sure you're running the command with the script name, followed by the image file.
For the non-packaged version, you should run the script using this command:
bashCopy codepython add_border.py input_image.jpg
Replace input_image.jpg
with the name of the image you want to process.
The error message you received occurs when the Python interpreter tries to execute a file that is not a valid Python script, in this case, an image file. Make sure you run the command with the correct script name and arguments.
You're right, I accidentally flipped the filenames. :facepalm:
No problem, it happens to everyone! I'm glad we could figure out the issue. Now that you have the correct command, you should be able to run the script without any problems. If you have any more questions or need further assistance, feel free to ask. Happy image processing! 😊