На прошлой неделе был топик «
Ломаем капчу» — каптча там была довольно простая, но в комментариях
предложили сломать каптчу Яндекса. Мне эта идея показалась интересной, и я решил попробовать.
Постановка задачи
Итак, у нас есть вот такая каптча:
Основные особенности:
- 6 цифр;
- Изображение монохромное;
- Фон белый;
- Искажения, которые, однако, незначительно смещают цифры со своих позиций;
- Две шумовые линии очень похожие на синусоиды.
Эту каптчу Яндекс использует давно (больше года, насколько я помню), что означает никто за это время её не сломал, они бы заметили, наверное. Поскольку даже человек иногда не в состоянии распознать все цифры, ставить задачу стопроцентного распознавания было бы глупо, да и цель у меня — просто решить интересную задачу, а не написать спам-бота. Поэтому поставим задачу распознавания каптчи с некоторой вероятность, даже одного процента будет достаточно.
Задача: написать программу, распознающую каптчу Яндекса с вероятностью не менее одного процента.
Первичная обработка
Приступим. Для начала сделаем самую простую часть — приведем изображение к удобному для распознаванию виду. Код написан на PHP (скриптовые языки для таких экспериментов удобнее), внизу будет ссылка на архив с исходниками.
Для начала закрасим логотип Яндекса белым прямоугольником (его координаты не меняются):
Иногда этот прямоугольник «откусывает» часть последней цифры, но это не страшно.
Теперь обрежем изображение по краям до первых отличных от фона пикселей (рамка добавлена для наглядности):
Убираем линии
И вот первая серьезная проблема — как убрать эти линии? Первое что пришло в голову — не убирать их совсем :) Однако, позже выяснилось, что шум они создают значительный и заметно увеличивают разность между двумя экземплярами одной и той же цифры. Придется убирать.
Второе решение, которое пришло в голову, — векторизовать изображение и удалить две самые длинные линии. Беглый взгляд по доступным готовым решениям показал, что в результате векторизации эти линии будут разбиты на несколько кривых, что усложняет задачу (придется как-то отличать их от кривых, принадлежащих цифрам). Хотя векторизация, на мой взгяд, более правильный путь, я решил от нее отказаться.
В итоге решено было использовать генетические алгоритмы, тем более, опыт работы с ними у меня большой. Останавливаться на описании ГА я не буду — об этом можно
прочитать в Википедии.
Суть работы ГА в нашем случае — подобрать такую линию (а точнее набор точек), которая лучше всего удовлетворяет нашей целевой функции. Целевая функция (
fitness) для каждого предложенного ГА варианта решения считает его приспособленность в виде одного положительного числа. В нашем случае это количество точек решения, которые попадают на пиксели, отличные от цвета фона, плюс дополнительные очки за «связность линии» (иначе решение будет состоять из отдельные точек).
Начальная популяция генерируется из линий, типичных для этой каптчи. Оператор мутации — «вытягивание» линии в случайном месте. Оператор кроссовера стандартный. Выбор особей в новую популяции по принципу рулетки.
Однако, генетический алгоритм — прожорливая штука. Адекватные решения он находил за 2-3 минуты, что в нашем случае неприемлемо, а также почти в каждом случае возникала одна проблема — линия «перескакивала» с полосы на часть цифры. С точки зрения нашей целевой функции такое поведение выявить было невозможно (см. 1-ю и 4-ю цифры):
В начале статьи я упомянул, что линии похожи на синусоиды — этот предположение оказалось полезным. В качестве решения я взял не набор точек, а параметры синусоиды (растяжение по осям, угол наклона, начальная фаза, сдвиг по вертикали). Перебирать 5 параметров намного проще, чем почти 200. К тому же теперь нам не надо следить за формой кривой — она изначально такая, которая нам необходима. Оператор мутации теперь меняет случайный параметр синусоиды, а оператор кроссовера не используется. В итоге за 8-10 секунд алгоритм неплохо справляется с поставленной задачей:
Теперь остается только закрасить белыми линиями толщиной в 3 пикселя (небольшой мусор остался):
И обрезать края с порогом в несколько пикселей:
Divide and conquer
Разделяем изображение на 6 равных частей, обрезаем лишние края, масштабируем каждое до размеров 20х30 пикселей:
Качество немного пострадало, но мы все ещё можем легко сказать, какие там изображены цифры, значит и робот сможет :) Задача сводится к распознаванию каждой отдельной цифры.
Если удастся добиться распознавания отдельной цифры с вероятностью не менее 50%, общая вероятность правильного распознавания каптчи будет 0,5^6 ~ 0.015, т.е. 1,5%, что нам и требуется.
Сначала я попробовал сгенерировать усредненный шаблон для каждой цифры и затем по наименьшему отклонению от шаблона проводить распознавание. Затея успехом не увенчалась — искажения слишком сильные, а некоторые цифры (8 и 9, например) отличаются незначительно. Лучше всего для таких задач подходит искусственная нейронная сеть, её я и решил использовать.
Самому кодить нейросеть дело неблагодарное, да ещё и на PHP — пошел искать библиотеку. Первое, что выдал Гугл, —
Fast Artificial Neural Network Library. Библиотека написана на C++, но имеет интерфейсы для большинства распространенных сред. Есть
extension и для PHP (правда на последнюю версию он ставится не без напильника).
На вход нейросети подается массив из 600 элементов (изображение 20х30 пикселей), каждый элемент задает яркость соответствующего пикселя вещественным числом 0 до 1. На выходе — массив из 10 элементов от 0 до 1, каждый из которых задает близость входных данных к соответствующей цифре. Между входным и выходным слоями всего лишь один слой из 150 нейронов. Почему один? Так получилось в результате практических экспериментов, что такая конфигурация работает лучше всего на этой задаче.
Библиотека действительно очень быстрая — на 300 образцах (50 каптчей) за 5 минут она обучила нейросеть до 30% вероятности распознавания. Неплохо, но нам нужны как минимум 50%.
Постепенно добавляя новые образцы для обучения (пришлось поработать китайцем), заветные 50% были получены на 1800 образцах после 3 часов обучения. На тестовом наборе из 100 каптчей (не участвовавших в обучении) удалось успешно распознать 2, что соответствует нашим предположениям. Время распознавания одной каптчи — 8-10 секунд.
Заключение
Поставленная задача выполнена. На все ушло два вечера (+ ночью компьютер сеть обучал). Можно было «добивать» каптчу и дальше, добавляя новые и новые образцы для обучения, но у меня на это нет времени да и желания. Хотя, если кто-то захочет, за пару дней сможет довести дело до конца, а пока у ребят из Яндекса есть время на усложнение каптчи :)
Архив с исходниками выложу минут через 30.
UPD: исходники:
yandex_captcha.tar.gz (1 Мб).
По просьбам в комментариях выкладываю без скриптов для обучения и вариант с 40% вероятностью распознавания (для каждой цифры). Думаю, Яндексу это не навредит.
UPD2: небольшое FAQ:
Q: 1,5% — это же мало!?
A: Дальнейшее увеличение точности зависит лишь от количества образцов для обучения и проблемы не представляет.
Q: Почему бы не попытаться «выправить» линии вместе с цифрами?
A: Искажения цифр и линий не зависят друг от друга.
комментарии (300)
Яндекс или сразу Гугл? :)
это баг или фича?
А вот попробуйте высказаться поперёк мнения любого гуру — выясните как шустро изменяется карма из-за серьёзных ответов.
ощущение, что для некоторых ip есть определенное количество «штрафных» капч, необходимых для успешной регистрации
самые крутые капчи у reCAPTCHA — и полезные и несложные человеку.
img13.nnm.ru/0/d/3/8/f/0d38fbce1d05cc1702089d54af96a8a8_full.jpg
только я поставлю попробовать в очередной раз — на оффсайте сообщение, что рапида сменила алгоритм, и пока капчи — руками))))
www.tremani.nl/open-source/neural-network/?p=source
для работы трекбол хорош когда нервы никто не треплет и все идет своим чередом, а если запара, то старая мыша выручает
Мыши (и тачпады) в утиль!
Но для работы он все же не удобен, там ведь абсолютное позиционирование и из-за большого размера как его самого, так и монитора, приходится совершать много движений, особенно если надо нажать кнопку в одном углу, а затем в другом. Впрочем, неплохая гимнастика для кисти:)
Мышка и трекбол удобнее, небольшое мановение руки и все :)
Может не надо исходники выкладывать?
лучше выложить — пусть подумают, поменяют.
и всем хорошо — нам интересно, им безопасно.
Кому надо у того уже есть.
А то иногда надоедает сильно :) Причём, ладно, если каптча как у Гугла — читаемая… так бывает же…
меня за это заминусовали так? :)
здесь она (про arctg):
copypast.ru/2009/05/15/15_samykh_strannykh_kaptch_15_foto.html
А почему это вообще должно представлять опасность для Яндекса? Вероятность меньше 5 процентов при времени распознавания 10 секунд? Эффективнее руками заполнять. Сколько автоматических успешных срабатываний будет, и за какой срок? 6 капч в минуту, 360 в час, 8640 за сутки, и распознается 200 капч. 200 капч быстрее заполнить за 10 минут руками.
Яндексу нет причин что-либо усложнять :).
браво! :)
«Так в природе тоже гермафродиты есть — смысл ГА в естественном отборе в первую очередь :) „
Сначала Яндексу их предложишь? :)
ocr-research.org.ua/teabag/0.X.html
а что человек зазнался, говорит что это практически невозможно :)
я знаю автора этой капчи, он когда-то мне говорил что она невзламываемая…
вот я и подумал что автору статьи может быть интересно было бы её сломать. чисто ради спортивного интереса
странная у вас логика…
img339.imageshack.us/gal.php?g=image5.gif
Шаг1. Получаем картинку: img339.imageshack.us/img339/4084/image5.gif
Шаг2. Бинаризируем изображение: img529.imageshack.us/img529/3653/image4x.gif
Шаг3. Находим острые углы изображения и считаем их углами трансформированного прямоугольника: img111.imageshack.us/img111/74/image3.gif
Шаг4. Трансформируем искажённый прямоугольник в нормальный: img188.imageshack.us/img188/7950/image1i.gif
Ну, а это уже можно распознать обычным шаблоном. Это было описано в предыдущем посте по распознаванию капчи.
P.S. Картинки вставлять в пост не могу, так что извиняйте
ocr-research.org.ua/teabag/1.X.html
ocr-research.org.ua/teabag/2.X.html
(похоже положили человеку сайт :))
1.0.1 вообще тривиальная, на 2.0 у них генератора нет.
Эксперименты в 0.х, увы, по большей части абсолютно нечитаемы.
В дальнейшем я как-то раз видел обсуждение как их правильно вычистить, чтобы они распознавались.
Далее отбрасываем все линии что на верхнем конце «вертикальных» линий. Далее остается два направления, направление идущее вверх вправо (cos(вектора(правый конец, левый конец)))>0,sin>0) восстанавливаем как X, другое как Y в плоскости. Используем алгоритм заливки полигонов для закраски. Превращаем в плоскую букву 3x5 пикселей и сравниваем с образцами (даже мутаций никаких не надо… можно даже 3x5 представить как 15 бит -> превратить в short int и сделать lookup таблицу)
СЛИШКОМ просто.
img339.imageshack.us/gal.php?g=image5.gif
Шаг1. Получаем картинку: img339.imageshack.us/img339/4084/image5.gif
Шаг2. Бинаризируем изображение: img529.imageshack.us/img529/3653/image4x.gif
Шаг3. Находим острые углы изображения и считаем их углами трансформированного прямоугольника: img111.imageshack.us/img111/74/image3.gif
Шаг4. Трансформируем искажённый прямоугольник в нормальный: img188.imageshack.us/img188/7950/image1i.gif
Ну, а это уже можно распознать обычным шаблоном. Это было описано в предыдущем посте по распознаванию капчи.
P.S. Картинки вставлять в пост не могу, так что извиняйте
заранее спасибо
Единсвтенное, почему-то не упоминаются возможные пути улучшения результата работы алгоритма (уж слишком хорошо буковки вписываются в две синусоиды по снизу и по верху). Думается, если сделать обратную трансформацию, качество распознавания можно улучшить на порядок (по собственному опыту разработки системы распознавания речи).
Хотя мне кажется, что после этой статейки этому варианту капчи на Яндексе осталось не больше двух недель…
Чем четче сигнал вы подаете в нейросеть, тем лучше результаты распознавания.
Поэтому если можно минимизировать возможные искажения входного сигнала, надо прибегнуть к фильтрации до процедуры распознавания.
В данном случае, я посмотрел несколько вариантов вывода капчи у Яндекса, и пришел к выводу, что искажение начертания цифр также определяется двумя синусоидами — сверху и снизу. Таким образом, их можно лекго выровнять, если применить обратное преобразование.
Скорее всего, это на порядок увеличит время работы алгоритма (но не думаю, что больше чем в 10 раз) и качество оценки (не думаю, что меньше чем в 10 раз). Естественно оценку эффективности придется делать, а то это среднее потолочное.
Просто тогда значительно увеличится «похожесть» цифр, и значительно уменьшится расстояние между подобными образами, а это очень хорошо влияет на качетсво распознавания нейросетей.
Вот сижу сейчас и понимаю что это был бы очень-очень хороший дипломный проект бы… Всучить-бы кому-нибудь на доработку… Причем как раз по приме ;0)
На диплом, имхо, не тянет. Максимум — курсовая на третьем курсе.
А насчет «незначительной смены алгоритма искажений»: все зависит от поставленной задачи. Если задача состоит в том, чтобы «распознать вот такой вот тип капчи у Яндекса», то любая заточка уместна. Незначительная смена — нашим легче, незначительно меняем алгоритм распознавалки.
Вы же сами остановились на двух синусоидах в начале :0) Это ли не заточка?
Ведь превратись две черты в логистические кривые, ваш алгоритм уже не заработает. А почему? Потому что задачу «распознать любую наперед заданную капчу» в обозримом будущем не решить. Поэтому и приходится только уповать на то, что новая капча будет из разряда тех, которые мы распознавать умеем.
Именно поэтому и невозможен на сегодняшний день «универсальный распознаватель капч». Он будет слишком сильно тормознутый, и будет не более полезен на практике чем «сферический конь в вакууме».
Суживаем задачу — получаем требуемый результат за приемлемое время.
twitter.com/bobuk/status/2649880690
Примерно такого и ожидал :)
…
Любая каптча разгадываема, это вопрос времени и ресурсов.
Как выше справедливо отметили, дешевле покупать разгаданные капчи за копейки, чем прогружать компьютеры.
тем бололее преобразования там несильнозамутнённые
натаскать ту же нейронную
А вот мои любимые:
и от mail.ru:
Просто вместо цифр и букв здесь иероглифы. А у кого они на клавиатуре есть!? :) Только у НЛО.
Поэтому и выводится блок с этими иероглифами, а докучи они перемешиваются %)
Что в некоторой степени усложняет распознавание
Пришлось искать в инете сервисы рисования иероглифов от руки, куда я 3 часа тщательно перерисовывал искаженные(!) иероглифы с капчи, копировал подходящий юникод и вставлял в поле ввода.
Когда наконец я осилил 6 этих символов, мне сказали «извини, таймаут». И по новой…
К 4му разу я, кажется, уже выучил китайский.
Собственно думаю мою капчу если захотят поломают за 1 вечер =)))
Нада придумать какой та способ защиты без капчи.
ждём новых постов по взлому каптч ;)
Правда если яндекс синусоиду поменяет на случайную кривую — все замедлится в разы.
И еще мне кажется подавать в нейросеть вещественные числа для пикселей с 4-мя оттенками серого не экономично, можно в 8-16 раз уменьшить объем входных данных.
я, конечно, дико извиняюсь, но, по моему скромному мнению, в данном случае общая вероятность будет равна минимальной вероятности из набора, т.е. 50%
зы: щаз набегут mathnazy ;-)
это отличное, но неприменимое здесь правило
У вас есть 6 событий распознания, каждое с двумя исходами: распознано — нераспознано.
Вероятность исхода «распознано» — 0.5
Вероятность 6ти исходов «распознано» = 0.5 х 0.5 х 0.5 х 0.5 х 0.5 х 0.5 = 0.015
прочитайте учебник, что ли :)
а равно как раз минимальной вероятности из набора событий. ибо если одно событие не наступило, то остальные события значения не имеют, и капча распознана не будет
Произведение вероятностей как раз это и учитывает — она отражает _одновременное_ возникновение _всех_ событий.
событие «выпадение орлов на всех десяти монетах» не равно событию «открытие сейфового замка из десяти цифр». которое, кстати, не равно событию «верная капча»
но спор скучен
Если мы считаем вероятность до того, как начинаем распознавать — нам не важно, одновременно события произойдут или нет.
Если у замка только цифры 1 и 0 — то равно. У нас же 50% вероятность угадать каждую цифру. Можете считать, что у нас только цифры 0 и 1 есть.
Так, выключай компьютер и срочно делать уроки!
и, ха: школота детектед. теорвер не преподают в школе
У нас есть 6 чисел. У нас равновероятны события, угадали мы цифру или нет. Пусть 1 — угадали, 0 — не угадали. Какие у нас события для всей капчи?
000000 — все 6 не угадали
000001 — угадали тоолько последнюю
000010 — угадали только предпоследнюю
000011 — угадали 2 последние
…
(ничего не напоминает?)
…
111110 — угадали только первые 5 цифр
111111 — угадали все 6 цифр.
Под понятие «правильного распознавания каптчи» попадает только последний случай. Хочется обратить внимание на то, что все случаи равновероятны. Всего возможных событий 2^6 (IT мы или не IT???) = 64. Тогда вероятность «правильного распознавания каптчи» = 1/64 = 0.015625. Бинго!
P.S. Как Хабрасообщество понимает этот комментарий не для habraname, а для тех школьников, которые попытаются его послушать.
P.P.S. 0.015625 скорее 1,6% ;)
Здесь рассчитывается вероятность события «все 6 цифр распознаны верно» — только тогда значение каптчи удастся ввести.
Минимальная вероятность из набора (0.5) здесь cовсем не при чём ;)
но, т-с-с-с!
а холивары не люблю
но ведь как минусуют, как минусуют! ;)
1) Есть предположение, что большее число нейронов на внутреннем слое (или нескольких) заработает, причем лучше, если увеличить количество образцов.
2) Есть подозрение. что если не просто замазывать синусоиды белым, а сохранять их там, где проходит линия под большими углами (близко к 90), то контуры букв лучше сохранятся, что сделает их лучше распознаваемыми.
3) Очень интересна была бы статистика по тому, какие цифры лучше распознаются, а какие хуже. У меня, например, обычно проблемы с 1 и 7 (при человеческом распознавании :) )
Заработает, согласен. Я и так увеличивал немного, когда образцы добавлял.
На общее количество «полезных» пикселей это не сильно повлияет. Хотя, конечно, немного получше будет.
Для нормальной статистики нужна большая тестовая выборка. А так да, 1 и 7 и 3 и 5 путаются чаще всенго.
Небльшое дополнение. Чтобы модуль php для работы с fann встал нормально, мне пришлось собирать fann 2.0 из исходников (я про linux-окружение), т.е. качаем 2.0, распаковываем, заходим в папку, делаем ./configure, make, make install.
После этого делаем pear install pecl.php.net/get/fann, и — вуаля — модуль поддержки fann для php установился.
А вот с версией 1.2.0 мне подружить php-расширение не удалось. Что-то у них там напутано с путями установки по умолчанию.
Кстати, куда после make install кладется .so-шник? А то по dl('fann.so') не находит, собака.
как вы боролись с
/root/tmp/pear/cache/fann-0.1.1/fann.c:493: warning: assignment makes pointer from integer without a cast
?
make: *** [fann.lo] Error 1
?
pecl.php.net/bugs/bug.php?edit=1&id=9954
Все собралось, но теперь при попытке сделать fann_create php молча вылетает.
У вас такого не было?
fann_create из доков к php-биндингу.
$ann = fann_create(array(2, 4, 1), 1.0, 0.7);
phpinfo():
fann
Fast Artificial Neural Network (FANN) library support — enabled
FANN object oriented API — disabled
В любом случае, спасибо за помощь.
Резюме по установке fann+php (для linux систем):
— с php нормально работает только версии 1.2.x
— ставить нужно так:
wget prdownloads.sourceforge.net/fann/fann-1.2.1.zip?download
unzip fann-1.2.1.zip
cd fann-1.2.1
./configure
make
make install
cd…
wget pecl.php.net/get/fann
tar -xzf fann
cd fann-0.1.1
вот здесь мы правим любимым редактором файл php_fann.h (как уже говорилось выше, комментируем 28-ю строку)
phpize && ./configure && make && make install
все. после этого должно работать.
cd… = cd пробел дветочки
и во всех адресах в начале пишем http: / / без пробелов
7 лет назад по этим темам курсовые писал.
Спасибо и успехов.
Конечно, сейчас мы меняем кое-что для того, чтобы затруднить работу этих скриптов.
Добавлю, что защита не состоит только из капчи. Для спамеров есть и другие неприятные сюрпризы :)
Олег Охотников.
Захожу отсюда:
serial0-0-0.otwaonnhd1-rtr1.nyroc.rr.com [24.92.224.14]