forked from rounakbanik/generative-art-nft
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nft.py
132 lines (100 loc) · 3.9 KB
/
nft.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from PIL import Image
import numpy as np
import os
import csv
from os import path
from progressbar import progressbar
from config import CONFIG
# Parse the configuration file
def parse_config():
for layer in CONFIG:
# Get traits in sorted order from each layer
directory = os.path.join("assets", layer["directory"])
traits = sorted([x for x in os.listdir(directory) if x[0] != '.'])
# If layer is not required, add None to the traits array
if not layer["required"]:
traits.append(None)
# Generate final rarity weights
if layer["rarity_weights"] is None:
rarities = [1 for x in traits]
elif type(layer["rarity_weights"] == "list") and len(traits) == len(layer["rarity_weights"]):
rarities = layer["rarity_weights"]
else:
raise ValueError("Rarity weights is invalid")
# Weight rarities and return a numpy array that sums up to 1
layer["rarity_weights"] = np.array(rarities) / sum(rarities)
layer["traits"] = traits
# Get total number of distinct possible combinations
def get_total_combinations():
total = 1
for layer in CONFIG:
total = total * len(layer["traits"])
return total
# Generate a single image given an array of layers
def generate_image(layers, name):
# Treat the first layer as the background
background = Image.open(path.join("assets", layers[0]))
# Loop through layers 1 to n and stack them on top of another
for layer in layers[1:]:
image = Image.open(path.join("assets", layer))
background.paste(image, (0, 0), image)
# Save the final image
background.save(name)
# Generate a trait combination based on rarity weights
def generate_data_item():
result = []
for layer in CONFIG:
index = np.random.choice(range(len(layer["traits"])), p=layer["rarity_weights"])
result.append(index)
return result
# Generate the requested amount of unique samples
def generate_data(n):
data = set()
while len(data) < n:
traits = generate_data_item()
data.add(tuple(traits))
return [list(x) for x in data]
# Generate metadata to describe the generated images
def generate_metadata(filepath, data):
with open(filepath, "w", encoding="UTF8", newline='') as file:
writer = csv.writer(file)
writer.writerow([""] + [x["name"] for x in CONFIG])
for n, item in enumerate(data):
traits = [CONFIG[i]["traits"][x] for i, x in enumerate(item)]
writer.writerow([n] + ["none" if x is None else path.splitext(x)[0] for x in traits])
# Get images paths for the given data item
def get_data_item_paths(item):
paths = []
for i, value in enumerate(item):
trait = CONFIG[i]["traits"][value]
if trait is not None:
paths.append(path.join(CONFIG[i]["directory"], trait))
return paths
# Generate the image set
def generate_images(name, number):
# Create output directory if it does not exist
output_path = path.join("output", name, "images")
if not path.exists(output_path):
os.makedirs(output_path)
# Generate the images data
data = generate_data(number)
# Generate metadata to describe the images
generate_metadata(os.path.join("output", name, "metadata.csv"), data)
# Generate the images
for i in progressbar(range(len(data))):
paths = get_data_item_paths(data[i])
generate_image(paths, path.join(output_path, f"{i}.png"))
def main():
parse_config()
combinations = get_total_combinations()
print(f"\nYou can create {combinations} distinct images")
print("How many images would you like to create? ")
while True:
num_avatars = int(input())
if 0 < num_avatars <= combinations:
break
print("What is the name of the collection? ")
name = input()
generate_images(name, num_avatars)
if __name__ == "__main__":
main()