Нейросеть + ZennoPoster = FakeAvatar Generator (а так же выносим в общий код все что не относится к логике проекта)

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
Всем добра!


С каждым днем область применения нейросетей растет — на сегодняшний день они способны генерировать вполне связные, последовательные тексты, отличить которые от человеческих иногда достаточно проблематично (привет копирайтерам), писать несложный код (это пипец, товарищи программеры), озвучвать тексты (неплохо, с интонациями), разукрашивать и ретушировать черно-белые исторические фото, добавлять на фото различные эффекты (или удалять лишние детали — фоны, бывших баб 8-), пьяных дятлов и т.д.)(и фотошоперам тоже привет), да и просто генерировать различные медиаобъекты (аудио, фото, видео), в том числе человеческие лица. Единственный (но очень жирный) недостаток нейросети — требования к железу для ее развертывания (особенно это касается GPU). И хотя это очень удручает (по крайней мере лично меня), в сети все чаще можно найти web-интерфейсы для получения платного и (как правило с ограничениями) бесплатного контента от мощных нейросетей.


В статье я предлагаю воспользоваться одним из web-сервисов, который возвращает сгенерированные лица никогда не существовавших людей. Правда максимальный размер бесплатного изображения составляет всего 512х512 пикселей (вот тут уточнение — пока писал шаблон, с¥ки заграничные стали отдавать картинки размером 256х256px), с учетом новых обстоятельств, конечно так себе, но при увеличении до 512px получается в принципе не сильно хуже, чем у многих юзеров соцсетей:

68993


И хотя, по большей части, генерированные лица получаются неплохо, ложка дегтя имеется — нейросеть (она же искусственный интеллект) на самом деле все же тупая железяка (по-крайней мере пока), и среди фейков отличного качества можно встретить красивых телок с вполне себе четкой щетиной или косоглазых, странных неопределимых оно, ну и прочие приколы, вроде прозрачных зубов :D. Да и качество генерации волос тоже так себе — в локонах присутствует грязь и следы каких-то фонов, а сами волосы иногда выглядят столь же нелепо, как выглядел бы учебник по C# в порнофильме:

68994


68995


Предлагаемый шаблон делает следующее:
  1. Собирает и сохраняет фейк-лица
  2. Ресайзит их и сохраняет в указанную папку (опционально)
  3. Собирает фоны, обрезает их под размер изображений несуществующих людей
  4. Накладывает фейки на фоны для уникализации изображений
Каждый пункт мы разберем подробнее чуть позже.

Шаб, за исключением стандартного Zenno-свитча, написан полностью на C#, не многопоточный, сбор ссылок и на лица, и на фоны выполняется через web. Имеется возможность указать проксю — я пользую для этих целей свистки и 3proxy, так что формат такой — protocol://ip:port (например http://localhost:3099). К использованию подходят только http прокси, т.к. я не нашел простого способа подружить Webclient (о нем ниже) с SOCKS проксями (стандартная реализация базового класса их не поддерживает). Штатным кубиком загрузить фото не удалось, через геты пару раз ткнулся и бросил — времени не было на эксперименты. Фильтровать орков с женским лицом и щетиной придется вручную, но тут уж ничего не попишешь.

Структура шаблона

В папке проекта находится сам шаблон, список для промежуточных операций и три папки — img, bg, ready. В папку img будут сохраняться фейки, в папку bg — фоны, а в папку ready готовые аватары.
В директории img имеется подпапка resize, в которую по-умолчанию сохраняются фото фейк-лиц с измененными размерами, а в директории bg подпапка croped (для сохранения обрезанных фоновых изображений ). В папке ready, при генерации создаются подпапки с именем файла фейк-фото,внутри которых текущее фото авы накладывается на все доступные фоны. Сделал так для того, чтобы можно было сразу выбрать то, что получилось более-менее адекватно (с учетом артефактов на волосах, прозрачных зубов и т.д.) и удалить всю лишнюю шнягу.

Сам шаблон состоит из трех веток — сбор фейк-фото, сбор фонов, создание аватар:


68998


1. Сбор фейк-фото

Страница ресурса, с которой будем брать фейковые лица https://generated.photos/faces.

На странице можно выбирать опции — положение головы, пол, возраст, расу цвет волос и т.д. А еще выбрать фон фейка — и это очень кстати, т.к. по-умолчанию страница отдает ссылки на .jpg, а вот если выбрать любой из предложенных вариантов, вместо жипегов подтягиваются ссылки на .png без фона. А это же просто песня!

68999


Немного поковыряв страницу, выяснил, что чекбоксы отвечающие за выбор красивых молодых телок (почти все, за исключением выбора фона) всего лишь добавляют параметры к ссылке. Например, если мы выбираем пол female, то к ссылке добавляется параметр /female, если выбираем белую расу (кстати — у создателей сайта никакого уважения к современным тенденциям политкорректности: тут тебе и черные, и латиносы, и азиаты), то к ссылке добавится /white-race ну и т.д. Конечно параметры добавляются в определенном порядке, но это же дело техники!

После изучения страницы нарисовался алгоритм действий:

1.1. Формирование ссылки на основании опций, выбираемых во входных настройках
1.1.1. Выбор типа лица (All, Natural, Beautefied)
1.1.2. Выбор позиции головы (front, left, right)
1.1.3. Выбор пола (male, female)
1.1.4. Выбор возраста ()
1.1.5. Выбор расы ()
Описывать все возможные опции не стал — сложность формирования ссылки возрастает, а профит от всего этого довльно сомнительный.
1.2. Переход по сформированной ссылке, проверка загрузки страницы
1.3. Выбор параметра Transparent в секции выбора фона (по умолчанию фото отдаются в jpg, чтобы брать фото в png нужно выбрать параметр transparent, какой-то фон из предложенных или указать свой цвет)
1.4. Выбор количества кликов на кнопку «showmore». По умолчанию страница возвращает 30 изображений, при нажатии на кнопке showmore подгружается еще 30 картинок и т.д.
1.5. Сбор коллекции ссылок в список проекта, сохранение фото в папку img, с unixtime в качестве уникальноого имени файла.
1.6. Ресайз сохраненных фейков на указанное количество процентов, сохранение в папке img/resize

Выбор и изменение параметров фото(пол, возраст, раса и т.п.), количество кликов на «show more» доступно из входных настроек.

2. Сбор фоновых изображений

В качестве донора выбрал https://www.pexels.com/, точнее их страницу поиска https://www.pexels.com/search/background/ — первое что попалось из бесплатных стоков (вру — первым был pixabay, но там оригиналы картинок лучше спрятаны, не стал усложнять шаб). Можно выбрать другую страницу этого же ресурса (например https://www.pexels.com/search/abstract/). Можно и другой ресурс, но придется переписывать xpath и имя аттрибутов, содержащих ссылки на фото. Из личных наблюдений — лучше всего подходят светлые фоны, при наложении на которые, сгенерированный мусор в волосах незаметен. Но выстреливают и другие.

Алгоритм сбора фонов получился такой:

1.1. По умолчанию страница возвращает 60 изображений. Чтобы увеличить их количество, добавил цикл из 10 итераций с использованием метода FullEmulationMouseMoveToHtmlElement.
1.2. Сбор ссылок на фоны по xpath
1.3. Сохранение фонов в папку bg
1.4. Кроп изображений до размера 512х512 px
1.5. Сохранение в папку bg/croped

3. Наложение собранных лиц на собранные фоны

Т.к. качество генерации аватар не всегда на высоте (я об этом выше писал), а следовательно на некоторых фонах будут видны артефакты нейросетевых косяков, я решил накладывать каждую фейк-морду на все фоны и сохранять результат в отдельную папку (например лицо 1 накладываем на фоны 11,12,13 — в папке 1 у нас будут фото с именами 11.jpg,12.jpg, 13.jpg), чтобы можно было сразу удалить ненужное г.

Ну вот, шаблон работает, клепает нам фейковые аватары пачками, и вроде все хорошо, но... Мне не очень нравится, что в нашем проекте смешаны мухи и котлеты, точнее логика сбора изображений (как фейк-лиц, так и фонов), их обработки и сохранения, с обработкой и проверкой промежуточных шагов, таких как проверки загрузки страницы, наличия элемента на ней и т.п. Предлагаю немного отредактировать шаблон, и вынести в общий код все, что не имеет отношения к логике, а заодно подробнее познакомиться с кодом шаблона.

4. Вынос в общий код всех операций которые не имеют отношения к логике шаблона

Открываем окно «Директивы using и общий код», и после всех указанных using и закрывающей фигурной скобки namespace ZennoLab.OwnCode добавляем свое пространство имен FakeGen, в котором и будем записывать наши доработки, в нем создаем класс ZHelper. Чтобы можно было работать с экземплярами (объектами) этого класса, создаем приватные поля и конструктор класса (который при создании экземпляра передает в него объекты ZP project и instance):

Создаем пространство имен и класс, в который вынесем все лишнее:
namespace FakeGen
{
    public class ZHelper
        {
            private Instance instance;
            private IZennoPosterProjectModel project;
            private Tab tab;
            private HtmlElement he;
            private HtmlElementCollection hecol;
            private Random rnd;


            #region Конструктор класса
            /// <summary>
            /// Конструктор класса — служит для создания экземпляра класса
            /// </summary>
            /// <param name="project">текущий проект</param>
            /// <param name="instance">текущий instance</param>
            public ZHelper(IZennoPosterProjectModel project, Instance instance)
            {
                //при создании экземпляра присваиваем закрытым полям класса
                this.instance = instance;
                this.project = project;
                tab = instance.ActiveTab;
                he = null;
                hecol = null;
                rnd = new Random();
            }
            #endregion
 
    }
}
Теперь вернемся к первому кубику проекта, где выполняется очистка кэша и куков, закрытие всех вкладок, проверка и установка прокси (я понимаю что похожие реализации для общего кода уже имеются на форуме):

C#:
//Чистим куки, кэш, закрываем вкладки
instance.ClearCache();
instance.ClearCookie();
instance.CloseAllTabs();

//проверяем переменную проекта и устанавливаем прокси если она не пуста
if(!string.IsNullOrEmpty(project.Variables["proxy"].Value))
{
    instance.SetProxy(project.Variables["proxy"].Value,false,true,true,true);
}
Вроде все понятно, но можно сделать еще чуть понятнее, и свернуть в один метод очистку кэша и куков, закрытие вкладок, а во второй метод проверку переменной и установку прокси. Добавляем методы очистки инстанса и установки прокси в класс ZHelper сразу после конструктора:

Общий код:
/// <summary>
/// Метод чистит куки, кэш и закрывает все вкладки
/// </summary>
/// <param name="instance">текущий инстанс проекта (instance)</param>
public static void InstanceClearAndClose(this Instance instance)
{
    instance.ClearCache();
    instance.ClearCookie();
    instance.CloseAllTabs();
}
/// <summary>
/// Метод проверяет переменную проекта proxy, если она не пуста, устанавливает прокси проекта
/// </summary>
/// <param name="project">текущий проект</param>
/// <param name="instance">текущий инстанс проекта (instance)</param>
public static void CheckAndSetProxy(IZennoPosterProjectModel project, Instance instance)
{
    string proxy = project.Variables["proxy"].Value;
    if(!string.IsNullOrEmpty(proxy))
    {
        instance.SetProxy(proxy,false,true,true,true);
        project.SendInfoToLog(string.Format("Установили прокси {0}", proxy), true);
    }
    else project.SendInfoToLog("Прокси не указан", true);
}
Так как это служебные методы шаблона, я указал их как статические, а так как они не возвращают никакх значений в кубик, вместо типа возвращаемого значения указываем ключевое слово void. Теперь кубик проекта можно переписать так:

C#:
//Очистили куки и кэш, закрыли все вкладки
FakeGen.ZHelper.InstanceClearAndClose(instance);

//Если в переменной проекта proxy не пусто, устанавливаем прокси
FakeGen.ZHelper.CheckAndSetProxy(project, instance);
И глаз уже не отвлекается на почти одинаковые действия с инстансом и проверку переменной проекта на пустоту.

4.1. Ветка сбора фейк-лиц — вынос в общий код операций, не имеющих отношения к логике

Первым действием мы собираем ссылку — здесь куча if-ов, которые режут глаза, и их хочется свернуть, чтобы получить одну красивую строчку. Но, это логика работы именно шаблона, поэтому я решил оставить все как есть.

Следующий кубикпереход на страницу и поиск контрольного элемента на странице — т.к. частенько окончательно страница формируется скриптами, мне кажется что наиболее верный способ убелиться в ее загрузке — попытиться найти html-элемент, с которым планируется работать. Вынесем эти действия в метод общего кода (я больше не буду упоминать, что все методы мы пишем в классе ZHelper пространства имен FakeGen), добавив к ним функционал, на автомате пробующий повторно загрузить страницу, если контрольный элемент не найден:

Общий код:
/// <summary>
/// Расширение стандартного Zenno-метода Navigate — Проверяет существование таба, переходит на страницу, ждет ее загрузку,
/// выдерживает указанную паузу(по умолчанию 10с) и проверяет по xpath наличие на стр. контрольного элемента.
/// Если элемент не найден, пробует загрузить еще раз
/// </summary>
/// <param name="url">Url страницы, на которую нужно перейти</param>
/// <param name="xpath">Xpath элемента, наличие которого будет подтверждением загрузки страницы</param>
/// <param name="refer">Страница с которой переходим, необязательный параметр</param>
/// <param name="counter">Количество попыток загрузить страницу, по умолчанию 3</param>
public Tab NavigateWaitAndCheckElement(string url, string xpath, int pause = 10, string refer = "", int counter = 3)
{
    if ((tab.IsVoid) || (tab.IsNull)) throw new Exception("Не найден Tab!");
    if (tab.IsBusy) tab.WaitDownloading();
 
    for(int i=0; i<counter; i++)
    {
        tab.Navigate(url, refer);
        if (tab.IsBusy) tab.WaitDownloading();
     
        //Пауза перед проверкой элемента подбирается индивидуально в зависимости от качества прокси и наличия-отсутствия скриптов подгужающих элементы страницы
        Thread.Sleep(pause*1000);
     
        if(tab.URL == url && tab.URL != "about:blank" && tab.URL != "about:startpage")
        {
            he = tab.FindElementByXPath(xpath, 0);
            if(!he.IsNull)
            {
                project.SendInfoToLog("На странице найден контрольный html-элемент", false);
                break;
            }
        }
        if(counter == i+1) throw new Exception(string.Format("Ошибка загрузки страницы {0}", url));
    }
    return tab;
}
До этого кубик перехода на страницу выглядел так:

C#:
Tab tab = instance.ActiveTab;
//ссылка которую собрали в предыдущем кубике
string url = project.Variables["AVA_link"].Value;
//xpath элемента для проверки загрузки страницы
string xpath = @"//div[@class='card-image']//img";


if ((tab.IsVoid) || (tab.IsNull)) return -1;
if (tab.IsBusy) tab.WaitDownloading();
tab.Navigate(project.Variables["AVA_link"].Value, "");
if (tab.IsBusy) tab.WaitDownloading();

var he = instance.ActiveTab.FindElementByXPath(xpath, 0);
if(he.IsNull) throw new Exception("Не найден элемент!");
После выноса в общий код всех проверок и нескольких попыток загрузить страницу, кубик стал выглядеть так:

C#:
//ссылка которую собрали в предыдущем кубике
string url = project.Variables["AVA_link"].Value;
//xpath элемента для проверки загрузки страницы
string xpath = @"//div[@class='card-image']//img";

//инициализируем экземпляр созданного нами класса ZHelper
ZHelper gotopage = new ZHelper(project, instance);
//Переходим на страницу — работая с экземпляром класса
gotopage.NavigateWaitAndCheckElement(url, xpath, 7);
Теперь нам нужно прокликать по кнопке «show more» чтобы увеличить количество возвращаемых страницей фотографий:

C#:
//количество кликов по кнопке из вх настроек
int counter = Int32.Parse(project.Variables["AVA_clickCount"].Value);
//если не указано — выходим из кубика
if(counter == 0) return "Количество кликов на кнопку «show more» не указано, по кнопке не кликаем";

//Кнопка показать еще
var he = instance.ActiveTab.FindElementByXPath(@"//button[@class='loadmore-btn']", 0);
if(he.IsNull || he.IsVoid) throw new Exception("Не найдена кнопка Show more");

//кликаем
for(int i=0; i<counter; i++)
{
    //рандом для паузы подбирал экспериментально. Чувствителен к качеству прокси
    he.Click();
    //пауза
    Thread.Sleep(rnd);
}
Выносим все это в общий код:

Общий код:
/// <summary>
/// Метод выполняет заданное количество кликов (минимум 1) по указанному html-элементу
/// </summary>
/// <param name="he">html-элемент по которому нужно кликать</param>
/// <param name="count">Количество кликов по элементу. По умолчанию = 0, поэтому указывать не обязательно</param>
public void FindElementAndCycleClick(HtmlElement he, int count = 0)
{
    for(int i=0; i<count; i++)
    {
        int rnd = new Random().Next(1533, 2711);
        he.Click();
        Thread.Sleep(rnd);
    }
}
И переделываем кубик:

C#:
//xpath кнопка показать еще
string xpath = @"//button[@class='loadmore-btn']";
//Количество кликов
int count = Int32.Parse(project.Variables["AVA_clickCount"].Value);
if(count == 0) return "Количество кликов на кнопку «show more» не указано, по кнопке не кликаем";

//новый экземпляр класса ZHelper
ZHelper shmore = new ZHelper(project, instance);
//Кнопка показать еще
var he = shmore.FindElementByXpathAndCheck(xpath);
//Кликаем по кнопке «Показать еще» столько, сколько указали во вх настройках или хотя бы 1 раз
shmore.FindElementAndCycleClick(he, count);
В следующем шаге нам нужно выбрать прозрачный фон фейков — в таком случае вместо jpg-картинок возвращаемых по умолчанию, подтянутся ссылки на .png без фона:

C#:
//Xpath дропдаун-кнопки «Background color»
string xddown = @"//div[@class='background-details']//button[@class='summary']";
//Xpath кнопки выбора прозрачного фона
string xselector = @"//div[@class='colors']/div[contains(@class,'transparent')]";

//ищем и разворачиваем дропдаун
var he = instance.ActiveTab.FindElementByXPath(xddown, 0);
if(he.IsNull || he.IsVoid) throw new Exception("Не найден html-элемент");
he.Click();

//pause
int rnd = new Random().Next(2357,3471);
Thread.Sleep(rnd);

//ищем элемент и выбираем прозрачный фон
he = instance.ActiveTab.FindElementByXPath(xselector,0);
if(he.IsNull || he.IsVoid) throw new Exception("Не найден html-элемент");
he.Click();
Пользуясь написанным ранее методом FindElementByXpathAndCheck, а так же стандартным методом ZP Click(), переписываем кубик выбора прозрачного фона:

C#:
//Xpath дропдаун-кнопки «Background color»
string xddown = @"//div[@class='background-details']//button[@class='summary']";
//Xpath кнопки выбора прозрачного фона
string xselector = @"//div[@class='colors']/div[contains(@class,'transparent')]";

//новый экземпляр класса ZHelper
ZHelper trselect = new ZHelper(project, instance);
//ищем и разворачиваем дропдаун
trselect.FindElementByXpathAndCheck(xddown,"button").Click();

//pause
int rnd = new Random().Next(2357,3471);
Thread.Sleep(rnd);

//ищем элемент и выбираем прозрачный фон
trselect.FindElementByXpathAndCheck(xselector, "div class contans transparent").Click();
Кубик «Проверка и установка путей для сохранения», проверяет на пустоту переменные, заданные во входных настройках пути к папкам, и устанавливает значения по умолчанию, если они пусты. Это логика шаблона, и я оставляю ее как есть.

Кубик получения в список ссылок на фейк-фото. Здесь мы получаем коллекцию html-элементов, получаем из каждого элемента значение аттрибута со ссылкой на фото и сохраняем эти ссылки в список:

C#:
//список проекта
IZennoList links = project.Lists["links"];
//очистили его перед испрользованием
links.Clear();
//xpath элементов коллекции
string xpath = @"//div[@class='card-image']//img";

//получили коллекцию
HtmlElementCollection hecol = instance.ActiveTab.FindElementsByXPath(xpath);
//проверяем, что коллекция не пуста
if(hecol.Count >0)
{
    project.SendInfoToLog(hecol.Count.ToString());
    //элементы коллекции в список
    foreach(HtmlElement he in hecol)
    {
        string link = he.GetAttribute("src");
        links.Add(link);
    }
}
Выносим обработку коллекции и сохранение в список в общий код:

Общий код:
/// <summary>
/// Метод выполняет заданное количество кликов (минимум 1) по указанному html-элементу
/// </summary>
/// <param name="he">html-элемент по которому нужно кликать</param>
/// <param name="count">Количество кликов по элементу. По умолчанию = 0, поэтому указывать не обязательно</param>
public void FindElementAndCycleClick(HtmlElement he, int count = 0)
{
    for(int i=0; i<count; i++)
    {
        int rnd = new Random().Next(1533, 2711);
        he.Click();
        Thread.Sleep(rnd);
    }
}
Кубик сохранения фейковых фото в указанную директорию:

C#:
//список проекта
IZennoList links = project.Lists["links"];
//Рандом
Random r = new Random();
//папка для сохранения
string dir = project.Variables["AVA_savePath"].Value;


//в цикле получаем ссылки и сохраняем фото
foreach(string url in links)
{
    //путь для сохранения и имя изображения
    string path = dir + DateTimeOffset.Now.ToUnixTimeSeconds().ToString()+".png";

    //сохраняем изображение из указанного урла в указанный путь используя стандартный класс C# WebClient
    System.Net.WebClient wc = new System.Net.WebClient();
    //прокси
    System.Net.WebProxy wp = new System.Net.WebProxy(project.Variables["proxy"].Value);//("http://127.0.0.1:3097");//
    wc.Proxy = wp;

    wc.DownloadFile(url,path);
    //случайная пауза
    int rnd = r.Next(977,1789);
    Thread.Sleep(rnd);
}
Опять перерабатываем его в метод общего кода:

Общий код:
/// <summary>
/// Метод получает из списка проекта строку с url фото и загружает фото в указанную директорию. Имя файла — текущее значение unixtime, расширение - png
/// </summary>
/// <param name="list">Список проекта, в котором собраны ссылки на фото</param>
/// <param name="dir">путь к директории в которую будем сохранять картинки</param>
/// <param name="ext">формат сохраняемого изображения - .jpg, .png</param>
public void GetListItemSaveImg(IZennoList list, string dir, string ext)
{
    //Проверяем что список не пуст
    if(list.Count == 0) throw new Exception("Методу был передан пустой список!");
    if(string.IsNullOrEmpty(ext)) throw new Exception("Не указан формат сохранения изображения");
    //сохраняем изображение из указанного урла в указанный путь используя стандартный класс C# WebClient
    System.Net.WebClient wc = new System.Net.WebClient();
    System.Net.WebProxy wp = new System.Net.WebProxy(project.Variables["proxy"].Value);//("http://127.0.0.1:3097");//
    wc.Proxy = wp;

    //в цикле получаем ссылки и сохраняем фото
    foreach(string url in list)
    {
        string path = dir+DateTimeOffset.Now.ToUnixTimeSeconds().ToString()+ext;
        //project.SendInfoToLog(url, false);
        wc.DownloadFile(url,path);
        //случайная пауза
        int pause = rnd.Next(977,1789);
        Thread.Sleep(pause);
    }
}
И переписываем кубик:

C#:
//список с прямыми ссылками на фото
IZennoList links = project.Lists["links"];
//путь для сохранения фото
string dir = project.Variables["AVA_savePath"].Value;

//экземпляр класса
ZHelper photosaver = new ZHelper(project, instance);
//Сохранили изображения из списка links в папку dir
photosaver.GetListItemSaveImg(links, dir, ".png");
Изначально я не планировал использовать WebClient, но стандартным кубиком загрузки изображений, так же как и повтором Get-запроса из траффик-инспектора скачать фото у меня не получилось, с разбегу не разобрался, а тратить время на продолжительные поиски желания не было. А WebClient, невзирая на отсутствие поддержки SOCKS-проксей, прекрасно загрузил фото.

В следующем кубике мы проверяем, нужно ли ресайзить фото, и если да, собираем в список проекта пути ко всем файлам директории с фонами (/bg по-умолчанию):

C#:
//если не выбрана галка, выходим из кубика
if(project.Variables["AVA_resizeImg"].Value == "false") return "Ресайз фейк-лиц не нужен";
//путь для сохранения фото
string dir = project.Variables["AVA_savePath"].Value;
IZennoList links = project.Lists["links"];
links.Clear();

links.AddRange(Directory.GetFiles(dir));
Тут нечего переписывать, поэтому не стал трогать.

А в последнем кубике ветки производится изменение размера изображения на указанное количество процентов. Для работы с графикой в C# имеется пространство имен System.Drawing. Используя классы Image, Bitmap, Graphics и структуру Rectangle этого пространства, мы и будем обрабатывать изображения в этом проекте. В кубике ниже, в цикле выполняются следующие операции:

  • Получаем из списка проекта путь к файлу изображения igetpath,
  • Создаем экземпляр класса Image srcimage загружая в него изображение из файла,
  • Вычисляем размер в пикселах высоты w и ширины h нового изображения, используя свойства srcimage.Width, srcimage.Height, и указанное количество процентов pct,
  • Создаем прямоугольник destRect, с координатами левого верхнего угла по оси x=0 и оси y=0? а так же размером по ширине x и по высоте y,
  • Создаем объект Bitmap dest, из изображения srcimage, с размерами x, y
  • Используя метод DrawImage класса Graphics рисуем на объекте dest изображение srcimage с размером и координатами начала рисования, заданными прямоугольником destRect,
  • Сохраняем объект Bitmap в объект Image dst,
  • Сохраняем объект dst как изображение в указанную директорию dirsavepath с именем iname



C#:
IZennoList list = project.Lists["links"];
//процент изменения размера. Если увеличиваем — положит число, если уменьшаем — отриц. число
int pct = 100;
//Папка для сохранения
string dirsavepath = project.Variables["AVA_saveResizePath"].Value;

foreach(string igetpath in list)
{
    //получили имя текущего файла
    string iname = Path.GetFileNameWithoutExtension(igetpath);
    //создали экземпляр Image из файла, чтобы получить его ширину-высоту
    Image srcimage = new Bitmap(igetpath);

    //процент от ширины
    double wplus = ((double) srcimage.Width/100)*pct;
    //процент от высоты
    double hplus = ((double) srcimage.Height/100)*pct;
    //новые ширина и высота
    int w = Convert.ToInt32(srcimage.Width + wplus);
    int h = Convert.ToInt32(srcimage.Height + hplus);

    //Создали экз. прямоугольника с новым размером
    Rectangle destRect = new Rectangle(0, 0, w, h);
    //создали экз. Bitmap с новыми размерами
    Bitmap dest = new Bitmap(srcimage, w, h);

    //Отрисовали в Bitmap картинку srcimage с размерами destRect
    using(Graphics g = Graphics.FromImage(dest))
    {
        g.DrawImage(srcimage, destRect);
    }
    //сохраняем объект Bitmap в объект Image
    Image dst = (Image)dest;
    //сохраняем в файл
    dst.Save(dirsavepath+iname+".png", System.Drawing.Imaging.ImageFormat.Png);
}
Его стоит вынести в общий код, чтобы свернуть все вспомогательные опрации:

Общий код:
/// <summary>
/// Метод в цикле получает из списка имя-путь изображения, изменяет размеры изображения на указанное количество процентов и сохраняет его в указанную папку
/// </summary>
/// <param name="list">Список проекта с путями к изображениям</param>
/// <param name="dirsavepath">Путь к папке для сохранения обрезанных картинок</param>
/// <param name="pct">Процент, на который нужно изменить размер (для уменьшения указывается со знаком "-"), по умолчанию 100%</param>
public string ResizeAndSaveImageFromList(IZennoList list, string dirsavepath,int pct = 100)
{
    foreach(string igetpath in list)
    {
        if(string.IsNullOrEmpty(igetpath)) throw new Exception("в списке с изображениями была пустая строка");
        //создали экземпляр Image из файла, чтобы получить его ширину-высоту
        Image srcimage = new Bitmap(igetpath);
        //получили имя текущего файла
        string iname = Path.GetFileNameWithoutExtension(igetpath);

        //процент от ширины
        double wplus = ((double) srcimage.Width/100)*pct;
        //процент от высоты
        double hplus = ((double) srcimage.Height/100)*pct;
        //новые ширина и высота
        int w = Convert.ToInt32(srcimage.Width + wplus);
        int h = Convert.ToInt32(srcimage.Height + hplus);
        //ЗАписали новые размеры в структуру хранящую размеры для создания прямоугольника
        Size size = new Size(w, h);

        //Создали экз. прямоугольника с новым размером
        Rectangle destRect = new Rectangle(0, 0, w, h);
        //создали экз. Bitmap с новыми размерами
        Bitmap dest = new Bitmap(srcimage,size);

        //Отрисовали в Bitmap картинку srcimage с размерами destRect
        using(Graphics g = Graphics.FromImage(dest))
        {
            g.DrawImage(srcimage, destRect);
        }
        //сохраняем объект Bitmap в объект Image
        Image dst = (Image)dest;
        //сохраняем в файл
        dst.Save(dirsavepath+iname+".png", System.Drawing.Imaging.ImageFormat.Png);
    }
    //метод CropAndSaveImageFromList был с типом возвращаемого значения void (т.е. не возвращает значений),
    //здесь, по завершению, метод возвращает строку со значением "Ok" (исключительно в демо целях)
    return "Ок";
}
Теперь перепишем кубик:

C#:
IZennoList list = project.Lists["links"];
//процент изменения размера. Если увеличиваем — положит число, если уменьшаем — отриц. число
int pct = Int32.Parse(project.Variables["AVA_percentResizing"].Value);
//Папка для сохранения
string dirsavepath = project.Variables["AVA_saveResizePath"].Value;

//экз. класса ZHelper
ZHelper zh = new ZHelper(project, instance);
//Изменили размер, сохранили файлы
zh.ResizeAndSaveImageFromList(list, dirsavepath);
На этом ветка сбора фейков полностью переписана. Я не буду так же подробно описывать ветку получения фонов (и версия с общим кодом, и версия без него есть в шаблоне — можно открыть и потрогать), остановлюсь лишь на кубиках обработки графики.

Последний кубик ветки сбора фонов
обрезает все фоны до размера фейк-изображения. Это нужно, чтобы избежать ситуаций, как на изображении ниже:

69000


Код кубика:

C#:
IZennoList links = project.Lists["links"];
//Папка для сохранения
string dirsavepath = project.Variables["BG_saveResizePath"].Value;


foreach(string file in links)
{
    string name = Path.GetFileNameWithoutExtension(file);
    Image srcimage = Image.FromFile(file);
    //если размер картинки меньше 512px по любой стороне => берем следующую
    if(srcimage.Width < 512 || srcimage.Height < 512) continue;

    //обрезаем края изображения, поэтому для начала вычисляем начальные координаты нашего прямоугольника
    //левый угол x = (центр = ширина/2) - половина будущей ширины
    int x = srcimage.Width/2 - 256;
    //левый угол y
    int y = srcimage.Height/2 - 256;

    //прямоугольник с координатами начала обрезки и размером нового изображения
    Rectangle crop = new Rectangle(x, y, 512, 512);
    //новый экземпляр класса Bitmap из изображения-источника
    Bitmap src = new Bitmap(srcimage);
    //новый экземпляр класса Bitmap для обрезанного изображения
    Bitmap dest = new Bitmap(crop.Width, crop.Height);

    //рисуем новое изображение
    using(Graphics g = Graphics.FromImage(dest))
    {
       g.DrawImage(src, new Rectangle(0, 0, dest.Width, dest.Height),
                        crop,                 
                        GraphicsUnit.Pixel);
    }
    //сохраняем объект Bitmap в объект Image
    Image dst = (Image)dest;
    //сохраняем файл
    dst.Save(dirsavepath + name + "_512.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

}
Распишу подробнее — здесь мы в цикле выполняем следующие действия:

  • Получаем строку с путем к файлу,
  • Создаем объект Image srcimage из файла, указанного в пути предыдущего шага,
  • Проверяем, что размеры изображения любой из сторон srcimage не меньше, чем требуемый размер нового изображения,
  • Вычисляем координаты начала и конца обрезки (обрезаем по-краям, поэтому отсчет от центра), используя свойства srcimage.Width и srcimage.Height,
  • Создаем прямоугольник crop с координатами начала и конца обрезки и размерами нового изображения,
  • Из srcimage создаем объект Bitmap src,
  • Создаем объект Bitmap dest с размерами crop.Width, crop.Height,
  • Используя объект g класса Graphics, рисуем в объект dest изображение из объекта src с координатами начала рисования и размерами new Rectangle(0, 0, dest.Width, dest.Height) и координатами обрезки и размерами, заданными прямоугольником crop и единицами измерения GraphicsUnit.Pixel (т.е. мы оперируем пикселями),
  • Сохраняем объект dest с новым изображением в объект Image dst,
  • Сохраняем объект dst в файл
Глаз начинает плакать каждый раз, когда я смотрю на эту портянку, и чтобы понять где здесь выполнение логики, а где вспомогательные операции, даже с коментами нужно время. Выносим все эти шаги в общий код, попутно добавляя возможность управлять рамерами обрезки из параметров, подаваемых в метод:

Общий код:
/// <summary>
/// Метод в цикле получает из списка имя-путь изображения, проверяет, что его размер соответствует указанным размерам,
/// если да, обрезает картинку до указанных размеров (от центра) и сохраняет ее в указанное место
/// </summary>
/// <param name="list">Список проекта с путями к изображениям</param>
/// <param name="dirpath">Путь к папке для сохранения обрезанных картинок</param>
/// <param name="isizew">Ширина, до которой нужно обрезать изображение, по умолчанию 512px</param>
/// <param name="isizeh">Высота, до которой нужно обрезать изображение, по умолчанию 512px</param>
public void CropAndSaveImageFromList(IZennoList list, string dirpath, int isizew = 512, int isizeh = 512)
{
    foreach(string file in list)
    {
        //Получаем имя файла без расширения
        string name = Path.GetFileNameWithoutExtension(file);
        //Создаем новый объект Image
        Image srcimage = Image.FromFile(file);

        //если размер картинки меньше 512px по любой стороне => берем следующую
        if(srcimage.Width < isizew || srcimage.Height < isizeh) continue;

        //обрезаем края изображения, поэтому для начала вычисляем начальные координаты нашего прямоугольника
        //левый угол x = (центр = ширина/2) - половина будущей ширины
        int x = srcimage.Width/2 - isizew/2;
        //левый угол y
        int y = srcimage.Height/2 - isizeh/2;

        //прямоугольник с координатами начала обрезки и размером нового изображения
        Rectangle crop = new Rectangle(x, y, isizew, isizeh);
        //новый экземпляр класса Bitmap из изображения-источника
        Bitmap src = new Bitmap(srcimage);
        //новый экземпляр класса Bitmap для обрезанного изображения
        Bitmap dest = new Bitmap(crop.Width, crop.Height);

        //рисуем новое изображение
        using(Graphics g = Graphics.FromImage(dest))
        {
           g.DrawImage(src, new Rectangle(0, 0, dest.Width, dest.Height), crop, GraphicsUnit.Pixel);
        }

        //сохраняем объект Bitmap в объект Image
        Image dst = (Image)dest;
        //сохраняем файл
        dst.Save(dirpath+name+"_w"+isizew+".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
    }
}
И переписываем кубик:

C#:
IZennoList links = project.Lists["links"];
//директория для сохранения
string dir = project.Variables["BG_saveResizePath"].Value;

//экземпляр класса
ZHelper zh = new ZHelper(project, instance);
//Получение фона, проверка размера, кроп и сохранение изображений из списка
zh.CropAndSaveImageFromList(links, dir);
Теперь не нужно скрипеть мозгом, чтобы понять что происходит в кубике :bf:

Ветка наложения фейков на фон состоит из 1 кубика, и здесь тоже используются методы простанства имен System.Drawing:

C#:
//путь к папке с фонами
string bgpath = project.Variables["CREATE_bgDirPath"].Value;
if(string.IsNullOrEmpty(bgpath)) throw new Exception("Укажите путь к папке с фонами");
//путь к папке с авами
string avapath = project.Variables["CREATE_avaDirPath"].Value;
if(string.IsNullOrEmpty(avapath)) throw new Exception("Укажите путь к папке с фейк-лицами");
//Путь к папке для сохранения готовых ав
string savepath = project.Variables["CREATE_saveDirPath"].Value;
if(string.IsNullOrEmpty(savepath))
{
    savepath = [email protected]"\ready\";
}

//Создаем временный список для фоновых изображений
List<string> bglist = new List<string>();
//Если директории не существует — ошибка
if(!Directory.Exists(bgpath)) throw new Exception("Указанная директория для фонов НЕ СУЩЕСТВУЕТ!");
//закидываем все файлы фонов вместе  путями к ним в список
bglist.AddRange(Directory.GetFiles(bgpath));
//если количество строк = 0 => ошибка
if(bglist.Count == 0) throw new Exception("В директории с фонами нет файлов");

//Создаем временный список для автар
List<string> avalist = new List<string>();
//Если директории не существует — ошибка
if(!Directory.Exists(avapath)) throw new Exception("Указанная директория для аватар НЕ СУЩЕСТВУЕТ!");
//закидываем все файлы ав вместе путями к ним в список
avalist.AddRange(Directory.GetFiles(avapath));
// если количество строк = 0 => ошибка
if(avalist.Count == 0) throw new Exception("В директории с аватарами нет файлов");

for(int i=0; i<avalist.Count; i++)
{
    //берем строку из списка
    string avafile = avalist.ElementAt(i);
    //получаем имя текущего файла
    string avaname = Path.GetFileNameWithoutExtension(avafile);
    //Создаем из файла новый объект Image
    Image iava = Image.FromFile(avafile);

    //в цикле берем все фоны, накладываем на них текущую аву и сохраняем в папку с ее именем
    for(int j=0; j<bglist.Count; j++)
    {
        //берем файл из списка
        string bgfile = bglist.ElementAt(j);
        string bgname = Path.GetFileNameWithoutExtension(bgfile); 
        //Создаем из файла новый объект Image
        Image ibg = Image.FromFile(bgfile);

        //Новый Bitmap-объект в котором будем рисовать фон и аву поверх него, с размером как у фонового изображения
        Bitmap dest = new Bitmap(ibg.Width, ibg.Height);
  
        //рисуем
        using(Graphics g = Graphics.FromImage(dest))
        {
            //Рисуем фон(фоновое изображение, левый верх край x, левый верх край y, ширина,высота, цветовое пространство)
            g.DrawImage(ibg, 0, 0);
            //Рисуем аву поверх фона(ава, левый верх край x, левый верх край y, ширина,высота, цветовое пространство)
            g.DrawImage(iava, 0, 0);
        }
  
        //создаем директорию только если ее нет
        string savedirpath = [email protected]"\"+avaname;
        if(!Directory.Exists(savedirpath))
        {
            Directory.CreateDirectory(savedirpath);
        }
  
        //путь и иммя сохраняемого файла
        string saveimgpath = savedirpath + @"\" + bgname + ".jpg";
        //сохраняем объект Bitmap в объект Image
        Image dst = (Image)dest;
        //сохраняем файл
        dst.Save(saveimgpath, System.Drawing.Imaging.ImageFormat.Jpeg);
    }
}
Здесь создаются два временных списка List<string> в которые сложены пути к файлам источникам феков и фонов (директории заданные переменными проекта project.Variables["CREATE_avaDirPath"].Value и project.Variables["CREATE_bgDirPath"].Value) А далее, используя вложенные циклы мы:

  • Получаем строку avafile с путем к файлу фейка,
  • Создаем из файла avafile объект Image iava,
  • Получаем строку bgfile с путем к фону,
  • Создаем обект Image ibg из файла bgfile,
  • Создаем объект Bitmap dest, в котором будем выполнять наложение изображений,
  • Используя объект g класса Graphics , рисуем на объекте dest изображение ibg с координатами начала рисунка 0,0,
  • А после, поверх первого рисуем изображение фейка
  • Сохраняем все фоновые изображения с наложенным на них текущим фейком в папку с именем файла-фейка, проверяя при этом что папка с таким именем существует, и создавая ее, если нет.
Здесь так же сложно определить логику шаба от обработки картинок, поэтому преписываем. Сначала выносим в метод общего кода создание списка и проверку существования директории и наличия в ней файлов (в идеале нужно еще проверять что файлы в директориях источниках — это изображения, а не что-то другое, например тексты, но я не стал так глубоко копать):

Общий код:
/// <summary>
/// Метод создает список List<string> и сохраняет в него пути ко всем файлам укеазанной директории
/// </summary>
/// <param name="path">Путь к директории, файлы из которой нужно поместить в список</param>
public List<string> GetDirFilesToList(string path)
{
    //Создаем список
    List<string> list = new List<string>();
    //Если директории не существует — ошибка
    if(!Directory.Exists(path)) throw new Exception("Указанная директория НЕ СУЩЕСТВУЕТ!");
    //закидываем все файлы фонов вместе  путями к ним в список
    list.AddRange(Directory.GetFiles(path));
    if(list.Count == 0) throw new Exception("Список файлов пуст! Возможно в директории нет файлов");
    return list;
}
А теперь выносим в метод операции по наложению изображений:

Общий код:
/// <summary>
/// Метод в цикле получает из списков имя-путь изображения фейк-лица и фонов, накладывает на каждый фон лицо и сохраняет в подпапках с именем файла фейк-лица в папке назначения
/// </summary>
/// <param name="avalist">Список с путями к изображениям фейк-лиц</param>
/// <param name="bglist">Список с путями к изображениям фонов</param>
/// <param name="savepath">Путь к папке для сохранения готовых аватар</param>
public void OverlayAndSaveImages(List<string> avalist, List<string> bglist, string savepath)
{
    if(string.IsNullOrEmpty(savepath))
    {
        savepath = project.Directory + @"\ready\";
    }

    for(int i=0; i<avalist.Count; i++)
    {
        //берем строку из списка
        string avafile = avalist.ElementAt(i);
        //получаем имя текущего файла
        string avaname = Path.GetFileNameWithoutExtension(avafile);

        //Создаем из файла новый объект Image
        Image iava = Image.FromFile(avafile);

        //в цикле берем все фоны, накладываем на них текущую аву и сохраняем в папку с ее именем
        for(int j=0; j<bglist.Count; j++)
        {
            //берем файл из списка
            string bgfile = bglist.ElementAt(j);
            string bgname = Path.GetFileNameWithoutExtension(bgfile);
           
            //Создаем из файла новый объект Image
            Image ibg = Image.FromFile(bgfile);

            //Новый Bitmap-объект в котором будем рисовать фон и аву поверх него, с размером как у фонового изображения
            Bitmap dest = new Bitmap(ibg.Width, ibg.Height);      

            //рисуем
            using(Graphics g = Graphics.FromImage(dest))
            {
                //Рисуем фон(фоновое изображение, левый верх край x, левый верх край y, ширина,высота, цветовое пространство)
                g.DrawImage(ibg, 0, 0);
                //Рисуем аву поверх фона(ава, левый верх край x, левый верх край y, ширина,высота, цветовое пространство)
                g.DrawImage(iava, 0, 0);
            }      

            //создаем директорию только если ее нет
            string savedirpath = [email protected]"\"+ avaname;
            if(!Directory.Exists(savedirpath))
            {
                Directory.CreateDirectory(savedirpath);
            }      

            //путь и иммя сохраняемого файла
            string saveimgpath = savedirpath + @"\" + bgname + ".jpg";
            //сохраняем объект Bitmap в объект Image
            Image dst = (Image)dest;

            //сохраняем файл
            dst.Save(saveimgpath, System.Drawing.Imaging.ImageFormat.Jpeg);

        }

    }

}
И переписываем кубик:

C#:
//путь к папке с фонами
string bgpath = project.Variables["CREATE_bgDirPath"].Value;
if(string.IsNullOrEmpty(bgpath)) throw new Exception("Укажите путь к папке с фонами");
//путь к папке с авами
string avapath = project.Variables["CREATE_avaDirPath"].Value;
if(string.IsNullOrEmpty(avapath)) throw new Exception("Укажите путь к папке с фейк-лицами");
//Путь к папке для сохранения готовых ав, если не указан, задаем по-умолчанию
string savepath = project.Variables["CREATE_saveDirPath"].Value;


//Новый экз. класса ZHelper
ZHelper zh = new ZHelper(project, instance);

//Получаем в список файлы из директории bgpath
List<string> bglist = zh.GetDirFilesToList(bgpath);
//Получаем в список файлы из директории avapath
List<string> avalist = zh.GetDirFilesToList(avapath);

//В цикле берем фейк-фото, накладываем на все имеющиеся фоны, создаем в указанной папке директорию с именем
//фейк-фото и сохраняем в нее все получившиеся аватары
zh.OverlayAndSaveImages(avalist, bglist, savepath);
Конечно, на таком небольшом проекте можно не понять прелесть выноса в общий код всех не относящихся к логике действий, но, все же, в этом имеются очень заметные плюсы:

1. Когда в проекте присутствует большое количество одинаковых действий (например парсинг атрибутов html-элементов товаров в магазине), вынос в общий код метода получения этих атрибутов дает возможность изменить метод в одном месте, а не переписывать его в множестве кубиков, со все увеличивающейся от правки к правке вероятностью появления косяка.
2. При необходимости сменить логику работы шаблона — нам не придется пречитывать простыни кода, выискивая в них именно логику, а не дополнительные действия, призванные эту логику обеспечить.
3. Поддерживать и дебажить такой код проще и приятнее — ведь и лада и тойота способны доставить нас к месту назначения, но с¥ка, добираться на япомобиле почему-то ощутимо кайфовее.
4. Освоение общего кода как инструмента, открывает новые возможности. А новые возможности это всегда круто!

Еще немного идей по нейросетевым технологиям:

1. Уникализация фото-контента — уже сейчас в сети есть web-интерфейсы, позволяющие:
а) удалять с изображений фоны (иногда довольно сносно, иногда совсем ущербно),
б) накладывать на изображения различные стили живописи,
в) встречал в телеге бота, который переделывает расу человека на фото в китайца-негра (набрать фоток порнозвезд, сменить им расу — вот и перспективные девочки для дейтинга)
2. Генерация фейк-новостей, фейк-резюме, и других текстовых прогонов (тестил русифицированную GPT-2 — иногда получается забавно, хотя обойтись без ручной фильтрации пока невозможно), ну и стандартно — чатботы всех мастей и расцветок.

В заключении пара ссылок на статьи из прошлых конкурсов (с большим респектом авторам), которые будут полезны для освоения инструментария общего кода:

https://zennolab.com/discussion/threads/king-of-parsing-nachalo-chast-1.79341/ от автора Маломальский
https://zennolab.com/discussion/threads/nestandartnye-podxody-k-razrabotke-shablonov.48583/ от автора shtift
https://zennolab.com/discussion/threads/bystryj-start-proektov-gotovoe-reshenie-uskorjaem-razrabotku-s-pomoschju-owncode-i-metodov-rasshirenija.79162/ от RoyalBank


Литература (субъективно):

Роберт Мартин «Чистый код: создание, анализ и рефакторинг»
Герберт Шилдт C# 4.0. Полное руководство
Албахари Джозеф, Албахари Бен, С# 6.0. Справочник. Полное описание языка, 6-е изд.

P.S. Вполне возможно что методы которые я здесь привел не оптимальны или не совсем правильны, а может быть вынесено что-то, что должно было остаться в кубиках — я не программист, и опыта работы с общим кодом мне не хватает, так что респект вем, кто укажет ошибки!
 
Номер конкурса статей
Четырнадцатый конкурс статей

Вложения

Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

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

Последнее редактирование модератором:

AlisaZ

Client
Регистрация
17.09.2014
Сообщения
139
Благодарностей
156
Баллы
43
ИИ сейчас в тренде даже ВВп озадачился чтоб пиндостан обогнать, но как обычно лыжы не те, ведь сам мозги в рашке не держит которые туда утекают :co:
 
  • Спасибо
Реакции: phoenixs и semafor

Kedr

Client
Регистрация
31.08.2016
Сообщения
36
Благодарностей
20
Баллы
8
весьма интересная статья
 
  • Спасибо
Реакции: semafor

soprano

Client
Регистрация
25.08.2011
Сообщения
451
Благодарностей
361
Баллы
63
Ну, осталось дело за малым: генерировать фейковые паспорта для подтверждения личности по фото.
Мне британский паспорт, пожалуйста.
Заплачу сгенерированными деньгами.
 

SHILY

Client
Регистрация
05.06.2016
Сообщения
158
Благодарностей
136
Баллы
43
Сколько кодааа... Это восхитительно!:ah:
Спасибо за столь прекрасную статью!:ay:
 
Последнее редактирование:
  • Спасибо
Реакции: semafor

one

Client
Регистрация
22.09.2015
Сообщения
6 038
Благодарностей
1 106
Баллы
113
Еще один сильный материал. Спасибо!
 
  • Спасибо
Реакции: semafor

one

Client
Регистрация
22.09.2015
Сообщения
6 038
Благодарностей
1 106
Баллы
113
  • Спасибо
Реакции: semafor

Nike59

Client
Регистрация
05.08.2011
Сообщения
124
Благодарностей
121
Баллы
43
Очень интересно по содержанию и профессионально реализовано по форме.
 
  • Спасибо
Реакции: semafor

zxt7

Новичок
Регистрация
18.01.2020
Сообщения
7
Благодарностей
4
Баллы
3
Статья- огонь, да. Подождем еще претендентов...)
 
Последнее редактирование модератором:

clocky

Client
Регистрация
26.09.2019
Сообщения
134
Благодарностей
14
Баллы
18
https://hankhank10.github.io/fakeface/ не благодарите. Просто гет запрос и готово. Автор прости, но это сильно перестарался))
 
Последнее редактирование:
  • Спасибо
Реакции: pix и Alexmd

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
https://hankhank10.github.io/fakeface/ не благодарите. Просто гет запрос и готово. Автор прости, но это сильно перестарался))
Не благодарю. Хотя бы потому, что различия между https://thispersondoesnotexist.com и https://generated.photos/faces доовольно существенные. И в качестве генерации, и в невозможности выбрать пол-возраст-расу, и в возврате .jpg, а не .png без фона. А написать 1 get-запрос конечно проще. Но будет ли это интересно выглядеть на конкурсе?
И спасибо за комент, я уж думал никто не вспомнит об этихчуваковнесуществует.
В следующий раз подавайте заявку на участие ))
 
Последнее редактирование:
  • Спасибо
Реакции: sergio197675
Регистрация
03.12.2020
Сообщения
129
Благодарностей
89
Баллы
28
Я уж подумал реально нейросетку к зенке прикрутили, а это всего лишь кликбейт :-)
 

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
Я уж подумал реально нейросетку к зенке прикрутили, а это всего лишь кликбейт :-)
Изображения сгенерированные нейросетью обрабатываются с помощью зенки, на выходе получаются уникальные с точки зрения любой соцсети аватары. Ровно как в заголовке — нейросеть, зеннопостер и фейковые аватары. А в чем кликбейт, не просветите?
 
Регистрация
03.12.2020
Сообщения
129
Благодарностей
89
Баллы
28
Изображения сгенерированные нейросетью обрабатываются с помощью зенки, на выходе получаются уникальные с точки зрения любой соцсети аватары. Ровно как в заголовке — нейросеть, зеннопостер и фейковые аватары. А в чем кликбейт, не просветите?
Нейросеть - кликбейт. Написали бы честно - сервис генерации лиц несуществующих людей, использующий нейросеть, - была бы правда. А так - кликбейт. Потому что, не дай бог, отрубят сервис, - шаблончик ваш перестанет фунциклировать...
А вот если бы вы развернули нейросеть на том же компе, а еще круче - встроили бы в шаблон - тогда слово "нейросеть" в названии темы не было бы кликбейтом.

Я доходчиво объясняю? (Уточню, на всякий случай, - вопрос риторический).
 

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
Нейросеть - кликбейт. Написали бы честно - сервис генерации лиц несуществующих людей, использующий нейросеть, - была бы правда. А так - кликбейт. Потому что, не дай бог, отрубят сервис, - шаблончик ваш перестанет фунциклировать...
А вот если бы вы развернули нейросеть на том же компе, а еще круче - встроили бы в шаблон - тогда слово "нейросеть" в названии темы не было бы кликбейтом.

Я доходчиво объясняю? (Уточню, на всякий случай, - вопрос риторический).
Спасибо за высказывание.

А теперь я:
В заголовке Вы где-то видите что нейросеть прикручена к зенке? Нет.
Для получения фейков используется нейросеть(прямо или косвенно)? Да.
Связка Зеннопостер + Нейросеть генерит фейк-аватары? Да

Ну и еще. В первом же абзаце статьи (сразу за спойлером) я жирным шрифтом выделяю с чем будем работать, чтобы не возбуждать всевозможных влажных мечтаний.
Я не оправдал ваших способностей искать смыслы между строк? Так это не ко мне.

Потому что, не дай бог, отрубят сервис, - шаблончик ваш перестанет фунциклировать...
На минутку. Конкурс посвящен работе с программой браузерной автоматизации. И если Вы потрудитесь изучить этот и предыдущие конкурсы, Вам откроется страшная тайна — все мы так или иначе используем сайты, сервисы, и соцсети, практически всегда с web-интерфейсом. И частенько случается так, что простое изменение верстки на этих ресурсах выводит из строя шаблон, не говоря уж о смерти сервисов (которые, кстати, появляются и исчезают с завидной регулярностью).

А вот если бы вы развернули нейросеть на том же компе, а еще круче - встроили бы в шаблон - тогда слово "нейросеть" в названии темы не было бы кликбейтом.
Вот Вы уже практически составили себе план статьи на следующий конкурс. Получайте статус клиента, и вперед. С неподдельным интересом почитаю.

Закончить хотелось бы Вашими словами:
Я доходчиво объясняю? (Уточню, на всякий случай, - вопрос риторический).
 
Регистрация
03.12.2020
Сообщения
129
Благодарностей
89
Баллы
28
На минутку. Конкурс посвящен работе с программой браузерной автоматизации.
Интересно, каким боком сборка сервера относится к браузерной автоматизации? https://zennolab.com/discussion/threads/uvelichivaem-proizvoditelnost-zennopostera-na-servere.86666/

А, наверное, его потом дисквалифицируют...

Хотя, скорее всего, кто-то правила не дочитал.
 

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
Интересно, каким боком сборка сервера относится к браузерной автоматизации? https://zennolab.com/discussion/threads/uvelichivaem-proizvoditelnost-zennopostera-na-servere.86666/

А, наверное, его потом дисквалифицируют...
Самым элементарным способом. Если Вы внимательно изучите статью, то в ней обсуждается сборка сервера для качественной и беспроблемной работы той самой программы браузерной автоматизации.
Хотя, скорее всего, кто-то правила не дочитал.
 

semafor

Client
Регистрация
27.12.2016
Сообщения
217
Благодарностей
189
Баллы
43
В каждой бочке затычка?
Вы стали скучны.
Дружище, Вы пришли в мою тему, начали мне рассказывать как надо было сделать, чтобы Вам понравилось, и теперь я стал Вам скучен? Серьезно? А могу я как-то реабилитироваться? А то мне так понравилось с Вами общаться...
 
  • Спасибо
Реакции: SHILY

Koqpe

Client
Регистрация
23.12.2014
Сообщения
1 031
Благодарностей
586
Баллы
113
  • Спасибо
Реакции: SHILY и semafor

Zedx

Client
Регистрация
12.06.2018
Сообщения
610
Благодарностей
260
Баллы
63
Спасибо. Люблю статьи с грамотной технической реализацией.
 
  • Спасибо
Реакции: semafor

Alex.A

Client
Регистрация
14.11.2020
Сообщения
38
Благодарностей
5
Баллы
8
Спасибо за идею. С меня + один голос.
 

akella

Client
Регистрация
28.02.2016
Сообщения
100
Благодарностей
17
Баллы
18
https://hankhank10.github.io/fakeface/ не благодарите. Просто гет запрос и готово. Автор прости, но это сильно перестарался))
Этот сайт возвращает заранее сгенерированные картинки из базы, причем часто повторяющиеся, то есть они не уникальны. Сколько человек их уже использовало до тебя и где - большой вопрос
 
  • Спасибо
Реакции: semafor

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)