Какое-то время назад мне пришлось сделать программку, которая скачивала в автоматическом режиме файлы с одного достаточно известного сайта. Проблема /на первый взгляд/ состояла в том, что там была капча. Однако одного взгляда на нее было достаточно, чтобы понять, решаемо и очень быстро :) По прошествии нескольких лет снова наткнулся на тот проект и решил вот выложить на хабр. Сразу оговорюсь, сайт называть не буду ибо капча там до сих пор такая и пусть такой и остается.
Перво-наперво была написана простенькая программка, которая дергала адрес с изображением капчи и складировала полученные изображения в отдельной папке. Когда я посмотрел на те 50 изображений, которые надергала программа, я понял, что все еще проще, чем я думал.
Вот посмотрите, это примеры капчи:
Цветов мало, цвет бэкграунда и самих цифр эпизодически менялся, но грани цифр видны четко, шумов нет. Соответственно я выбрал простейший вариант решения — простейший разбор по маске.
Чтобы создать маску, нужно достичь изображения из двух цветов. Для этого к программке скачивавшей изображения с сайта, была написана еще одна, которая брала изображение, просчитывала, какие цвета в изображении присутствуют и делала срез белый фон + черная маска на месте каждого цвета. После разбора изображения получалось несколько болванок, на одной из которых четко проступили символы используемые в капче.
Приведу пример тех изображений:
Здесь я привел маску с только самыми часто встречаемыми цветами. Чтобы отсеять лишнее я убрал из маски все цвета, которые содержат менее 25 пикселей на форме. В принципе это дает возможность промахнуться с определением, если в капче будет 1-2 символа, которые займут меньше 25 пикселей, но я ни одного такого изображения в этой капче не встретил и заморачиваться не стал.
Итак в последнем отображении видно, что у нас фактически есть маска. Абсолютно чистая, которая выглядит в редакторе вот так:
Привожу код, которым выдергивал варианты масок по цветам:
Когда я увидел работу кода указанного выше, осталось только собрать все числа используемые в капче и приступать. Для этого была запущена программа, которая сохраняет изображения капчи с целью получить 200 изображений. Из полученных изображений выбрал те, в которых были отображены все основные символы и с помощью кода приведенного выше были получены их маски. Итог этой работы выглядел так:
По какой-то причине, в капче не использовался символ 9, но не важно. Дальше просто. Каждое число берем в квадрат, правая часть — минимальный бит, левая — максимальный. Кто работал с ассемблером на 8086 и делал маски символов меня поймет, для остальных пример:
На изображении сверху поставлены номера бит, справа готовое число. Чтобы было еще более понятно, черные точки заменяем на 1, белые на ноль. Например верхняя строчка данного числа выглядит в двоичной системе как 0001111111000, т.е. 1016 в десятичной системе. Под каждую цифру был сделан массив, описывающий матрицу.
Дальнейший алгоритм выглядел так. Была написана функция, которая очищала полученное изображение от белых точек сверху, снизу и по бокам. И была написана функция, которая возвращала массив чисел, представляющих указанную область. После этого все было просто. Так как все делалось автоматически, я сделал так, что после очистки изображения и отрезания ненужных данных делалась проверка, чтобы высота изображения попадала под размер цифры (в данном случае имело смысл искать цифру). После чего в цикле слева направо сравнивалась область с каждой цифрой до совпадения. Совпала цифра -> передвигаемся по изображению на ширину символа вправо. Проверяем следующую область и так далее. В итоге, все получилось не сильно быстрым, я приложу проект, чтобы вы могли сами посмотреть, но решение возвращало 100% правильно распознанную капчу.
P.S. Сильно не пинайте за код проекта, писалось давно и на скорость. Многое можно оптимизировать, но это уже задачка вам, если конечно интересно :)
P.P.S. Все таки думаю ничего не изменится, если я укажу адрес сайта, это был cracks.ms
Проект для VS2010
Этап первый: Сбор информации
Перво-наперво была написана простенькая программка, которая дергала адрес с изображением капчи и складировала полученные изображения в отдельной папке. Когда я посмотрел на те 50 изображений, которые надергала программа, я понял, что все еще проще, чем я думал.
Вот посмотрите, это примеры капчи:
Цветов мало, цвет бэкграунда и самих цифр эпизодически менялся, но грани цифр видны четко, шумов нет. Соответственно я выбрал простейший вариант решения — простейший разбор по маске.
Этап второй: Первоначальный разбор изображения, создание маски
Чтобы создать маску, нужно достичь изображения из двух цветов. Для этого к программке скачивавшей изображения с сайта, была написана еще одна, которая брала изображение, просчитывала, какие цвета в изображении присутствуют и делала срез белый фон + черная маска на месте каждого цвета. После разбора изображения получалось несколько болванок, на одной из которых четко проступили символы используемые в капче.
Приведу пример тех изображений:
Здесь я привел маску с только самыми часто встречаемыми цветами. Чтобы отсеять лишнее я убрал из маски все цвета, которые содержат менее 25 пикселей на форме. В принципе это дает возможность промахнуться с определением, если в капче будет 1-2 символа, которые займут меньше 25 пикселей, но я ни одного такого изображения в этой капче не встретил и заморачиваться не стал.
Итак в последнем отображении видно, что у нас фактически есть маска. Абсолютно чистая, которая выглядит в редакторе вот так:
Привожу код, которым выдергивал варианты масок по цветам:
public Bitmap ClearBitmap(Bitmap input, Color clr)
{
var result = new Bitmap(input.Width, input.Height);
for (var x = 0; x < input.Width; x++)
{
for (var y = 0; y < input.Height; y++)
{
var color = input.GetPixel(x, y);
result.SetPixel(x, y, clr == color ? Color.Black : Color.White);
}
}
return result;
}
public void Main()
{
var bitmap = new Bitmap("D:\\check_image1227.png");
var palette = new Dictionary<Color, int>();
for (var x = 0; x < bitmap.Width; x++)
{
for (var y = 0; y < bitmap.Height; y++)
{
var clr = bitmap.GetPixel(x, y);
if (!palette.ContainsKey(clr))
{
palette.Add(clr, 1);
}
else
{
palette[clr] = palette[clr] + 1;
}
}
}
var i = 0;
foreach (var c in palette)
{
if (c.Value > 30)
{
var temp = this.ClearBitmap(bitmap, c.Key);
temp.Save(String.Format("D:\\mask-{0}.bmp", i));
i++;
}
}
}
Этап три: В бой!
Когда я увидел работу кода указанного выше, осталось только собрать все числа используемые в капче и приступать. Для этого была запущена программа, которая сохраняет изображения капчи с целью получить 200 изображений. Из полученных изображений выбрал те, в которых были отображены все основные символы и с помощью кода приведенного выше были получены их маски. Итог этой работы выглядел так:
По какой-то причине, в капче не использовался символ 9, но не важно. Дальше просто. Каждое число берем в квадрат, правая часть — минимальный бит, левая — максимальный. Кто работал с ассемблером на 8086 и делал маски символов меня поймет, для остальных пример:
На изображении сверху поставлены номера бит, справа готовое число. Чтобы было еще более понятно, черные точки заменяем на 1, белые на ноль. Например верхняя строчка данного числа выглядит в двоичной системе как 0001111111000, т.е. 1016 в десятичной системе. Под каждую цифру был сделан массив, описывающий матрицу.
Дальнейший алгоритм выглядел так. Была написана функция, которая очищала полученное изображение от белых точек сверху, снизу и по бокам. И была написана функция, которая возвращала массив чисел, представляющих указанную область. После этого все было просто. Так как все делалось автоматически, я сделал так, что после очистки изображения и отрезания ненужных данных делалась проверка, чтобы высота изображения попадала под размер цифры (в данном случае имело смысл искать цифру). После чего в цикле слева направо сравнивалась область с каждой цифрой до совпадения. Совпала цифра -> передвигаемся по изображению на ширину символа вправо. Проверяем следующую область и так далее. В итоге, все получилось не сильно быстрым, я приложу проект, чтобы вы могли сами посмотреть, но решение возвращало 100% правильно распознанную капчу.
P.S. Сильно не пинайте за код проекта, писалось давно и на скорость. Многое можно оптимизировать, но это уже задачка вам, если конечно интересно :)
P.P.S. Все таки думаю ничего не изменится, если я укажу адрес сайта, это был cracks.ms
Проект для VS2010