На определённом этапе разработки своей игры я осознал, что мне нужна система диалогов с лицами-аватарами.
Поэтому я решил создать генератор лиц на основе знаменитой игры Papers, Please.

Генератор лиц Dukope (из Papers, Please)
Сбор данных
Я скачал примерно 50 портретов с pixabay.com (все изображения имеют лицензию CC и не требуют указания авторства).
Затем я вырезал фон каждого изображения в Photoshop (просто выбрал всё инструментом выделения многоугольника и нажал на Delete). В дальнейшем я планировал воспользоваться эффектом cutout , чтобы скрыть все недочёты.

Изображения с удалённым фоном
Сегментация лиц
Для сегментации лиц я использую слои. Просто размываю кистью «нос и рот», а затем «глаза и брови», а затем создаю для них в PS новый слой. Это я делаю, выбрав видимые пиксели (Ctrl+нажатие на миниатюру слоя) и создав новый слой из выделенного. Все волосы, очки и другие объекты, закрывающие лицо, отправляются на слой под названием «hair_overlap». Я помечаю сегменты во всех 50 изображениях.

Процесс работы со слоями
Также мне нужно подготовить лица для только что созданных элементов. Я использую Clone Stamp Tool, клонирую кожу и затираю все черты лица (новый слой затёрт инструментом «Палец» (Smudge)).
Извлечение черт лица
Некоторые скачанные мной портретные фото не подходят для использования целиком, поэтому я извлекаю только те черты лиц, которые можно использовать в генераторе (например, только нос и губы).
Я буду извлекать черты при помощи библиотек Python psd_tools и Pillow (их можно установить командой pip install psd-tools Pillow или при помощи IDE).

Этот фрагмент кода должен составить список всех слоёв и ограничивающих их прямоугольников.

… что он и делает.
Затем мне нужно экспортировать все слои в отдельные файлы изображений. Также я создаю по папке для каждого названия слоя (представленный ниже фрагмент кода создаёт множество папок и файлов, например, eyes/girl-919048_1920.psd.png).

Извлечение слоёв в файлы изображений

Извлечённые слои «eyes»

Извлечённые слои «nose_mouth»
Генератор лиц
Теперь мне просто нужно всё это соединить вместе, и в этом мне поможет Pillow Image.

Слой за слоем
Сгенерированные изображения довольно уродливы, поэтому нужно попытаться как-то скрыть их несовершенство фильтром cutout.

Сгенерированные изображения
Фильтр cutout довольно хорошо справляется с сокрытием изъянов.

Влияние Cutout в действии
К сожалению, в Python невозможно создать этот эффект без использования фильтров Собеля, медианных фильтров и дизеринга, для реализации которых потребуется очень много времени. Я нашёл плагин для GIMP FU_artist_cutout.scm, который делает то же самое, но использовать его будет неправильно. Я буду применять библиотеку G’MIC, у которой также есть онлайн-UI.

Онлайн-утилита G’MIC
Как оказалось, gmic может только использовать скрипты, у него нет параметра командной строки для эффекта cutout. Я нашёл определение эффекта cutout в файле gmic_stdlib.gmic:
fx_cutout :
-repeat $! -l[$>] -split_opacity -l[0]
-median {10-$3}
-quantize $1
--area. 0,1 med=${-med} -rm.
-inpaint_holes {$med*$2%},0,1
-if $4 -n 0,255 -endif-endl -a c -endl -done
Теперь я мог создать собственный фильтр cutout для g’mic:
# File `myCutout.gmic`
cutout :
-echo "Arguments : $1,$2"
-input $1
-repeat $! -l[$>] -split_opacity -l[0]
-median 6
-quantize 20
--area. 0,1 med=${-med} -rm.
-inpaint_holes {$med*0%},0,1
-endl -a c -endl -done -output $2
Имея его и инструмент командной строки, я готов к дальнейшей работе.

G’MIC в действии

Случайное лицо. Наложение волос работает, замечательно.
Теперь я просто подключаю всё это к подпроцессу и жду, пока он не сгенерирует нужное мне количество случайных лиц. Готово.

Код и несколько файлов .psd есть на моём github.
Автор: PatientZero