Капча, частный случай: рвём нейронную сеть тридцатью строками кода

       Уже не помню, как я наткнулся на статью habr.com/ru/post/464337, но она запала мне в мозг и не давала покоя вплоть до минувшего дня. Несколько раз я пытался понять происходящее, пару раз пытался заставить это работать, но безрезультатно: я совершенно ничего не понимаю в нейронных сетях и даже программирую не как настоящий программист.
    счастливая капча


       Наконец, несколько дней назад я осилил запуск питона и решил, а почему бы и не да и всё такое. Забыв всё, что я прочитал в упомянутой статье, пошёл своим путём.
       Вспоминая несметное количество решённых капч, я предположил, что можно решать их банальным сравнением с маской, что и подтвердилось впоследствии.
       Во-первых, вручную собрал тестовые капчи (83 штуки) и дал им очевидные имена. Скриптом превратил их в битовые изображения.

       Цифры в капчах бывают двух размеров по высоте с разницей в 1 пиксель и трёх-четырёх начертаний по ширине. Базовая линия всех символов во всех капчах одинаковая. Всё это разнообразие, как оказалось, имеет некую общую маску, сравнение с которой однозначно идентифицирует цифру. Вырезал по нескольку (сначала – по 5, потом добавлял ещё по 1-2; с «4» провозился дольше остальных) одинаковых цифр из разных капч. В paint.net наложил их друг на друга и получил общую для всех начертаний каждой цифры маску.

       Единственную проблему обнаружил позднее, уже при массовой обработке, но успешно её обошёл
    при помощи костыля
       Первоначально, распознавание шло по порядку — по исходному образу прогонялась маска «1», потом «2» и т.д. до «9». Оказалось, что в некоторых случаях, когда толстая линия шума накладывается на ножку «4», то одинаково успешно распознаются и «4», и «1». Пришлось, во-первых, изменить порядок применения масок с «123456789» на «423156789» и во-вторых, при успешном распознавании «4» заливать это место белым, чтобы гарантированно исключить «1».

       Кроме этого небольшого недоразумения шум совершенно не мешает. Итогом этого этапа стал набор из 9 масок. Два вложенных цикла и вуаля! – все мои 83 капчи распознаются на ура!

       

       Дальше встал вопрос: где взять большой набор капч для проверки. И я скачал «29 000 капч» из упомянутой статьи.
    Однако, это оказалось пустой тратой времени.
       Во-первых (точнее, во-вторых, т.к. я обнаружил уже позднее), там присутствуют идентичные файлы: один и тот же файл сохранён под разными именами: 6503 раза, 5420 раз, 760 и т.д. – т.е. всего уникальных файлов 14882, что, впрочем, тоже немало.
    Во-вторых, а на самом деле – во-первых, – это не настоящие капчи. Сайт отдаёт картинку в формате PNG, а в наборе – JPG, причём крайне плохого качества, причём со сдвигом. Могу предположить, что именно такова была цель автора – статья же недаром называется «”зашумленная” капча».

       Так что пришлось расчехлить гугл и самостоятельно намайнить идеальных капч: за ночь набралось 3224 файла, в том числе 49 абсолютно пустых, как выяснилось позднее. Cпасибо Ганеше за код.

       Собственно распознавание капчи укладывается в 26 строк скучного кода на питоне. Из внешних модулей нужен только PIL. Скорость работы – примерно 1000 капч в минуту (одна тысяча капч в минуту) на стареньком Core 2 «четыре ядра четыре гига». На более приличном восьмипоточном i5 заметно быстрее, хотя дело, конечно, не в потоках.    Распознавание 100% или очень к тому близко: выборочная проверка не показала ошибок.

       Конечно, всё это не интересно в смысле нейронных сетей и прочих блокчейнов, но имеет совершенно определённое преимущество перед предложенным ранее вариантом: скорость и точность. Так же верно и то, что любое изменение параметров капчи – гарнитуры или размера шрифта, вид шума и т.д. – приведёт к полной неработоспособности моего решения.

    Скачать архив с капчами с Яндекс.Диска (14МБ).

    Исходный код
    from PIL import Image, ImageTk
    
    def recognize(filepath):
      Zlist = [] # [(x1, z1), (x2, z2), (x3, z3), etc.] - position and digit
      captcha = ""
      originalimage = Image.open(filepath).convert('L').point(lambda x : 255 if x > 20 else 0, mode='1').convert('1').convert('RGBA')
      if originalimage.getextrema() == ((0, 0), (0, 0), (0, 0), (255, 255)):
        return("empty image")
      for z in [4, 2, 3, 1, 5, 6, 7, 8, 9]: # reorder to exclude false 1 on 4
        mask = Image.open('mask' + str(z) + '.png').convert('RGBA')
        previ = 0
        for i in range(15, 120): # no digit in left part
          resultimage = Image.alpha_composite(originalimage.crop((i, 0, i + 30, 0 + 50)), mask)
          if resultimage.getextrema() == ((0, 0), (0, 0), (0, 0), (255, 255)):
            if z == 4: # delete 4 to exclude false 1 on 4
              maskx = Image.open('mask4x.png').convert('RGBA') 
              originalimage.paste(Image.alpha_composite(originalimage.crop((i, 0, i + 30, 0 + 50)), maskx), (i, 0))
            if previ == 0 or i > previ + 15: #no digit closer then 15 px
              Zlist.append((i, z))
              if len(Zlist) == 5:
                  Zlist.sort()
                  for z in Zlist:
                    captcha = captcha + str(z[1])
                  return(captcha)
              previ = i
              i = i + 15 #skip a little
      Zlist.sort()
      return(str(Zlist)) #if less then 5 digits recognized
    	
    def main():
      captcha = recognize(entry.path)
    #----------------------------------------------#
    #  в архиве полный код для массовой обработки  #
    #----------------------------------------------#
    
    main()
    




    Дополнение от 13.02.2020.
       Ради чего всё затевалось? Не ради же спортивного распознавания сохранённых картинок? Нет, всё это было исключительно в прагматических целях.
       Готовое решение для работы. — локальный http-сервер распознавания плюс расширение для Chrome.
       Пока единственное, что оно умеет (я надеюсь, что умеет) — автоматически вставлять капчу в нужное место. В планах:
      — очистить интерфейс сайта, оставив необходимый минимум;
      — автоматизировать обновление капчи при просмотре сведений, т.к. одна капча даёт возможность открыть всего 4 объекта.
      — загружать сразу все готовые выписки, а не по одной.

    Дополнение от 05.03.2020.
       Готовое решение для работы. — локальный http-сервер распознавания плюс расширение для Chrome.
       Обновил расширение для Chrome. Теперь оно умеет, помимо автоподставновки капчи,
     1. при открытии страниц с информацией об объектах недвижимости разворачивать сведения о правах;
     2. собирать информацию с этих страниц для последующей обработки.
       Скриншот в комментарии habr.com/post/488018/#comment_21360646

    Дополнение от 17.04.2020.
       Бот для заказа выписок по списку — в связи с ограничением на частоту заказа выписок — 1 выписка за 5 минут. Скриншот в комментарии

    Средняя зарплата в IT

    110 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 8 593 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 29

      +2
      Опубликуйте на github, яндекс диск считается плохим тоном!
        +2
        Полагаете, 26 строк кода того стоят? Архив с капчами всё равно куда-то нужно выложить, а код в архиве — так, маленький бонус.
          +2
            0
            Да, спасибо. К сожалению, чтобы код заработал, нужны файлы с масками. Подскажите, пожалуйста, как их туда выложить.
              0
              Как вариант вставить ссылки на ядиск в комментариях в коде
                0
                Я уже вышел из положения — просто вставил их в код в виде BASE64-строк. И немного научился в гитхаб.
        0
        мне кажется 26 строк кода можно было опубликовать прямо в статье :)
          +2
          Они спрятались под спойлером «Исходный код».
            0
            спасибо, не заметил :)
          –2
          несколько дней назад я осилил запуск питона и решил

          что можно решать их банальным сравнением с маской, что и подтвердилось впоследствии.

          Я понимаю этот энтузиазм, но мне кажется что этот восторг не стоит статьи на хабр.


          Банальное сравнение по маске объекта на "зашумленном" изображении да еще при таких граничных условиях, не стоит статьи (и тем более публикации этих "26 строчек кода на питоне").

            +2
            Вот такой «Hello, World!»
            Я поделился не восторгом, а простым решением одной конкретной небольшой проблемы. Полагаете, лучше на пикабу? Или оно вообще никому не интересно?
              –3

              Извините, но эта задачка настолько банальна (OCR алгоритмы).
              Я догадываюсь, что, наверное, это сейчас уже не преподают или Вы это не проходили.


              Изобрести велосипед в самом древнем варианте (деревянные колеса и отталкивание ногами) — это наверное здорово (если никогда велосипедов не видел раньше).


              Возможно я слишком жестко написал о своем впечатлении от статьи.


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


              "Сортировка" в котором узнаете частный случай (например только байтов с ограничением размера массива) сортировки "пузырьком".


              Мысль, которая мне понравилась в статье: не надо пихать везде нейронные сети.
              А вот изобретение велосипеда — не очень понравилось. Все же OCR алгоритмы это не ново.

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

                  Заменил. Но тогда надо пойти до конца, и представить что перед этим на хабре была статья "Как отсортировать: нейросеть на Tensorflow,Keras,python v неотсортированный список". И сразу предложенная статья начинает казаться уместной, и даже необходимой.
                0
                Я понимаю этот энтузиазм, но мне кажется что этот восторг не стоит статьи на хабр

                Ну почему же. Любой опыт, проведенный своими руками интересен, если его результаты не совсем уж очевидны. Мне было интересно.

                +1
                И правда история имеет свойство повторяться. Будь эта статья опубликована завтра, был бы юбилей посвященный нейросетям и капче.
                13 февраля 2010 года на хабре появилась статья «Первый опыт создания нейронной сети. Хабракапча», на нее уже ответили чуть больше 30 человек, пост вылез на глагне, как автор наслушавшись, что произошло с прошлыми героями, зас*ал и удалил статью.

                Лурк
                  +5

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

                    +1
                    Я умилился, вспомнив, что в своё время по такому же алгоритму писал распознавание капчи RapidShare на PHP для скрипта PHP RapidGet Pro.
                    Отдельно доставило потом обнаружить, как кто-то не поленился мой код изучить и выложить в виде туториала (даже в закладки сохранил).
                      0
                      Добавил бонус. Если кто-то решит попробовать в работе, прошу отписаться как оно. Есть планы по доработке.
                        0
                        Попробуйте на капче ФНС
                          0
                          Обновил расширение для Chrome. Теперь оно умеет, помимо автоподставновки капчи,
                          1. при открытии страниц с информацией об объектах недвижимости разворачивать сведения о правах;
                          2. собирать информацию с этих страниц для последующей обработки.
                          image
                            0
                            Только сейчас обнаружил эту статью после парсинга Хабра.
                            Мне стыдно, что написал про капчу так, что сложно оказалось повторить и вы сделали такой круг.
                            Странно, что из 29000 капч столько дублей, но это я собирал seleniumoм, потому что requestы приводили к банам.
                            Надо потестить ваше решение, его краткость подкупает!

                            p.s. у меня висит решение по гугл-капче в черновиках, но публиковаться вряд ли будет, т.к. это откроет ящик Пандоры для спамеров разного рода. Могу с вами поделиться. Росреестр заметно хуже стал работать после того, как «нашли решение с капчей».
                              0
                              Ваша статья написана отлично, просто на тот момент для меня были непонтяны обе темы — с питоном и с нейросетями. Сейчас по питону вопросов нет, так что остались только сети. Если подходить к этому как к чёрному ящику, то и вообще всё ясно и логично.
                              Гуглокапча пока не вызывает боли, я её вижу раз в год, так что пусть она лежит в тайне от спамеров.
                              Росреестр, мне кажется, сам по себе помирает — там, по-моему, системные проблемы. У них на сайте есть ещё один вид капчи, пугающий своей сложностью; елси бы они хоткли, уже давно бы её включили.
                                +1
                                Вы в Росреестр по одному объекту ходите (ФГИС) или сразу в один запрос несколько объектов заряжаете? Там можно несколько объектов через запятую сразу запрашивать, при этом капча одна.
                                Если тема по сетям интересна, напишите email в личку, скину книг почитать.
                                  0
                                  Божечки, я по одному всё делал. Стыдоба-то какая! Ах-ха-ха! Спасибо!
                                  По сетям, честно говоря, особо интереса нет.
                                    0
                                    Надо написать статью, может Росреестр перестанет висеть )
                              0
                              github.com/0-6-1-7/rosreestr/tree/master/EGRN%20quest
                              С начала опреля 2020 они решили, что мы за свои деньги не можем заказывать выписки чаще, чем одна выписка за пять минут.
                              Заголовок спойлера
                              штош

                              А перепоручим-ка мы это дело боту.
                              После запуска скрипта открывается окно Хрома. Авторизуемся. вставляем сколько нужно кадастровых номеров в форму поиска, дожидаемся списка объектов для заказа выписок. Далее в консоли набираем GetAll() и созерцаем процесс.
                              Заголовок спойлера
                              image

                              В случае ошибок копируем из консоли обработанные КН и удаляем их из исходного списка.
                              Повторяем: копируем список КН в форму поиска, GetAll() и т.д. Ошибки возникают крайне редко (раз в пять минут, ха-ха). В целом, 2-3 десятка выписок без ежиного разрыва — вполне обычно.
                                0
                                Правильная ссылка бот
                                Заголовок спойлера
                                image

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое