User Tools

Site Tools


No ACL setup yet! Denying access to everyone.
en:edu:zmitac:ai:projects:idrecognition

This is an old revision of the document!


ID MRZ RECOGNITION

1. Co to jest MRZ

MRZ (ang. machine readable zone – strefa odczytu maszynowego) – obszar na różnego rodzaju dokumentach, zawierający zapisane czcionką do odczytu maszynowego (w przypadku dowodów osobistych jest to OCR-B) dane zawarte w danym dokumencie w sposób skrócony. Celem takiego zapisu jest ułatwienie komputerowego odczytu dokumentu na lotniskach. Dane zawarte z MRZ dowodów osobistych ułożone są w 3 liniach po 30 znaków. Używane są, podobnie jak w paszportach, litery A-Z, cyfry 0-9 oraz znak “<”.

Wiersz 1

Pole Znak Znaczenie
1A-ZI, A lub C na oznaczenie dokumentu podróży innego niż paszport
2A-Ztyp
3-5A-Zkod kraju wydającego dokument
6-14A-Z, 0-9Numer dokumentu
150-9cyfra kontrolna dla pól 6-14
16-30A-Z, 0-9inne dane, zgodnie z uznaniem państwa wydającego

Wiersz 2

Pole Znak Znaczenie
1-60-9Data urodzenia (RRMMDD)
70-9cyfra kontrolna dla pól 1-6
8A-Zpłeć (M dla mężczyzn, F dla kobiet, < dla płci nieokreślonej)
9-140-9data ważności dokumentu (RRMMDD)
150-9cyfra kontrolna dla pól 9-14
16-18A-Zobywatelstwo
19-29A-Z, 0-9inne (dla posiadaczy dokumentów amerykańskich może to być umieszczony numer ich paszportu)
300-9cyfra kontrolna dla pól 6-30 z 1. wiersza oraz 1-7, 9-15, 19-29 z 2. wiersza

Wiersz 3 Tu umieszczone są nazwisko (nazwiska) i imiona posiadacza w formacie NAZWISKO«IMIE«. W przypadku, gdy nazwisko jest dwuczłonowe, oba człony rozdziela znak “<”, a gdy ma się 2 imiona to również są one rozdzielone znakiem “<”. Dla osoby, która nazywa się Adam Jerzy Bachleda-Curuś, zapis będzie wyglądał BACHLEDA<CURUS«ADAM<JERZY«<. Narodowe znaki diakrytyczne są transliterowane na odpowiednie znaki alfabetu łacińskiego. Transliteracja znaków cyrylicy zależy od państwa.

Przykładowy dowód osobisty

2. Realizacja, opis program

Program do odczytu strefy MRZ został napisyany w języku PYTHON (ver 2.7) z użyciem biblioteki** OpenCV. Jako klasyfikator w naszej sieci użyliśmy algorytmu **KNN (Metoda K Najbliższych Sąsiadów (k-Nearest Neighbors)- (algorytm ten jest zaimplementowany w biliotece OpenCV)

Po uruchomieniu program wyświetli nam listę zadań które można wykonać:

2.1 Start Training

Uczenie Nadzorowane - jest to funkcja, której zadaniem jest wygenerowanie niezbędnych plików dla klasyfikatora KNN (pliki z tablicą binarną znaku „generalresponses.data”, tablica znaków: „generalsamples.data”.)

Algorytm działania:

Step Description
1 Wyświetl przygotowany przez użytkownika plik z danymi wzorcowymi (w naszym przypadku będzie to zbiór znaków zapisanych przy użyciu trzcionki ** OCR-B**. W pliku każdy znak jest zapisany kilka razy w rożnych rozmiarach.
2Funkcja nakłada niezbędne filtry na plik z danymi:
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) 
blur = cv2.GaussianBlur(gray, (5, 5), 0) 
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)

W wyniku czego uzyskujemy:

3 Następnie funkcja wyszukuje znaki, wycina oraz skaluje do rozmiaru 20×20:
image, contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    if cv2.contourArea(cnt) > 20:
        [x, y, w, h] = cv2.boundingRect(cnt)
        if h > 10:
            im = copy.copy(im_copy)
            cv2.rectangle(im, (x, y), (x + w, y + h), (0, 0, 255), 2)
            roi = thresh[y:y + h, x:x + w]
            roismall = cv2.resize(roi, (20, 20))

W wyniku czego otrzymujemy obraz znaku 20×20:

4 Następnie Użytkownik jest proszony o wpisanie na klawiaturze jaki to znak. Po tej operacji obraz oraz numer znaku jest zapisywany w tablicy pamięci programu.
5 Po klasyfikacji wszystkich znaków tablice danych są zapisane na dysku
np.savetxt('generalsamples.data', samples)
np.savetxt('generalresponses.data', responses)

2.2 Verify Engine

Testowanie i weryfikacja „nauczenia Sieci

Step Description
1 Inicjujemy Silnik sieci oraz ładujemy dane uczące:
self.samples = np.loadtxt("generalsamples.data", np.float32)
self.responses = np.loadtxt("generalresponses.data", np.float32)
self.model = cv2.ml.KNearest_create()
self.model.train(self.samples, cv2.ml.ROW_SAMPLE, self.responses)
2 Funkcja lądujemy obrazek z danymi testowymi. w/w obrazek został przygotowany do testu pozytywnego (zakładamy że wszystkie znaki powinny być odczytane prawidłowo. Można w pliku do testu zapisać dane inna czcionka tak aby zweryfikować czy silnik „nie rozpoznaje” lecz w wielu Trzcionkach pewne znaki mogą zostać zinterpretowane prawidłowo. Nasz zespół skupił się na „porawidłowym” odczycie tylko i wyłącznie trzcionki OCR-B (tylko ta trzcionak występuje w dowodach osobistych MRZ)
3 Nakładamy niezbędne filtry
4 Na podstawie konturów extraktujemy pojedyncze linie (uwaga sprawdzamy czy są rozpoznane 3 linie (obligatoryjnie dowód posiada 3) i listę z parametrami położenia linii, w innym przypadku zwracamy None.
ef extract_lines(self, image):
    line_list = {}
    # dilation
    cv2.imshow('input extract_line', image)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 1))
    img_dilation = cv2.dilate(image, kernel, iterations=3)
    # find contours
    image, contours, _ = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for cnt in contours:
        [x, y, w, h] = cv2.boundingRect(cnt)
        # ic contour is big enough
        #print w * h
        if w * h > 5000: # wszystko co jest poniżej 5000 ignorujemy
            line_list[y] = [x, y, w, h]
    # sort contours
    print "Liczba Linii %s" % len(line_list)
    if len(line_list) == 3:
        return sorted(line_list.items())
    else:
        print "Read Error, Detected Lines Count Error ! -> %s" % len(line_list)
        return None
5 Wycinamy obrazki każdej Lini na podstawie otrzymanych parametrów z extract_lines i przekazujemy je do funkcji OCR.
def decode(self, image):
    if len(image) > 0 and image is not None:
        contours = self.extract_lines(image)
        MZR_lines = []
        if contours is not None:
            for key, value in contours:
                # Get bounding box
                x, y, w, h = value
                line_image = image[y:y + h, x:x + w]
                MZR_lines.append(self.ocr(line_image))
                cv2.imshow("linie", line_image)
            return MZR_lines
    return None
6 Funkcja OCR extraktuje z każdej linii znak przy użyciu funkcji extract_chars_from_line:
def extract_chars_from_line(self, image):
    image, contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    char_list = []
    i = 0
    lista_rect = {}
    for cnt in contours:
        [x, y, w, h] = cv2.boundingRect(cnt)
        if cv2.contourArea(cnt) > 10:
            char_list.append([x, y, w, h])
            lista_rect[x] = [x, y, w, h]
    return sorted(lista_rect.items())

Następnie na podstawie zwróconych parametrów znaku funkcja OCR wycina obrazek, skaluje do rozmiaru 20×20 i przekazuje do Sieci KNN jako dane wejściowe. Jako informacje zwrotną sieć KNN zwraca kod rozpoznanego znaku.

def validate(self, read_data):
    print "Read Data: %s" % read_data
    print "Test Data: %s" % self.test_lines
    read_line_count = len(read_data)
    test_line_count = len(self.test_lines)
    PASS_COUNT = 0;
    FAIL_COUNT = 0;
    if (test_line_count == read_line_count):
        for i in xrange(read_line_count):
            read_line_data = read_data[i]
            test_line_data = self.test_lines[i]
            if (len(read_line_data) == len(test_line_data)):
                for x in xrange(len(read_line_data)):
                    if read_line_data[x] == test_line_data[x]:
                        PASS_COUNT += 1
                    else:
                        FAIL_COUNT += 1
            else:
                return None
    else:
        return None
    print "Validation Summary: PASS: %s, FAIL: %s" % (PASS_COUNT, FAIL_COUNT)
    Accuracy = float(float(PASS_COUNT) / float(PASS_COUNT + FAIL_COUNT)) * 100
    return Accuracy
7 Zwrócony String w postaci tablicy 3 linii porównujemy z wzorce. Zliczamy znaki dobrze rozpoznane i znaki zle rozpoznane na tej podstawie wyliczamy Accuracy naszego silnika.
def validate(self, read_data):
    print "Read Data: %s" % read_data
    print "Test Data: %s" % self.test_lines
    read_line_count = len(read_data)
    test_line_count = len(self.test_lines)
    PASS_COUNT = 0;
    FAIL_COUNT = 0;
    if (test_line_count == read_line_count):
        for i in xrange(read_line_count):
            read_line_data = read_data[i]
            test_line_data = self.test_lines[i]
            if (len(read_line_data) == len(test_line_data)):
                for x in xrange(len(read_line_data)):
                    if read_line_data[x] == test_line_data[x]:
                        PASS_COUNT += 1
                    else:
                        FAIL_COUNT += 1
            else:
                return None
    else:
        return None
    print "Validation Summary: PASS: %s, FAIL: %s" % (PASS_COUNT, FAIL_COUNT)
    Accuracy = float(float(PASS_COUNT) / float(PASS_COUNT + FAIL_COUNT)) * 100
    return Accuracy

2.3 Photo MRZ Recognition

2.3 Video MZR Recognition

en/edu/zmitac/ai/projects/idrecognition.1523095449.txt.gz · Last modified: by marikol897