Наталья Кайда 30 сентября 2024

🎨 Как сделать генератор ASCII-графики на Python

Энтузиасты делают ASCII-ремейки «Звездных войн» и ролевые ASCII-игры. А мы напишем GUI-приложение для конвертации изображений в олдскульную ASCII-графику с помощью Python и библиотеки Pillow.
🎨 Как сделать генератор ASCII-графики на Python

Техника создания иллюстраций с помощью печатных символов появилась задолго до появления компьютеров – в конце 19-го века. Первые текстовые изображения создавали на печатных машинках и публиковали в руководствах для машинисток в качестве упражнений:

Арт-печать считалась хорошим упражнением для развития навыков
Арт-печать считалась хорошим упражнением для развития навыков

При создании изображений на печатной машинке можно располагать символы внахлест – благодаря этому изображения выглядят почти как типографские иллюстрации. Автором первой полноценной текстовой иллюстрации считается машинистка Флора Стэйси. Эта картинка была опубликована в «Фонетическом журнале» в октябре 1898 г:

Иллюстрация составлена из множества скобок, дефисов, звездочек, точек и нескольких прописных букв
Иллюстрация составлена из множества скобок, дефисов, звездочек, точек и нескольких прописных букв

С появлением первых компьютеров текстовый арт сразу вышел на новый уровень: теперь печатную графику можно было создавать программно. Первые примеры ASCII-арта можно найти в компьютерных журналах и документации 1960-х годов, чуть позже с помощью ASCII стали создавать сложную графику, анимацию и игры.

ASCII-портрет Моны Лизы в фильме «Служебный роман» 1977
ASCII-портрет Моны Лизы в фильме «Служебный роман» 1977

Компьютерная графика прошла долгий путь от простых текстовых артов до фотореалистичных изображений, сгенерированных с помощью ИИ. Но ASCII-арт никуда не делся и продолжает пользоваться популярностью: энтузиасты создают программы для генерации и редактирования текстовой графики/анимации, делают ASCII-версии «Звездных войн» и потрясающие игры.

Как конвертировать изображение в ASCII-арт с помощью Python

Мы напишем приложение для генерации ASCII-графики на Python. Это приложение:

  • Обрабатывает изображения в форматах jpg, png и bmp.
  • Имеет GUI (интерфейс) на Tkinter.
  • Сохраняет готовый ASCII-арт в текстовом файле с тем же именем, что и название исходного изображения.
🎨 Как сделать генератор ASCII-графики на Python

У приложения есть одна внешняя зависимость – библиотека Pillow, которую необходимо установить с помощью команды pip install pillow.

В библиотеке Pillow есть множество инструментов для сложных манипуляций с изображениями. В нашем случае Pillow поможет:

  • Уменьшить исходное изображение до желаемой ширины, и пропорционально скорректировать его высоту.
  • Преобразовать изображение в оттенки серого.

Интерфейс

Создание основного окна приложения

В начале кода мы импортируем необходимые модули и создаем класс ImageToASCIIConverter. Этот класс будет основным окном нашего приложения. В конструкторе __init__ мы определяем размеры окна и его положение на экране, а также инициализируем словарь для хранения ссылок на изображения:

        class ImageToASCIIConverter(tk.Tk):
    def __init__(self):
        super().__init__()
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        # Вычисляем координаты для размещения окна по центру
        x = (screen_width // 2) - (700 // 2)  # Ширина окна - 700 пикселей
        y = (screen_height // 2) - (600 // 2)  # Высота окна - 600 пикселей
        self.geometry(f"700x600+{x}+{y}")
        self.title("Конвертация изображения в ASCII-арт")
        self.original_image = None
        self.ascii_image = None
        self.images = {}
        self.create_widgets()
    

Создание виджетов

Метод create_widgets отвечает за создание всех виджетов (кнопок, текстовых полей, холста для отображения изображений) в окне приложения. Здесь мы создаем фреймы для размещения виджетов, холст для отображения исходного изображения, текстовое поле для отображения ASCII-арта, а также кнопки для выбора изображения, преобразования его в ASCII-арт и сохранения результата:

            def create_widgets(self):
        # Создание фрейма и элементов интерфейса
        main_frame = tk.Frame(self)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)

        # Исходное изображение
        original_image_frame = tk.Frame(main_frame)
        original_image_frame.pack(side="left", padx=10)

        # Плейсхолдер
        self.canvas = tk.Canvas(original_image_frame, width=300, height=300)
        self.canvas.pack()
        self.canvas.create_rectangle(0, 0, 300, 300, fill="light blue", outline="")
        self.canvas.create_text(150, 150, text="Выберите изображение", font=("Verdana", 16))

        # Текстовое поле для вывода ASCII-арта
        ascii_text_frame = tk.Frame(main_frame)
        ascii_text_frame.pack(side="right", padx=10)
        self.ascii_text = tk.Text(ascii_text_frame, width=100, height=50, font=("Courier", 4), state="disabled")
        self.ascii_text.pack(side="top", pady=10)

        # Кнопки
        button_frame = tk.Frame(self)
        button_frame.pack(pady=10)
        choose_image_button = tk.Button(button_frame, text="Выбрать изображение", command=self.choose_image)
        choose_image_button.pack(side="left", padx=5)
        self.convert_button = tk.Button(button_frame, text="Конвертировать", command=self.display_ascii_art, state="disabled")
        self.convert_button.pack(side="left", padx=5)
        self.save_button = tk.Button(button_frame, text="Сохранить", command=self.save_ascii_art, state="disabled")
        self.save_button.pack(side="left", padx=5)
    

Основная функциональность

Выбор изображения

Метод choose_image открывает диалоговое окно для выбора файла изображения. Все доступные для обработки форматы файлов определены в image_exts, при желании пользователь может выбрать изображение определенного формата. Если пользователь выбирает изображение, оно отображается на холсте, а кнопка Конвертировать становится доступной для нажатия:

            def choose_image(self): #Типы файлов, видимые в диалоговом окне
        image_exts = r"*.jpg *.jpeg *.png *.bmp"
        image_filetypes = [
            ("Изображения", image_exts),
            ("JPEG", "*.jpg;*.jpeg"),
            ("PNG", "*.png"),
            ("BMP", "*.bmp"),
            ("Все файлы", "*.*")
        ]
        file_path = filedialog.askopenfilename(filetypes=image_filetypes)
        if file_path:
            try:
                self.original_image = Image.open(file_path)
                self.display_original_image()  # Отображаем изображение на холсте
                self.convert_button.config(state="normal")
            except Exception as e:
                print(f"Ошибка при открытии файла: {e}")
    

Преобразование изображения в ASCII-арт

Процесс создания ASCII-версии исходного изображения состоит из нескольких этапов:

  • Пропорциональное уменьшение картинки. Изображение уменьшается до желаемого размера с помощью метода resize_img. Примечание: ASCII-версия получается слегка вытянутой по вертикали. Пропорции можно скорректировать, уменьшив высоту, но мелкие детали при этом теряются.
            def resize_img(self, image, new_width=100):
        # Вычисляем высоту и ширину уменьшенного изображения, сохраняя соотношение сторон
        width, height = image.size
        ratio = height / width 
        new_height = int(new_width * ratio)
        resized_img = image.resize((new_width, new_height))
        return resized_img
    
  • Преобразование исходного изображения в оттенки серого. Это делается для упрощения процесса конвертации, так как оттенки серого позволяют более точно оценить яркость каждого пикселя. Метод pixel_to_grayscale принимает изображение в качестве аргумента и использует метод convert("L") из библиотеки Pillow для преобразования изображения в оттенки серого.
            def pixel_to_grayscale(self, image):
        # Преобразование изображения в оттенки серого
        grayscale_img = image.convert("L")
        return grayscale_img
    
  • Сопоставление каждого пикселя с соответствующим ASCII-символом. Метод pixel_to_ascii принимает изображение в оттенках серого и извлекает данные о пикселях с помощью метода getdata(). Затем для каждого пикселя вычисляется индекс в списке ASCII_CHAR, который зависит от его яркости. Этот список содержит символы, отражающие различные уровни яркости, начиная от самых темных (@) до самых светлых (.) Для вычисления индекса яркость пикселя делится на 25 (поскольку в списке ASCII_CHAR 25 символов, каждый из которых представляет определенный диапазон яркости), и результат округляется вниз до ближайшего целого числа. В результате получается строка, состоящая из ASCII-символов, представляющих исходное изображение.
            def pixel_to_ascii(self, image):
        # Преобразованиe пикселей изображения в ASCII символы
        pixels = image.getdata()
        characters = "".join(ASCII_CHAR[pixel // 25] for pixel in pixels)
        return characters
    
  • Конвертация изображения в ASCII-арт. Метод convert_to_ascii объединяет предыдущие три шага – sначала изображение уменьшается до желаемого размера, затем преобразуется в оттенки серого и, наконец, каждый пиксель преобразуется в ASCII-символ. Полученная строка ASCII-символов форматируется так, чтобы каждый символ соответствовал одному пикселю исходного изображения, и разбивается на строки с заданной шириной.
            def convert_to_ascii(self, image, new_width=100):
        grayscale_image = self.pixel_to_grayscale(self.resize_img(image))
        new_image_data = self.pixel_to_ascii(grayscale_image)
        pixel_nb = len(new_image_data)
        ascii_img = "\n".join(new_image_data[i : i + new_width] for i in range(0, pixel_nb, new_width))
        return ascii_img
    

🎨 Как сделать генератор ASCII-графики на Python

Код готового приложения можно взять в репозитории ascii_art.

🐍 Библиотека питониста
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»
🐍🎓 Библиотека Python для собеса
Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека Python для собеса»
🐍🧩 Библиотека задач по Python
Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python»

Заключение

Несмотря на потрясающие возможности современной компьютерной графики, ASCII-арт остается популярным – благодаря своей уникальной эстетике, ностальгическому очарованию и обширным возможностям для творчества. С помощью ограниченного набора текстовых символов ASCII-художники создают удивительные визуальные образы, демонстрируя свое мастерство и умение работать в рамках ограниченной «палитры».

Новые проекты появляются регулярно, один из последних интересных примеров – ASCII-кинотеатр, в котором показывают текстовые версии новинок кинопроката. В общем, несмотря на свой почтенный возраст, ASCII-арт продолжает жить, вдохновляя творческих людей на новые эксперименты, позволяющие им воплотить свои идеи в самой минималистичной, но вместе с тем элегантной и выразительной форме текстового искусства.

***

Хочешь создавать крутые проекты, как генератор ASCII-графики? Курс «Основы программирования на Python» от Proglib Academy поможет тебе начать:

  • Освоишь базу Python
  • Научишься тестировать код
  • Создашь бота для Телеграм, калькулятор для ипотеки, генератор безопасных паролей

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Java Engineer
по итогам собеседования
Senior Java Developer
от 350000 RUB до 400000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ