HPUNIX Сайт о ОС и не только!

Фрактальная капча на Python

17 августа 2012 - unix
Фрактальная капча на Python

После написания заметки о генераторах фракталов, меня посетила одна мысль. В свое время я интересовался автоматическим определением капч (которые captcha). Итак вот, определение происходит в несколько шагов.

Текст капчи отделяются от фона, вычищается от мусора, нарезается на буковкы. Потом буковкы приводятся к одному размеру и употребляются для обучения нейронной сети. Притом 1-ый шаг — отделение текста от фона, обычно, является достаточно обычным.

Давайте поглядим на некие капчи:

Примеры Captcha

Направьте внимание на номера с 4-го по 7-й. Эти капчи довольно контрастны для того, чтоб конвертировать их в черно-белое изображение (матрицу ноликов и единичек) фактически без утраты инфы. С капчей номер Три та же неувязка, хотя это и наименее разумеется.

По сути она даже слабее других капч, так как позволяет просто разбить изображенное число на числа. Эту капчу когда-то издавна я написал для собственного первого форума.

Вы могли выяснить капчу номер 1, она использовалась (до сего времени употребляется?) Yandex'ом. И она была удачно взломана без особенной магии. Капча номер Два может показаться надежной, но по сути она такой не является.

Капча номер 6 также была взломана.

Номера Четыре и 5 — это KCaptcha и Blogger.com соответственно. Обе пока держатся, видимо за счет трудности нарезания букв. KCaptcha, если вдруг кто не в курсе, пользуется большой популярностью.

К примеру, она употребляется в движке DLE и на rutracker.org. Так что сломав ее можно поиметь хороший профит.

Но вернемся к упомянутой сначала заметки идее. Она состоит в том, что капчу можно сделать лучше, залатав одно из ее уязвимых мест, а конкретно — усложнить отделение текста от фона. Для чего выводить текст каким-то одним цветом на белоснежном либо любом другом фоне, если можно сделать фон и цвет текста повсевременно меняющимися?

При чем здесь фракталы? Рад, что вы спросили (по сути вы не спрашивали, но возразить-то все равно не сможете :). Как выяснилось, есть таковой метод генерации прекрасных картинок под заглавием плазма.

Метод очень обычный. И да, картина, которую он выдает на выходе — это тоже фрактал.

Иллюстрация к предшествующим двум абзацам:

Алгоритм создания Captcha

Остается доработать кое-какие мелочи (поменять смещение, наклон и размер цифр, применить пару фильтров), но идею, надеюсь, вы сообразили. Такая капча, естественно, не очень презентабельна в плане юзабилити. Но когда речь входит о безопасности, всегда приходится находить компромисс.

Плавненько перебегаем от теории к практике. Реализовывал я все это хозяйство на Python. Просто ради того, чтоб не прослыть питононенавистником, а заодно испытать библиотеку PIL. Вот, что вышло:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# FractalCaptcha.py v 0.1
# (c) Alexandr A Alexeev Две тыщи одиннадцать | http://eax.me/

from PIL import Image, ImageDraw, ImageFont, ImageFilter
from random import random

# создаем капчу, содержащую знаки из строчки secret
def captcha(secret, width=200, height=80,
    fontName='arial.ttf', fontSize=54,
    blur = 2):
  mask = Image.new('RGBA', (width, height))
  font = ImageFont.truetype(fontName, fontSize)

  x_offset = -10
  draw = ImageDraw.Draw(mask)
  for i in range(len(secret)):
    x_offset += 20 + int(random()*20)
    y_offset = -10 + int(random()*30)
    draw.text((x_offset, y_offset), secret[i], font=font)

  # последний знак также должен быть повернут
  angle = -10 + int(random()*15)
  mask = mask.rotate(angle)

  bg = plazma(width, height)
  fg = plazma(width, height)
  result = Image.composite(bg, fg, mask)

  # blur усложнит выделение границ знаков
  # другой вариант - гаусово размытие:
  # http://rcjp.wordpress.com/2 Апреля 2008/gaussian-pil-image-filter/
  if blur > 0:
    for i in range(blur):
      result = result.filter(ImageFilter.BLUR)
 
  # почему-либо blur время от времени не действует на границах капчи
  # использовать crop?
  return result

# генерируем "плазму" размером width x height
def plazma(width, height):
  img = Image.new('RGB', (width, height))
  pix = img.load();

  for xy in [(0,0), (width-1, 0), (0, height-1), (width-1, height-1)]:
    rgb = []
    for i in range(3):
      rgb.append(int(random()*256))
    pix[xy[0],xy[1]] = (rgb[0], rgb[1], rgb[2])

  plazmaRec(pix, 0, 0, width-1, height-1)
  return img
Фрактальная капча на Python

# рекурсивная составля функции plazma
def plazmaRec(pix, x1, y1, x2, y2):
  if (абс(x1 - x2) <= 1) and (абс(y1 - y2) <= 1):
    return
   
  rgb = []
  for i in range(3):
    rgb.append((pix[x1, y1][i] + pix[x1, y2][i])/2)
    rgb.append((pix[x2, y1][i] + pix[x2, y2][i])/2)
    rgb.append((pix[x1, y1][i] + pix[x2, y1][i])/2)
    rgb.append((pix[x1, y2][i] + pix[x2, y2][i])/2)
   
    tmp = (pix[x1, y1][i] + pix[x1, y2][i] +
           pix[x2, y1][i] + pix[x2, y2][i])/4
    diagonal =  ((x1-x2)**2 + (y1-y2)**2)**0.5
    while True:
      delta = int ( ((random() - 0.5)/100 * min(100, diagonal))*255 )
      if (tmp + delta >= 0) and (tmp + delta <= 255):
        tmp += delta
        break
    rgb.append(tmp)

  pix[x1, (y1 + y2)/2] = (rgb[0], rgb[5], rgb[10])
  pix[x2, (y1 + y2)/2]= (rgb[1], rgb[6], rgb[11])
  pix[(x1 + x2)/2, y1] = (rgb[2], rgb[7], rgb[12])
  pix[(x1 + x2)/2, y2] = (rgb[3], rgb[8], rgb[13])  
  pix[(x1 + x2)/2, (y1 + y2)/2] = (rgb[4], rgb[9], rgb[14])
   
  plazmaRec(pix, x1, y1, (x1+x2)/2, (y1+y2)/2)
  plazmaRec(pix, (x1+x2)/2, y1, x2, (y1+y2)/2)
  plazmaRec(pix, x1, (y1+y2)/2, (x1+x2)/2, y2)
  plazmaRec(pix, (x1+x2)/2, (y1+y2)/2, x2, y2)

if __name__ == '__main__':
  result = captcha("12345")
  result.save("result.png", "PNG")

Если под Windows интерпретатор будет браниться по поводу поддержки шрифтов, поставьте PIL отсюда. Капча будет смотреться привлекательнее, если использовать шрифт Comic Sans MS.

Принципиальный момент. Перед тем, как возвратить картину, мы несколько раз применяем к ней фильтр blur. Если этого не делать, будет относительно нетрудно выделить контуры знаков.

Я отрисовывал капчи в Gimp в согласовании с последней иллюстрацией, делал разные варианты размытия, а потом пробовал выделить границы знаков при помощи фильтров типа «Лаплас», «Неон» и их композиций. Выяснилось, что идеальнее всего границы скрываются или после неоднократного размытия изображения по Гауссу, или после неоднократного blur.

Так как в PIL нет размытия по Гауссу, выбор пришлось сделать в пользу blur. С другой стороны, а что нам мешает использовать стороннюю реализацию размытия по Гауссу? Вот соответственная заплата к скрипту:

8a9,22
> from numpy import mgrid, exp
>
> def gaussian_grid(size = 5):
>     m = size/2
>     n = m+1  # remember python is 'upto' n in the range below
>     x, y = mgrid[-m:n,-m:n]
>     fac = exp(m**2)
>     g = fac*exp(-0.5*(x**2 + y**2))
>     return g.round().astype(int)
>
> class GAUSSIAN(ImageFilter.BuiltinFilter):
>     name = "Gaussian"
>     gg = gaussian_grid().flatten().tolist()
>     filterargs = (5,5), sum(gg), 0, tuple(gg)
32,34d45

list_0f_awes0meness

<   # blur усложнит выделение границ знаков
<   # другой вариант - гаусово размытие:
<   # http://rcjp.wordpress.com/2 Апреля 2008/gaussian-pil-image-filter/
37,38c48
<       result = result.filter(ImageFilter.BLUR)
<  
---
>       result = result.filter(GAUSSIAN)

Число применений фильтра передается функции сотворения капчи в качестве аргумента, так что вы просто можете отыскать компромисс меж безопасностью и юзабилити. Примеры капч:

Фрактальные капчи

В этой заметке я желал показать только идею. Все же, такая реализация капчи кажется мне надежной. Буду рад, если кто-либо продемонстрирует мне оборотное. Направьте внимание, что неважно какая графическая капча просто ломается при помощи Anti-Captcha.

Для защиты от схожих сервисов необходимы дополнительные ухищрения.

Похожие статьи

  • Тест производительности скриптов на Python

    Если вы издавна читаете мой блог, то сможете держать в голове, как несколько раз я гласил о Python различные противные вещи, мол он неспешный и памяти много ест. При всем этом даже приводились разные пруфлинки...

  • Berp Восемь тыщ двести двенадцать достаточно увлекательный компилятор Python

    Не так давно я натолкнулся на один любознательный проект. Он именуется Berp и представляет собой транслятор скриптов на языке Python в программки на Haskell. Со стороны юзера Berp смотрится как интерпр...

  • Памятка по постоянным выражениям

    Решил написать шпаргалку по постоянным выражениям. Вдруг я когда-нибудь их подзабуду. Не считая того, этот пост можно считать продолжением к моей серии уроков по Perl.

    1. ВведениеПара слов д...

  • Написал собственный 1-ый Perl-скрипт для Android

    Есть таковой проект под заглавием Scripting Layer for Android (SL4A). Это штука, которая позволяет запускать на Android-устройствах скрипты, написанные на Perl, Python, JRuby, Lua, JavaScript и друг...

  • Написал библиотеку для мемоизации в Erlang

    В заметке о 12-ти действенных способах оптимизации программ мемоизация была названа в качестве 1-го из действенных способов. Сейчас совместными усилиями мы напишем маленькую библиотеку на Er...

Теги:
Рейтинг: +4 Голосов: 166 1541 просмотр
Комментарии (0)

Нет комментариев. Ваш будет первым!

Найти на сайте: параметры поиска

Windows 7

Среда Windows 7 на первых порах кажется весьма непривычной для многих.

Windows 8

Если резюмировать все выступления Microsoft на конференции Build 2013.

Windows XP

Если Windows не может корректно завершить работу, в большинстве случаев это

Windows Vista

Если к вашему компьютеру подключено сразу несколько мониторов, и вы регулярно...