├── requirements.txt ├── result ├── 1 │ ├── 1.png │ └── specifications.txt ├── 8 │ ├── 8.png │ └── specifications.txt └── 9 │ ├── 9.png │ └── specifications.txt ├── data ├── original.png ├── items │ ├── hats │ │ ├── 5 │ │ │ └── 5_5.png │ │ ├── 10 │ │ │ └── 8_10.png │ │ ├── 20 │ │ │ └── 7_20.png │ │ ├── 50 │ │ │ └── 10_50.png │ │ └── 80 │ │ │ └── 3_80.png │ ├── mustache │ │ ├── 5 │ │ │ └── 5_5.png │ │ ├── 10 │ │ │ └── 5_5.png │ │ ├── 20 │ │ │ └── 5_5.png │ │ ├── 50 │ │ │ └── 5_5.png │ │ └── 80 │ │ │ └── 5_5.png │ └── subjects │ │ ├── 5 │ │ └── 5_5.png │ │ ├── 10 │ │ └── 5_5.png │ │ ├── 20 │ │ └── 5_5.png │ │ ├── 50 │ │ └── 9_50.png │ │ └── 80 │ │ └── 7_80.png └── fonts │ ├── Ubuntu-Bold.ttf │ ├── Ubuntu-Italic.ttf │ ├── Ubuntu-Light.ttf │ ├── Ubuntu-Medium.ttf │ ├── Ubuntu-Regular.ttf │ ├── Ubuntu-BoldItalic.ttf │ ├── Ubuntu-LightItalic.ttf │ └── Ubuntu-MediumItalic.ttf ├── main.py ├── handlers ├── storage.py ├── rare_items.py └── slime_modification.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow==8.4.0 2 | pyfade==3.1 3 | -------------------------------------------------------------------------------- /result/1/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/result/1/1.png -------------------------------------------------------------------------------- /result/8/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/result/8/8.png -------------------------------------------------------------------------------- /result/9/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/result/9/9.png -------------------------------------------------------------------------------- /data/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/original.png -------------------------------------------------------------------------------- /data/items/hats/5/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/hats/5/5_5.png -------------------------------------------------------------------------------- /data/fonts/Ubuntu-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-Bold.ttf -------------------------------------------------------------------------------- /data/fonts/Ubuntu-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-Italic.ttf -------------------------------------------------------------------------------- /data/fonts/Ubuntu-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-Light.ttf -------------------------------------------------------------------------------- /data/fonts/Ubuntu-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-Medium.ttf -------------------------------------------------------------------------------- /data/items/hats/10/8_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/hats/10/8_10.png -------------------------------------------------------------------------------- /data/items/hats/20/7_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/hats/20/7_20.png -------------------------------------------------------------------------------- /data/items/hats/50/10_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/hats/50/10_50.png -------------------------------------------------------------------------------- /data/items/hats/80/3_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/hats/80/3_80.png -------------------------------------------------------------------------------- /data/fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /data/items/mustache/10/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/mustache/10/5_5.png -------------------------------------------------------------------------------- /data/items/mustache/20/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/mustache/20/5_5.png -------------------------------------------------------------------------------- /data/items/mustache/5/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/mustache/5/5_5.png -------------------------------------------------------------------------------- /data/items/mustache/50/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/mustache/50/5_5.png -------------------------------------------------------------------------------- /data/items/mustache/80/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/mustache/80/5_5.png -------------------------------------------------------------------------------- /data/items/subjects/10/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/subjects/10/5_5.png -------------------------------------------------------------------------------- /data/items/subjects/20/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/subjects/20/5_5.png -------------------------------------------------------------------------------- /data/items/subjects/5/5_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/subjects/5/5_5.png -------------------------------------------------------------------------------- /data/fonts/Ubuntu-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-BoldItalic.ttf -------------------------------------------------------------------------------- /data/fonts/Ubuntu-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-LightItalic.ttf -------------------------------------------------------------------------------- /data/items/subjects/50/9_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/subjects/50/9_50.png -------------------------------------------------------------------------------- /data/items/subjects/80/7_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/items/subjects/80/7_80.png -------------------------------------------------------------------------------- /data/fonts/Ubuntu-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zproger/NFT-Generator/HEAD/data/fonts/Ubuntu-MediumItalic.ttf -------------------------------------------------------------------------------- /result/1/specifications.txt: -------------------------------------------------------------------------------- 1 | Редкость предметов (шанс получить предмет в %) 2 | 3 | Усы: 100% 4 | Шляпа: 100% 5 | Оружие: 100% 6 | 7 | Вероятность получить такого же персонажа в % 8 | -> 100.0% -------------------------------------------------------------------------------- /result/9/specifications.txt: -------------------------------------------------------------------------------- 1 | Редкость предметов (шанс получить предмет в %) 2 | 3 | Усы: 100% 4 | Шляпа: 80% 5 | Оружие: 100% 6 | 7 | Вероятность получить такого же персонажа в % 8 | -> 80.0% -------------------------------------------------------------------------------- /result/8/specifications.txt: -------------------------------------------------------------------------------- 1 | Редкость предметов (шанс получить предмет в %) 2 | 3 | Усы: 20% 4 | Шляпа: 100% 5 | Оружие: 80% 6 | 7 | Вероятность получить такого же персонажа в % 8 | -> 16.000000000000004% -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from handlers.storage import banner 2 | from handlers.slime_modification import create_image 3 | from pyfade import Fade, Colors 4 | 5 | 6 | def main(): 7 | print(Fade.Vertical(Colors.red_to_blue, banner)) 8 | print("\nСколько изображений сгенерировать?") 9 | amount = int(input("-> ")) 10 | for number in range(amount): 11 | slime_num = number + 1 12 | create_image(slime_num) 13 | print(f"[slime-{slime_num}][{amount}/{slime_num}] - успешно создан") 14 | 15 | 16 | if __name__ == "__main__": 17 | main() -------------------------------------------------------------------------------- /handlers/storage.py: -------------------------------------------------------------------------------- 1 | 2 | # Баннер 3 | banner = """ 4 | ██████╗██████╗ ██╗ ██╗██████╗ ████████╗ ██████╗ 5 | ██╔════╝██╔══██╗╚██╗ ██╔╝██╔══██╗╚══██╔══╝██╔═══██╗ 6 | ██║ ██████╔╝ ╚████╔╝ ██████╔╝ ██║ ██║ ██║ 7 | ██║ ██╔══██╗ ╚██╔╝ ██╔═══╝ ██║ ██║ ██║ 8 | ╚██████╗██║ ██║ ██║ ██║ ██║ ╚██████╔╝ 9 | ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ 10 | 11 | █████╗ ██████╗ ████████╗███████╗ 12 | ██╔══██╗██╔══██╗╚══██╔══╝██╔════╝ 13 | ███████║██████╔╝ ██║ ███████╗ 14 | ██╔══██║██╔══██╗ ██║ ╚════██║ 15 | ██║ ██║██║ ██║ ██║ ███████║ 16 | ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ 17 | """ 18 | 19 | # Размеры изображений 20 | WIDTH = 2000 21 | HEIGHT = 2000 22 | 23 | # Список мягких RGB цветов 24 | RGB_BASE = [ 25 | (2, 63, 165), (125, 135, 185), (190, 193, 212), 26 | (214, 188, 192), (187, 119, 132), (142, 6, 59), 27 | (74, 111, 227), (133, 149, 225), (181, 187, 227), 28 | (230, 175, 185), (224, 123, 145), (211, 63, 106), 29 | (17, 198, 56), (141, 213, 147), (198, 222, 199), 30 | (234, 211, 198), (240, 185, 141), (239, 151, 8), 31 | (15, 207, 192), (156, 222, 214), (213, 234, 231), 32 | (243, 225, 235), (246, 196, 225), (247, 156, 212) 33 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFT Generator 2 | 3 | Автоматическая генерация крипто-артов на основе слоев изображения. 4 | 5 | ## Установка 6 | ```sh 7 | pip3 install -r requirements.txt 8 | rm -rf result/* 9 | ``` 10 | 11 | ## Как это работает 12 | Первым делом софт рисует фон рандомного цвета, используя мягкие RGBA оттенки. 13 | После чего используется data/original.png изображение и накладывается сверху нашего фона. 14 | Далее извлекаются предметы персонажа из папки data/items используя их % редкости. 15 | Всё это соединяется в 1 изображение, и каждый предмет проходит цветовую обработку, 16 | сдвигая все пиксели в нужную позицию, чтобы цвета фона и персонажа совпадали. 17 | 18 | Далее все готовые изображения отправляются в папку result. 19 | Под каждого персонажа создается своя папка с его изображением и характеристиками. 20 | В папке result/all_slimes, помещаются все изображения, чтобы было удобно ориентироваться и искать 21 | нужного персонажа. 22 | 23 | ## Обратите внимание 24 | 1. Все изображения должны быть в формате PNG 25 | 2. Все изображения должны быть размером 2000х2000 26 | 27 | ## Как пользоваться инструментом 28 | 1. Замените персонажа data/original.png на собственного. 29 | Файл должен называться также и быть размером 2000х2000 30 | 2. Замените все предметы на свои в data/items. 31 | Каждый предмет имеет свою редкость, поэтому самые редкие 32 | предметы должны лежать в папке с названием 5, а предметы с шансом 33 | выпадения в 80%, в папке 80. Они также должны быть размером 2000х2000, 34 | формат изображения PNG и они должны заранее находится в нужной позиции. 35 | 3. Запустите генерацию персонажей - python3 main.py и укажите нужное количество 36 | 37 | 38 | Каждый слой изображения рисуется отдельно в Photoshop. 39 | После того как все слои будут готовы, вы сможете создать свою собственную коллекцию. 40 | 41 | ### Демонстрация работы 42 | https://youtu.be/A0umx1xfgCs 43 | -------------------------------------------------------------------------------- /handlers/rare_items.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from PIL import Image 4 | 5 | 6 | # Получить рандомный предмет после определения уникальности 7 | def get_item(folder_name, percent): 8 | item_list = os.listdir(f"data/items/{folder_name}/{percent}") 9 | if len(item_list) > 0: 10 | return [percent, random.choice(item_list)] 11 | 12 | 13 | # Получить процент редкости предмета 14 | # Используемая редкость предметов (5%, 10%, 20%, 50%, 80%) 15 | def percent_checker(folder_name): 16 | rare_percent = random.randint(1, 100) 17 | if rare_percent <= 5: 18 | return get_item(folder_name, 5) 19 | elif rare_percent <= 10: 20 | return get_item(folder_name, 10) 21 | elif rare_percent <= 20: 22 | return get_item(folder_name, 20) 23 | elif rare_percent <= 50: 24 | return get_item(folder_name, 50) 25 | elif rare_percent <= 80: 26 | return get_item(folder_name, 80) 27 | else: 28 | return None 29 | 30 | 31 | # В случае успеха, возвращает объект с изображением усов 32 | # Также в 2м аргументе % редкости предмета 33 | def add_mustache(): 34 | item = percent_checker("mustache") 35 | # Дополнительно делаем уникальность 40% 36 | if random.randint(0, 100) > 60 and item is not None: 37 | return [Image.open(f"data/items/mustache/{item[0]}/{item[1]}").convert("RGBA"), item[0]] 38 | 39 | 40 | # В случае успеха, возвращает объект с изображением предмета 41 | # Также в 2м аргументе % редкости предмета 42 | def add_subjects(): 43 | item = percent_checker("subjects") 44 | # Дополнительно делаем уникальность 40% 45 | if random.randint(0, 100) > 60 and item is not None: 46 | return [Image.open(f"data/items/subjects/{item[0]}/{item[1]}").convert("RGBA"), item[0]] 47 | 48 | 49 | # В случае успеха, возвращает объект с изображением шляпы 50 | # Также в 2м аргументе % редкости предмета 51 | def add_hat(): 52 | item = percent_checker("hats") 53 | # Дополнительно делаем уникальность 40% 54 | if random.randint(0, 100) > 60 and item is not None: 55 | return [Image.open(f"data/items/hats/{item[0]}/{item[1]}").convert("RGBA"), item[0]] -------------------------------------------------------------------------------- /handlers/slime_modification.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import random 4 | from PIL import Image 5 | from PIL import ImageDraw 6 | from PIL import ImageFont 7 | 8 | from handlers.storage import * 9 | from handlers.rare_items import * 10 | 11 | 12 | # Добавляем текст к изображению 13 | def generate_text(): 14 | overlay = Image.new("RGBA", (500, 150), color=(0, 0, 0, 80)) 15 | dr1 = ImageDraw.Draw(overlay) 16 | fnt = ImageFont.truetype('data/fonts/Ubuntu-Bold.ttf', size=104) 17 | text = str(random.randint(100, 999)).encode("utf-8").hex() 18 | dr1.text( 19 | (14, 14), 20 | f"0x{text}", 21 | font=fnt, 22 | fill=(255, 255, 255, 160), 23 | ) 24 | return overlay 25 | 26 | 27 | # Изменить цвет слайма 28 | def change_color(slime_image): 29 | # Извлекаем пиксели 30 | slime_pixels = slime_image.load() 31 | 32 | # Генерируем диапазон смещения байта RGBA 33 | r_byte = random.randint(0, 70) 34 | g_byte = random.randint(0, 70) 35 | b_byte = random.randint(0, 70) 36 | 37 | # Редактируем пиксели слайма 38 | for w in range(WIDTH): 39 | for h in range(HEIGHT): 40 | # Получаем текущий пиксель и кортеж RGBA 41 | pixel = slime_pixels[w, h] 42 | 43 | if pixel != (0, 0, 0, 0): 44 | # Меняем цвет на рандомный диапазон 45 | # Красим только объект слайма 46 | slime_pixels[w, h] = ( 47 | pixel[0] + r_byte, 48 | pixel[1] + g_byte, 49 | pixel[2] + b_byte, 50 | 255 51 | ) 52 | 53 | 54 | def create_image(image_name): 55 | # Задний фон изображения 56 | background = random.choice(RGB_BASE) 57 | 58 | # Создаем объект изображения в памяти 59 | img = Image.new("RGB", (WIDTH, HEIGHT), background) 60 | original_slime = Image.open("data/original.png").convert("RGBA") 61 | 62 | # Генерируем текст и редактируем слайма 63 | overlay = generate_text() 64 | change_color(original_slime) 65 | 66 | # Добавляем слайма 67 | img.paste(original_slime, (0, 0), original_slime) 68 | 69 | # Добавляем усы 70 | mustache = add_mustache() 71 | if mustache is not None: 72 | change_color(mustache[0]) 73 | img.paste(mustache[0], (0, 0), mustache[0]) 74 | else: 75 | mustache = 100 76 | 77 | # Добавляем шляпу 78 | hat = add_hat() 79 | if hat is not None: 80 | change_color(hat[0]) 81 | img.paste(hat[0], (0, 0), hat[0]) 82 | else: 83 | hat = 100 84 | 85 | # Добавляем предмет 86 | subject = add_subjects() 87 | if subject is not None: 88 | change_color(subject[0]) 89 | img.paste(subject[0], (0, 0), subject[0]) 90 | else: 91 | subject = 100 92 | 93 | # Добавляем текст с HEX значением 94 | img.paste(overlay, (100, 100), overlay) 95 | 96 | # Сохраняем результат в отдельную папку 97 | if not os.path.exists(f"result/{image_name}"): 98 | os.mkdir(f"result/{image_name}") 99 | img.save(f"result/{image_name}/{image_name}.png") 100 | 101 | # Добавляем характеристики каждого слайма 102 | with open(f"result/{image_name}/specifications.txt", "w") as file: 103 | # Вероятность получить такого же персонажа в % 104 | mustache_percent = mustache[1] if isinstance(mustache, list) else mustache 105 | hat_percent = hat[1] if isinstance(hat, list) else hat 106 | subject_percent = subject[1] if isinstance(subject, list) else subject 107 | character_rarity = ((mustache_percent * 0.01) * (hat_percent * 0.01) * (subject_percent * 0.01)) * 100 108 | 109 | data = "Редкость предметов (шанс получить предмет в %)\n\n"\ 110 | f"Усы: {mustache_percent}%\n"\ 111 | f"Шляпа: {hat_percent}%\n"\ 112 | f"Оружие: {subject_percent}%\n\n"\ 113 | "Вероятность получить такого же персонажа в %\n"\ 114 | f"-> {character_rarity}%" 115 | file.write(data) 116 | 117 | # Сохраняем результат в общую папку 118 | if not os.path.exists("result/all_slimes"): 119 | os.mkdir("result/all_slimes") 120 | img.save(f"result/all_slimes/{image_name}({str(character_rarity).replace('.', ',')}).png") 121 | 122 | --------------------------------------------------------------------------------