Сегодня я хотел бы показать вам больше того, чего вы можете достичь с помощью Python и библиотеки Pillow. Для тех, кто не знаком, Pillow - это библиотека изображений, то есть она помогает программно работать с изображениями.
В этой статье я хочу показать вам, как автоматизировать создание снимков экрана Twitter, например этого:
Как видите, изображение содержит фотографию пользователя, а также его имя, дескриптор Twitter и собственно твит. Не центрировать содержимое было сознательным решением.
И последнее, что нужно сделать перед кодом. Мы будем использовать Pillow, стороннюю библиотеку. Таким образом, вам сначала нужно установить его с помощью pip, менеджера пакетов Python. Просто перейдите в командную строку и введите pip install pillow
. Если вы работаете в Windows, как я, не забудьте запустить командную строку от имени администратора, иначе вы не сможете установить библиотеку.
А теперь давайте погрузимся в код. Мы будем шаг за шагом объяснять каждую часть кода, но есть суть кода с полным сценарием в конце.
Начиная с импорта, они довольно простые: три модуля из Pillow и функция из встроенного модуля textwrap
.
from PIL import Image, ImageDraw, ImageFont from textwrap import wrap
И это все. Я перейду к wrap
через минуту, но перед этим позвольте мне показать некоторые полезные постоянные переменные, которые мы можем определить для дальнейшего улучшения читаемости в скрипте.
FONT_USER_INFO = ImageFont.truetype("arial.ttf", 90, encoding="utf-8") FONT_TEXT = ImageFont.truetype("arial.ttf", 110, encoding="utf-8") WIDTH = 2376 HEIGHT = 2024 COLOR_BG = 'white' COLOR_NAME = 'black' COLOR_TAG = (64, 64, 64) COLOR_TEXT = 'black' COORD_PHOTO = (250, 170) COORD_NAME = (600, 185) COORD_TAG = (600, 305) COORD_TEXT = (250, 510) LINE_MARGIN = 15
Теперь позвольте мне объяснить, что они будут делать. Первые две переменные - это настройки шрифта, а именно одна для текста информации о пользователе (две строки рядом с фотографией пользователя), а другая для тела твита. Мы используем шрифт Arial размером 90 и 110 соответственно в кодировке UTF-8. Обратите внимание, что в зависимости от того, как ImageFont.truetype()
ищет шрифты, выбранный вами шрифт должен находиться либо в той же папке, что и сценарий, либо в каталоге шрифтов по умолчанию вашей операционной системы. Хотя вместо этого вы также можете передать абсолютный путь. Остальные переменные просто устанавливают размеры, цвета и координаты места рисования. (зная, что (0, 0) - это верхний левый угол изображения).
Далее идет информация об изображении, весь текст, который будет нарисован, и переменная с именем, которое будет использоваться файлом изображения, который мы создадим.
user_name = "José Fernando Costa" user_tag = "@soulsinporto" text = "Go out there and do some fun shit, not because it makes money, but because it is fun for you!" img_name = "work_towards_better_tomorrow"
А теперь о настройке переменных трекера и расчетах. Я разделю эту часть на три пояснения: одно для вышеупомянутой wrap()
функции, одно для переменных x
и y
и третье для расчета высоты строки.
text_string_lines = wrap(text, 37)
Здесь функция wrap()
разбивает единственную строку, содержащую наш твит, на более мелкие строки. На самом деле мы не можем сказать скрипту нарисовать эту одну строку и ожидать, что она будет красиво разделена на строки текста. Вместо этого у нас естьwrap()
, которые делают это за нас, возвращая список строк. Второй аргумент 37
означает, что длина строки не превышает 37 символов.
x = COORD_TEXT[0] y = COORD_TEXT[1]
Это просто переменные трекера. x
на самом деле никогда не меняется, я создал его только для того, чтобы впоследствии передать (x,y)
в качестве координат рисования, но y
, с другой стороны, будет отслеживать текущее вертикальное положение, в котором нужно рисовать строку текста. Как видите, обе переменные занимают горизонтальное и вертикальное положение соответственно, в котором будет нарисована первая строка твита.
temp_img = Image.new('RGB', (0, 0)) temp_img_draw_interf = ImageDraw.Draw(temp_img) line_height = [ temp_img_draw_interf.textsize( text_string_lines[i], font=FONT_TEXT )[1] for i in range(len(text_string_lines)) ]
(Приносим извинения за не очень оптимальное форматирование кода в этом примере)
А теперь несколько кратких расчетов. Чтобы извлечь высоту, необходимую для рисования каждой строки тела твита, мы сначала создаем новый Image
. Технически изображения не существует, поскольку оно имеет нулевую ширину и высоту, но нам нужно создать объект этого типа и его интерфейс рисования, чтобы мы могли вызвать метод textsize()
для извлечения высоты, необходимой для рисования каждой строки текста. используя наши настройки шрифта, определенные ранее. textsize()
возвращает кортеж, состоящий из двух элементов, ширину и высоту, необходимые для рисования линии, но нас интересует только второе значение, то есть с индексом один ([1]
).
Если вы не знакомы с синтаксисом, используемым для переменной line_height
, это специальный синтаксис Python, называемый составлением списка (или сокращенно составлением списка). По сути, это цикл for, в котором мы перебираем каждую строку текста, хранящуюся в списке text_string_lines
, и для каждой строки вызываем метод textsize()
, чтобы извлечь высоту, необходимую для рисования этой строки. В результате line_height
представляет собой список целых чисел, которые представляют высоту, необходимую для рисования каждой строки текста для тела твита. Для получения дополнительных сведений о списках я настоятельно рекомендую прочитать Документацию Python по этому вопросу.
Теперь мы, наконец, подошли к последней части сценария, собственно созданию и отрисовке изображения. Опять же, давайте пойдем по одному фрагменту за раз.
img = Image.new('RGB', (WIDTH, HEIGHT), color='white') draw_interf = ImageDraw.Draw(img)
Это то, что вы видели раньше при создании временного Image
объекта, а теперь о создании нашего «настоящего» Image
объекта. Изображение использует цветовую модель RGB, оно имеет ширину WIDTH
и высоту HEIGHT
, а его фоновое изображение белое (белый - это именованный цвет в модуле, поэтому мы можем использовать имя вместо передачи значений RGB). Следующая строка создает интерфейс рисования для изображения, который, как следует из названия, отвечает за любые действия рисования в изображении.
draw_interf.text(COORD_NAME, user_name, font=FONT_USER_INFO, fill=COLOR_NAME) draw_interf.text(COORD_TAG, user_tag, font=FONT_USER_INFO, fill=COLOR_TAG)
В этих строках мы просто рисуем имя пользователя, а затем дескриптор пользователя. Список переданных параметров: координаты, с которых следует начать рисование контента, контент, который будет рисоваться (текст в обоих случаях), настройки шрифта и цвет, который будет использоваться для рисования. Просто деталь, COLOR_NAME
- это именованный цвет, черный, а COLOR_TAG
вместо этого использует значения RGB, переданные как кортеж из трех значений.
for index, line in enumerate(text_string_lines): draw_interf.text((x, y), line, font=FONT_TEXT, fill=COLOR_TEXT) y += line_height[index] + LINE_MARGIN
В блоке выше мы рисуем те строки текста, о которых говорили с самого начала. Короче говоря, мы перебираем список линий и рисуем каждую линию, как мы это делали с именем пользователя и дескриптором.
В деталях, мы используем аккуратную функцию enumerate()
, чтобы на каждой итерации мы имели доступ как к строке текста (line
), так и к ее индексу в списке (index
). В первой строке цикла мы рисуем line
, точно так же, как с именем пользователя и дескриптором, но здесь координаты рисования задаются отслеживающими переменными x
и y
, которые мы обсуждали ранее. В следующей строке мы обновляем y
, трекер вертикального положения, в котором отрисовывается текст. Мы увеличиваем его текущее значение на высоту, необходимую для рисования только что нарисованной строки текста, а затем добавляем еще немного расстояния, которое работает как межстрочный интервал.
Теперь все, что нам не хватает для полного изображения, - это добавить фотографию пользователя. Прежде чем объяснять, как сделать эту последнюю часть, позвольте мне предварить ее, сказав, что эта пользовательская фотография должна быть кругом 250x250 с прозрачностью для углов, поскольку значения и размеры, используемые в этом примере, были оптимизированы для этого размера. Конечно, вы можете поэкспериментировать со всем этим и создать свои собственные изображения. В конце концов, это цель этой статьи, я просто даю вам (новые) инструменты, которые помогут реализовать ваше творчество!
user_photo = Image.open('user_photo.png', 'r')
Итак, сначала мы загружаем фото пользователя. Если фотография находится в том же каталоге, что и сценарий, вы можете использовать имя файла, как в примере, но если это не так, вам необходимо указать путь к нему. Второй аргумент, r
, означает, что фотография загружена в режиме чтения, то есть ее нельзя изменить.
img.paste(user_photo, COORD_PHOTO, mask=user_photo)
И это вторая последняя строка сценария. Мы берем ту фотографию, которую только что загрузили, и вставляем ее в наше рабочее изображение. Первый аргумент - это изображение, которое нужно вставить, второй - координаты, в которые оно будет вставлено, а третий - маска, которая будет использоваться в процессе.
К настоящему времени первые два аргумента легко понять, но третий может вызвать путаницу. Видите ли, когда вы вставляете изображение, если маска не указана, то загруженное изображение будет вставлено поверх вашего рабочего изображения, игнорируя его прозрачность. Таким образом, если мы также используем изображение для вставки в качестве собственной маски, тогда Pillow будет вставлять изображение только поверх области, покрытой маской. Маска не учитывает прозрачность как область рисования, поэтому в результате мы вставляем в рабочее изображение только круг 250x250, а не квадрат 250x250.
img.save(f'{img_name}.png')
И мы наконец дошли до конца. Модификации изображения завершены, поэтому мы можем просто сохранить его как новый файл на нашем компьютере. Если вы не знакомы с f перед строкой, то это для f-строк, лучшего форматирования строк, представленного в Python 3.6. Я рекомендую вам ознакомиться с этим руководством, если вы не знакомы с ним, поскольку это лучший способ форматирования строк в Python. Другими словами, мы сохраняем изображение как файл PNG (отсюда и расширение .png) с именем (строкой), установленным в переменной img_name
.
И это все. Из этой статьи вы узнали, как создавать новые изображения с помощью Pillow, как рисовать текст и как вставлять изображения (с прозрачностью). Как видите, эта библиотека чрезвычайно полезна для программной работы с изображениями, и это еще не все. Вы можете рисовать фигуры и тому подобное, изменять размер изображений, добавлять фильтры и многое другое.
Последнее, что у меня есть для вас, - это суть кода с полным сценарием.
Теперь поэкспериментируйте с этой фантастической библиотекой и автоматизируйте создание собственных изображений.
Как обычно, любые отзывы приветствуются для улучшения будущих статей :)