Распознавание каптчи


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

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

Термины​

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

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

Для начала надо создать изображение. Для этого воспользуемся функцией imagecreatefrompng, которая создает изображение из указанного файла.
Синтаксис:
PHP:
#resource imagecreatefrompng (string filename)
Использование:
PHP:
$captcha = imagecreatefrompng(«php_captcha.png»);
Теперь каптчу нужно разделить на пять ровных частей — для каждой цифры свое изображение. Конечно, вручную делать мы этого не будем, эту работу мы предоставим интерпретатору, тобишь PHP. Создадим цикл, который будет повторяться пять раз, каждый раз обрабатывая новое изображение.
Далее создадим новое изображение с шириной 7px, высотой 10px. Каждый символ каптчи имеет именно такие размеры.
PHP:
$im = imagecreatetruecolor(7, 10);
Теперь надо скопировать туда символ каптчи. Воспользуемся функцией 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, $captcha, 0, 0, 9 * $i, 13, 7, 10);
Это значит, что мы часть изображения $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($im, 0, $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

Так вот, в массиве выше у нас первый столбец этой матрицы, по которому и будем распознавать число. Сразу нарождается вопрос — как именно? Я постараюсь ответить на этот вопрос. Дело в том, что каждый символ имеет пиксели, индекс цвета которых уникален только для него, поэтому его можно легко распознать. Например в тройки закрашен первый и восьмой пиксель, если считать с нуля:

Немножко посидев и посмотрев на эти числа в фотошопе, я вывел таблицу в виде массива уникальных индексов цвета и их закраски в цифрах 0-9.
PHP:
$numbers = array
(
1 => array
(
0 => 0,
1 => 0,
2 => 0,
3 => 0,
5 => 0
),
2 => array
(
0 => 0,
2 => 1,
9 => 1
),
3 => array
(
1 => 1,
8 => 1
),
4 => array
(
4 => 0,
5 => 1,
6 => 1),
5 => array
(
0 => 1,
1 => 1,
5 => 0
),
6 => array
(
0 => 0,
1 => 0,
4 => 1,
7 => 1
),
7 => array
(
0 => 1,
9 => 1
),
8 => array
(
0 => 0,
1 => 0,
2 => 1,
4 => 0,
6 => 1
),
9 => array
(
0 => 0,
1 => 0,
3 => 1,
5 => 0
),
0 => array
(
0 => 0,
2 => 0,
3 => 1,
4 => 1,
5 => 1,
7 => 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:
1, 9 => 1), 3 => array(1 => 1, 8 => 1), 4 => array(4 => 0, 5 => 1, 6 => 1), 5 => array(0 =>
1, 1 => 1, 5 => 0), 6 => array(0 => 0, 1 => 0, 4 => 1, 7 => 1), 7 => array(0 => 1, 9 => 1),
8 => array(0 => 0, 1 => 0, 2 => 1, 4 => 0, 6 => 1), 9 => array(0 => 0, 1 => 0, 3 => 1, 5 =>
0), 0 => array(0 => 0, 2 => 0, 3 => 1, 4 => 1, 5 => 1, 7 => 0));

for ($i = 0; $i <= 4; $i++)
{
$im = imagecreatetruecolor(7, 10);
imagecopy($im, $captcha, 0, 0, 9 * $i, 13, 7, 10);

for ($j = 0; $j <= 9; $j++) { if (imagecolorat($im, 0, $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;
?>

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Перейти к верхней панели