TikTokAngleSolver (софт) и TikTokAngleSolution (библиотека) — методы для вычисления угла в капче по аналогии с TikTok в ZennoPoster

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
131792

Сегодня хочу поделиться своей разработкой — решением задачи поиска нужного угла в капче, аналогичной TikTok.

Этот проект не предназначен для обхода капч TikTok или других сервисов. Для предотвращения возможных недоразумений я создал офлайн-аналог страницы, имитирующей процесс верификации. Это позволяет тестировать и демонстрировать алгоритм без нарушения правил использования сервисов. Во избежание претензий со стороны соцсети TikTok к ZennoLab, проект реализован в строгом соответствии с правилами и стандартами. Наш форум Zenno.club представляет собой дружелюбное сообщество разработчиков, ориентированное на автоматизацию рутинных задач, а не на взлом систем защиты. Проект создан исключительно в образовательных целях и для участия в конкурсе ZennoLab.

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

Программа TikTok Angle Solver предназначена для визуальной настройки параметров, которые затем указываются в библиотеке TikTok Angle Solution и используются в Zenno Poster.

Для наглядности и лучшего понимания я создал файл HTML, который можно открыть в браузере, просто дважды щелкнув по TikTok_.html.

131793


131795


Выберите любое изображение на вашем компьютере.

131796


Появилась капча, похожая на TikTok. Мы видим цель — 123° и слайдер, который пока стоит на 0°.

131797


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

131798


Интересно, как это работает? Нажмите F12, чтобы открыть DevTools (Инструменты разработчика) и посмотреть код страницы с пояснениями. Или просто откройте файл TikTok_.html в Блокноте.

131800


Обратите внимание на функцию checkCompletion(angle) — в настоящей капче TikTok также учитывается допустимая погрешность.

В функции checkCompletion(angle) можно настроить допустимую погрешность угла для проверки точности. В текущем примере погрешность установлена на , что означает точное совпадение углов. Однако, если нужно учесть небольшие отклонения, вы можете изменить значение погрешности в условии:

131801


Если вы хотите, чтобы погрешность была, например, , просто замените 0 на 1 в условии. Это позволит алгоритму учитывать небольшие отклонения и не считать задачу выполненной только при идеальном совпадении углов.

в DevTools (Инструменты разработчика)

131802


На скриншоте выше обе части изображений (круг и кольцо) закодированы в base64. Мы будем работать именно с ними, чтобы определить угол. Представьте, что строки с подсказкой ('Угол: 123° / Цель: 123°') нет – мы не знаем, какой там угол, и нам нужно вычислить его самостоятельно.

Для подбора подходящих значений порогов фильтров и определения угла поворота изображения было разработано консольное приложение TikTok Angle Solver, написанное на .NET 9. Оно работает в связке с Selenium WebDriver.

131804


Для работы консольного приложения TikTokAngleSolver нужно скачать и установить .NET 9, как показано на скриншоте (выделено красным) - скачать .Net 9

131806


131807


При первом запуске приложения автоматически создаётся файл config.json с настройками, включающими справку и все параметры.

131808

131809


Файл config.json можно редактировать без закрытия приложения — после сохранения в Блокноте (Ctrl + S) настройки обновляются в реальном времени, и нет необходимости постоянно открывать и закрывать файл.

Итак, давайте начнём с настроек. По умолчанию все они включены. Кратко разберём каждый блок настроек.

Блок GeneralSettings включает несколько важных настроек:

"DefaultToBaseline": true/false — позволяет включить работу с базовым углом по умолчанию, если установлено значение true, или с перевёрнутым углом на 180 градусов, если false.
"EnableFilters": true/false — включает или отключает фильтры. Если фильтры отключены, будет использоваться только угол, выбранный по умолчанию.
"EnableManualCorrection": true/false — управляет возможностью ручной корректировки, определяя, будет ли включена опция для сохранения хеша.
"EnableHashLookup": true/false — отвечает за поиск по хешу в словаре. Файл manual_corrections.json будет автоматически создан при первом сохранении хеша.
"EnableVoting": true/false — включает или отключает голосование фильтров. Если голосование отключено, применяются фильтры из списка ListFilterOrder.
"EnableMajorityVoting": true/false — позволяет выбрать вид голосования. Если включено, используется мажоритарное голосование; если выключено, комбинированное.
"UseWinningAngle": true/false — выбирает результат проголосовавших фильтров. Если включено, выбирается вариант с большей уверенностью, если отключено — с меньшей уверенностью.

По умолчанию включены все опции - true

131810


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

Сейчас DefaultToBaseline": false – значит, угол перевернут на 180°

131811


131812


А тут "DefaultToBaseline": true – значит, угол остаётся базовым.

131813


131814


Здесь у нас включён базовый угол, не перевёрнутый, и список активных фильтров.

131815


131816


Далее мы включили ручную корректировку с опцией "EnableManualCorrection": true. Как видите, в консоли появилась возможность задать свой правильный угол или просто нажать Enter, чтобы пропустить.

131817


131818


Затем мы включили поиск по хешу с опцией "EnableHashLookup": true и голосование с опцией "EnableVoting": true. Однако, поскольку следующая опция отключена — "EnableMajorityVoting": false — будет использоваться комбинированное голосование.

131819


131820


На этом этапе мы включили мажоритарное голосование с опцией "EnableMajorityVoting": true. Обратите внимание на результат, выделенный зеленым цветом: уверенность снизилась, а неправильный результат стал правильным. Это связано с тем, что опция "UseWinningAngle": false сейчас отключена — именно эта опция переключает результаты голосования.

131821


131822


Тут снова включены все опции. "UseWinningAngle": true – теперь результаты снова поменялись местами, всё стало на своё место.

131823


131824


Блок ThresholdsSettings включает несколько важных настроек пороговых значений для фильтров:

С подбором настроек особо не заморачивался, на глаз все делал. Но это работает!

Значения нужно подбирать самостоятельно. Ваша цель — установить значение порогов так, чтобы они подходили для большинства изображений в среднем. Например, если у вас есть 100 изображений, пороговые значения следует настроить так, чтобы они подходили хотя бы для 60-75 изображений. Для остальных, которые не подходят, можно использовать словарь с хешами — manual_corrections.json

131825


Блок ListFilterOrder содержит список активных фильтров:

131827


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

Например:

131828


или

131829


или

131830


или

131831



Блок DictionaryFilterWeights содержит словарь весов влияния фильтров:

В этом словаре каждое значение представляет вес фильтра. Можно провести аналогию с ролями в обществе: например, президент имеет наибольшее влияние — его слово закон. Если он сказал, что вода сухая, значит, она сухая, и его вес может быть равен 2 или выше. Директор тоже имеет влияние, он знает, что вода мокрая, но перед президентом его слово будет гораздо менее весомым, и его вес может быть равен 1.5. Учитель, несмотря на свой авторитет, имеет меньшее влияние, его вес может быть равен 1. Сантехник Вася и тракторист Дима имеют одинаковое влияние, их вес может быть равен 0.5. Школьник, с минимальным влиянием, имеет вес, близкий к 0.1. Вес фильтров может быть дробным (например, 0.5 или 1.5), что позволяет гибко настраивать их влияние. Чем выше вес фильтра, тем сильнее его влияние на результат. Вы сами выбираете и устанавливаете вес влияния каждому фильтру на ваше усмотрение.

131832

В папке с программой находится файл manual_corrections.json (файл создается сам при первом обращении), в котором хранятся хеш (в шестнадцатеричном формате) и соответствующее им значение угла. Если включена настройка "EnableHashLookup" (поиск по хешу), то сначала выполняется поиск угла в этом файле. Если совпадений не найдено, угол определяется с помощью фильтров.

131835


Чтобы добавить хеш в словарь, всё просто: нужно ввести угол и нажать Enter. Если хотите пропустить, просто нажмите Enter.
Для создания хеша нужно указать угол

131836


Нажимаем Enter и видим что сработал поиск по хешу.

131837


Что такое перцептивный хэш?

Перцептивный хэш (pHash)
— это способ представить изображение в виде короткого цифрового кода, который отражает его визуальное содержание. Он позволяет находить похожие изображения, даже если они были изменены (например, сжаты, осветлены или слегка искажены).

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

Например, найденный угол при помощи поиска по хешу:

131838


131839


131840

Если не удалось найти по хешу и в настройках включены соответствующие опции, то автоматически применяется выбранное голосование с фильтрами.

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

131841


131842


131843


131844


131845


131846


131848


131849


131850


131851

Для работы в Zenno Poster была специально разработана библиотека TikTok Angle Solution, написанная на .NET Framework 4.7.2

131853


Как подключать любую библиотеку, думаю, знают все уважающие себя пользователи форума zenno.club — этот вопрос уже не раз обсуждался на форуме.
Скопировать саму библиотеку TikTokAngleSolution в папку ExternalAssemblies
В Директивы using и общий код нужно прописать: using TikTokAngleSolution;

131859


Выбрать библиотеку в Ссылки из GAC

131858

Прошу обратить внимание на настройку settings.InnerRadiusFactor = 0.3

В файле TikTok_.html изображение делится в соотношении 70 на 30, где 70 — это кольцо, а 30 — круг. Это важно, поскольку в реальной капче TikTok внутренний радиус (круг) может отличаться. Настройка InnerRadiusFactor позволяет регулировать этот параметр и адаптировать алгоритм под различные варианты радиусов.

В библиотеке TikTokAngleSolution убрана настройка EnableManualCorrection (ручная корректировка при добавлении хеша) из-за соображений многопоточной работы. Если кому-то это действительно нужно, вы можете добавить её самостоятельно в исходнике. Хеш добавляется через консольное приложение, и формируется файл, который затем можно переместить в нужную папку и указать путь к этому файлу manual_corrections.json

Указываем настройки все как в консольном приложении в файлике config.json

131860


131861


Весь код кубика "Свой C# код" в Project Maker
C#:
// Создаем объект с настройками решения капчи
CaptchaSolverSettings settings = new CaptchaSolverSettings();

// Включаем режимы работы алгоритма
settings.DefaultToBaseline = true;   // Если true — используем базовый угол, если false — перевернутый на 180°
settings.EnableFilters = true;       // Включить или выключить использование фильтров для коррекции угла
settings.EnableHashLookup = true;    // Использовать поиск хешей в словаре (сравнение по хешам)
settings.EnableVoting = true;        // Включить систему голосования, если false то использует только список фильтров без голосования
settings.EnableMajorityVoting = true;// Если true — мажоритарное голосование, если false — комбинированное
settings.UseWinningAngle = true;    // Включить использование угла, выбранного большинством голосов (true), или меньшинством голосов (false)

// Указываем путь к файлу (словарю) с корректировками, который используется для поиска изображений по хешу.
// Этот файл в формате JSON содержит данные, где для каждого хеша изображения указан соответствующий угол поворота,
// который используется для корректировки изображения, если его угол не удаётся определить автоматически.
//settings.FilePath = @"D:\ZennoTemplates\TikTokAngleSolver\manual_corrections.json";
settings.FilePath = $"{project.Directory}manual_corrections.json";

// Задаем коэффициент внутреннего радиуса, который определяет размер внутреннего круглого изображения
// относительно общего размера (например, процент от ширины или высоты изображения).
// Значение 0.3 означает, что внутренний круг будет занимать 30% от общего размера.
settings.InnerRadiusFactor = 0.3;

// Устанавливаем пороговые значения различных параметров обработки изображения
settings.PixelDifferenceThreshold = 130; // Минимальная разница пикселей для обнаружения изменений
settings.BrightnessThreshold = 10;       // Порог яркости (насколько сильны изменения в освещении)
settings.ContoursThreshold = 1.5;        // Порог обнаружения контуров (определяет резкость границ)
settings.VerticalGradientThreshold = 50; // Порог вертикального градиента (анализ вертикальных изменений)
settings.ColorHistogramThreshold = 1.5;  // Порог цветовой гистограммы (различия в цветах)
settings.SymmetryThreshold = 1.2;        // Порог симметрии (анализирует, насколько изображение симметрично)
settings.DarkPixelThresholdAtBottom = 50;// Порог темных пикселей внизу изображения

// Определяем список активных фильтров, которые будут применяться к изображению
settings.ActiveFilters = new List<CorrectionFilter>
{
    CorrectionFilter.PixelDifference,     // Фильтр по разнице пикселей
    CorrectionFilter.Brightness,          // Фильтр по яркости
    CorrectionFilter.VerticalGradient,    // Фильтр по вертикальному градиенту
    CorrectionFilter.Contours,            // Фильтр по контурам
    CorrectionFilter.RotatedBrightness,   // Фильтр яркости с учетом поворота
    CorrectionFilter.Symmetry,            // Фильтр симметрии
    CorrectionFilter.DarkPixelsAtBottom   // Фильтр темных пикселей внизу
};

// Определяем словарь весов для каждого фильтра, где ключ - название фильтра, а значение - его вес (значимость).
settings.FilterWeights = new Dictionary<string, double>
{
    { "PixelDifference", 1.0 },      // Разница пикселей
    { "Brightness", 1.0 },           // Яркость изображения
    { "Contours", 1.0 },             // Выявление контуров
    { "VerticalGradient", 1.0 },     // Вертикальный градиент яркости
    { "ColorHistogram", 1.0 },       // Гистограмма цветов
    { "Symmetry", 1.0 },             // Оценка симметрии
    { "RotatedBrightness", 1.0 },    // Яркость после поворота
    { "DarkPixelsAtBottom", 1.0 }    // Количество тёмных пикселей в нижней части изображения
};

// Извлекаем изображение центра (круга) из HTML-элемента и преобразуем в строку Base64
HtmlElement htmlCenter = instance.ActiveTab.FindElementByXPath("//*[@id='center-image']", 0);
if (htmlCenter.IsVoid) throw new Exception("Не удалось найти элемент для получения центра изображения!");
string base64CenterImage = htmlCenter.GetAttribute("src").Replace("data:image/png;base64,", "");

// Извлекаем изображение внешней части (кольца) из HTML-элемента и преобразуем в строку Base64
HtmlElement htmlOuter = instance.ActiveTab.FindElementByXPath("//*[@id='outer-image']", 0);
if (htmlOuter.IsVoid) throw new Exception("Не удалось найти элемент для получения кольца изображения!");
string base64OuterImage = htmlOuter.GetAttribute("src").Replace("data:image/png;base64,", "");

// Создаем объект CaptchaSolver и передаем настройки через метод SetSettings
CaptchaSolver solver = new CaptchaSolver();
solver.SetSettings(settings);

// Передаем изображения в обработчик и получаем угол поворота
int angle = solver.FindTargetAngleFromBase64Async(base64CenterImage, base64OuterImage).GetAwaiter().GetResult();

// Сохраняем результат в переменную проекта
project.Variables["angle"].Value = angle.ToString();

// Возвращаем угол
return angle;

131862


131863


131864


131865


131866


131867


131868


131869


131870

Также решил поделиться исходниками на случай, если кто-то захочет внести свои дополнения, расширить функционал или продолжить развитие проекта.

131872


131871


131873


131874


131875
131878


131879


131880


131881


131882

Вот такая интересная и полезная разработка у меня получилась. Уверен, что она найдет свое применение и сможет принести пользу. :-)
 

Вложения

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

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 874
Благодарностей
2 588
Баллы
113
Спасибо!
Разработка действительно интересная.
Времени на погружение во все это забрала наверно не мало?
 
  • Спасибо
Реакции: samsonnn

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
Последнее редактирование:
  • Спасибо
Реакции: BAZAg

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 874
Благодарностей
2 588
Баллы
113
Это опыт, 10+ лет занимаюсь разными капчами, картинками, заказами)
Это же решение, как это вижу я - полноценный кейс.
Просто упаковать в какое-то API и монетизировать чисто распознавалки.
Или в наше время с нейросетями спроса на такое может не быть?
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
Это же решение, как это вижу я - полноценный кейс.
Просто упаковать в какое-то API и монетизировать чисто распознавалки.
Или в наше время с нейросетями спроса на такое может не быть?
Да, ты прав, иногда проще найти решение для текущей задачи, чем развертывать целую нейросеть, особенно когда можно обойтись более простыми методами. Зависит от задачи и ресурсов. Если нужно быстро и эффективно решить проблему, лучше выбрать готовое решение, которое при этом будет достаточно универсальным. В любом случае, если что-то нужно будет доработать или адаптировать, всегда можно подойти с другой стороны.
 
Последнее редактирование:
  • Спасибо
Реакции: BAZAg

radv

Client
Регистрация
11.05.2015
Сообщения
3 851
Благодарностей
2 081
Баллы
113
Делал что то подобное для себя, чтобы отладку решения капчи локально проводить. Полезная и ценная вещь. :ay: :az:
 
  • Спасибо
Реакции: samsonnn

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 874
Благодарностей
2 588
Баллы
113
Мне вот интересно, а зачем используется .NET 9?
Какая-то конкретная зависимость не работает или для поворота изображения без этого никак?
Под капотом просто какие-то алгоритмы или подключаются какие-то обученные нейронные сети?

Я не скачивал проект (для моих задач он пока не нужен).
Но, хотел бы все же удовлетворить своё любопытство...

Это опыт, 10+ лет занимаюсь разными капчами, картинками, заказами)
Эх...
Как быстро летит время...
Кажется будто совсем недавно купил зенку, зарегистрировался на форуме и начал искать ответы...


Делал что то подобное для себя, чтобы отладку решения капчи локально проводить. Полезная и ценная вещь. :ay: :az:
Я что-то подобное шаманил с каптчей-часами, но здесь это всё выглядит куда серьёзнее.
Сам я скорее всего даже не брался бы за подобную задачу (не представляю с какого боку к ней подходить).
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
Мне вот интересно, а зачем используется .NET 9?
можно написать на любом .NET или .NET Framework я написал на .NET 9 так как он быстрее, лучше, оптимизированный. Стараюсь идти в ногу со временем, так жить легче.

Под капотом просто какие-то алгоритмы или подключаются какие-то обученные нейронные сети?
Алгоритмы, вычисления, вес влияния, нету там нейросети.

Эх...
Как быстро летит время...
Кажется будто совсем недавно купил зенку, зарегистрировался на форуме и начал искать ответы...
Дааа, я тоже вроде вчера зенку купил, а уже столько времени пролетело, так и жизнь пролетит не заметим и уже будет 70+ лет а вроде и не жил.

Сам я скорее всего даже не брался бы за подобную задачу (не представляю с какого боку к ней подходить).
Если бы срочно нужно было, и были бы клиенты, то взялся бы)
 

The_vAe

Client
Регистрация
30.05.2019
Сообщения
3 623
Благодарностей
1 408
Баллы
113
Есть возможность поделится, пожалуйста, хотя бы кратко , по какому алгоритму работает решение этого кружка?
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
Есть возможность поделится, пожалуйста, хотя бы кратко , по какому алгоритму работает решение этого кружка?
скачайте исходник, там все прокомментировано. Чтоб понять можете выполнить в VS по шагам и поймете весь принцип работы
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 890
Благодарностей
1 631
Баллы
113
Я что-то подобное шаманил с каптчей-часами, но здесь это всё выглядит куда серьёзнее.
Если бы было обычно я бы на конкурс эту статью не писал, мне было бы стыдно, а так да тут не все так просто и очевидно. Казалось бы ну угол и что? Но если вникнуть и понять как этот угол определяется, вот тогда начинаешь осознавать и понимать всю ценность разработки. Это разрабатывалось все специально для конкурса, больше не хочу, не выгодно, мороки очень много, ну прям очень очень много. Проще за потраченное время написать штук 5 - 10 шаблонов на заказ по 100 - 150$ и не парить себе мозги.
 

The_vAe

Client
Регистрация
30.05.2019
Сообщения
3 623
Благодарностей
1 408
Баллы
113
Спасибо за старание
Сегодня хочу поделиться своей разработкой — решением задачи поиска нужного угла в капче, аналогичной TikTok.

Этот проект не предназначен для обхода капч TikTok или других сервисов. Для предотвращения возможных недоразумений я создал офлайн-аналог страницы, имитирующей процесс верификации. Это позволяет тестировать и демонстрировать алгоритм без нарушения правил использования сервисов. Во избежание претензий со стороны соцсети TikTok к ZennoLab, проект реализован в строгом соответствии с правилами и стандартами. Наш форум Zenno.club представляет собой дружелюбное сообщество разработчиков, ориентированное на автоматизацию рутинных задач, а не на взлом систем защиты. Проект создан исключительно в образовательных целях и для участия в конкурсе ZennoLab.

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

Программа TikTok Angle Solver предназначена для визуальной настройки параметров, которые затем указываются в библиотеке TikTok Angle Solution и используются в Zenno Poster.

Для наглядности и лучшего понимания я создал файл HTML, который можно открыть в браузере, просто дважды щелкнув по TikTok_.html.

Посмотреть вложение 131793

Посмотреть вложение 131795

Выберите любое изображение на вашем компьютере.

Посмотреть вложение 131796

Появилась капча, похожая на TikTok. Мы видим цель — 123° и слайдер, который пока стоит на 0°.

Посмотреть вложение 131797

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

Посмотреть вложение 131798

Интересно, как это работает? Нажмите F12, чтобы открыть DevTools (Инструменты разработчика) и посмотреть код страницы с пояснениями. Или просто откройте файл TikTok_.html в Блокноте.

Посмотреть вложение 131800

Обратите внимание на функцию checkCompletion(angle) — в настоящей капче TikTok также учитывается допустимая погрешность.

В функции checkCompletion(angle) можно настроить допустимую погрешность угла для проверки точности. В текущем примере погрешность установлена на , что означает точное совпадение углов. Однако, если нужно учесть небольшие отклонения, вы можете изменить значение погрешности в условии:

Посмотреть вложение 131801

Если вы хотите, чтобы погрешность была, например, , просто замените 0 на 1 в условии. Это позволит алгоритму учитывать небольшие отклонения и не считать задачу выполненной только при идеальном совпадении углов.

в DevTools (Инструменты разработчика)

Посмотреть вложение 131802

На скриншоте выше обе части изображений (круг и кольцо) закодированы в base64. Мы будем работать именно с ними, чтобы определить угол. Представьте, что строки с подсказкой ('Угол: 123° / Цель: 123°') нет – мы не знаем, какой там угол, и нам нужно вычислить его самостоятельно.

Для подбора подходящих значений порогов фильтров и определения угла поворота изображения было разработано консольное приложение TikTok Angle Solver, написанное на .NET 9. Оно работает в связке с Selenium WebDriver.

Посмотреть вложение 131804

Для работы консольного приложения TikTokAngleSolver нужно скачать и установить .NET 9, как показано на скриншоте (выделено красным) - скачать .Net 9

Посмотреть вложение 131806

Посмотреть вложение 131807

При первом запуске приложения автоматически создаётся файл config.json с настройками, включающими справку и все параметры.

Посмотреть вложение 131808
Посмотреть вложение 131809

Файл config.json можно редактировать без закрытия приложения — после сохранения в Блокноте (Ctrl + S) настройки обновляются в реальном времени, и нет необходимости постоянно открывать и закрывать файл.

Итак, давайте начнём с настроек. По умолчанию все они включены. Кратко разберём каждый блок настроек.

Блок GeneralSettings включает несколько важных настроек:

"DefaultToBaseline": true/false — позволяет включить работу с базовым углом по умолчанию, если установлено значение true, или с перевёрнутым углом на 180 градусов, если false.
"EnableFilters": true/false — включает или отключает фильтры. Если фильтры отключены, будет использоваться только угол, выбранный по умолчанию.
"EnableManualCorrection": true/false — управляет возможностью ручной корректировки, определяя, будет ли включена опция для сохранения хеша.
"EnableHashLookup": true/false — отвечает за поиск по хешу в словаре. Файл manual_corrections.json будет автоматически создан при первом сохранении хеша.
"EnableVoting": true/false — включает или отключает голосование фильтров. Если голосование отключено, применяются фильтры из списка ListFilterOrder.
"EnableMajorityVoting": true/false — позволяет выбрать вид голосования. Если включено, используется мажоритарное голосование; если выключено, комбинированное.
"UseWinningAngle": true/false — выбирает результат проголосовавших фильтров. Если включено, выбирается вариант с большей уверенностью, если отключено — с меньшей уверенностью.

По умолчанию включены все опции - true

Посмотреть вложение 131810

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

Сейчас DefaultToBaseline": false – значит, угол перевернут на 180°

Посмотреть вложение 131811

Посмотреть вложение 131812

А тут "DefaultToBaseline": true – значит, угол остаётся базовым.

Посмотреть вложение 131813

Посмотреть вложение 131814

Здесь у нас включён базовый угол, не перевёрнутый, и список активных фильтров.

Посмотреть вложение 131815

Посмотреть вложение 131816

Далее мы включили ручную корректировку с опцией "EnableManualCorrection": true. Как видите, в консоли появилась возможность задать свой правильный угол или просто нажать Enter, чтобы пропустить.

Посмотреть вложение 131817

Посмотреть вложение 131818

Затем мы включили поиск по хешу с опцией "EnableHashLookup": true и голосование с опцией "EnableVoting": true. Однако, поскольку следующая опция отключена — "EnableMajorityVoting": false — будет использоваться комбинированное голосование.

Посмотреть вложение 131819

Посмотреть вложение 131820

На этом этапе мы включили мажоритарное голосование с опцией "EnableMajorityVoting": true. Обратите внимание на результат, выделенный зеленым цветом: уверенность снизилась, а неправильный результат стал правильным. Это связано с тем, что опция "UseWinningAngle": false сейчас отключена — именно эта опция переключает результаты голосования.

Посмотреть вложение 131821

Посмотреть вложение 131822

Тут снова включены все опции. "UseWinningAngle": true – теперь результаты снова поменялись местами, всё стало на своё место.

Посмотреть вложение 131823

Посмотреть вложение 131824

Блок ThresholdsSettings включает несколько важных настроек пороговых значений для фильтров:

С подбором настроек особо не заморачивался, на глаз все делал. Но это работает!

Значения нужно подбирать самостоятельно. Ваша цель — установить значение порогов так, чтобы они подходили для большинства изображений в среднем. Например, если у вас есть 100 изображений, пороговые значения следует настроить так, чтобы они подходили хотя бы для 60-75 изображений. Для остальных, которые не подходят, можно использовать словарь с хешами — manual_corrections.json

Посмотреть вложение 131825


Блок ListFilterOrder содержит список активных фильтров:

Посмотреть вложение 131827

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

Например:

Посмотреть вложение 131828

или

Посмотреть вложение 131829

или

Посмотреть вложение 131830

или

Посмотреть вложение 131831


Блок DictionaryFilterWeights содержит словарь весов влияния фильтров:

В этом словаре каждое значение представляет вес фильтра. Можно провести аналогию с ролями в обществе: например, президент имеет наибольшее влияние — его слово закон. Если он сказал, что вода сухая, значит, она сухая, и его вес может быть равен 2 или выше. Директор тоже имеет влияние, он знает, что вода мокрая, но перед президентом его слово будет гораздо менее весомым, и его вес может быть равен 1.5. Учитель, несмотря на свой авторитет, имеет меньшее влияние, его вес может быть равен 1. Сантехник Вася и тракторист Дима имеют одинаковое влияние, их вес может быть равен 0.5. Школьник, с минимальным влиянием, имеет вес, близкий к 0.1. Вес фильтров может быть дробным (например, 0.5 или 1.5), что позволяет гибко настраивать их влияние. Чем выше вес фильтра, тем сильнее его влияние на результат. Вы сами выбираете и устанавливаете вес влияния каждому фильтру на ваше усмотрение.

Посмотреть вложение 131832

В папке с программой находится файл manual_corrections.json (файл создается сам при первом обращении), в котором хранятся хеш (в шестнадцатеричном формате) и соответствующее им значение угла. Если включена настройка "EnableHashLookup" (поиск по хешу), то сначала выполняется поиск угла в этом файле. Если совпадений не найдено, угол определяется с помощью фильтров.

Посмотреть вложение 131835

Чтобы добавить хеш в словарь, всё просто: нужно ввести угол и нажать Enter. Если хотите пропустить, просто нажмите Enter.
Для создания хеша нужно указать угол

Посмотреть вложение 131836

Нажимаем Enter и видим что сработал поиск по хешу.

Посмотреть вложение 131837

Что такое перцептивный хэш?

Перцептивный хэш (pHash)
— это способ представить изображение в виде короткого цифрового кода, который отражает его визуальное содержание. Он позволяет находить похожие изображения, даже если они были изменены (например, сжаты, осветлены или слегка искажены).

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

Например, найденный угол при помощи поиска по хешу:

Посмотреть вложение 131838

Посмотреть вложение 131839

Посмотреть вложение 131840

Если не удалось найти по хешу и в настройках включены соответствующие опции, то автоматически применяется выбранное голосование с фильтрами.

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


Для работы в Zenno Poster была специально разработана библиотека TikTok Angle Solution, написанная на .NET Framework 4.7.2

Посмотреть вложение 131853

Как подключать любую библиотеку, думаю, знают все уважающие себя пользователи форума zenno.club — этот вопрос уже не раз обсуждался на форуме.
Скопировать саму библиотеку TikTokAngleSolution в папку ExternalAssemblies
В Директивы using и общий код нужно прописать: using TikTokAngleSolution;

Посмотреть вложение 131859

Выбрать библиотеку в Ссылки из GAC

Посмотреть вложение 131858

Прошу обратить внимание на настройку settings.InnerRadiusFactor = 0.3

В файле TikTok_.html изображение делится в соотношении 70 на 30, где 70 — это кольцо, а 30 — круг. Это важно, поскольку в реальной капче TikTok внутренний радиус (круг) может отличаться. Настройка InnerRadiusFactor позволяет регулировать этот параметр и адаптировать алгоритм под различные варианты радиусов.

В библиотеке TikTokAngleSolution убрана настройка EnableManualCorrection (ручная корректировка при добавлении хеша) из-за соображений многопоточной работы. Если кому-то это действительно нужно, вы можете добавить её самостоятельно в исходнике. Хеш добавляется через консольное приложение, и формируется файл, который затем можно переместить в нужную папку и указать путь к этому файлу manual_corrections.json

Указываем настройки все как в консольном приложении в файлике config.json

Посмотреть вложение 131860

Посмотреть вложение 131861


Весь код кубика "Свой C# код" в Project Maker
C#:
// Создаем объект с настройками решения капчи
CaptchaSolverSettings settings = new CaptchaSolverSettings();

// Включаем режимы работы алгоритма
settings.DefaultToBaseline = true;   // Если true — используем базовый угол, если false — перевернутый на 180°
settings.EnableFilters = true;       // Включить или выключить использование фильтров для коррекции угла
settings.EnableHashLookup = true;    // Использовать поиск хешей в словаре (сравнение по хешам)
settings.EnableVoting = true;        // Включить систему голосования, если false то использует только список фильтров без голосования
settings.EnableMajorityVoting = true;// Если true — мажоритарное голосование, если false — комбинированное
settings.UseWinningAngle = true;    // Включить использование угла, выбранного большинством голосов (true), или меньшинством голосов (false)

// Указываем путь к файлу (словарю) с корректировками, который используется для поиска изображений по хешу.
// Этот файл в формате JSON содержит данные, где для каждого хеша изображения указан соответствующий угол поворота,
// который используется для корректировки изображения, если его угол не удаётся определить автоматически.
//settings.FilePath = @"D:\ZennoTemplates\TikTokAngleSolver\manual_corrections.json";
settings.FilePath = $"{project.Directory}manual_corrections.json";

// Задаем коэффициент внутреннего радиуса, который определяет размер внутреннего круглого изображения
// относительно общего размера (например, процент от ширины или высоты изображения).
// Значение 0.3 означает, что внутренний круг будет занимать 30% от общего размера.
settings.InnerRadiusFactor = 0.3;

// Устанавливаем пороговые значения различных параметров обработки изображения
settings.PixelDifferenceThreshold = 130; // Минимальная разница пикселей для обнаружения изменений
settings.BrightnessThreshold = 10;       // Порог яркости (насколько сильны изменения в освещении)
settings.ContoursThreshold = 1.5;        // Порог обнаружения контуров (определяет резкость границ)
settings.VerticalGradientThreshold = 50; // Порог вертикального градиента (анализ вертикальных изменений)
settings.ColorHistogramThreshold = 1.5;  // Порог цветовой гистограммы (различия в цветах)
settings.SymmetryThreshold = 1.2;        // Порог симметрии (анализирует, насколько изображение симметрично)
settings.DarkPixelThresholdAtBottom = 50;// Порог темных пикселей внизу изображения

// Определяем список активных фильтров, которые будут применяться к изображению
settings.ActiveFilters = new List<CorrectionFilter>
{
    CorrectionFilter.PixelDifference,     // Фильтр по разнице пикселей
    CorrectionFilter.Brightness,          // Фильтр по яркости
    CorrectionFilter.VerticalGradient,    // Фильтр по вертикальному градиенту
    CorrectionFilter.Contours,            // Фильтр по контурам
    CorrectionFilter.RotatedBrightness,   // Фильтр яркости с учетом поворота
    CorrectionFilter.Symmetry,            // Фильтр симметрии
    CorrectionFilter.DarkPixelsAtBottom   // Фильтр темных пикселей внизу
};

// Определяем словарь весов для каждого фильтра, где ключ - название фильтра, а значение - его вес (значимость).
settings.FilterWeights = new Dictionary<string, double>
{
    { "PixelDifference", 1.0 },      // Разница пикселей
    { "Brightness", 1.0 },           // Яркость изображения
    { "Contours", 1.0 },             // Выявление контуров
    { "VerticalGradient", 1.0 },     // Вертикальный градиент яркости
    { "ColorHistogram", 1.0 },       // Гистограмма цветов
    { "Symmetry", 1.0 },             // Оценка симметрии
    { "RotatedBrightness", 1.0 },    // Яркость после поворота
    { "DarkPixelsAtBottom", 1.0 }    // Количество тёмных пикселей в нижней части изображения
};

// Извлекаем изображение центра (круга) из HTML-элемента и преобразуем в строку Base64
HtmlElement htmlCenter = instance.ActiveTab.FindElementByXPath("//*[@id='center-image']", 0);
if (htmlCenter.IsVoid) throw new Exception("Не удалось найти элемент для получения центра изображения!");
string base64CenterImage = htmlCenter.GetAttribute("src").Replace("data:image/png;base64,", "");

// Извлекаем изображение внешней части (кольца) из HTML-элемента и преобразуем в строку Base64
HtmlElement htmlOuter = instance.ActiveTab.FindElementByXPath("//*[@id='outer-image']", 0);
if (htmlOuter.IsVoid) throw new Exception("Не удалось найти элемент для получения кольца изображения!");
string base64OuterImage = htmlOuter.GetAttribute("src").Replace("data:image/png;base64,", "");

// Создаем объект CaptchaSolver и передаем настройки через метод SetSettings
CaptchaSolver solver = new CaptchaSolver();
solver.SetSettings(settings);

// Передаем изображения в обработчик и получаем угол поворота
int angle = solver.FindTargetAngleFromBase64Async(base64CenterImage, base64OuterImage).GetAwaiter().GetResult();

// Сохраняем результат в переменную проекта
project.Variables["angle"].Value = angle.ToString();

// Возвращаем угол
return angle;


Также решил поделиться исходниками на случай, если кто-то захочет внести свои дополнения, расширить функционал или продолжить развитие проекта.

Посмотреть вложение 131872


Вот такая интересная и полезная разработка у меня получилась. Уверен, что она найдет свое применение и сможет принести пользу. :-)
 
  • Спасибо
Реакции: samsonnn

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