17.06 2008

OCR - zrobię to sam…

Kategorie: php/db, projekty

Jak część z was zapewne wie, bawię się w czytanie różnego rodzaju obrazków, popularnie nazywanych captcha - takie niegroźne hobby (nigdzie nie publikowane, bez robienia szkód serwisom, które mają “słabe” captcha). W przypadku niektórych używałem gocr do ostatecznego odczytania tekstu (przez lenistwo) - niestety ostatnio gocr mnie zawodził, więc…

…postanowiłem napisać swój system rozpoznawania znaków. System bardzo prosty, ale jednocześnie bardzo skuteczny - opierający się na rozpoznawaniu wybranych elementów charakterystycznych dla znaków. Nie pozwala on co prawda na czytanie skanów, ale bez problemu rozpozna teksty drukowane na obrazkach.

W pierwszym etapie filtruję obrazki (usuwając wszelkie śmieci, które mogą zniekształcić obrazek), pozostałość (kiedy zostają już tylko litery)  robię cz/b, rozbijam na pojedyncze litery (zapomniałem dodać, że sprawdzam, czy litera nie jest przekręcona),  a te litery parsuję moim “OCR” (doszedłem do wniosku, że gdybym miał z miesiąc wolnego, mógłbym napisać “poważny” OCR, ale z pracy 3 dniowej też jestem zadowolony).

W pojedynczych literkach skupiam się na kilku charakterysytycznych cechach (ATM mówimy jedynie o literach drukowanych, małe literki pojawią się w wersji 2.0) - liniach prostych, skosach i zaokrągleniach - ścięciach. Okazuje się, że wcale nie jest ich tak dużo, oraz że litery różnią się znacząco od siebie. Po pierwszym filtrze zostaje nam ~2-6 liter, które trzeba dokładniej sprawdzić, po drugim zostaje max 3 (zazwyczaj 1), dla których porównujemy zapisane różnice (np. między D a O różnice są w prawym górnym i lewym dolnym rogu, różnica między F i E jest na dolnej kresce i dolnym prawym rogu - ale te różnice powinny zostać wychwycone już w pierwszych 2 etapcach i jedna z nich powinna zostać wykluczona).

System doskonale sprawdza się w przypadku liter drukowanych i “prostych” znaków - choć zapewne, jeśli sprecyzować dokładność sprawdzania (np. dla rogów mam sprawdzanie, czy jest prostokąt w pełni wypełniony, czy wypełniony “po skosie prawym (slash)”, “skosie lewym (backslash)” i która część skosu jest zapełniona.

Docelowo planuję jeszcze dodać (w przypadku, jeśli zostaną jakieś niepewności (G vs 6 - sprawdzenie, czy jest połączenie między kreseczką w środku) sposób porównywania właśnie typowych “niepewnych”.

System jeszcze nie dokończony, a już pojawił się nowy model sprawdzania litery - opływanie. Dla pojedynczej litery sprawdzamy jak wygląda jej kształt zamieniony na proste, w przypadku których koniec jednej jest początkiem drugiej (trudno to opisać, łatwo zobaczyć na rysunku, ale takowego akurat nie posiadam). Proste krótkie są później zamieniane na dłuższe (3 proste, których X zmienł się o 4px, a Y o 30 można śmiało zamienić na jedną długą z X początkowym pierwszego punktu, i końcowym ostatniego) - i te proste byłyby później porównywane. System ten, jest dużo bardziej skomplikowany, ale osiągnięty efekt może być dużo lepszy.

…a w trakcie pisania powyższego paragrafu, doszedłem do wniosku, że potrzebny mi będzie jeszcze system usuwania kapitalików przed parsowaniem liter…

4 Responses to “OCR - zrobię to sam…”

  1. Nowaker

    Nieźle, myślałem że technika OCR to coś bardzo skomplikowanego. W życiu nie pomyślałbym, że jakieś proste OCR dla zabawy można napisać w 3 dni.

  2. MaKARON

    Wszystko opiera się na prostych zasadach, które każdy może wymyśleć - nie gwarantują one co prawda rozpoznawania tekstu w sytuacjach ekstremalnych (złe skany) - ale do moich potrzeb są wystarczające.

    Co więcej - ATM mam rozpoznawanie wyłącznie pojedynczych liter (ze względu na captcha, jest to rozwiązanie, do którego dążyłem) - mając do dyspozycji “kartkę liter”, można wiązać pewnego rodzaju dopasowania (albo, jak to jest w “poważnych programach OCR” - dawać użytkownikowi możliwość poprawy błędnie rozpoznanego znaku i odpowiednio reagować) - jeśli np rozpoznawanie “środkowej poziomej kreski” jest błędne, i użytkownik zwróci na to uwagę, w następnych porównaniach przesuwamy ją w odpowiednim kierunku.

  3. kulasart

    Od niedawna staram się wykonać własny system OCR - rowniez oparty na PHP. Wszystko byłoby OK gdyby nie fakt że nie mam zielonego pojęcia jak rozdzielić na pojedyncze litery całe frazy w momencie, gdy owe litery są obrócone o pewien (różny dla każdej z nich) kąt i znajdują się bardzo blisko siebie.
    Kiedy litery są proste wystarczy znaleźć puste kolumny aby je rozdzielić.
    Wpadłem na pomysł aby korzystając z funkcji liniowej sprawdzać ukośnie pixele w poszukiwaniu pustych na całej długości “wykresu” tej funkcji w obrębie obrazka ale nie do końca wiem jak całość napisać aby działała jak należy…

    Mógłby Pan podzielić się swoimi rozwiązaniami w tej kwesti? (nie koniecznie od strony kodowej)

    pozdrawiam

  4. MaKARON

    Zależy, czy litery na siebie nachodzą

    - jeśli nie nachodzą, to sprawa jest bardzo prosta - potrzeba litery “wycinać” od lewej: wyszukujemy pierwszy piksel i przechodzimy po obrazku znajdując wszystkie połączone z nim piksele (używając lekkiego marginesu błędu [zależnie od wielkości obrazka]) - powstaje nam litera, wycinamy ją z obrazu głównego i powtarzamy wszystko do momentu aż zostanie pusty obrazek - litery mamy rozdzielone, trzeba je obrócić i przeskanować. Metoda ta posiada błąd znaków składających się z kilku elementów “: ? ! i ó ż ” (można to naprawić łącząc znalezione małe elementy z dużymi (małe to max 20% dużych)

    - jeśli litery na siebie nachodzą, to nie ma niestety prostego rozwiązania (zwłaszcza dla algorytmu “nieuczącego” się litery [wielkości, kształtu, błędów, położenia wierszy]) - w zasadzie można próbować różnych kombinacji, ale często mogą to być błędne próby (wykrycie “potencjalnego” obrotu litery i jej wielkości - wycięcie wraz z “obcą” częścią i wyszukiwanie w takim kontekście).

    Pierwszej metody używałem i sprawdza się dobrze, druga jest ryzykowna, jak będę miał za dużo wolnego czasu (w przyszłym tysiącleciu), albo pojawi się potrzeba - przetestuję :)

    P.S. w przypadku obróconych litery wyciętych pierwszą metodą nie trzeba wyszukiwać kąta obrotu na podstawie wzoru (choć to jest bardziej eleganckie) - metoda “brute” jest prostsza - obracamy o kąty -60 do +60 i patrzymy kiedy szerokość jest najmniejsza [nie dotyczy wszystkich czcionek, ale w większości przypadków wystarczy].

Leave a Reply

O mnie...

  Programista, freelancer - prowadzący małą działalność gospodarczą (oprogramowanie dla małych i średnich przedsiębiorstw) - szukam przede wszystkim wyzwań i ciekawych projektów, dopiero później rozmawiam o pieniądzach.
  Na blogu znajdziesz informacje zarówno o sprawach firmowych, jak i prywatnych (choć tych jest zdecydowanie więcej).

Kategorie

ostatnio ulubiony

www.danceshaker.com - mój w pełni swój, pierwszy “poważny” serwis, nad którym zamierzam poważniej popracować! Na razie we wczesnej fazie beta, ale już wrzuciłem i dodałem linka na blogu, żeby ulubione google znalazło serwis. Na starcie lekko ponad 1000 filmików z różnego rodzaju tańcami - począwszy od hip-hopu, przez elektro, tańce uliczne aż po tango i foxtrota. Informacje do filmików też są jeszcze w powijakach, ale mam nadzieję, że z czasem wszystko się ładnie uzupełni (z waszą pomocą).

Lista TODO jest ogromna, nie będę jej jeszcze publikował, postaram się wybić parę robaków do końca tygodnia. Blog na google już postawiony, ale pusty - to jest pierwsza informacja dotycząca serwisu. Zostaje jeszcze optymalizacja i kilka innych kroków, które jednak nie mogą opóźniać startu. Nie ma jeszcze reklam.. miałe nie pisać todo, ale się rozkręciłem.

Zapraszam do rzucenia okiem i podesłania ew uwag.

W międzyczasie założyłem bloga dla danceshakera,  i rozwinąłem kilka rzeczy. Przejrzałem też API mikroblogów - w niektórych serwis publikuje już linki do filmików. Ciągłym problemem jest brak linków do serwisu i mała oglądalność. Pojawił się pierwszy raz gugiel - myślę, że pierwsze sensowne wyniki pojawią się dopiero pod koniec przyszłego tygodnia.

Checking Page Ranking