Наталья Кайда 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
  • Научишься тестировать код
  • Создашь бота для Телеграм, калькулятор для ипотеки, генератор безопасных паролей

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

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