Оптическое распознавание символов

Процессы распознавания символов. Шаблонные и структурные алгоритмы распознавания. Процесс обработки поступающего документа. Обзор существующих приложений по оптическому распознаванию символов. Определение фиксированного шага и сегментация слов.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 11.02.2017
Размер файла 3,3 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

3. Затем изображение сравнивается с пороговой поверхностью и все пиксели с интенсивностью большей пороговой, делаются белыми(255), а все интенсивности меньшие пороговой делаются черными(0)

4. Для данного алгоритма единственный входной параметр - размер окна.

Входной параметр - размер окна:

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

2. Таким образом, алгоритм способен обрабатывать образцы с различным размером текста

3. Но требуется система оценивающая высоту текста

2.5.3 Оценка высоты текста

1. Для каждой ячейки на изображении в «оттенках серого» вычисляются интенсивности. Это позволяет получить массив дисперсий, у которых длина равна высоте изображения.

2. Области дисперсии, у которых расхождение больше порога «Tv» - рассматриваются как текст.(«Tv» - среднее значение всего набора дисперсии)

3. Медианная ширина зон текста большая «Tv» - высота текста.

2.5.4 Удаление линий (через анализ области вокруг текста)

Шаги алгоритма:

1. Локализация связанных областей текста в бинаризованном изображении.

2. Обнаружение линий на изображении без текста.

3. Удаление линий из оригинального изображения.

Связанные области текста:

1. Высота текста нам известна(h)

2. Бинаризованное изображение расширяется с помощью следующего ядра:

3. Изображение обрабатывается нормализованным hxh ядром:

4. Результирующая обработка ограничена порогом в 0.25

5. Полученные блобы (Blob - регион цифрового изображения, обладающий постоянными свойствами) вероятнее всего были получены из текста, т.о связанные регионы теста найдены.

Обнаружение линий

1. Удаление всех зон текста (все, что находится внутри связанных регионов)

2. Применяем трансформацию Хафа чтобы найти горизонтальные линии (Линии в большинстве случаев «сломаны» и обладают не одинаковой толщиной, поэтому трансформация Хафа применяется с порогом равным половине высоты, и максимальном расстоянию между линиями равному половине ширины)

3. Предыдущий шаг найдет список позиций горизонтальных линий

Удаление линий

1. Используем позиции горизонтальных линий и извлекаем пиксели из оригинального изображения, которые принадлежат линиям, но не к текстовым символам.

2. Предполагаем, что толщина линий не постоянна, но не больше чем h/10.

3. Для каждой возможной толщины, и каждой координаты на линии, накладываем на оригинальное изображение маску и отмечаем все пиксели ,которые совпадают с маской.

4. Создаем отклонение, двигая маску вверх-вниз со стартовой позиции. Данное перемещение не должно быть больше толщины линии.

Используемая маска:

,

где «d» - это толщина линии.

5. Нули добавлены, чтобы убедиться, что маска не накладывается на текст.

6. Если при наложении маски, найдены пиксели подходящие ей, то он маркируются для удаления.

Входное изображение должно содержать текст, с окружающей его зоной, достаточно большой, чтобы иметь фоновые линии после того, как текстовые зоны будут удалены. Линии могут быть под углом, пока возможно их обнаружение по кусочкам. При обработке, текст не будет потерян. Иногда небольшие участки текста остаются, но они могут быть удалены в последующих шагах обработки.

2.6 Обученные данные

Обучающая выборка состоит из стандартного пакета русского языка для tesseract. Результаты обработки с ее помощью были не идеальны, поэтому были улучшены путем внесения изменений в файлы обучающей выборки.

2.7 Результат работы программы

Рис. 27 Результат работы программы

На данный момент, система демонстрирует 94-95% точность распознавания.

2.8 Описание графического интерфейса пользователя

Разработанное приложение обладает простым интуитивно понятным интерфейсом. При запуске приложения пользователь видит основное окно (рис. 28)

Рис. 28 Стартовый экран приложения

Для того, чтобы система начала свою работу необходимо нажать кнопку «Open» и выбрать изображение паспорта, затем нажатием кнопки «Recognize» запускается процесс распознавания данных с изображения паспорта. Результат работы программы будет выведен в соответствующие графы под изображением (Рис. 29.)

Рис. 29 Результат работы программы

2.9 Тестирование функции распознавания паспортных данных

Исходными данными для тестирования являются сканированные изображения паспортов и файлы с проверочными данными для каждого изображения в определенном формате. На данный момент корректные данные заносятся в проверочные файлы вручную, в дальнейшем планируется разработать GUI для удобного редактирования данных.

Рис. 30. Проверочные данные для тестирования

2.9.1 Процесс тестирования

В процессе работы утилиты для каждого изображения формируется файл с распознанными данными в том же формате, что и проверочный файл. Далее проводится сравнение полученных и корректных данных используя Расстояние Левенштейна.

2.9.2 Результат тестирования

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

Рис. 31 Результат тестирования.

В целом проводилось тестирование на 50 различных паспортах, ошибка составила 5%. Данный процент ошибки - в основном результат применения различных шрифтов в разных регионах нашей страны, и не идеальность алгоритма удаления фона.

Заключение

распознавание символ документ оптический

В настоящие время качественные системы распознавания текста достаточно дороги, поэтому недостаточно широко распространены. Например, FineReader 12 показывает лучший результат. Он великолепно распознает как сканированные, так и сфотографированные изображения. Однако, минимальная его стоимость составляет 80€ за лицензию на 10000 распознаваний в год. Данная же система не имеет таких ограничений и может быть установлена, например, в бухгалтерии или отделе кадров любой компании, для автоматизации сбора и обновления данных. Наше OCR приложение на базе Tesseract позволяет с достаточно высокой степенью точности распознавать символы, при наличии различных шумов и помех, благодаря разработанному алгоритму предварительной обработки изображений и системой постобработки текста, основанной на словарном контроле результатов. Несомненным преимуществом нашей системы является возможность быстрой доработки под иной формат документов и интуитивно понятный интерфейс пользователя. Кроме того, разработанное решение было успешно внедрено в Территориальный фонд Обязательного медицинского страхования Нижегородской области, следовательно, имеет практическую значимость.

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

В дальнейшем планируется разработка web-based прототипа приложения, расширение возможностей по распознаванию данных: поддержка других видов документов, борьба с шумами, более качественные метод удаления фона, видоизменение стандартного алгоритма определения базовой линии (используя openCV).

Список литературы

1. Богданов В., Ахметов К. Системы распознавания текстов в офисе. // Компьютер-пресс -- 1999 №3, с.40-42.

2. Павлидис Т. Алгоритмы машинной графики и обработки изображений. М:, Радиоисвязь, 1986

3. Shani U. Filling Regions in Binary Raster Images -- a Graph-theoretic Approach. // SIGGRAPH'80, pp 321-327.

4. Merrill R.D. Representation of Contours and Regions for Efficient Computer Search. // CACM, 16 (1973), pp. 69-82.

5. Pavlidis T. Filling Algorithms for Raster Graphics. // CGIP, 10 (1979), pp. 126141.

6. https://habrahabr.ru/post/112442/

7. Lieberman H. How to Color in a Coloring Book. // SIGGRAPH'78, Atlanta, Georgia, (August, 1978), pp. 111-116. PublishedbyACM.

8. Fuller, R. (2004). Fuzzy logic and neural nets in intelligent systems. In C. Carlsson (Ed.)Information Systems Day, pp. 74-94. Turku: Abo Academi University Press.

9. Melin P., Urias J., Solano D., Soto M., Lopez M., Castillo O., Voice Recognition with Neural Networks, Type-2 Fuzzy Logic and Genetic Algorithms. Engineering Letters, 13:2, 2006.

10. R.W. Smith, The Extraction and Recognition of Text from Multimedia Document Images, PhD Thesis, University of Bristol, November 1987.

11. R.W.Smith, “A Simple and Efficient Skew Detection Algorithm via Text Row Accumulation”, Proc. of the 3rd Int. Conf. on Document Analysis and Recognition (Vol. 2), IEEE 1995, pp. 1145-1148.

12. R.W. Smith, An Overview of the Tesseract OCR Engine, IEEE 2007 pp. 629 - 633

13. http://yann.lecun.com/exdb/mnist/

14. International Journal of Computer Vision 57(2), 137-154, 2004

15. Canny Edge Detection Tutorial by Bill Green, 2002.

16. Shapiro, L. G. & Stockman, G. C: "Computer Vision", page 137, 150. PrenticeHall, 2001

17. OpenCV Python tutorials(http://opencv-python-tutroals.readthedocs.org/)

18. Багрова И. А., Грицай А. А., Сорокин С. В., Пономарев С. А., Сытник Д. А. Выбор признаков для распознавания печатных кириллических символов // Вестник Тверского Государственного Университета 2010 г., 28, стр. 59-73

19. Journal of Signal and Information Processing, 2013, 4, 173-175

20. https://ru.wikipedia.org/wiki/Tesseract

21. https://en.wikipedia.org/wiki/Comparison_of_optical_character_recognition_software

22. Квасников В.П., Дзюбаненко А.В. Улучшение визуального качества цифрового изображения путем поэлементного преобразования // Авиационно-космическая техника и технология 2009 г., 8, стр. 200-204

23. https://habrahabr.ru/company/abbyy/blog/225215/

24. Mark S. Nixon and Alberto S. Aguado. Feature Extraction and Image Processing. -- Academic Press, 2008. -- С. 88.

25. Hsueh, M. (2011). Interactive text recognition and translation on a mobile device. Electrical Engineering and Computer Sciences, 57, 47-60.

26. Gllavata, G. & Ewerth, R. (2003). A robust algorithm for text detection in images. New York: Halstead Press.

27. Bieniecki, W. & Grabowski, S. (2007). Image preprocessing for improving OCR accuracy. Columbia: Columbia University Press.

Приложение

1. Prototype.py

try:

import Image

except ImportError:

from PIL import Image

import safepytesseract

import cv2

from multiprocessing import Pool

import autorotate

import cleanbg

import postactions

import findboxes

def do_recognize(img, dbg=''):

""" save image and process with tesseract """

if dbg != '':

filename = dbg + '_tmp.png'

cv2.imwrite(filename, img)

img = Image.fromarray(img, 'RGB')

result = safepytesseract.image_to_string(img, lang='rus')

return result

def recognize(img, rot, (l1, t1, r1, b1), coeff, post=None, dbg='', shrinkboxfactor=0):

""" prepares picture for recognition, passes picture

to tesseract and does post actions:

img - image to process

(l, t, r, b) - box to look into

rot - rotation flag for the box above

(l1, t1, r1, b1) - box to ignore within (l, t, r, b)

dbg - debug flag

post - array of post actions

shrinkboxfactor - by how much factor of height will the box be shrinked from each side (left and right) after

background cleaning and before it is passed to OCR. Must be >=0

"""

# do rotation if needed

if rot:

img = cv2.transpose(img)

img = cv2.flip(img, 0)

img = cleanbg.clean_bg((img, coeff, True))

if shrinkboxfactor < 0:

raise ValueError("shrinkbox must be >=0")

shrinkpixels = int(img.shape[0] * shrinkboxfactor)

if shrinkpixels > 0:

img = img[:, shrinkpixels:-shrinkpixels, :]

# wash the box which is to be ignored

if l1 != 0 or t1 != 0 or r1 != 0 or b1 != 0:

for rr in range(t1, b1):

for pp in range(l1, r1):

img[rr][pp] = [255, 255, 255]

result = do_recognize(img, dbg + str(coeff) if dbg != '' else '')

result = postactions.do_post_actions(result, post)

if dbg != '':

print result

return result

# noinspection PyArgumentList

def recognize_wrapper(args):

""" wrapper function to be passed

to the process pool """

return recognize(*args)

def do_box(img, (l, t, r, b), rot, (l1, t1, r1, b1), post=None, agressive=0, dbg='', expandboxfactor=0):

""" Does recognition for image within given box """

if dbg != '':

print '_do_box (l:%u t:%u r:%u b:%u rot:%u)' % (l, t, r, b, rot)

if rot != 0: # don't expand rotated images

expandboxfactor = 0

if expandboxfactor != 0:

l -= int(expandboxfactor * (b-t))

r += int(expandboxfactor * (b-t))

# crop the box

img = img[t:b, l:r]

if agressive:

def frange(x, y, jump):

while x < y:

yield x

x += jump

coeffs = frange(0.0, 2.0, (2.0 - 0.0) / agressive)

pool = Pool(4)

attempts = pool.map(recognize_wrapper,

[(img.copy(), rot, (l1, t1, r1, b1),

coeff, post, dbg, expandboxfactor) for coeff in coeffs])

result = postactions.do_sift(attempts, dbg)

else:

coeff = 1.0

result = recognize(img, rot, (l1, t1, r1, b1), coeff, post, dbg, expandboxfactor)

return result

# noinspection PyArgumentList

def do_box_wrapper(args):

""" wrapper function to be passed

to the process pool """

return do_box(*args)

boxes = (('surname', (0.44, 0.85, 0.54, 0.62), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('name', (0.51, 0.80, 0.62, 0.67), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('patronymic', (0.51, 0.80, 0.67, 0.71), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('series', (0.92, 0.98, 0.05, 0.25), 90, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('number', (0.92, 0.98, 0.25, 0.48), 90, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('whom', (0.10, 0.92, 0.09, 0.20), 0, (0.00, 0.16, 0.00, 0.22), [postactions.newlines_to_spaces,

postactions.post_remove_spaces_before_dots,

postactions.smart_replacement]),

('when', (0.18, 0.399, 0.20, 0.25), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces]),

('birthdate', (0.57, 0.90, 0.71, 0.75), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces,

postactions.commas_to_dots]),

('birthplace', (0.36, 0.90, 0.75, 0.86), 0, (0.00, 0.06, 0.00, 0.10), [postactions.newlines_to_spaces,

postactions.

post_remove_spaces_before_dots]),

('gender', (0.38, 0.49, 0.71, 0.75), 0, (0, 0, 0, 0), [postactions.post_remove_newlines,

postactions.post_remove_spaces,

postactions.remove_dots_and_commas]))

def process_image(filename, dbg=0, agressive=0, rotate=1, adaptive_bboxes=False, expandboxfactor=0):

""" innter1 function """

if dbg:

print filename

print 'agressive = ', agressive

# read and rotate the picture

img = cv2.imread(filename)

if rotate:

img = autorotate.auto_rotate((img, filename if dbg != 0 else ''))

rows, cols, _ = img.shape

if rows == 0 or cols == 0:

return ''

# print findboxes.get_boxes(img, dbg)

# print findboxes.get_boxes()

if adaptive_bboxes:

bboxes = findboxes.get_boxes(img, dbg)

result = ''

for name, rects, rot, ignore, post in bboxes:

# print rects

fieldres = name + ': '

for r in rects:

out = do_box(img, r, rot, ignore, post, expandboxfactor=expandboxfactor)

if name == 'series':

out = out[:4]

elif name == 'number':

out = out[-6:]

fieldres += out

fieldres += ' '

result += fieldres + '\n'

return result

else:

bboxes = findboxes.get_boxes()

if agressive:

# for agressive recognition process boxes one by one;

# do_box() starts its own process for every coeff;

# python doesn't allow to start child processes from childs

out = []

for name, (l, r, t, b), rot, (l1, r1, t1, b1), post in bboxes:

out.append(do_box(img,

(int(l * cols), int(t * rows), int(r * cols), int(b * rows)),

rot,

(int(l1 * r * cols), int(t1 * b * rows), int(r1 * r * cols), int(b1 * b * rows)),

post=post,

agressive=agressive,

dbg=filename + "_" + name if dbg != 0 else ''))

return out # [(result,weights),...]

else:

# for fast recognition start pool of workers and

# and recognize each box in its separate process

'''

# uncomment this and comment the code below for serial processing

out = ''

for name, (l, r, t, b), rot, (l1, r1, t1, b1), post in boxes:

out += name + ': ' + do_box(img.copy(),

(int(l * cols), int(t * rows), int(r * cols), int(b * rows)),

rot,

(int(l1 * r * cols), int(t1 * b * rows), int(r1 * r * cols),

int(b1 * b * rows)),

post,

0,

filename + "_" + name if dbg != 0 else '') + '\n'

return out

'''

pool = Pool(4)

out = pool.map(do_box_wrapper, [(img.copy(),

(int(l * cols), int(t * rows), int(r * cols), int(b * rows)),

rot,

(int(l1 * r * cols), int(t1 * b * rows), int(r1 * r * cols),

int(b1 * b * rows)),

post,

0,

filename + "_" + name if dbg != 0 else '')

for name, (l, r, t, b), rot, (l1, r1, t1, b1), post in bboxes])

names = [name for name, _, _, _, _ in boxes]

result = [name + ": " + out[names.index(name)] for name in names]

return reduce(lambda aa, bb: aa + '\n' + bb, result)

2. Cleanbg.py

from scipy.ndimage import measurements

import numpy as np

import cv2

BORDER_WIDTH = 10

BORDER_HEIGHT = 10

def clean_bg_v1(img, hist_max_ix, deviation, coeff):

""" cleans image bg by washing colors

inside [hist_max_ix +/- coeff*deviation] """

rows, cols, _ = img.shape

thres = [coeff * deviation[0], coeff * deviation[1], coeff * deviation[2]]

for row in range(0, rows):

for col in range(0, cols):

for channel in [0, 1, 2]:

if img[row][col][channel] > hist_max_ix[channel] - thres[channel]:

img[row][col][channel] = 255

# bitwise remaining colors

if img[row][col][0] != 255 or img[row][col][1] != 255 or img[row][col][2] != 255:

img[row][col] = [0, 0, 0]

def clean_bg_v2(img, coeff):

"""

# the algorithm breaks colors on the box on three areas:

# - upper area, which is to be cleaned up, this must be bg

# - middle area, area of interest which colors constructs characters

# - lowers area, area with colors below characters colors, nothing must be here in the begging

# upper and lower areas are to be cleaned up and middle area is to be stretched from 0 up to 225

# upper cutting threshold is fixed for the whole picture

# lower threeshold is floating from pixel to pixel

#

# the idea is that character pixels with overlights from watermarks should be scaled down to

# more lower value than usual pixels

"""

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

rows, cols = img.shape

# statistics for overall box

avr = np.average(img)

std = np.std(img)

median = np.median(img)

# print 'average: ', avr

# print 'median: ', median

# print 'std: ', std

# statistics for character colors

# colors below 110 are considered related to the characters

# TODO: to try calculate 110 dynamically

blacks = []

for row in range(0, rows):

for col in range(0, cols):

if img[row][col] < 110:

blacks.append(img[row][col])

# blacks_avr = np.average(blacks)

blacks_median = np.median(blacks)

blacks_std = np.std(blacks)

# print 'black average: ', blacks_avr

# print 'black median: ', blacks_median

# print 'black std: ', blacks_std

# statistics for character colors

# colors higher 110 are considered related to the bg

# TODO: to try calculate 110 dynamically

bg = []

for row in range(0, rows):

for col in range(0, cols):

if img[row][col] > 110:

bg.append(img[row][col])

# bg_avr = np.average(bg)

# bg_median = np.median(bg)

# bg_std = np.std(bg)

# print 'bg average: ', bg_avr

# print 'bg median: ', bg_median

# print 'bg std: ', bg_std

# comparing meadian values for the whole box and for the characters only

# we can try to assume if the box contains watermarks.

# due to high watermaks colors, diff between above values are less on

# the pictures with watermaks compared to the ones without them.

# empirically detected values:

# - with watermaks: bg =~ 200, charaters =~ 100

# - without watermaks: bg =~ 220, charaters =~ 90

# TODO: to be verified on other samples

# watermark detection is needed to set propper upper threshold.

# the recognition result is very sensitive for the upper threshold.

# magic numbers 1.1 and 0.7 are also empirically settled and may not work on othen pictures.

# TODO: is it possible to figure out some common algorith which would set propper upper threshold?

delta = median - blacks_median

if delta > (100 + 130) / 2:

up_thresh = avr - 1.0 * coeff * std

else:

up_thresh = median - 0.7 * coeff * std

# print 'delta: ', delta

# print 'coeff: ', coeff

# print 'up_thresh: ', up_thresh

# print img.shape

img = cv2.copyMakeBorder(img, BORDER_HEIGHT, BORDER_HEIGHT, BORDER_WIDTH, BORDER_WIDTH, cv2.BORDER_REPLICATE)

rows, cols = img.shape

mask = np.zeros(img.shape, np.float32)

box_rows = 10

box_cols = 10

# the lower threshold is calculated dynamically for each pixel

# as min value for some locality of that pixel

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

mask[row][col] = np.min(img[row - box_rows / 2:row + box_rows / 2, col - box_cols / 2:col + box_cols / 2])

# scale up colors from 0...up_thresh to 0...255

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

if img[row][col] > up_thresh:

img[row][col] = 255

else:

img[row][col] = img[row][col] * 255 / up_thresh

# scale down colors from mask[row][col]...255 to 0...255

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

if img[row][col] < mask[row][col]:

img[row][col] = 0

elif mask[row][col] != 255:

img[row][col] = 255 - (255 - img[row][col]) * 255 / (255 - mask[row][col])

# cut box borders

img = img[BORDER_HEIGHT:rows-BORDER_HEIGHT, BORDER_WIDTH:cols-BORDER_WIDTH]

rows, cols = img.shape

# this magic stuff is needed to get rid of the noise happened to appear in the box.

# proper setting of upper threshold could help to clean that noise also, but

# but there is no common algorithm to calculate upper threshold for each box personally.

# condition to do cleaning: no watermarks and characters are fat enough

if delta > (100 + 130) / 2 and blacks_std > 6:

# print 'do noise clean'

kernel = np.ones((3, 3), np.float32) / 9

dst = cv2.filter2D(img, -1, kernel)

for row in range(0, rows):

for col in range(0, cols):

if dst[row][col] > 255 * (9 - 3) / 9:

img[row][col] = 255

img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)

return img

def get_char_dot_height(img):

""" calculates apprx charater height

and dot height on the image """

fimg = np.float32(img.copy())

dst = cv2.cornerHarris(fimg, 20, 31, 0.04)

dst = cv2.dilate(dst, None)

fimg = img.copy()

fimg[dst < 0.01*dst.max()] = 0

contours, hierarchy = cv2.findContours(fimg.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

cnts = sorted(contours, key=cv2.contourArea, reverse=True)

hsum = 0

hcount = 0

for c in cnts:

peri = cv2.arcLength(c, True)

if peri > 100:

x, y, w, h = cv2.boundingRect(c)

hsum += h

hcount += 1

if hcount != 0:

char_height = hsum/hcount/2

else:

char_height = 20

dot_height = char_height/3 - 2

# print char_height

# print dot_height

return char_height, dot_height

def clean_bg_v3(img, coeff, clean_noise):

"""

# the algorithm breaks colors on the box on three areas:

# - upper area, which is to be cleaned up, this must be bg

# - middle area, area of interest which colors constructs characters

# - lowers area, area with colors below characters colors, nothing must be here in the begging

# upper and lower areas are to be cleaned up and middle area is to be stretched from 0 up to 225

# upper cutting threshold is fixed for the whole picture

# lower threeshold is floating from pixel to pixel

#

# the idea is that character pixels with overlights from watermarks should be scaled down to

# more lower value than usual pixels

#

# diff against v2:

# - adaptive upper threshold

# - noise cleaning

"""

img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

rows, cols = img.shape

# calculate an apprx charater height and dot height on the orig image

# used later in noise filtering

if clean_noise:

char_height, dot_height = get_char_dot_height(img)

# calculate upper threshold by picking one row and analazing its colors:

# - filter image saving characters border

# - pick the most black line

# - colors on the line should be easily clasterized into bg color and char color

# - take upper threshold as charaters color minus one third from distance between

# bg color and char colors (one third is an empirical coeff)

# apply bilateral filter, TODO: kernel size to be calculated in runtime

sigmacolor = np.std(img.flatten())

bltf = cv2.bilateralFilter(img, 51, sigmacolor, 21)

# pick the most black line

sum_per_row = []

for rr in range(0, rows):

_sum_per_row = 0

for cc in range(0, cols):

_sum_per_row += (255 - bltf[rr][cc])

sum_per_row.append(_sum_per_row)

rix = sum_per_row.index(max(sum_per_row))

# print 'row ix: ', rix

# extract the line

# almost sure numpy can do this as well

rowx = []

for cc in range(0, cols):

rowx.append(bltf[rix][cc])

# clusterize color on that line

rowx = np.float32(rowx)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.1)

ret, labels, centers = cv2.kmeans(rowx, 2, criteria, 100, cv2.KMEANS_RANDOM_CENTERS)

# print ret, labels, centers

# determine if watermarks are present as it is done in v2 algo

median = np.median(img)

blacks = []

for row in range(0, rows):

for col in range(0, cols):

if img[row][col] < 110:

blacks.append(img[row][col])

blacks_median = np.median(blacks)

delta = median - blacks_median

whatermarks = (delta > (100 + 130) / 2)

if whatermarks:

up_thresh = max(centers) - (max(centers)-min(centers))/2.0*coeff

else:

up_thresh = max(centers) - (max(centers)-min(centers))/4.0*coeff

print coeff, up_thresh

# expand the box to be able to do 'convolution' properly

img = cv2.copyMakeBorder(img, BORDER_HEIGHT, BORDER_HEIGHT, BORDER_WIDTH, BORDER_WIDTH, cv2.BORDER_REPLICATE)

rows, cols = img.shape

# mask for the lower threshold

mask = np.zeros(img.shape, np.float32)

box_rows = 10

box_cols = 10

# the lower threshold is calculated dynamically for each pixel

# as min value for some locality of that pixel

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

mask[row][col] = np.min(img[row - box_rows / 2:row + box_rows / 2, col - box_cols / 2:col + box_cols / 2])

# scale up colors from 0...up_thresh to 0...255

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

if img[row][col] > up_thresh:

img[row][col] = 255

else:

img[row][col] = img[row][col] * 255 / up_thresh

# scale down colors from mask[row][col]...255 to 0...255

for row in range(box_rows / 2, rows - box_rows / 2):

for col in range(box_cols / 2, cols - box_cols / 2):

if img[row][col] < mask[row][col]:

img[row][col] = 0

elif mask[row][col] != 255:

img[row][col] = 255 - (255 - img[row][col]) * 255 / (255 - mask[row][col])

if clean_noise:

# fill the borders with the white, need later on

cv2.rectangle(img, (0, 0), (cols, BORDER_HEIGHT/2), (255, 255, 255), -1)

cv2.rectangle(img, (0, rows-BORDER_HEIGHT/2), (cols, rows), (255, 255, 255), -1)

cv2.rectangle(img, (0, 0), (BORDER_WIDTH/2, rows), (255, 255, 255), -1)

cv2.rectangle(img, (cols-BORDER_WIDTH/2, 0), (cols, rows), (255, 255, 255), -1)

# binarize into tmp image

tmp = img.copy()

for row in range(0, rows):

for col in range(0, cols):

if tmp[row][col] < 255:

tmp[row][col] = 0

# find contours on binarized image

# one of returned contours is the box itself, how to get rid of it?

contours, hierarchy = cv2.findContours(tmp.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

bratio = float(rows)/cols if rows < cols else float(cols)/rows

for cnt in contours[1:]:

x, y, w, h = cv2.boundingRect(cnt)

# loop over the contours and filter them by the condition below

cratio = float(w)/h if w < h else float(h)/w

if w < dot_height or h < dot_height or ((w > 1.5*char_height or h > 1.5*char_height) and cratio < 0.1) or \

((w < char_height or h < char_height) and cratio < bratio):

# -1 in the last param fills the contour with given color

cv2.drawContours(img, [cnt], -1, (255, 255, 255), -1)

# else:

# print cratio

# cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 1)

# cut box borders

img = img[BORDER_HEIGHT:rows-BORDER_HEIGHT, BORDER_WIDTH:cols-BORDER_WIDTH]

img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)

return img

# noinspection PyPep8Naming

def locally_adaptive_binarization(img, window_size):

"""

__author__ = 'Vladan Krstic'

Binarizes the image according to the algorithm briefly explained in

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.19.4933&rep=rep1&type=pdf

This algorithm is better than OpenCV's locally adaptive thresholding because it adopts a global minimum for

threshold. This will leave white areas in the image to be white (it will not calculate a threshold based on local

pixels 'at all cost' - half on one side and half of other side).

:param img: Image array to be binarized. Expecting 8bit gray image (vals = 0..255, ndims = 2)

:param window_size: Size of the window used to calculate locally adaptive binarization threshold. Must be odd!

:return: binarized image as np.uint8

"""

if window_size % 2 == 0 or window_size <= 0:

raise ValueError("window_size must be odd and positive")

k = (window_size - 1) / 2

img_expanded = cv2.copyMakeBorder(img, k, k, k, k, cv2.BORDER_REPLICATE)

rows, cols = img_expanded.shape

R = 0

M = np.min(img)

s = np.zeros(img.shape)

m = np.zeros(img.shape)

c = 0.2

for i in range(k, rows - k):

for j in range(k, cols - k):

w = img_expanded[i - k:i + k + 1, j - k:j + k + 1]

s[i - k, j - k] = np.std(w)

m[i - k, j - k] = np.mean(w)

if s[i - k, j - k] > R:

R = s[i - k, j - k] # global maximum of stdev

if R == 0:

raise ValueError("maximum stdev is zero - this shouldn't happen")

# binarization threshold:

T = m - c * (1 - s / R) * (m - M) # per-element matrix operations (avoids having another pair of nested for loops)

result = np.zeros(img.shape, dtype=np.uint8)

result[img > T] = 255

return result

def estimate_text_height(img, print_msg=False):

"""

__author__ = 'Vladan Krstic'

Estimates text height by looking at the pixel variance profile. The idea is to go row by row and observe pixel

variance. The rows with higher variance are assumed to be areas with text information. If multiple lines of text are

detected, median of their height is taken and returned as result. Areas smaller than 9px are ignored.

Input image should contain area bigger than the text who's height is to be determined (text shouldn't touch upper or

lower edge of the image).

:param img: Grayscale image of the text (dtype=uint8)

:param print_msg: Bool switch - Controls whether debug messages are printed to console.

:return: Estimated text height in pixels

"""

h_var = np.var(img, 1)

mh = np.mean(h_var)

if h_var[0] >= mh and print_msg:

print "Warning: text probably touching the edge of the image."

heights = []

a = 0

for i in range(1, h_var.size):

if h_var[i - 1] < mh <= h_var[i]:

a = i # top of the text

continue

if h_var[i - 1] >= mh > h_var[i]:

if i-a < 9:

# expect text to be at least 9px high

# todo: this is not universal solution, consider algorithms for outlier detection on heights list

continue

heights.append(i - a) # bottom of the text

if not heights:

if print_msg:

print "Warning: unable to determine text height"

# raise ValueError("Unable to determine text height.")

if a == 0: # appears like text height is equal to image height.

t_height = img.shape[0]

else: # or text is touching the lower edge of the image, take helf the image height as text height

t_height = img.shape[0] / 2

else:

t_height = int(np.median(heights)) # text line height

if print_msg:

print "Estimated text height:", t_height

return t_height

# noinspection PyPep8Naming,PyUnresolvedReferences

def remove_lines(img, textHeight):

"""

__author__ = 'Vladan Krstic'

Remove thin horizontal lines from binary image.

1. Take binary image, do the dilatation with a row-vector kernel to ensure that adjacent letters will be recognized

as one blob.

2. Take the convolution of the dilated image with a square kernel size = text height

3. Threshold the convolution result to get binary blobs. Find bounding boxes for those blobs. -> words bboxes

4. Remove the bboxes from the image and analyze the rezulting image to find the lines

5. Remove the lines found from the original binary image.

Input image should contain area wide enough to still have background lines after the text area is removed.

Recommended minimum width is two times text height on each (lef/right) side of the text.

:param img: binarized image, dtype=uint8

:param textHeight: height of text - used as basis for other parameters

:return: image with lines removed

"""

# image form suitable for operations

imgbw = 1 - img / 255.

# dilate with a row kernel

kernel = np.ones((1, textHeight / 2), np.float32)

blobs_tmp = cv2.dilate(imgbw, kernel)

# convolve with a box xernel

k = textHeight

kernel = np.ones((k, k), np.float32) / (k * k)

boxconv = cv2.filter2D(blobs_tmp, -1, kernel)

t = 0.25 # todo: experiment with this thresh (must be between 0 and 1)

blobs_tmp = boxconv > t

# find bounding boxes

contours, hierarchy = cv2.findContours(np.uint8(blobs_tmp), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

rects = []

for cnt in contours:

x, y, w, h = cv2.boundingRect(cnt)

rects.append([x, y, w, h])

# get the image without text

img_notext = imgbw.copy()

for rect in rects:

x, y, w, h = rect

for i in range(y, y + h):

for j in range(x, x + w):

img_notext[i, j] = 0

# find lines

img_lines = np.dstack((np.uint8(255 * img_notext), np.uint8(255 * img_notext), np.uint8(255 * img_notext)))

img_notext_8bit = np.uint8(img_notext * 255)

maxgap = img.shape[1] / 2

threshold = textHeight / 2

# maxgap = int(img.shape[1] * 0.95)

# threshold = int(textHeight * 0.1)

lines = cv2.HoughLinesP(img_notext_8bit, 1, np.pi / 2, threshold, maxLineGap=maxgap)

line_pos = set() # i want unique values

if lines is not None:

for x1, y1, x2, y2 in lines[0]:

cv2.line(img_lines, (x1, y1), (x2, y2), (0, 255, 0), 1)

if y1 == y2:

line_pos.add(y1)

# for each line found, do the filtering on the image with text

# during filtering, make variations of the column mask to accomodate varying thickness of the line

max_thickness = (textHeight + 5) / 10

imgbw_padded = np.pad(imgbw, ((max_thickness, max_thickness), (0, 0)), mode='constant')

deletemask = np.zeros(imgbw_padded.shape)

if line_pos:

for y in line_pos:

y += max_thickness # compensate for padding

for t in range(1, max_thickness+1):

col_mask = np.ones(t+2)

col_mask[0] = 0

col_mask[-1] = 0

for v in range(0, t):

# variations are formed by moving the column mask up/down by a number of pixels up

# to the thickness of the line.

rows = imgbw_padded[y-t+v:y+v+2, :]

mask_rows = deletemask[y-t+v:y+v+2, :] # creates a view of the rows of deletmask

for j in range(0, rows.shape[1]):

if all(rows[:, j] == col_mask):

mask_rows[:, j] += col_mask

deletemask = deletemask[max_thickness:-max_thickness] # remove padding from the mask

# remove line pixels from the original image

img_cleaned = img.copy()

img_cleaned[deletemask > 0] = 255

return img_cleaned

# noinspection PyUnusedLocal

def clean_bg_v4(img, a, b):

"""

__author__ = 'Vladan Krstic'

Algorithm steps:

1. Text height is estimated.

2. Image is locally adaptively binarized with regards to text height.

3. Background lines are removed.

The algorithm assumes that:

1. The text doesn't touch img edges.

2. Background lines are horizontal and extend beyond the width of the text bounding boxes.

Image should have at least 5px area above and below the text. Image should contain enough of the passport form

line left and/or right of the text so it can be detected when the text is removed.

Rule of thumb: image should contain area bigger than text bounding box by at least two heights of the text (more

is better, but not too much so it doesn't pick up too much background noise).

"""

grayimg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# blur background while keeping edges (bilateral filter)

sigmacolor = np.std(grayimg.flatten()) / 2

grayimg = cv2.bilateralFilter(grayimg, 5, sigmacolor, 5) # todo: calculate kernel size automatically

# determine text height

t_height = estimate_text_height(grayimg, True)

# locally adaptive thresholding

window_size = t_height / 2 # set window size relative to line height.

if window_size % 2 == 0: # must be odd

window_size += 1

bin_result = locally_adaptive_binarization(grayimg, window_size)

img_cleaned = remove_lines(bin_result, t_height)

# remove all blobs smaller than some value...

minpixels = t_height / 2

blobsies = img_cleaned

labeled_blobs, num_blobs = measurements.label(blobsies == 0, np.ones((3, 3)))

for i in range(1, num_blobs + 1):

if np.sum((labeled_blobs == i) * np.ones(labeled_blobs.shape)) < minpixels:

blobsies[labeled_blobs == i] = 255

# labeled_blobs[labeled_blobs == i] = 0

result = cv2.cvtColor(blobsies, cv2.COLOR_GRAY2RGB)

return result

# noinspection PyUnusedLocal

def clean_bg(arg):

return clean_bg_v3(*arg)

Размещено на Allbest.ru


Подобные документы

  • Оптическое распознавание символов как механический или электронный перевод изображений рукописного, машинописного или печатного текста в последовательность кодов. Компьютерные программы для оптического распознавания символов и их характеристика.

    презентация [855,2 K], добавлен 20.12.2011

  • Методы предобработки изображений текстовых символов. Статистические распределения точек. Интегральные преобразования и структурный анализ. Реализация алгоритма распознавания букв. Анализ алгоритмов оптического распознавания символов. Сравнение с эталоном.

    курсовая работа [2,1 M], добавлен 20.09.2014

  • Необходимость в системах распознавания символов. Виды сканеров и их характеристики. Оптимальное разрешение при сканировании. Программы распознавания текста. Получение электронного документа. FineReader - система оптического распознавания текстов.

    презентация [469,2 K], добавлен 15.03.2015

  • Обзор математических методов распознавания. Общая архитектура программы преобразования автомобильного номерного знака. Детальное описание алгоритмов: бинаризация изображения, удаление обрамления, сегментация символов и распознавание шаблонным методом.

    курсовая работа [4,8 M], добавлен 22.06.2011

  • Проектирование приложения на языке С# в среде Microsoft Visual Studio 2008: составление алгоритмов сегментации текста документа и распознавания слова "Указ" в нем, создание архитектуры и интерфейса программного обеспечения, описание разработанных классов.

    курсовая работа [2,4 M], добавлен 05.01.2011

  • Подсистема управления процессами и потоками вычислительной системы. Формирование новых символов для матричного принтера, разработка команд для загрузки символов в оперативную память принтера и программы, реализующей процесс печати заданных символов.

    курсовая работа [201,1 K], добавлен 23.06.2011

  • Ознакомление с приемами управления работой печатающих устройств в MS-DOS. Формирование новых символов для матричного принтера, разработка команд загрузки символов в оперативную память принтера и программы, реализующей процесс печати заданных символов.

    курсовая работа [1,2 M], добавлен 22.06.2011

  • Как работает система оптического распознавания. Деление текста на символы. Образ страницы и распознавание по шаблонам, особенности коррекции ошибок. Увеличение скорости бесклавиатурного ввода документов в технологиях электронного документооборота.

    контрольная работа [15,6 K], добавлен 29.04.2011

  • Этап предварительной обработки данных, классификации, принятия решения. Изображения обучающих рукописных символов, тестового символа. Выход нейронной сети для тестового символа. График тренировки нейронной сети. Последовательность точек. Входные вектора.

    статья [245,7 K], добавлен 29.09.2008

  • Оптико-электронная система идентификации объектов подвижного состава железнодорожного транспорта. Автоматический комплекс распознавания автомобильных номеров. Принципы и этапы работы систем оптического распознавания. Особенности реализации алгоритмов.

    дипломная работа [887,3 K], добавлен 26.11.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.