VIJAY
VIJAY

Reputation: 49

How to generate random image with emojis using python

I want to create a image that contains 5 random emojis from emoji list. I know how to create random text captcha using python captcha module but it didn't work with emojis. I want to create image like this

IMAGE

Upvotes: 0

Views: 726

Answers (2)

Simon David
Simon David

Reputation: 1036

For a different use case, I faced the same challenge of pasting emojis into an image. The previous answer by @McKing does not allow to rotate emojis, which is desired in the example of OP. The code below allows for easily pasting multiple emojis into an image and scale/rotate them.

The approach of the code is to convert each emoji into an image by writing it onto an interim image with a transparent background. Subsequently, this interim image is scaled/rotated according to the user input and added to the base image at the desired location. Similar to the answer of @McKing, the code translates an "emoji prompt" e.g. :thumbs_up: to it's associated emoji.

Note: The emojis which OP used in his example stem from Apple's proprietary AppleColorEmoji.ttf font, whose use might cause licensing issues for you, depending on your use case. The code below uses the noto-emoji font, which is available under the Apache 2.0 license.

Here's an example of how the code below works:

from pathlib import Path
from urllib.request import urlopen

from PIL import Image

from emoji_image_creator import EmojiImageCreator

if __name__ == "__main__":

    noto_font_fp = Path().resolve().joinpath("emoji_to_image").joinpath(
        "NotoColorEmoji.ttf")
    emoji_image_creator = EmojiImageCreator(emoji_font_fp=str(noto_font_fp))

    base_image_to_use = Image.open(urlopen("https://placehold.co/600x400.png"))

    base_image_w_emojis = emoji_image_creator.generate_and_add_to_img(
        xy_coords=(50, 50),
        base_image=base_image_to_use,
        emoji_prompt=":thumbs_up:",
        tar_height=75,
        angle=30)

    base_image_w_emojis = emoji_image_creator.generate_and_add_to_img(
        xy_coords=(200, 300),
        base_image=base_image_w_emojis,
        emoji_prompt=":smiling_face_with_halo:",
        tar_width=100)

    base_image_w_emojis = emoji_image_creator.generate_and_add_to_img(
        xy_coords=(400, 200),
        base_image=base_image_w_emojis,
        emoji_prompt=":dog_face:",
        tar_height=200,
        angle=-90)
    
    # this emoji will be skewed!
    base_image_w_emojis = emoji_image_creator.generate_and_add_to_img(
        xy_coords=(200, 0),
        base_image=base_image_w_emojis,
        emoji_prompt=":woozy_face:",
        tar_height=200,
        tar_width=50,
        angle=-90)

    base_image_w_emojis.save("example.png", format="png")

Which returns (the :woozy_face: is deliberately skewed):

enter image description here

Code:

# -*- coding: utf-8 -*-
"""Emoji to image converter.

The code below allows to easily add emojis to an image, where each emoji can be
individually scaled in size and rotated. Note: Code was written using noto-emojis,
which are available under the Apache 2.0 license. Link to font repo:
https://github.com/googlefonts/noto-emoji/tree/main
"""
from pathlib import Path
from typing import Optional, Tuple

from PIL import Image, ImageFont, ImageDraw
import emoji


class EmojiImageCreator:
    """Provides methods to create images of emojis and paste them on another image.
    
    This class uses a trick to dynamically create emoji images by writing the emoji
    as a text on a transparent base image. The class automatically detects the required
    base image size (lest the emoji could be cropped off on a base image that is too small),
    where the effective emoji size is regulated by the `size` parameter specified by the user.
    """

    def __init__(self, emoji_font_fp: str) -> None:
        """Class instantiation.
        
        Args:
            emoji_font_fp: String filepath pointing to emoji font which should be
                used.
        """
        # str: emoji-alias, as defined here: https://www.webfx.com/tools/emoji-cheat-sheet/
        self.emoji_prompt: Optional[str] = None
        # str: emoji associated with emoji-alias
        self.emoji: Optional[str] = None

        self.emoji_font_fp = str(emoji_font_fp)
        assert Path(self.emoji_font_fp).is_file()

        # ImageFont.FreeTypeFont: emoji font to be used to generate the emoji images
        self.font = self.get_font()

    def generate_emoji(self,
                       emoji_prompt: str,
                       tar_width: Optional[int] = None,
                       tar_height: Optional[int] = None,
                       angle: int = 0) -> Image.Image:
        """Generates an emoji image.

        Args:
            emoji_prompt: Emoji-alias associated with a certain emoji. E.g.
                `:thumbs_up:` is `👍`. See: https://www.webfx.com/tools/emoji-cheat-sheet/
            tar_width: Target width as number of pixels. User can specify target
                width and/or target height.
            tar_height: Target height as number of pixels.
            angle (optional): Angle by which image should be rotated.

        Returns:
            Generated emoji image.
        """
        self.set_params(emoji_prompt=emoji_prompt)

        emoji_img = self.emoji_to_image()

        emoji_img = EmojiImageCreator.scale_image(img=emoji_img,
                                                  tar_width=tar_width,
                                                  tar_height=tar_height)

        emoji_img = EmojiImageCreator.rotate_image(img=emoji_img, angle=angle)

        return emoji_img

    def generate_and_add_to_img(self,
                                base_image: Image.Image,
                                xy_coords: Tuple[int, int],
                                emoji_prompt: str,
                                tar_width: Optional[int] = None,
                                tar_height: Optional[int] = None,
                                angle: int = 0) -> Image.Image:
        """Generates an emoji image.

        Args:
            base_image: Image on which generated emoji should be added.
            xy_coords: Coordinates on base_image should be added (xy values represent
                top right corner of the pasted emoji).
            emoji_prompt: Emoji-alias associated with a certain emoji. E.g.
                `:thumbs_up:` is `👍`. See: https://www.webfx.com/tools/emoji-cheat-sheet/
            tar_width: Target width as number of pixels. User can specify target
                width and/or target height.
            tar_height: Target height as number of pixels.
            angle (optional): Angle by which image should be rotated.

        Returns:
            Generated emoji image.
        """
        emoji_img = self.generate_emoji(emoji_prompt=emoji_prompt,
                                        tar_width=tar_width,
                                        tar_height=tar_height,
                                        angle=angle)

        base_image.paste(emoji_img, xy_coords, mask=emoji_img)

        return base_image

    def set_params(self, emoji_prompt: str) -> None:
        """Sets instance attributes required to generate emoji images.
        
        Args:
            emoji_prompt: Emoji-alias associated with a certain emoji. E.g.
                `:thumbs_up:` is `👍`. See: https://www.webfx.com/tools/emoji-cheat-sheet/
        """
        self.emoji_prompt = emoji_prompt
        self.emoji = EmojiImageCreator.prompt_to_emoji(
            emoji_prompt=emoji_prompt)

    def get_font(self) -> ImageFont.FreeTypeFont:
        """Method loading font specified during class instantiation.

        Note: Font size is hardcoded given that `NotoColorEmoji.tff` only works
        with the specified size. For more info see:
        `https://github.com/python-pillow/Pillow/issues/6166`.

        Returns:
            Emoji font.
        """
        return ImageFont.truetype(font=self.emoji_font_fp,
                                  size=109,
                                  encoding='unic')

    @classmethod
    def prompt_to_emoji(cls, emoji_prompt: str) -> str:
        """Converts a emoji prompt to it's respective symbol.

        Args:
            emoji_prompt: Emoji-alias associated with a certain emoji. E.g.
                `:thumbs_up:` is `👍`. See: https://www.webfx.com/tools/emoji-cheat-sheet/
        
        Returns:
            Emoji symbol associated with prompt.
        """
        return emoji.emojize(emoji_prompt)

    def get_emoji_dimensions(self) -> Tuple[int, int]:
        """Calculates the width and height of the emoji.

        Note: This information allows to dynamically create the transparent
        base image on which the emoji will appear as a text. Since the emoji should
        exactly fit the base image, the dimensions of each base image needs to
        depend on the emoji that will be written on it.

        Returns:
            width: Effective width of emoji, given hardcoded font size.
            height: Effective height of emoji, given hardcoded font size.
        """
        assert isinstance(
            self.emoji,
            str), "Parameters not set; call `.set_params()` priorly."

        left, top, right, bottom = self.font.getbbox(self.emoji)
        width, height = right - left, bottom - top

        return width, height

    def emoji_to_image(self) -> Image.Image:
        """Converts emoji to an image by writing it as a text on a transparent image.
        
        Returns:
            Image of emoji.
        """
        assert isinstance(
            self.emoji,
            str), "Parameters not set; call `.set_params()` priorly."

        emoji_width, emoji_height = self.get_emoji_dimensions()

        base_img = Image.new(mode='RGBA',
                             size=(emoji_width, emoji_height),
                             color=(0, 0, 0, 0))

        draw = ImageDraw.Draw(base_img)
        draw.text(xy=(0, 0),
                  text=self.emoji,
                  font=self.font,
                  embedded_color=True)

        return base_img

    @classmethod
    def scale_image(cls,
                    img: Image.Image,
                    tar_width: Optional[int] = None,
                    tar_height: Optional[int] = None) -> Image.Image:
        """Scales image to user-specified size.

        Allows for specifying target width and/or target height to which image
        should be scaled.

        Args:
            img: Target image to scale
            tar_width: Target width as number of pixels. User can specify target
                width and/or target height.
            tar_height: Target height as number of pixels.
        
        Returns:
            Scaled image.
        """
        width, height = img.size

        if tar_width and not tar_height:
            width_scale = tar_width / width
            height_to_scale = int(height * width_scale)

            return img.resize((tar_width, height_to_scale))

        if tar_height and not tar_width:
            height_scale = tar_height / height
            width_to_scale = int(width * height_scale)

            return img.resize((width_to_scale, tar_height))

        if tar_width and tar_height:
            return img.resize((tar_width, tar_height))

        raise ValueError(
            "Either `tar_width` or `tar_height` must be specified!")

    @classmethod
    def rotate_image(cls, img: Image.Image, angle: int) -> Image.Image:
        """Rotates image using user-specified angle.

        Args:
            img: Target image to scale
            angle: Angle by which image should be rotated.

        Returns:
            Rotated image.
        """
        return img.rotate(angle, expand=True)

Upvotes: 0

McKing
McKing

Reputation: 46

As far as I know. You can use Pillow to render emoji on a picture.

Example:

from PIL import Image, ImageDraw, ImageFont
import emoji

thumbs_up = emoji.emojize(':thumbs_up:')
base_img = Image.open(r'base_image.jpg')
draw = ImageDraw.Draw(base_img)
font = ImageFont.truetype('OpenSansEmoji.ttf', 64, encoding='unic')
draw.text(xy=(100, 150), text=thumbs_up, fill=(255, 255, 255), font=font)

base_img.save('base_image_1.jpg')

The OpenSansEmoji.ttf is in here.

But that's not a perfect solution. I think rendering emoji as a picture is a better way. Such as base_img.paste(Image.open(r'emoji.png'))

Upvotes: 2

Related Questions