Статьи Распознавание каптчи (PHP + GD)

Discussion in 'Статьи' started by mailbrush, 27 Jan 2010.

  1. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    INTRO​

    Здравствуйте! Здесь я постараюсь рассказать о том, как с помощью PHP и GD распознавать обычные каптчи на примере 000webhost.com (http://www.000webhost.com/includes/php_captcha.php). Сразу хочу сказать, что на основе этого способа можно распознавать только простые каптчи.

    [​IMG]

    Долго я искал статьи, подобные этой, но не нашёл. И мне в голову пришла идея, которую мне получилось успешно реализовать. Именно ею я и хочу поделиться с вами!

    Термины​

    Итак, для начала хочу объяснить некоторые термины (© WikiPedia):
    • PHP - скриптовый язык программирования.
    • GD - библиотека для динамической работы с изображениями.
    • CAPTCHA — полностью автоматизированный публичный тест Тьюринга для различия компьютеров и людей, используемый для того, чтобы определить, кем является пользователь системы: человеком или компьютером. В Рунете часто транскрибируется как капча. Применяется CAPTCHA для того, чтобы предотвратить множественные автоматические регистрации и отправления сообщений программами-роботами. Т. е. задача CAPTCHA — защита от спама, флуда, и захвата аккаунтов.

    Распознавание​

    Для начала надо создать изображение. Для этого воспользуемся функцией imagecreatefrompng, которая создает изображение из указанного файла.
    Синтаксис:
    PHP:
    #resource imagecreatefrompng (string filename)
    Использование:
    PHP:
    $captcha imagecreatefrompng("php_captcha.png");
    Теперь каптчу нужно разделить на пять ровных частей - для каждой цифры свое изображение. Конечно, вручную делать мы этого не будем, эту работу мы предоставим интерпретатору, тобишь PHP. Создадим цикл, который будет повторяться пять раз, каждый раз обрабатывая новое изображение.
    Далее создадим новое изображение с шириной 7px, высотой 10px. Каждый символ каптчи имеет именно такие размеры.
    PHP:
        $im imagecreatetruecolor(710);
    Теперь надо скопировать туда символ каптчи. Воспользуемся функцией imagecopy:
    PHP:
    #int imagecopy (resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h)
    Она копирует часть изображения src_im в dst_im, начиная с x,y-координат src_x, src_y, с шириной src_w и высотой src_h.Определённая часть будет скопирована в x,y-координаты dst_x и dst_y. Объясню:
    • src_im - ресурс исходного изображения.
    • dst_im - ресурс нового изображения, в которое и будет записан наш символ.
    • src_x, src_y - начальные X и Y координаты нашего символа.
    • src_w, src_h - длина и высота символа.
    Использовать будем так:
    PHP:
    imagecopy($im$captcha00$i13710);
    Это значит, что мы часть изображения $captcha, начиная с координат
    X: 9 * $i
    Y: 13
    с шириной 7 и высотой 10. Эта часть и будет скопирована в $im. Наверное непонятно, почему X = 9 * $i, Y - 13. $i - переменная, которая при каждом выполнении цикла увеличивается на единицу. Возьмем первое повторение, когда $i - равно нулю. Таким образом X = 0, Y = 13. Если открыть изображение любым из редакторов, то можно увидеть, что именно эти координаты и будут началом первого символа. При втором выполнении, в $im скопируется второй символ, т.к. X = 9, Y = 13, и т.д... Это изображение - $im выглядит так:
    [​IMG]
    Теперь создаем еще один цикл, в теле которого по очереди будет перебирать каждый пиксель первого столбца, и засовывать в массив 1 или 0, в зависимости от того, закрашен этот пиксель, или нет... Воспользуемся функцией imagecolorat, которая возвращает индекс цвета пикселя.
    PHP:
    #int imagecolorat (resource image, int x, int y)
    Этой функции надо передать три значение - ресурс изображения, X и Y координаты.
    PHP:
        for ($j 0$j <= 9$j++)
        {

            if (
    imagecolorat($im0$j) != 16777215)
                
    $gd[$j] = 1;
            else
                
    $gd[$j] = 0;

        }
    Если индекс цвета не равен 16777215, тоесть не белый, записываем в массив $gd единицу, в противном случае - нолик. Итак, у нас образовался массив $gd. Если воспользоваться функцией print_r, которая выводит все ключи и элементы массива, то можно увидеть следующее:
    Code:
    Array
    (
        [0] => 1
        [1] => 1
        [2] => 1
        [3] => 1
        [4] => 1
        [5] => 0
        [6] => 0
        [7] => 1
        [8] => 0
        [9] => 0
    )
    
    Нет, это не бинарные числа :) Это первый столбец по матрице числа 5. А вот и вся она:
    Code:
    1111111
    1100000
    1100000
    1101110
    1110011
    0000001
    0000001
    1100001
    0110011
    0011110
    А теперь выделим единички красным цветом:
    Code:
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]00000
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]00000
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]00[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    000000[COLOR="Red"]1[/COLOR]
    000000[COLOR="Red"]1[/COLOR]
    [COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0000[COLOR="Red"]1[/COLOR]
    0[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]00[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    00[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0
    Так вот, в массиве выше у нас первый столбец этой матрицы, по которому и будем распознавать число. Сразу нарождается вопрос - как именно? Я постараюсь ответить на этот вопрос. Дело в том, что каждый символ имеет пиксели, индекс цвета которых уникален только для него, поэтому его можно легко распознать. Например в тройки закрашен первый и восьмой пиксель, если считать с нуля:

    Code:
    [COLOR="Cyan"][B]0[/B][/COLOR] 0[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0
    [COLOR="Cyan"][B]1[/B][/COLOR] [B][COLOR="Yellow"][U]1[/U][/COLOR][/B][COLOR="Red"]1[/COLOR]000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]2[/B][/COLOR] 000000[COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]3[/B][/COLOR] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]4[/B][/COLOR] 000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0
    [COLOR="Cyan"][B]5[/B][/COLOR] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]6[/B][/COLOR] 000000[COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]7[/B][/COLOR] 000000[COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]8[/B][/COLOR] [B][COLOR="Yellow"][U]1[/U][/COLOR][/B][COLOR="Red"]1[/COLOR]000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [COLOR="Cyan"][B]9[/B][/COLOR] 0[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0
    А в цифры 4 - закрашен пятый и шестой, но не закрашен четвертый.

    Code:
    [B][COLOR="Cyan"]0[/COLOR][/B] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]1[/COLOR][/B] 0000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]2[/COLOR][/B] 000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]3[/COLOR][/B] 00[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]0[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]4[/COLOR][/B] [COLOR="Yellow"][U][B]0[/B][/U][/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]00[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]5[/COLOR][/B] [COLOR="Yellow"][U][B]1[/B][/U][/COLOR][COLOR="Red"]1[/COLOR]000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]6[/COLOR][/B] [COLOR="Yellow"][U][B]1[/B][/U][/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]7[/COLOR][/B] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]8[/COLOR][/B] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    [B][COLOR="Cyan"]9[/COLOR][/B] 00000[COLOR="Red"]1[/COLOR][COLOR="Red"]1[/COLOR]
    
    Немножко посидев и посмотрев на эти числа в фотошопе, я вывел таблицу в виде массива уникальных индексов цвета и их закраски в цифрах 0-9.
    PHP:
    $numbers = array
    (
    => array
        (
        
    => 0
        
    => 0
        
    => 0
        
    => 0
        
    => 0
        
    ), 
    => array
        (
        
    => 0
        
    => 1
        
    => 1
        
    ), 
    => array
        (
        
    => 1
        
    => 1
        
    ), 
    => array
        (
        
    => 0
        
    => 1
        
    => 1), 
    => array
        (
        
    => 1
        
    => 1
        
    => 0
        
    ), 
    => array
        (
        
    => 0
        
    => 0
        
    => 1
        
    => 1
        
    ), 
    => array
        (
        
    => 1
        
    => 1
        
    ), 
    => array
        (
        
    => 0
        
    => 0
        
    => 1
        
    => 0
        
    => 1
        
    ), 
    => array
        (
        
    => 0
        
    => 0
        
    => 1
        
    => 0
        
    ),
    => array
        (
        
    => 0
        
    => 0
        
    => 1
        
    => 1
        
    => 1
        
    => 0)
    );
    Итак, половина работы проделана, остается каждое из этих чисел проверить по первому столбцу изображения, и если все индексы и их закраска в массиве равны индексам в первом ряде символа, то вывести это число.

    PHP:
        foreach ($numbers as $k_num => $number)
        {
            foreach (
    $number as $key => $value)
            {
                if (
    $gd[$key] == $value)
                    
    $k++;
            }
            if (
    $k == count($number))
            {
                
    $cp .= $k_num;
            }
            
    $k 0;
        }
    foreach - цикл предназначен специально для перебора массивов. Здесь команды циклически выполняются для каждого элемента массива, при этом очередная пара ключ=>значение оказывается в переменных $ключ и $значение.

    В первом цикле перебираются все массивы номеров. Дальше в цикле идет цикл, в котором - если массив $gd с индексом $key равно $value, то переменная $k увеличивается на еденицу. Тобишь если все пары индекс => цвет верны, то $k будет равно текущему числу а если это так, то оно дописывается в переменную $cp.

    После всего этого остается вывести переменную с каптчей:
    PHP:
    echo $cp;
    А вот и весь наш код:

    PHP:
    <?php

    error_reporting
    (E_ALL E_NOTICE);

    $captcha imagecreatefrompng("php_captcha.png");

    $numbers = array(=> array(=> 0=> 0=> 0=> 0=> 0), => array(=> 0=>
        
    1=> 1), => array(=> 1=> 1), => array(=> 0=> 1=> 1), => array(=>
        
    1=> 1=> 0), => array(=> 0=> 0=> 1=> 1), => array(=> 1=> 1),
        
    => array(=> 0=> 0=> 1=> 0=> 1), => array(=> 0=> 0=> 1=>
        
    0), => array(=> 0=> 0=> 1=> 1=> 1=> 0));

    for (
    $i 0$i <= 4$i++)
    {
        
    $im imagecreatetruecolor(710);
        
    imagecopy($im$captcha00$i13710);

        for (
    $j 0$j <= 9$j++)
        {

            if (
    imagecolorat($im0$j) != 16777215)
                
    $gd[$j] = 1;
            else
                
    $gd[$j] = 0;

        }

        foreach (
    $numbers as $k_num => $number)
        {
            foreach (
    $number as $key => $value)
            {
                if (
    $gd[$key] == $value)
                    
    $k++;
            }
            if (
    $k == count($number))
            {
                
    $cp .= $k_num;
            }
            
    $k 0;
        }
    }
    echo 
    $cp;
    ?>
    Постскриптум ​

    Это моя первая статья по PHP, так что прошу не судить строго :) Оценивайте, отписывайтесь, критикуйте, спрашивайте ;) Копипаст разрешен только с согласия автора, тоесть меня! Стучите в асю 674542 с просьбой об копировании на свой форум.
     
    #1 mailbrush, 27 Jan 2010
    Last edited: 27 Jan 2010
    -Pacman-, (Dm), warlok and 12 others like this.
  2. ElteRUS

    ElteRUS Elder - Старейшина

    Joined:
    11 Oct 2007
    Messages:
    367
    Likes Received:
    460
    Reputations:
    93
    С тех пор, как появились “сервисы” anti-captcha.com и ей подобные я думал, что все забросят это дело и начнут повсеместно использовать быдлотруд ишачащих там школьников ( что в принципе и случилось ). Не может не радовать тот факт, что есть люди, которые продолжают работать в направлении автоматического распознавания капчи. mailbrush, большой плюс.

    Их на античате было как минимум 3 (если не поудаляли). И все посвящены распознаванию элементарных капчей. Кроме того кое-какие материалы по этой теме можно найти на хабре.

    То, что нашел сходу
    http://forum.antichat.net/showthread.php?p=1131976

    Так же рекомендую ознакомиться с этим
    https://docs.google.com/fileview?id=0B-xpafMYi--7M2U4OGY3MWEtYzNjYi00OTkyLTg0ZDYtZjQzZTVkMmJhYWEz&hl=ru

    Эх было б здорово чтобы какой-нибудь кодер реализовал приведенные там методы и алгоритмы =)
     
  3. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Возможно, были готовые скрипты, пускай даже с комментами, просто у меня руки кривые, и не нашёл :)

    Хех, завтра почитаю, а то текста много :)
     
    #3 mailbrush, 27 Jan 2010
    Last edited: 27 Jan 2010
    1 person likes this.
  4. Pashkela

    Pashkela Динозавр

    Joined:
    10 Jan 2008
    Messages:
    2,750
    Likes Received:
    1,044
    Reputations:
    339
    Очень даже прикольно, зачетная статья, риспект. Осталось добавить только немного AI для распознавания чуть других капч, не таких именно. Но все равно классно
     
  5. Funk

    Funk Member

    Joined:
    8 Jun 2009
    Messages:
    12
    Likes Received:
    17
    Reputations:
    0
    https://forum.antichat.ru/threadnav62896-1-10.html

    ыот тоже про капчи. имхо беспонтовая статья. именно из за простоты. надоели однообразные. в тысячу раз интереснее почитать как на php распознать капчу e-gold'a или еще какие сложные..а тут ниче интересного
     
  6. Мяфк

    Мяфк New Member

    Joined:
    14 May 2009
    Messages:
    9
    Likes Received:
    0
    Reputations:
    0
    Вот так мы определяем координаты по Y значению, то есть по цифрам справо, а как сделать что бы можно было и по x значению определить?
     
  7. mailbrush

    mailbrush Well-Known Member

    Joined:
    24 Jun 2008
    Messages:
    1,997
    Likes Received:
    996
    Reputations:
    155
    Передавать это в качестве первого параметра ф-ции imagecolorat
     
  8. Мяфк

    Мяфк New Member

    Joined:
    14 May 2009
    Messages:
    9
    Likes Received:
    0
    Reputations:
    0
    Спасибо, и на этом, но как сделать сразу 2, т.е. X и Y одновременно?
     
  9. jumper423

    jumper423 New Member

    Joined:
    26 Feb 2011
    Messages:
    3
    Likes Received:
    0
    Reputations:
    0