Commit a6752569 authored by inkhey's avatar inkhey

Big update

parent ef5d5314
# PiouPiou
A generator of avatar optimised to generate or random avatars based on multiple image layers, able to return same image from a string "seed".
Heavily inspired by [David Revoy]('s [cat avatar generator]( and [MonsterID by Andreas Gohr's](
This generator rely on Pillow library to do image processing.
## Usage
Easy way to use it, is to be based on FolderAvatarTheme. To do this, you should create many .png file of same image, all
with transparency. You should allow follow similar naming pattern as for sample example.
To test it, you can simply use sample in example:
Cat avatar (by David Revoy](
from pioupiou import FolderAvatarTheme
theme = FolderAvatarTheme(folder_path="sample/cat_revoy", layers_name=["body", "fur","eyes","mouth","accessorie"])
avatar = theme.generate_avatar(token="just a random string")
theme.save_on_disk(avatar, path="/tmp/saved_file.png")
Bird avatar (by David Revoy](
from pioupiou import FolderAvatarTheme
theme = FolderAvatarTheme(
layers_name=["tail", "hoop", "body", "wing", "eyes", "bec", "accessorie"],
avatar = theme.generate_avatar(token="just a random string")
theme.save_on_disk(avatar, path="/tmp/saved_file.png")
from pioupiou.avatar_generator import AvatarTheme # noqa: F401
from pioupiou.avatar_generator import FolderAvatarTheme # noqa: F401
from pioupiou.avatar_generator import Layer # noqa: F401
......@@ -4,71 +4,83 @@ import typing
from PIL import Image
LAYER_FILE_IMAGE_PATTERN = "{folder_path}/{layer_name}_[0-9]*.png"
class Layer(object):
def __init__(self, level: int, paths: str, name: str):
def __init__(self, level: int, variation_images_paths: typing.List[str], name: str):
An layer is a part of an avatar theme, a layer
:param variation_images_paths: paths of different images possible for this layer, a
layer as differents image variations
:param level: level of layer, higher mean image will apply be over others.
:param images: path of image
self.level = level
self.paths = paths
self.variations_images_paths = variation_images_paths = name
def Image(self, key: str) -> Image:
assert self.paths
def get_random_image(self) -> Image:
"""Get random image from layer based on current seed"""
assert self.variations_images_paths
image =
# INFO - GM - 03/07/2019 - We do not support image without alpha layer
assert image.mode in PILLOW_ALPHA_MODES
return image
class AvatarTheme(object):
def __init__(self, layers: typing.List[Layer]):
self.layers = layers
def generate_avatar(self, key) -> Image:
def generate_avatar(self, token: str) -> Image:
Generate avatar Image by obtaining layer and applying them
:param token: token used as seed to decide layer variation to use.
sorted_layers = sorted(self.layers, key=lambda x: x.level)
current_image = sorted_layers[0].Image(key)
current_image = sorted_layers[0].get_random_image()
for layer in sorted_layers[1:]:
current_image = Image.alpha_composite(current_image, layer.Image(key))
current_image = Image.alpha_composite(current_image, layer.get_random_image())
return current_image
def save_on_disk(self, image: Image, path: str):
def save_on_disk(self, avatar: Image, path: str) -> str:
Save avatar on disk
:param avatar: image of avatar to save
:param path: path where image should be saved
:return: path of image saved, same as path given in entry
return path
class FolderAvatarTheme(AvatarTheme):
def __init__(self, folder: str, layers_name: typing.List[str]):
self.folder = folder
Theme based on folder allowing easy setup of theme.
def __init__(self, folder_path: str, layers_name: typing.List[str]):
:param folder_path: path of folder where different layers are available
:param layers_name: name of layers, order is important to decide which layer is over which. last layers are
put over previous one.
self.folder_path = folder_path
self.layers_name = layers_name
layers = []
for layer_number, layer_name in enumerate(layers_name):
escaped_layer = glob.escape(layer_name)
file_list = glob.glob("{}/{}*.png".format(self.folder, escaped_layer))
layers.append(Layer(level=layer_number, paths=file_list, name=layer_name))
file_list = glob.glob(
folder_path=self.folder_path, layer_name=glob.escape(layer_name)
assert file_list
Layer(level=layer_number, variation_images_paths=file_list, name=layer_name)
assert layers
if __name__ == "__main__":
# body = Layer(
# level=1,
# name="body",
# paths=["../avatars_set/bird_revoy/body_1.png", "../avatars_set/bird_revoy/body_2.png"]
# )
# bec = Layer(
# level=2,
# name="body",
# paths=["../avatars_set/bird_revoy/bec_1.png", "../avatars_set/bird_revoy/bec_2.png"]
# )
# layers = [body, bec]
# theme = AvatarTheme(layers)
theme = FolderAvatarTheme(
layers_name=["tail", "hoop", "body", "wing", "eyes", "bec", "accessorie"],
# theme = FolderAvatarTheme("../avatars_set/cat_revoy", layers_name=["body", "fur","eyes","mouth","accessorie"])
avatar = theme.generate_avatar("inkey")
theme.save_on_disk(avatar, path="/tmp/toto.png")
avatar = theme.generate_avatar("éléphant")
theme.save_on_disk(avatar, path="/tmp/titi.png")
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment