Build your own profile photo sticker/frame tool
Tags: python, images, beginners, programming
Published: 2022-11-01 21:35:57

---
title: Build your own profile photo sticker/frame tool
published: true
description: 
tags: python, images, beginners, programming
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ul0g6o2la59a5fl99c6l.png
---

Across most of the Internet, our identities are squashed into a little profile photo and display name, and these are the miniature canvas upon which we can express our personalities or make a statement. Maybe to take part of a social movement, represent an organization or brand; or just to announce that we're "open to work" or feeling the Halloween spirit 👻

Curious about how to build one of those image generators for people to liven up their profile photos? Here is a simple version in Python that uses a partially transparent image overlay for a custom frame and stickers. 

This takes a user's profile photo (from Twitter), adds the overlay image on top, and then you can grab the resulting image as a data url to render in HTML or write it as a (temporary) file (which can also be passed back to Twitter to update the user's profile photo). 

_Inspiration for this post: We recently built this feature as a fun little project for Block Party. Interested in working with us? [We're hiring!](https://block-party.breezy.hr/)_

---

First, let's fetch the user's profile photo from Twitter. I've hand-waved the piece about the Twitter client (sorry) but you can use something like [Tweepy](https://github.com/tweepy/tweepy). 

```python
from PIL import Image 
import requests 

PFP_IMAGE_SIZE = (400, 400)  # max/standard size on Twitter

def get_original_pfp_image(twitter_screen_name):
    # TODO(you) Implement this
    client = create_twitter_client_for_app()  

    twitter_user_json = client.get_twitter_user_data_by_screen_name(
        twitter_screen_name)
    pfp_url = twitter_user_json.get("profile_image_url_https", "")
    pfp_original_url = pfp_url.replace("_normal", "")   

    pfp_image = Image.open(
        requests.get(pfp_original_url, stream=True).raw)

    # We aren't guaranteed that the user's profile image is 
    # the right size, which will cause issues when trying to 
    # apply the overlay. 
    if pfp_image.size != PFP_IMAGE_SIZE:  
        pfp_image = pfp_image.resize(
            PFP_IMAGE_SIZE, Image.ANTIALIAS)

    return pfp_image
```

If you want to simplify for development and testing, you can pass in a local image path instead. 

```python
from PIL import Image 

PFP_IMAGE_SIZE = (400, 400)  

# Note that this has a slightly different function signature 
# than the alternative sample code. 
def get_original_pfp_image(pfp_image_path):
    pfp_image = Image.open(pfp_image_path)

    if pfp_image.size != PFP_IMAGE_SIZE:  
        pfp_image = pfp_image.resize(
            PFP_IMAGE_SIZE, Image.ANTIALIAS)

    return pfp_image

``` 
---
Next, let's load the overlay image, composite it with the original profile photo, and create the new overlaid image.

The overlay image here should be a 400x400 PNG with transparency. 

![Spooky Blobby frame and sticker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sohxvsltgvymb1kwaw4h.png)

```python
import PIL
from PIL import Image

def get_overlay_image(overlay_image_path):
    overlay_image = Image.open(overlay_image_path)
    return overlay_image

def generate_overlaid_image(twitter_screen_name):
    pfp_image = get_original_pfp_image(twitter_screen_name)
    if not pfp_image:
        return None

    # We need RGBA mode for transparency in the image
    pfp_image_rgba = pfp_image.convert(mode="RGBA")  

    # TODO(you) Replace with your overlay image path
    overlay_image = get_overlay_image("overlay.png")
    overlaid_pfp_image = PIL.Image.alpha_composite(
        pfp_image_rgba, overlay_image)
    return overlaid_pfp_image
```
---
Now let's get the image as a data url so we can render it in HTML!

```python
import base64
from io import BytesIO

DATA_IMAGE_PREFIX = "data:image/png;base64,"

def generate_overlaid_pfp_image_data_url(twitter_screen_name):
    overlaid_pfp_image = generate_overlaid_pfp_image(
        twitter_screen_name)
    if not overlaid_pfp_image:
        return None

    image_io = BytesIO()
    overlaid_pfp_image.save(image_io, "png", quality=95)
    data_url = DATA_IMAGE_PREFIX + base64.b64encode(
        image_io.getvalue()).decode("ascii")
    return data_url
```

To preview in Jinja -- for example, at all the different sizes that Twitter uses -- we just use this image data url as the `src` of an `img` element.

```html
{% for dims in [(24, 24), (48, 48), (73, 73), (400, 400)] %}
    <img src="{{ image_data_url }}" 
         width="{{ dims[0] }}" 
         height="{{ dims[1] }}" />
{% endfor %}
```
---
Lastly, if you do want to set the user's profile photo back on Twitter, you can create a temporary file and send it through the API. 

```python
import base64
from io import BytesIO

DATA_IMAGE_PREFIX = "data:image/png;base64,"

def update_user_twitter_pfp(twitter_user_id, image_data_url):
    image_data = image_data_url[len(DATA_IMAGE_PREFIX):]

    buffer = BytesIO(base64.b64decode(image_data))
    image_tempfile = tempfile.NamedTemporaryFile(suffix=".png")

    try:
        image_tempfile.write(buffer.read())
        image_tempfile.flush()

        # This client is created with the user's OAuth 
        # credentials (vs. app credentials) so we can 
        # set their profile photo. 
        client = create_twitter_client_for_user(
            twitter_user_id)
        client.update_twitter_user_profile_image(
            image_tempfile.name)
    finally:
        buffer.close()
        image_tempfile.close()
```
---
Here's how it looks!

![Profile photo sticker/frame tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ul0g6o2la59a5fl99c6l.png)