keyworddiscovery captcha
Dziś kolejne “łamanie” captcha – tym razem coś ciekawszego, bo nie będziemy wykorzystywali ocr. Zrobimy proste rozpoznawanie liter na podstawie dopasowania do wzorcow. Proste, niesamowicie skuteczne – dokładnie pokazujące wszystkie niedoskonałości captcha.
Co prawda obrazki pochodzą z keyworddiscovery.com, ale widziałem tego typu captcha na wielu innych stronach. Dla wszystkich można odczytać tekst z obrazka poniższym skryptem.

Zasada odczytywania jest bardzo prosta. Każda generowana litera ma równą szerokość i wysokość (mieści się w okreslonym prostokącie), położone są zawsze w jednym miejscu i mają jeden kolor.

Na obrazku występują trzy kolory: biały, niebieski i granatowy. Interesuje nas tylko granatowy – więc będziemy “wyciągali” piksele, w których barwa czerwona nieprzekracza 32 (w skali 0-255). Z tak zmodyfikowanego obrazka możemy wyciąć poszczególne litery – zamienimy je na tablice zer i jedynek.

Teraz pozostaje już tylko porównanie tablic z tablicami wzorców liter. Poniżej przykładowy wzorzec dwóch liter:
<?php
$pLetters[ '6' ] = array(
'0000000001111000',
'0000000011111000',
'0000000011110000',
'0000000111110000',
'0000000111100000',
'0000001111100000',
'0000001111000000',
'0000011111000000',
'0000011110000000',
'0000111110000000',
'0000111100000000',
'0001111100000000',
'0001111101000000',
'0011111111111000',
'0011111111111100',
'0111111111111110',
'0111111111111110',
'0111110000111110',
'0111100000011111',
'0111100000011111',
'0111100000001111',
'0111100000011111',
'0111100000011110',
'0111110000111110',
'0011111111111110',
'0011111111111100',
'0001111111111000',
'0000111111110000',
'0000000110000000',
'0000000000000000',
'0000000000000000',
'0000000000000000',
);
$pLetters[ 'M' ] = array(
'1111000000011111',
'1111100000011111',
'1111100000011111',
'1111100000111111',
'1111110000111111',
'1111110000111111',
'1111110001111111',
'1111110001111111',
'1111111001111111',
'1111111001111111',
'1111111011111111',
'1111111111111111',
'1111011111101111',
'1111011111101111',
'1111001111001111',
'1111001111001111',
'1111001111001111',
'1111000110001111',
'1111000110001111',
'1111000110001111',
'1111000110001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'1111000000001111',
'0000000000000000',
'0000000000000000',
'0000000000000000',
);Porównanie jest proste – dla każdej pasującej pary (1=1 lub 0=0) zwiększamy licznkik poprawnych dopasowań, dla rozbieżności zwiększamy licznik rozbieżności. Dla każdej litery obliczamy procentowe dopasowanie. Spośród wszystkich wzorców wybieramy ten, którego procent dopasowania jest najwyższy. Zwracamy nazwę znalezionej litery/liczby. Powtarzamy czynność czterokrotnie i mamy odczytany tekst.
<?php
/**
* Porównuje tablice 0 i 1 znaków zwracając procent odpowiadających sobie znaków
*
* @param array of array $aOne (oryginał)
* @param array of array $aTwo
* @return double % zgodności
*/
protected function Compare( $aOne, $aTwo )
{
$pWidth = max( count( reset( $aOne ) ), count( reset( $aTwo ) ) );
$pHeight = max( count( $aOne ), count( $aTwo ) );
$aDebug = 0;
$pSumOK = 0;
$pSumBad = 0;
static $pE = 0;
$pE++;
$pOrigOK = 0;
for( $i = 0; $i < $pWidth; $i++ ) {
$pOrigOK += array_sum( $aOne[ $i ] );
for( $j = 0; $j < $pHeight; $j++ ) {
if ( isset( $aOne[ $i ] ) && isset( $aOne[ $i ][ $j ] ) && isset( $aTwo[ $i ] ) && isset( $aTwo[ $i ][ $j ] ) &&
$aOne[ $i ][ $j ] == 1 && $aTwo[ $i ][ $j ] == 1 )
$pSumOK++;
else
if ( isset( $aOne[ $i ] ) && isset( $aOne[ $i ][ $j ] ) && $aOne[ $i ][ $j ] == 1 ) {
if ( !isset( $aTwo[ $i ] ) || !isset( $aTwo[ $i ][ $j ] ) || $aTwo[ $i ][ $j ] == 0 )
$pSumBad++;
} else if ( isset( $aTwo[ $i ] ) && isset( $aTwo[ $i ][ $j ] ) && $aTwo[ $i ][ $j ] == 1 )
if ( !isset( $aOne[ $i ] ) || !isset( $aOne[ $i ][ $j ] ) || $aOne[ $i ][ $j ] == 0 )
$pSumBad++;
}
}
$pRet = ( $pSumOK < $pSumBad ? 0 : ( $pSumOK - $pSumBad ) / $pOrigOK );
if ( $aDebug )
echo( '+' . $pSumOK . '-' . $pSumBad . '/ (' . $pWidth . '*'. $pHeight . ')' ) . '=' . $pRet . '<br />';
return '' . $pRet;
}Trochę o kodzie. Nie jest on optymalny, wyszukałem go w archiwach – wg daty modyfikacji pliku powstał trzy lata temu :-) Przede wszystkim czyszczenie obrazka powinno w tym przypadku odbywać się “na literach” – a nie na obrazku (i tak wiemy, gdzie są litery). Kod jest dość niechlujny – wyciąłem go z większego skryptu odpytywania keyworddiscovery o najlepsze słowa kluczowe – doszedłem do wniosku, że nie warto go poprawiać – idee zrozumiecie, a nie jest to “gotowy program” (choć działa i dla zamieszczonych 6 przykładowych obrazków skuteczność jest 100%). Zauważyłem też pewne niezgodności w literach – najprawdopodobniej zmienił się sposób renderowania czcionek, albo sama czcionka – nie chciałem też poświęcać czasu na sprawdzenie wszystkich nowych literek. Traktujcie to wszystko raczej jako koncept, niż gotowe do wdrożenia rozwiązanie – choć działające i niezwykle skuteczne.



Pytanie co jezeli do Captcha uzyjemy
1) Flash z animacją i zmiana wielkości oraz obracającej sie grafiki
2) Gif animowany z rozszerzana i obracająca sie grafiką gdzie
a) pierwsza klatka wcale nie musi przedstawiać cały tekst
b) jego długość nie musu byc stała , czyli najszerszy tekst może byc rozciągnietą animacją
itp można jeszcze użyć…