2010-01-01 18 views
27

Nadal jestem początkującym, ale chcę napisać program rozpoznawania znaków. Ten program nie jest jeszcze gotowy. Dużo edytowałem, dlatego komentarze mogą nie zgadzać się dokładnie. Będę używał 8-podłączeń do etykietowania podłączonego komponentu.Mój własny program OCR w Pythonie

from PIL import Image 
import numpy as np 

im = Image.open("D:\\Python26\\PYTHON-PROGRAMME\\bild_schrift.jpg") 

w,h = im.size 
w = int(w) 
h = int(h) 

#2D-Array for area 
area = [] 
for x in range(w): 
    area.append([]) 
    for y in range(h): 
     area[x].append(2) #number 0 is white, number 1 is black 

#2D-Array for letter 
letter = [] 
for x in range(50): 
    letter.append([]) 
    for y in range(50): 
     letter[x].append(0) 

#2D-Array for label 
label = [] 
for x in range(50): 
    label.append([]) 
    for y in range(50): 
     label[x].append(0) 

#image to number conversion 
pix = im.load() 
threshold = 200 
for x in range(w): 
    for y in range(h): 
     aaa = pix[x, y] 
     bbb = aaa[0] + aaa[1] + aaa[2] #total value 
     if bbb<=threshold: 
      area[x][y] = 1 
     if bbb>threshold: 
      area[x][y] = 0 
np.set_printoptions(threshold='nan', linewidth=10) 

#matrix transponation 
ccc = np.array(area) 
area = ccc.T #better solution? 

#find all black pixel and set temporary label numbers 
i=1 
for x in range(40): # width (later) 
    for y in range(40): # heigth (later) 
     if area[x][y]==1: 
      letter[x][y]=1 
      label[x][y]=i 
      i += 1 

#connected components labeling 
for x in range(40): # width (later) 
    for y in range(40): # heigth (later) 
     if area[x][y]==1: 
      label[x][y]=i 
      #if pixel has neighbour: 
      if area[x][y+1]==1: 
       #pixel and neighbour get the lowest label    
       pass # tomorrows work 
      if area[x+1][y]==1: 
       #pixel and neighbour get the lowest label    
       pass # tomorrows work    
      #should i also compare pixel and left neighbour? 

#find width of the letter 
#find height of the letter 
#find the middle of the letter 
#middle = [width/2][height/2] #? 
#divide letter into 30 parts --> 5 x 6 array 

#model letter 
#letter A-Z, a-z, 0-9 (maybe more) 

#compare each of the 30 parts of the letter with all model letters 
#make a weighting 

#print(letter) 

im.save("D:\\Python26\\PYTHON-PROGRAMME\\bild2.jpg") 
print('done') 
+0

Hm ... diabeł tkwi w szczegółach. Aby to działało dobrze, myślę, że musisz załadować wiele różnych czcionek. Mam przeczucie, że programy OCR przechodzą przez różne czcionki, dopóki nie znajdą tego, który im się podoba. Oczywiście jest wiele publikacji na ten temat. Dlaczego chcesz zaimplementować to jako jedno z pierwszych zadań w Pythonie? –

+0

Proces porównywania jest drugą częścią. :) Na początku chcę mówić o tym procesie wyszukiwania listów. - Chcę to zrobić, ponieważ myślę, że mogę to zrobić. Myślę, że nie jest to takie trudne. Zrobiłem to również: http://kkaammee.blogspot.com/ To też nie jest łatwe dla mnie. – kame

+1

Więcej wyjaśnień: wszystko jest dobrze, jeśli twój kod jest czarno-biały. Co jednak jeśli niektóre litery/słowa będą szare? Chcesz czegoś takiego jak operacja GIMP "wybierz region według koloru". Osobiście zacznę od obliczenia rozkładu ciemności - średniej ciemności + std obrazu. Zacznę wtedy od "białego" miejsca i dalej wybieram biały, dopóki nie zidentyfikuję wysp pochodzenia niebiałego - to są potencjalne litery. Nawiasem mówiąc, nie potrzebujesz przypadkowości - pierwsze wyszukiwanie może pomóc Ci zlokalizować wszystkie czarne piksele, a także w zlokalizowaniu wysp. –

Odpowiedz

33

OCR nie jest łatwym zadaniem. Dlatego tekst CAPTCHA nadal działa :)

Aby mówić tylko o wyodrębnianiu liter, a nie o rozpoznawaniu znaków, technika, której używasz do oddzielania liter, nazywa się Connected Component Labeling. Ponieważ pytasz o bardziej efektywny sposób, spróbuj wdrożyć algorytm dwuprzebiegowy opisany w tym artykule. Inny opis można znaleźć w artykule Blob extraction.

EDIT: Oto implementacja algorytmu że sugerowali:

import sys 
from PIL import Image, ImageDraw 

class Region(): 
    def __init__(self, x, y): 
     self._pixels = [(x, y)] 
     self._min_x = x 
     self._max_x = x 
     self._min_y = y 
     self._max_y = y 

    def add(self, x, y): 
     self._pixels.append((x, y)) 
     self._min_x = min(self._min_x, x) 
     self._max_x = max(self._max_x, x) 
     self._min_y = min(self._min_y, y) 
     self._max_y = max(self._max_y, y) 

    def box(self): 
     return [(self._min_x, self._min_y), (self._max_x, self._max_y)] 

def find_regions(im): 
    width, height = im.size 
    regions = {} 
    pixel_region = [[0 for y in range(height)] for x in range(width)] 
    equivalences = {} 
    n_regions = 0 
    #first pass. find regions. 
    for x in xrange(width): 
     for y in xrange(height): 
      #look for a black pixel 
      if im.getpixel((x, y)) == (0, 0, 0, 255): #BLACK 
       # get the region number from north or west 
       # or create new region 
       region_n = pixel_region[x-1][y] if x > 0 else 0 
       region_w = pixel_region[x][y-1] if y > 0 else 0 

       max_region = max(region_n, region_w) 

       if max_region > 0: 
        #a neighbour already has a region 
        #new region is the smallest > 0 
        new_region = min(filter(lambda i: i > 0, (region_n, region_w))) 
        #update equivalences 
        if max_region > new_region: 
         if max_region in equivalences: 
          equivalences[max_region].add(new_region) 
         else: 
          equivalences[max_region] = set((new_region,)) 
       else: 
        n_regions += 1 
        new_region = n_regions 

       pixel_region[x][y] = new_region 

    #Scan image again, assigning all equivalent regions the same region value. 
    for x in xrange(width): 
     for y in xrange(height): 
       r = pixel_region[x][y] 
       if r > 0: 
        while r in equivalences: 
         r = min(equivalences[r]) 

        if not r in regions: 
         regions[r] = Region(x, y) 
        else: 
         regions[r].add(x, y) 

    return list(regions.itervalues()) 

def main(): 
    im = Image.open(r"c:\users\personal\py\ocr\test.png") 
    regions = find_regions(im) 
    draw = ImageDraw.Draw(im) 
    for r in regions: 
     draw.rectangle(r.box(), outline=(255, 0, 0)) 
    del draw 
    #im.show() 
    output = file("output.png", "wb") 
    im.save(output) 
    output.close() 

if __name__ == "__main__": 
    main() 

I tu jest plik wyjściowy:

Dead link

To nie jest w 100% doskonałe, ale ponieważ robisz to tylko w celach edukacyjnych, może być dobrym początkiem po int. Za pomocą obwiedni każdego znaku możesz teraz korzystać z sieci neuronowej, jak sugerowali inni.

+0

Witaj jbochi. Zanim napisałeś do mnie, miałem pomysł na łączone etykietowanie komponentów. Będę go używał w mojej nowszej wersji. – kame

+0

Popełniłem błąd. Oglądałem linię na linii. Powinienem najpierw lepiej obejrzeć cały list, a potem przejść do następnego listu. jak opisałeś wcześniej. :) – kame

+1

, ale dlaczego piksel północy i zachodu (biorąc pod uwagę 4-łączność), a nie pikseli południe i zachód? zaczynam w lewym górnym rogu i idę od lewej do prawej. – kame

7

OCR jest bardzo, bardzo trudny. Nawet w przypadku postaci generowanych komputerowo, jest to dość trudne, jeśli nie znasz wcześniej czcionki i rozmiaru czcionki. Nawet jeśli dopasowujesz dokładnie postacie, nie nazwałbym tego "projektowaniem początkowym"; to dość subtelne.

Jeśli chcesz rozpoznać zeskanowane lub odręczne postacie, jest to jeszcze trudniejsze - musisz użyć zaawansowanej matematyki, algorytmów i uczenia maszynowego. Jest sporo książek i tysiące artykułów na ten temat, więc nie trzeba wymyślać koła.

Podziwiam twój wysiłek, ale nie sądzę, że dostałeś się na tyle daleko, aby trafić w rzeczywiste trudności. Do tej pory po prostu losowo eksplorujesz piksele i kopiujesz je z jednej tablicy do drugiej. Nie dokonałeś jeszcze żadnego porównania i nie jestem pewien celu twojego "przypadkowego spaceru".

  • Dlaczego losowy? Pisanie poprawnych randomizowanych algorytmów jest dość trudne. Polecam najpierw zacząć od algorytmu deterministycznego.
  • Dlaczego kopiujesz z jednej tablicy do drugiej? Dlaczego nie po prostu porównać bezpośrednio?

Po uzyskaniu porównania trzeba się uporać z faktem, że obraz nie jest dokładnie taki sam jak "prototyp" i nie jest jasne, jak sobie z tym poradzić.

Na podstawie napisanego do tej pory kodu mam jednak pomysł: spróbuj napisać program, który znajdzie się w "labiryncie" na obrazie. Wejściowy będzie obraz, plus piksel początkowy i piksel docelowy. Wyjście to ścieżka przez labirynt od początku do celu. Jest to o wiele łatwiejszy problem niż OCR - rozwiązywanie labiryntów jest czymś, na czym świetnie sobie radzą komputery - ale wciąż jest zabawne i stanowi wyzwanie.

+0

Witaj dmazzoni. W nowszej wersji nie używam losowości. Teraz użyję DFS lub BFS./Kopiowanie z jednej tablicy do drugiej? Ponieważ chcę porównać literę z literami modelu./Nie powiedziałem, jak chcę to porównać, ale mam plan;) Rzecz z labiryntem jest również interesująca, ale zrobię to z OCR pomimo ostrzeżeń. :) – kame

4

OCR jest bardzo, bardzo trudny! Jakie podejście do próby użycia OCR będzie oparte na tym, co próbujesz osiągnąć (ręczne zapisywanie informacji, czytanie tekstu wygenerowanego komputerowo, itp.)

Jednak, aby zacząć, przeczytaj o sieciach neuronowych i OCR. Oto kilka jump-prawo-w artykuły na temat:

http://www.codeproject.com/KB/cs/neural_network_ocr.aspx

http://www.codeproject.com/KB/dotnet/simple_ocr.aspx

Użyj swojej ulubionej wyszukiwarki, aby znaleźć informacje.

Miłej zabawy!

+0

Witaj tgiphil! Wielkie dzięki! – kame

5

Większość algorytmów OCR obecnie opiera się na algorytmach sieci neuronowych. Hopfield networks to dobre miejsce na rozpoczęcie. Na podstawie modelu Hopfielda dostępnego pod here in C zbudowałem bardzo prosty algorytm rozpoznawania obrazów w pythonie, podobny do tego, co opisujesz. Wysłałem pełne źródło here. Jest to projekt zabawkowy i nie nadaje się do prawdziwego rozpoznawania OCR, ale może sprawić, że zaczniesz we właściwym kierunku.

Model Hopfielda służy jako autoassociative pamięci do sklepu i przypominają zestaw bitmapy. Obrazy są przechowywane przez obliczenie odpowiedniej macierzy wagi. Następnie, zaczynając od dowolnej konfiguracji, pamięć ustali dokładnie ten zapisany obraz, który jest najbliższy początkowej konfiguracji pod względem odległości Hamminga. W ten sposób podana niekompletna lub uszkodzona wersja zapisanego obrazu, sieć jest w stanie przywołać odpowiedni oryginalny obraz.

apletu Java zabawki z przykładem mogą być here; sieć jest szkolona z przykładowymi wejściami dla cyfr 0-9. Narysuj w polu po prawej, kliknij test i zobacz wyniki z sieci.

Nie pozwól, aby notacja matematyczna Cię zastraszyła, algorytmy są proste, gdy dojdziesz do kodu źródłowego.

+0

Jestem bardziej zastraszony przez bałagan Pythona kod połączony z notacji matematycznej. Czy mogę zaproponować czyszczenie go, jeśli masz zamiar to zrobić jako część twojej odpowiedzi. –

Powiązane problemy