Python отлично подходит для ряда вещей. Он способствует 1,4% Интернета , НАСА много использует! И вы можете использовать его в Создать искусство Анкет В честь День Звездных войн Я хотел создать программу, которая сгенерировала световые мечь и написал в Твиттере их один раз в день.
TL; DR
Я создал компьютерную программу, которая генерирует уникальный световой меч, сделанный из четырех разных частей (лезвие, рукоятка, племена и кнопка) и твиты в Твиттере раз в день вместе с некоторыми статистическими данными о световом мете.
У Люка ненадолго был красный световой меч
Начиная
Общая предпосылка этого сценария довольно проста. Он случайным образом выбирает четыре части светового меча, а затем собирает их вместе. Для достижения этого я использовал онлайн -инструмент под названием Пискель Чтобы генерировать все 8-битные детали. В то время как Пискель обычно используется для создания анимированных спрайтов для веб-сайтов, это один из лучших 8-битных художественных редакторов. Моя любимая особенность – это возможность зеркала линий. Это упрощает процесс изготовления симметричных световых мечей.
Как только я создал базовый набор частей, положите их в изображения
каталог. Этот каталог имеет 5 папок, по одной для каждой части и выходной папки ( Световые мечты
). Это позволяет мне сохранить все организованное, а также помогает позже при генерации световых мечей.
images/ blades/ b1.png b2.png hilts/ h1.png h2.png buttons/ u1.png pommels/ p1.png lightsabers/ h1b2u1p1.png
Чтобы сделать все это, я установил три пакета Python. Я использую Подушка (Версия 7.1.1) Для всей обработки изображений, Tweepy (Версия 3.8.0), чтобы отправить твиты и Numpy (версия 1.18.3), чтобы сделать некоторую цветовую обработку, которую я буду обсуждать позже. В целом это очень простые требования.
# requirements.txt numpy==1.18.3 Pillow==7.1.1 tweepy==3.8.0
Первая версия этого сценария была действительно такой простой. Позвоните Generate_lightSaber
и это все. Далее я расскажу о том, как я сгенерировал Начальный световой меч (только лезвие и рукоять), тогда как я добавил кнопки и Pommels И наконец, как я твитнул финальное изображение Анкет
# lightsaber.py import os, random from pathlib import Path, PurePath # This is actually Pillow but it's API is backwards compatible with the older PIL from PIL import Image def generate_lightsaber(): # Will talk about this more next if __name__ == " __main__": generate_lightsaber()
Добавление лезвий и рук
В моем первоначальном дизайне у меня были только лезвия и хлебы, так что я начал. Моя первая цель состояла в том, чтобы получить единый лезвие, а один рукоятник объединился и правильно выстроился на одном изображении. Я впервые добавил код, чтобы принести уникальную часть светового меча на основе приведенной выше макета каталога.
# lightsaber.py # Set some constants to keep the code clean and consistent IMAGE_PATH = Path('../images') BLADE_PATH = IMAGE_PATH / 'blades' HILT_PATH = IMAGE_PATH / 'hilts' OUTPUT_PATH = IMAGE_PATH / 'lightsabers' def fetch_lightsaber_parts(): hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}") blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}") return (hilt, blade)
Во -первых, у нас есть os.listdir ()
, который возвращает список имен файлов в данном каталоге. Список находится в произвольном порядке, и он не включает в себя специальные записи ». и «..» даже если они присутствуют в каталоге. Однако он включает в себя любые папки или специальные файлы (например, .ds_store в OS X), поэтому убедитесь, что ваша папка включает только файлы изображений. Мы передаем это имя файла в random.choice
который выберет случайное имя файла. Наконец, мы создаем полный путь файла и возвращаем его как для рукоятью, так и для лезвия.
Как только мы получили пути изображения, мы сможем начать объединять их в конечное изображение.
# lightsaber.py def generate_lightsaber(): blade_path, hilt_path = fetch_lightsaber_parts() # Open the images using Pillow blade = Image.open(blade_path, 'r') hilt = Image.open(hilt_path, 'r') # Open the output image. Twitter displays the entire image if it's 1024x512 output = Image.new("RGB", (1024, 512), (255, 255, 255)) # Paste the blade and hilt onto the output image. output.paste(blade, blade_offset, mask=blade) output.paste(hilt, hilt_offset, mask=hilt) # Save the output image to disk img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))
Во -первых, мы возьмем пути, как описано выше. После этого мы будем использовать подушку, чтобы открыть изображения, а также создать новое выходное изображение с шириной и высотой, указанной Twitter, чтобы позволить нам показать все изображение в качестве предварительного просмотра. Наконец, мы вставляем два оригинальных изображения и сохраняем вывод.
Вставьте изображения на вывод, вы увидите два аргумента, которые я еще не показал,
и Маска
Анкет Смещение используется для определения того, где части размещаются на выходном изображении. Когда вы поймете это неправильно, могут произойти забавные вещи.
Самый крошечный из лезвий
Чтобы рассчитать смещение, где изображение помещается, мне нужно получить и сохранить все размеры изображений. Затем мы рассчитываем среднюю позицию по ширине (output_w - blade_w)//2
Это выравнивает его горизонтально на изображении. Это верно как для лезвия, так и для руководителя.
Чтобы позиционировать лезвие и рукоять вертикально, требуется немного больше математики. Для рукояти мы берем высоту выходного изображения (512px) и вычитаем высоту изображения рукояти (от 70 до 120 пикселей в зависимости от конструкции). Это заставляет рукоять начинаться внизу изображения.
# lightsaber.py output_w, output_h = output.size blade_w, blade_h = blade.size hilt_w, hilt_h = hilt.size blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2) hilt_offset = (output_h - hilt_h, (output_w - hilt_w) // 2)
Вы можете придумать изображение как массив
Используя массив выше в качестве примера, если бы у нас был световой меж шириной 1PX и высотой 2PX (1PX, 1PX -лезвие), математика была бы.
blade_offset = ((4 - 1 - 1), (5 - 1) // 2 ) # (2, 2) hilt_offset = ((4 - 1), (5 - 1) // 2) # (3, 2)
Наконец, мы должны передать исходное изображение в качестве маски, в противном случае подушка по умолчанию будет по умолчанию прозрачные фоны в черный. Используя подушку маски только записывает данные на выходное изображение для пикселей, которые имеют цвет. На изображении ниже слева применяется маска, а справа нет.
Добавление смещений рукояти и дополнительную информацию
При расчете смещения лезвия вы, возможно, заметили переменную hilt_offset
Анкет В некоторых дизайнах та часть, где лезвие выходит из рукояти, не является началом светового меча. В этих случаях мне нужно было начать лезвие, чтобы начать «ниже», где начинается рукоять. Зная, что мне нужно сделать это, но также хранить метаданные на уклонах, лезвиях, кнопках и популярности в будущем, я создал файл manifest.py для хранения этих значений по умолчанию.
# manifest.py MANIFEST = { "hilt": { "h1": { "offsets": { "blade": 5 } } } } # lightsaber.py from manifest import MANIFEST def get_hilt_offset(hilt): return MANIFEST['hilt'][hilt]['offsets']['blade'] def generate_lightsaber(): ... hilt_offset = get_hilt_offset(hilt) blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2)
Без этого смещения я изначально получал плавающее лезвие для определенных дизайнов. Ниже вы можете видеть, что нижняя часть лезвия выходит с вершиной самой правой стороны светового меча, но он все еще выглядит смешно.
Сила должна держать эту вещь вместе
Этот манифестный файл также содержит информацию о смещениях кнопок, цветов и информации о твите, но я более подробно расскажу об этом позже.
Добавление кнопок
Получив линию лопастей и вырубков, я понял, что достиг 10 000 уникальных световых мечей только с лезвиями и руками, которые мне нужно было бы разработать по 100 по 100 (всего 200). Мои 8-битные художественные навыки просто не в этом. Добавляя кнопки, я мог бы сократить уникальные конструкции, необходимые только 22 каждая (всего 66). Кнопки были добавлены к конечному изображению точно так же, как и лезвия.
# lightsaber.py # Set some constants to keep the code clean and consistent IMAGE_PATH = Path('../images') BLADE_PATH = IMAGE_PATH / 'blades' HILT_PATH = IMAGE_PATH / 'hilts' BUTTON_PATH = IMAGE_PATH / 'buttons' OUTPUT_PATH = IMAGE_PATH / 'lightsabers' def fetch_lightsaber_parts(): hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}") blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}") button = Path(f"{BUTTON_PATH}/{random.choice(os.listdir(BUTTON_PATH))}") return (hilt, blade, button) def generate_lightsaber(): blade_path, hilt_path, button_path = fetch_lightsaber_parts() # Open the images using Pillow blade = Image.open(blade_path, 'r') hilt = Image.open(hilt_path, 'r') button = Image.open(button_path, 'r') # Open the output image. Twitter displays the entire image if it's 1024x512 output = Image.new("RGB", (1024, 512), (255, 255, 255)) output_w, output_h = output.size blade_w, blade_h = blade.size hilt_w, hilt_h = hilt.size button_w, button_h = button.size hilt_offset = get_hilt_offset(hilt) blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2) hilt_offset = (output_h - hilt_h, (output_w - hilt_w) // 2) # Paste the blade and hilt onto the output image. output.paste(blade, blade_offset, mask=blade) output.paste(hilt, hilt_offset, mask=hilt) output.paste(button, get_button_offset(hilt), mask=button) # Save the output image to disk img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))
Основное осложнение с кнопками заключается в том, что их можно помещать в квадрат на рукоять, и этот квадрат отличается для каждого рукояти. Есть несколько разных способов сделать это автоматически (обнаруживать ребра, обнаружить ширину и высоту лезвия и т. Д.), Но для простоты я просто решил вручную рассчитать его для каждого рукояти и сохранить его в манифестном файле. X.
# manifest.py "hilt": { "h1": { "offsets": { "blade": 0, "button": { "x": (8, 9), "y": (110, 111) }, }, }, } # lightsaber.py def get_button_offset(hilt): between_x = MANIFEST['hilt'][hilt]['offsets']['button']['x'] between_y = MANIFEST['hilt'][hilt]['offsets']['button']['y'] return (random.randint(between_x[0], between_x[1]), random.randint(between_y[0], between_y[1]))
Добавление Pommels
Еще раз, в изображение были добавлены племени почти так же, как и другие, только на этот раз мне нужно было обновить высоту как лезвия, так и рукоятки, чтобы приспособить Pommel. Сначала это не прошло хорошо.
Это то, что происходит, когда вы получаете смешанные +’и –
# lightsaber.py # Set some constants to keep the code clean and consistent IMAGE_PATH = Path('../images') BLADE_PATH = IMAGE_PATH / 'blades' HILT_PATH = IMAGE_PATH / 'hilts' BUTTON_PATH = IMAGE_PATH / 'buttons' POMMEL_PATH = IMAGE_PATH / 'pommels' OUTPUT_PATH = IMAGE_PATH / 'lightsabers' def fetch_lightsaber_parts(): hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}") blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}") button = Path(f"{BUTTON_PATH}/{random.choice(os.listdir(BUTTON_PATH))}") pommel = Path(f"{POMMEL_PATH}/{random.choice(os.listdir(POMMEL_PATH))}") return (hilt, blade, button, pommel) def generate_lightsaber(): blade_path, hilt_path, button_path, pommel_path = fetch_lightsaber_parts() # Open the images using Pillow blade = Image.open(blade_path, 'r') hilt = Image.open(hilt_path, 'r') button = Image.open(button_path, 'r') pommel = Image.open(pommel_path, 'r') # Open the output image. Twitter displays the entire image if it's 1024x512 output = Image.new("RGB", (1024, 512), (255, 255, 255)) output_w, output_h = output.size blade_w, blade_h = blade.size hilt_w, hilt_h = hilt.size button_w, button_h = button.size pommel_w, pommel_h = pommel.size pommel_offset = pommel_h hilt_offset = get_hilt_offset(hilt_name) - pommel_offset button_offset = get_button_offset(hilt_name) blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2) hilt_offset = (output_h - hilt_h - pommel_offset, (output_w - hilt_w) // 2) pommel_offset = (output_h - pommel_h, (output_w - pommel_w) // 2) # Paste the blade and hilt onto the output image. output.paste(blade, blade_offset, mask=blade) output.paste(hilt, hilt_offset, mask=hilt) output.paste(button, get_button_offset(hilt), mask=button) # Save the output image to disk img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))
Следующим вызовом, с которым я столкнулся с Pommels, было их проектирование, поэтому они выглядели красиво на всех руках. Когда я впервые спроектировал световой меч, я включил и рукоять, и Pommel. Когда я понял, что мне нужно разделить их, чтобы обеспечить более уникальные комбинации, я просто разрезаю старые 8-битные изображения на два. Проблема состоит в том, что цветовые схемы между хильтами и популярностью просто не совпадали.
После того, как я много думал, я решил, что смогу сделать что -то похожее на то, как работают зеленые экраны. Если бы я спроектировал изображения с определенными цветами (в данном случае, и) я мог бы вытащить их, используя подушку и Numpy и заменить их цветами, которые я хочу.
# manifest.py "hilt": { "h1": { "offsets": { "blade": 0, "button": { "x": (8, 9), "y": (110, 111) }, }, "colours": { "primary": (216,216,216), #d8d8d8 "secondary": (141,141,141), #8d8d8d "tertiary": (180, 97, 19), #b46113 }, }, } # lightsaber.py import numpy as np def convert_colours(img, hilt): img = img.convert('RGBA') data = np.array(img) # Grab the pixels which are 100% red, 100% blue and 100% green red, green, blue, alpha = data.T primary = (red == 255) & (blue == 0) & (green == 0) secondary = (red == 0) & (blue == 255) & (green == 0) tertiary = (red == 0) & (blue == 0) & (green == 255) # Substitute out the colours for the hilt colour scheme data[..., :-1][primary.T] = MANIFEST['hilt'][hilt_name]['colours']['primary'] data[..., :-1][secondary.T] = MANIFEST['hilt'][hilt_name]['colours']['secondary'] data[..., :-1][tertiary.T] = MANIFEST['hilt'][hilt_name]['colours']['tertiary'] return Image.fromarray(data) def generate_lightsaber(): ... pommel = Image.open(pommel_path, 'r') pommel = convert_colours(pommel, hilt_name) ...
Как только я это сделал, все было установлено. У Pommels теперь есть правильная цветовая схема в качестве светового меча.
Создание текста случайного твита
Последней частью всего этого является генерирование фактического твита. Я добавил несколько полей в манифестный файл, включая длину и материал, цвет лезвия, кристалл и кто его использовал, и, наконец, длину племени. После этого новая функция, GENERATE_TWEET_TEXT
, это собирает всю эту новую информацию вместе и генерирует текст, чтобы твитнуть.
# manifest.py "hilt": { "h1": { "offsets": { "blade": 0, "button": { "x": (8, 9), "y": (110, 111) }, }, "colours": { "primary": (216,216,216), #d8d8d8 "secondary": (141,141,141), #8d8d8d "tertiary": (180, 97, 19), #b46113 }, "length": 24, "materials": "Alloy metal/Salvaged materials" }, }, "blade": { "b1": { "colour": "Red", "crystal": ["Ilum crystal", "Ultima Pearl"], "type": "Sith" }, }, "pommel": { "p1": { "length": 5, }, } # lightsaber.py AVERAGE_HILT_LENGTH = 25 AVERAGE_POMMEL_LENGTH = 3 AVERAGE_BLADE_LENGTH = 90 NAMES = ['List', 'of', 'generated', 'names'] def generate_tweet_text(hilt, blade, pommel): hilt_details = MANIFEST['hilt'][hilt] blade_details = MANIFEST['blade'][blade] pommel_details = MANIFEST['pommel'][pommel] hilt_length = hilt_details['length'] pommel_length = pommel_details['length'] total_length = hilt_length + pommel_length average_length = AVERAGE_HILT_LENGTH + AVERAGE_POMMEL_LENGTH blade_length = int(AVERAGE_BLADE_LENGTH * (total_length / average_length)) title = blade_details['type'] if type(title) is list: title = random.choice(title) crystal = MANIFEST['blade'][blade]['crystal'] if type(crystal) is list: crystal = random.choice(crystal) name = f"{title} {random.choice(NAMES)}" tweet = f'''Owner: {name} Hilt Length: {total_length} cm Blade Length: {blade_length} cm Blade Colour: {MANIFEST['blade'][blade]['colour']} Kyber Crystal: {crystal} #StarWars ''' return tweet
Чтобы сгенерировать уникальные имена, я использовал это Генератор имени Звездных войн Чтобы сгенерировать несколько имен. Затем я случайно выбираю один для использования.
Чтобы твитнуть последний твит, я использую библиотеку Python Tweepy сделать для меня всю тяжелую работу. Поскольку у меня уже есть изображение, сохраненное в файле, все, что мне нужно сделать, это получить учетные данные с переменной среды, загрузить носитель, а затем опубликовать твит.
consumer_key = os.getenv('CONSUMER_KEY') consumer_secret = os.getenv('CONSUMER_SECRET') access_token = os.getenv('ACCESS_TOKEN') access_token_secret = os.getenv('ACCESS_TOKEN_SECRET') auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) tweet_text = generate_tweet_text(hilt, blade, pommel) media = api.media_upload(path) api.update_status(status=tweet_text, media_ids=[media.media_id,])
Если вы впервые настроите бота в Твиттере, вам нужно Сначала создайте приложение Возьмите свои учетные данные, а затем храните их в переменной среды.
Завершая все это
Теперь, когда все работает правильно, у меня есть уникальный световой меч и разместить его в Twitter, мне просто нужно Добавьте его в Cron бежать каждый день.
12 21 * * * python3 lightsaber.py >> lightsaber.log
Это будет запускать программу каждый день в 21:12. И с этим мы закончили! У нас есть программа Python, которая генерирует уникальный световой меч, каждый день и пишет в Твиттере. Какой джедаи/ситх владеет вашим любимым световым мечом?
Пост Использование Python для создания более 10 000 уникальных 8-битных световых мечей появился первым на Откладывание разработчика Анкет
Оригинал: “https://dev.to/adammckerlie/using-python-to-generate-over-10-000-unique-8-bit-lightsabers-31ae”