# Play With Pillow

Who hasn’t heard PIL? It’s an image processing library made by python (Python Image Library). Pillow is an extension of it.

## Installation

So installing pillow is really easy:

``````pip install pillow
``````

Now we are going to do some image processing cool stuff:

``````from PIL import Image, ImageOps

img = Image.open('image.png')
``````

## Resize image

Suppose We want to resize an image and maintain its aspect ratio. Here we need to get the aspect ratio after resizing. Here is a snippet for it:

``````def get_resize_image_size(image, baseheight=None):
"""
Get resize image size
"""
size = 1200, 1200
print("Getting resize image size")
if not baseheight:
baseheight = size[1]
hpercent = (baseheight / float(image.size[1]))
wsize = int((float(image.size[0]) * float(hpercent)))
return wsize, baseheight
``````

Or we may want to our desired image size.

After we get the desired aspect ratio, now we resize it with the following code:

``````def resize_image(img, size=None):
"""
Resize Image
img: Image file opened by PIL
size: if Size not given, it will be
calculated
"""
print("Resizing image")
if not size:
size = get_resize_image_size(img)
img = img.resize(size, Image.ANTIALIAS)
return img
``````

## Crop image in box shape

When we try to crop, we can either give it positions for cropping or we can calculate the points, which will be used in cropping. Suppose we have a random image, but we want to crop it in middle position and we want to crop in such way that, we will only take out 1200x1200 size out of the sample image, the following is the code:

``````def crop_position(image):
"""
Get Crop Image Positions
"""
print("Getting Crop Image Positions")
width, height = image.size
new_width, new_height = 1200, 1200
left = (width - new_width) / 2
top = (height - new_height) / 2
right = (width + new_width) / 2
bottom = (height + new_height) / 2
return left, top, right, bottom
``````

Now we got the position, lets do cropping:

``````def crop_image(img, positions=None):
"""
Crop Image in Box Shape
"""
if not positions:
positions = crop_position(img)
return img.crop(box=positions)
``````

## Resize image and crop in center to convert it to a fixed size

Let’s take an image in size and convert it to 1200x1200 image:

``````def convert_image(pil_image):
"""
Resize Image and Crop in Center to Convert it to a Fixed Size
"""
try:
if pil_image.size == self.size:
print("Nothing to change, returning image")
return image
# Resize to size given in settings
pil_image = resize_image(pil_image)
# Cropping to adjust size given in settings
pil_image = crop_image(pil_image)
pil_image.save(path)
except (KeyError, Exception) as exp:
print(str(exp))
raise exp
``````

## Paste image over a background image

The following code will do the trick:

``````def add_overlay_over_background(background_image, overlay_image, offset=(0, 0)):
"""
Add overlay image over background image
"""
return background_image
``````

## Draw text over an image

For that, we first we need fonts:

``````path = 'path/to/font'
def get_font(size):
"""
Get Font Object
"""
return ImageFont.truetype(path, size=size)
``````

Then we need to co-ordinates where the texts will be written:

Here are two scenarios:

1. You provide the exact positions.
2. You can provide the height of the text position, we will calculate where the texts will be printed in the image. Here we are assuming that texts will be aligned in middle horizontally. It will be symmetric in perspective middle vertical line.
``````def process_text_coordination(position, font=None, vertical_only=False, text=None):
"""
Process Co-Ordination of the position
"""
if vertical_only:
# Case Two
size = font.getsize(text)
width = (self.size[0] - size[0]) / 2
return width, position
# Case One
return position
``````

Now we got everything, so let’s do writing text on image:

``````def write_text_on_image(image, font, text,position, color, size, vertical_only=False):
"""
Write Text over Image
"""
draw = ImageDraw.Draw(image)
font = get_font(size)
position = process_text_coordination(
position, font, vertical_only, text
)
draw.text(position, text, font=font, fill=color)
return image
``````

BTW, here color can be hexa value of the color you want to use. For white, you need use: `#fffff`

## Adjast brightness of an image

Let’s lighten or darken image based on amount:

``````def darken_or_lighten_pixels(image, amount=0.5):
"""
Enhance Image
"""
converter = ImageEnhance.Brightness(image)
return converter.enhance(amount)
``````

## Adjast color of an image

Let’s adjust color of image based on amount

``````def add_color_saturation(image, amount=0.5):
"""
Color saturation
"""
converter = ImageEnhance.Color(image)
return converter.enhance(amount)
``````

## Save the PIL image in django models or serve as django file

This code will convert PIL Images to Content file:

``````def process_django_file(pil_image, name, format='png'):
"""
Process the PIL file to Django File
"""
file_object = BytesIO()
pil_image.save(file_object, format=format)
content = file_object.getvalue()
return ContentFile(content, name=name)
``````

And saving image will be damn easy:

``````pil_image = Image.open('image.png')

content_file = process_django_file(pil_image, name="some name", format='png')

my_model.image = content_file
my_model.save()
``````

Put watermark on your image by `PILService` object’s `water_mark_on_image` method:

``````img= Image.open('foobar.jpg')
pil_service = PILService()
pil_service.water_mark_on_image(
img,
"Text",
"20", # percent of height
"font/path",
opacity=50,
margin=10
)
``````

## Convert to webp

For this feature you need to install.jpg driver in your machine. For mac use you need to run the following(using homebrew):

``````brew install webp
``````

Or you can install `libwebp` in Unix/Linux.

Then you need to re-install pillow. Now, run this command to convert all the files to `*.webp` format in a certain directory.

``````DIR = "Path/To/Images"

for root, dirs, files in os.walk(DIR):
for file in files:
infile = os.path.join(root, file)
name, ext = os.path.splitext(file)
try:
im = Image.open(infile)
im.save(os.path.join(root, name) + ".webp", "WEBP")
os.remove(infile)  # comment out this line if you want to keep original images
except Exception as e:
print(e)
``````

## Convert to JPG/JPEG

Before converting to JPG/JPEG, you need to know that JPG is a lossy compression, and it can’t convert RGBA(Red, Green, Blue, Alpha). Meaning it can’t convert Alpha. So you can try like this:

``````im = Image.open(infile)
im = im.convert('RGB')  # convert RGB
im.save(os.path.join(root, name) + ".jpg", "JPEG")
``````

But above code will convert any transparent background to black. So instead you can use a white background to replace transparent part and then convert to RGB:

``````im = Image.open(infile)
new_image = Image.new("RGBA", im.size, "WHITE")  # White Image
new_image.paste(im, (0, 0), im)  # Setting as background
im = new_image.convert('RGB')
im.save(os.path.join(root, name) + ".jpg", "JPEG")
``````

## And Many more

Many more methods are given in this Repo: https://github.com/ruddra/play-with-pillow