Почему мне это понадобилось?


Недавно, блуждаю по сайту фриланса, наткнулся на интересное задание — нужно было скачать около 65000 файлов. Скачивание ограничивалось вводом капчи, состоящей из четырех цифр. Сначала хотел использовать сервис antigate.com, о чем прямо сообщил заказчику, но мое желание сэкономить любопытство заставило меня взяться за написание скрипта, который бы разгадывал капчу автоматически.

Немного о коде


Я программирую на PHP недолго, поэтому код у меня еще не качественный, сильно не бейте.

Пример капчи


image

Поехали взламывать


Для начала загружаем изображение в скрипт, определяем его высоту и ширину и устанавливаем для него белый и черный цвет.
    $image = imagecreatefrompng($img);
    $width = imagesx($image);
    $height = imagesy($image);
    $white = imagecolorallocate($image,255,255,255);
    $black = imagecolorallocate($image,0,0,0);

Внимательно изучив картинку под фотошопом микроскопом, понял, что фоновый шум всегда состоит из одного цвета RGB(216,216,216), причем в цифрах этот цвет не встречается. Поэтому очищаем изображение от этого цвета, заменив его на белый:
for ($x=0;$x<$width;$x++) {
    for($y=0;$y<$height;$y++) {
        $color_index = imagecolorat($image,$x,$y);
        $color_back = imagecolorsforindex($image,$color_index);
        if ($color_back['red'] == 216 && $color_back['green'] == 216 && $color_back['blue'] == 216) {
            imagesetpixel($image,$x,$y,$white);
        }
    }
}

Затем перекрашиваем все цифры в черный цвет, исходя из того, что любой цвет, кроме белого, принадлежит цифрам:
for ($x=0;$x<$width;$x++) {
    for($y=0;$y<$height;$y++) {
        $color_index = imagecolorat($image,$x,$y);
        $color_back = imagecolorsforindex($image,$color_index);
        if ($color_back['red'] + $color_back['green'] + $color_back['blue'] != 765) {
            imagesetpixel($image,$x,$y,$black);
        }
    }
}

Далее снова взялся за фотошом микроскоп. Проанализировав цифры, понял, что все они имеют размеры 8х10 рх и располагаются строго по одной вертикали (первая цифра от 15 до 22 пикселя, вторая — 25 — 32 пиксель и т. д.). Причем цифры не искажаются, то есть состоят из строго определенного набора пикселей. Еще раз посмотрев на цифры, я составил для каждой из них что-то типа матрицы. К примеру для семерки:
11111111
00000011
00000011
00000110
00001100
00011000
00110000
01100000
11000000
11000000

Где 0 — это белый пиксель, а 1 — черный. Чтобы было меньше кода для сравнивания цифр, я преобразовал матрицы цифр в строку. Все та же семерка:
11111111000000110000001100000110000011000001100000110000011000001100000011000000
Далее анализируем нужный нам квадрат с цифрой, указывая, что белый пиксель равен нулю, а черный — единице. Для первой цифры:
	for ($x=15;$x<=22;$x++) {
		for($y=0;$y<$height;$y++) {
			$color_index = imagecolorat($image,$x,$y);
			$color_back = imagecolorsforindex($image,$color_index);
			if ($color_back['red'] + $color_back['green'] + $color_back['blue'] == 0) {
				$temp[$y][] = 1;
			} else {
				$temp[$y][] = 0;
			}
		}
	}

Для второй — четвертой цифр аналогично.
Получается двухмерный массив, превращаем его в одномерный:
	for ($i=0;$i<count($temp);$i++) {
		$temp[$i] = implode('',$temp[$i]);
	}

Далее из получившегося массива удаляем чистые ряды, то есть ряды, содержащие только '00000000'
	foreach ($temp as $value) {
		if ($value != '00000000') {
			$digit1[] = $value;
		}
	}

Получившийся массив $digit1 объединяем в строку:
$digit1 = implode('',$digit1);
.
И сравниваем получившееся значение с имеющимися матрицами цифр с помощью небольшой самописной функции:
	function compare($digit) {
		$one 	= file_get_contents('1.txt');
		$two 	= file_get_contents('2.txt');
		$three 	= file_get_contents('3.txt');
		$four 	= file_get_contents('4.txt');
		$five 	= file_get_contents('5.txt');
		$six 	= file_get_contents('6.txt');
		$seven 	= file_get_contents('7.txt');
		$eight 	= file_get_contents('8.txt');
		$nine 	= file_get_contents('9.txt');
		$zero 	= file_get_contents('0.txt');
		if ($digit === $one) $value = '1';
		if ($digit === $two) $value = '2';		
		if ($digit === $three) $value = '3';
		if ($digit === $four) $value = '4';
		if ($digit === $five) $value = '5';
		if ($digit === $six) $value = '6';
		if ($digit === $seven) $value = '7';
		if ($digit === $eight) $value = '8';
		if ($digit === $nine) $value = '9';
		if ($digit === $zero) $value = '0';
		return $value;

	}

Результат сразу для четырех цифр:
    $result = compare($digit1) . compare($digit2) . compare($digit3) . compare($digit4);
    return $result;

Результаты

  1. Написано всего 150 строк кода за 3 часа.
  2. Приобретен опыт взлома капчи.
  3. Получен огромный выигрыш во времени (antigate разгадывает капчу за 10-20 секунд, скрипт за 1-2).
  4. Сэкономлено почти $100.