DEV Community

Cover image for Build your own profile photo sticker/frame tool
Tracy Chou 👩🏻‍💻
Tracy Chou 👩🏻‍💻

Posted on

Build your own profile photo sticker/frame tool

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!


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.

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
Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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

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
Enter fullscreen mode Exit fullscreen mode

Now let's get the image as a data url so we can render it in HTML!

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
Enter fullscreen mode Exit fullscreen mode

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.

{% for dims in [(24, 24), (48, 48), (73, 73), (400, 400)] %}
    <img src="{{ image_data_url }}" 
         width="{{ dims[0] }}" 
         height="{{ dims[1] }}" />
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

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.

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()
Enter fullscreen mode Exit fullscreen mode

Here's how it looks!

Profile photo sticker/frame tool

Top comments (1)

Collapse
 
sherrydays profile image
Sherry Day

This is neat :)