Reputation: 49
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
Upvotes: 0
Views: 726
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):
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
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