Ruddra.com

Play With Pillow

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:

First Load Image:

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
    """
    background_image.paste(overlay_image, offset, mask=overlay_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

Hope it was helpful. Cheers!!!

Last updated: Apr 05, 2024


← Previous
Python: Selenium with PhantomJs to capture Screenshots

This article is deprecated as PhantomJs has been deprecated from Selenium as driver. PhantomJS is a …

Next →
Ubuntu Hacks: Launching Chrome Apps at Startup

Chrome apps are really handy when you don’t want to overhaul your system with apps for every …

Share Your Thoughts
M↓ Markdown