Skip to content
Advertisement

Issue with creating multiple images through alpha compositing together in batches

I am basically creating a program using Pillow for Python where it randomly generates pictures of owls. It does this by choosing different parts for the owl, that being filepaths for different premade art asset images, from various nested dictionaries. These parts include: Bases (the body of the owl), patterns (patterns layered onto the owls feathers), and accessories (like hats, glasses, and such). The filepaths that make up each of these chosen parts are then put into one big list and are put through a for loop that uses the file paths to create each image, colors them, and then alphacomposites the finished images into a final image. The issue I am having is that sometimes, mainly when I have the program create multiple owls in one go as a batch, sometimes an owl will have assets on them that were not intended. The types of uncalled for parts I see on these incorrect owls are always either incorrect patterns and/or accessories. An example of which is below:

The Owl that came out of the batch: The Owl that came out of the batch

Now, each owl created is given a special code as part of their file name. This code is basically a list of dictionary keys that, when put back into the program, allows it to use those keys to sift through the dictionaries of filepaths for the parts of the owl and then recreate the owl image.

The owl I showed above has a code key of: BC0_PC2_CC_Orca_White_ACG15. I’ll go through each part of the code to show what each part means. BC0 means this Owls base is supposed to be a Great Grey Owl, which it is. PC2 means its pattern is supposed to be leaves, which it seems to have, but it also has a checkerboard pattern. CC means it will have a simple color scheme. Orca means its base will be colored in the orca color scheme and White means its eyes, beak, and feet will be colored white. ACG15 means the owl will have a certain type of glasses accessory.

When I put in this owl’s code back into the program, it recreates the owl correctly, as you can see below:

The Owl after its code was put back into the program: enter image description here

I am looking for advice on how to deal with this issue. For context, Below I will explain the basic process on how an owl image is created through commented code, especially in regard to it’s patterns and accessories.

Part 1: Creating an Owl’s Code Key

def owl_pull():

def owl_pull():
    # for this program, when you want to generate an owl(s) it all ask you to choose the tier of probability.
    # 1 = Normal and 2 = Lucky. This tier determines the probabilities for generating parts of different rarity for th owl
    print("Please type in the number a rarity tier. This will modify the different probabilities of pulling the different rarities for each part of your owl.")
    print("Tier 1 nTier 2 n")
    tier = int(input("Type Tier:"))
    # This value is the number of times the program creates an owl
    pull_times = 1
    while pull_times > 0:
        # while pull times is above 0, it will generate an owl code and then create the owl before saving it into a file
        # it takes the tier your chose and puts it into the function to generate the code
        final_owl_code = create_code(tier)
        reincarnate_owl(final_owl_code)
        print(f"Your owl's Code is: {turn_code_to_string(final_owl_code)}")

        pull_times-=1

def create_code(rarity):

def create_code(rarity):
    owl_code = []

    b = create_base_code(rarity)
    owl_code.append(b)

    # Once it determines the code for the base of the owl, it will take the code and the tier of rarity requested
    # to determine the code for the owl's pattern.
    p = create_pattern_code(rarity, b)
    # Once done, it will add on the code to the final code of the owl.
    owl_code.append(p)

    c = create_color_code(rarity)
    owl_code.append(c)
    color_name = generate_color()
    owl_code.append(color_name)

    eye_color_name = generate_color()
    owl_code.append(eye_color_name)

    # For accessories, we need to determine what type of accessories, if any, the owl can have.
    # An owl can have up to 2 accessories slots filled as long as they are not of the same type
    # These types of accessories are represented by dictionary keys
    a_type1 = create_accessory_type_code(rarity)
    a_type2 = create_accessory_type_code(rarity)
    print(a_type1, a_type2)
    a1 = ""
    a2 = ""
    # Once the type of accessories is determined, it will then compare in the types are the same.
    # If they are both the same, and it didn't determine that it didn't need a first accessory
    # the program will drop the 2nd accessory and add then generate what actual accessory the owl
    # will have and add it to the code.
    if a_type1 == a_type2 and a_type1 != "N/A":
        a1 = create_accessory_code(a_type1)
        owl_code.append(a1)
    # Else it will add on any accessories of the types it rolled as long as it
    # was determined the owl would have that accessory slot filled to begin with.
    else:
        if a_type1 != "N/A":
            a1 = create_accessory_code(a_type1)
            owl_code.append(a1)
        if a_type2 != "N/A":
            a2 = create_accessory_code(a_type2)
            owl_code.append(a2)

    return owl_code

Part 1a: Creating the pattern Code Key

def create_pattern_code(rarity, base):

def create_pattern_code(rarity, base):
    # The function determines the rarity of the pattern for the owl
    whats_the_rarity_pattern = chance_decider(rarity, "pattern").lower()
    # Each pattern is tied to a specific owl's base.
    # Within the patterns list, each owl is within a dictionary based on it's rarity
    # EX: "Common" owl bases have their patterns nested in the "Common" Dictionary
    # Each owl has Commom, Uncommon, Rare, and Legendary patterns
    which_base_rarity = 0
    if "C" in base:
        which_base_rarity = 0
    elif "U" in base:
        which_base_rarity = 1
    elif "R" in base:
        which_base_rarity = 2
    elif "L" in base:
        which_base_rarity = 3

    # it takes the rarity of the owl base, finds the owl's code key within the dictionary
    # then, based on the rarity of pattern rolled, it will chose a pattern from within a dictionary represnting each rarity
    # and then randomly chose an individual pattern's dictionary key
    if whats_the_rarity_pattern == "common":
        pattern_dict = Owl_Attributes.Patterns[which_base_rarity][base]["PC"]
        pattern = random.choice((list(pattern_dict.keys())))
        return pattern

    elif whats_the_rarity_pattern == "uncommon":
        pattern_dict = Owl_Attributes.Patterns[which_base_rarity][base]["PU"]
        pattern = random.choice((list(pattern_dict.keys())))
        return pattern

    elif whats_the_rarity_pattern == "rare":
        pattern_dict = Owl_Attributes.Patterns[which_base_rarity][base]["PR"]
        pattern = random.choice((list(pattern_dict.keys())))
        return pattern

    elif whats_the_rarity_pattern == "legendary":
        pattern_dict = Owl_Attributes.Patterns[which_base_rarity][base]["PL"]
        pattern = random.choice((list(pattern_dict.keys())))
        return pattern

Part 1b: Creating the accessories Code Key

def create_accessory_type_code(rarity):

def create_accessory_type_code(rarity):
    # the function takes the tier you rolled and determines the rarity of the accessory
    whats_the_rarity_accessory = chance_decider(rarity, "accessory").lower()
    if whats_the_rarity_accessory == "n/a":
        accessory_type_key = "N/A"
        return accessory_type_key
    if whats_the_rarity_accessory == "common":
        accessory_type_dict = Owl_Attributes.Accessories[0]["AC"]
        accessory_type_key = random.choice(list(accessory_type_dict.keys()))
        return accessory_type_key
    elif whats_the_rarity_accessory == "uncommon":
        accessory_type_dict = Owl_Attributes.Accessories[0]["AU"]
        accessory_type_key = random.choice(list(accessory_type_dict.keys()))
        return accessory_type_key
    elif whats_the_rarity_accessory == "rare":
        accessory_type_dict = Owl_Attributes.Accessories[0]["AR"]
        accessory_type_key = random.choice(list(accessory_type_dict.keys()))
        return accessory_type_key
    elif whats_the_rarity_accessory == "legendary":
        accessory_item_dict = Owl_Attributes.Accessories[0]["AL"]
        accessory_type_key = random.choice(list(accessory_item_dict.keys()))
        return accessory_type_key

def create_accessory_code(type):

def create_accessory_code(type):
    # if the type of accessory has "AC" in its key, meaning "Common" it makes sure to draw from with the
    # "Common" dictionary. Same for "AU" for "Uncommon", "AR" for "Rare" and "AL" for "Legendary"
    # It will then take the dictionary key for the type of accessory it previously rolled and search for a random
    # key for an accessory, returning the key to be added to the owl's final code name
    if "AC" in type:
        accessory_item_dict = Owl_Attributes.Accessories[0]["AC"][type]
        accessory_item_key = random.choice(list(accessory_item_dict.keys()))
        return accessory_item_key
    elif "AU" in type:
        accessory_item_dict = Owl_Attributes.Accessories[0]["AU"][type]
        accessory_item_key = random.choice(list(accessory_item_dict.keys()))
        return accessory_item_key
    elif "AR" in type:
        accessory_item_dict = Owl_Attributes.Accessories[0]["AR"][type]
        accessory_item_key = random.choice(list(accessory_item_dict.keys()))
        return accessory_item_key
    elif "AL" in type:
        accessory_item_dict = Owl_Attributes.Accessories[0]["AL"][type]
        accessory_item_key = random.choice(list(accessory_item_dict.keys()))
        return accessory_item_key

Part 2: Generating an Owl

def reincarnate_owl(code):

def reincarnate_owl(code):

    remade_owl = Image.open("Owl_project_pictures\_owl_blank_NC.png")
    # the code key for the base is always the first value in the code
    base_key = code[0]
    # the code key for the batter is always the second value in the code
    pat_key = code[1]
    color_key = code[3]
    color_parts = convert_colorlist_RGB(Owl_Attributes.Colors[0][color_key])
    eye_color_key = code[4]
    eye_color_parts = convert_colorlist_RGB(Owl_Attributes.Colors[0][eye_color_key])
    eye_color = eye_color_parts[0]
    common_colors = False
    uncommon_colors = False
    part_count = 0
    acc_count = 0
    # This list will soon be filled with strings representing the file paths for the art assets that will create the owl
    base_parts = []

    if "C" in base_key:
        base_parts = Owl_Attributes.Bases[0][base_key]
    elif "U" in base_key:
        base_parts = Owl_Attributes.Bases[1][base_key]
    elif "R" in base_key:
        base_parts = Owl_Attributes.Bases[2][base_key]
    elif "L" in base_key:
        base_parts = Owl_Attributes.Bases[3][base_key]

    # Patterns need to be layered properly on the owl to look right.
    # In this case, they are always layered on after the image with the file name ending in "_accents.png"
    # To make sure this happens, the pattern is generated and then, using a for loop, finds it's place within
    # the list of file path strings and inserted after the file path string that has "_accents" in it.
    # sometimes Patterns are a list of images meant to be layered on top of eachother, thus they are always treated as a
    #list when adding them.
    for parts in base_parts:
        if "_accent" in parts:
            part_count += 1
            # To add on the pattern, we need the image's file path. The base code key and it's respective pattern code key
            # are put through the generate_pattern function to retrieve the file path.
            pattern_list = generate_pattern(pat_key, base_key)
            for pat in pattern_list:
                base_parts.insert(part_count, pat)
                part_count += 1
        part_count += 1

    if "CC" in code[2]:
        common_colors = True
    elif "UC" in code[2]:
        uncommon_colors = True
    bonus_count = 0
    for b_parts in base_parts:
        if "_bonus1" in b_parts and common_colors:
            base_parts.remove(base_parts[bonus_count])
            base_parts.remove(base_parts[bonus_count])
        elif "_bonus2" in b_parts and uncommon_colors:
            base_parts.remove(base_parts[bonus_count])
        bonus_count+= 1

    # For each Accessory it uses a for loop to find each accessory code key within the code
    # it then takes each code key and runs it through the generate_accessory that returns that keys definition
    # that being a file path for an image.
    # depending on the file names within the path it will either add on the file path to the front of the list of file paths
    # or to the back of the list of file paths.
    for accessory in code:
        if "AC" in accessory or "AU" in accessory or "AR" in accessory or "AL" in accessory:
            accessory_part = generate_accessory(code[acc_count])
            if "Back Accessories" in accessory_part:
                base_parts.insert(0, accessory_part)
            elif "Front Accessories" in accessory_part:
                base_parts.append(accessory_part)
        acc_count+=1

Part 2a: Generating the Pattern

def generate_pattern(part_key, base_key):

def generate_pattern(part_key, base_key):
    # it takes the code key for the pattern and removes the numbers from it to create the key for rarity of the pattern
    # it takes the rarity of the owl, owl base code key, the code key of the rarity of the owl's pattern, and finally the patterns code key
    # to navigate the nested dictionaries to find the pattern image's list of file paths, which it then returns
    pattern = ""
    patt_rarity_key = remove_numbers(part_key)
    # print(patt_rarity_key)
    if "C" in base_key:
        pattern = Owl_Attributes.Patterns[0][base_key][patt_rarity_key][part_key]
        return pattern
    elif "U" in base_key:
        pattern = Owl_Attributes.Patterns[1][base_key][patt_rarity_key][part_key]
        return pattern
    elif "R" in base_key:
        pattern = Owl_Attributes.Patterns[2][base_key][patt_rarity_key][part_key]
        return pattern
    elif "L" in base_key:
        pattern = Owl_Attributes.Patterns[3][base_key][patt_rarity_key][part_key]
        return pattern

Part 2b: Generating the Accessories

def generate_accessory(part_key):

def generate_accessory(part_key):
    # it takes the code key for the accessory and removes the numbers from it to create the key for the type of
    # accessory it is to find it within the accessory dictionaries
    # it takes the rarity of the accessory as a key, then the type of accessory as a key
    # and then the code key of the accessory itself to find the file path of that accessory image, which it then returns
    accessory = ""
    acc_rarity_key = remove_numbers(part_key)
    if "AC" in part_key:
        accessory = Owl_Attributes.Accessories[0]["AC"][acc_rarity_key][part_key]
        return accessory
    elif "AU" in part_key:
        accessory = Owl_Attributes.Accessories[0]["AU"][acc_rarity_key][part_key]
        return accessory
    elif "AR" in part_key:
        accessory = Owl_Attributes.Accessories[0]["AR"][acc_rarity_key][part_key]
        return accessory
    elif "AL" in part_key:
        accessory = Owl_Attributes.Accessories[0]["AL"][acc_rarity_key][part_key]
        return accessory

Part 3: Relevant Dictionaries

Below are the relevant samples of the dictionaries for the owls file Owl Patterns

Patterns = [
    # Common Owls
    {
        # Great Grey Owl Patterns
        "BC0":
        {
            # Common Patterns
            "PC":
            {
                "PC0": ["Owl_project_pictures\_owl_blank_NC.png"],
                "PC1": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_feathers.png"],
                "PC2": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_leaves.png"],
                "PC3": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_piebald_NC.png"],
                "PC4": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_stripes.png"],
                "PC5": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_checkerboard.png"],
                "PC6": ["Owl_project_pictures\Common\Great Grey Owl\patterns\Common patterns\_greatgrey_raindrops.png"]
            },

Owl Accessories

Accessories = [
    {
        # Common Accessories
        "AC":
        {
                # Glasses
                "ACG":
                {
                    "ACG0": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_hex1.png",
                    "ACG1": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_hex2.png",
                    "ACG2": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_hex3.png",
                    "ACG3": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_hex4.png",
                    "ACG4": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_hex5.png",
                    "ACG5": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_rectangle1.png",
                    "ACG6": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_rectangle2.png",
                    "ACG7": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_rectangle3.png",
                    "ACG8": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_rectangle4.png",
                    "ACG10": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_rectangle5.png",
                    "ACG11": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_smallround1.png",
                    "ACG12": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_smallround2.png",
                    "ACG13": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_smallround3.png",
                    "ACG14": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_smallround4.png",
                    "ACG15": "Owl_project_pictures\Accessories\Common Accessories\Front Accessories\Glasses\_glasses_smallround5.png"
                },

Advertisement

Answer

Instead of this:

    for parts in base_parts:
        if "_accent" in parts:
            part_count += 1
            # To add on the pattern, we need the image's file path. The base code key and it's respective pattern code key
            # are put through the generate_pattern function to retrieve the file path.
            pattern_list = generate_pattern(pat_key, base_key)
            for pat in pattern_list:
                base_parts.insert(part_count, pat)
                part_count += 1
        part_count += 1

Do this:

    new_base = []
    for parts in base_parts:
        new_base.append( parts )
        if "_accent" in parts:
            new_base.extend( generate_pattern(pat_key, base_key) )
    base_parts = new_base

Now you’re not modifying a list while iterating it, and you aren’t trying to track the list indexes.

Although, now that I look at it, since that generate_pattern call doesn’t depend on the loop value, what is the purpose of this? Are you really trying to apply the pattern multiple times? What you have is the same as:

    pattern_list = generate_pattern(pat_key, base_key)
    new_base = []
    for parts in base_parts:
        new_base.append( parts )
        if "_accent" in parts:
            new_base.extend( pattern_list )
    base_parts = new_base
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement