Python + OpenCV: сегментирование изображения OCR

Я пытаюсь сделать OCR из этого игрушечного примера квитанций. Использование Python 2.7 и OpenCV 3.1.

введите описание изображения здесь

Оттенки серого + размытие + Обнаружение внешнего края + Сегментация каждой области в квитанциях (например, "Категория", чтобы увидеть позже, какая из них отмечена - в этом случае наличные деньги -).

Я считаю сложным, когда изображение "перекошено", чтобы иметь возможность правильно преобразовать, а затем "автоматически" сегментировать каждый сегмент квитанций.

Пример:

введите описание изображения здесь

Любое предложение?

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

Любая помощь более чем оценена!:)

import os;
os.chdir() # Put your own directory

import cv2 
import numpy as np

image = cv2.imread("Rent-Receipt.jpg", cv2.IMREAD_GRAYSCALE)

blurred = cv2.GaussianBlur(image, (5, 5), 0)

#blurred  = cv2.bilateralFilter(gray,9,75,75)

# apply Canny Edge Detection
edged = cv2.Canny(blurred, 0, 20)

#Find external contour

(_,contours, _) = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

Ответ 1

Отличный учебник на первом описанном вами шаге доступен в pyimagesearch (и у них отличные учебники в целом)

Короче говоря, как описано Ella, вам нужно будет использовать cv2.CHAIN_APPROX_SIMPLE. Несколько более надежным методом было бы использовать cv2.RETR_LIST вместо cv2.RETR_EXTERNAL, а затем сортировать области, так как это должно прилично работать даже на белом фоне/если страница вписывает большую фигуру в фоновом режиме и т.д.

Следуя во второй части вашего вопроса, хорошим способом сегментирования символов будет использование максимально стабильного экстракта экстремальной области, доступного в OpenCV. Полная реализация в CPP доступна здесь в проекте, который я недавно помогал. Реализация Python будет идти по строкам (код ниже работает для OpenCV 3.0+. Для синтаксиса OpenCV 2.x проверьте его в Интернете)

import cv2

img = cv2.imread('test.jpg')
mser = cv2.MSER_create()

#Resize the image so that MSER can work better
img = cv2.resize(img, (img.shape[1]*2, img.shape[0]*2))

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
vis = img.copy()

regions = mser.detectRegions(gray)
hulls = [cv2.convexHull(p.reshape(-1, 1, 2)) for p in regions[0]]
cv2.polylines(vis, hulls, 1, (0,255,0)) 

cv2.namedWindow('img', 0)
cv2.imshow('img', vis)
while(cv2.waitKey()!=ord('q')):
    continue
cv2.destroyAllWindows()

Это дает результат как

введите описание изображения здесь

Теперь, чтобы устранить ложные срабатывания, вы можете просто прокрутить точки в корпусах и вычислить периметр (сумму расстояния между всеми соседними точками в корпусах [i], где оболочки [i] - это список всех точек в одной выпуклой оболочке). Если периметр слишком велик, классифицируйте его как символ.

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

img = img[5:-5,5:-5,:]

который дает выход

введите описание изображения здесь

Ответ 2

Опция на верхней части головы требует выделения из 4 углов искаженного изображения. Это делается при использовании cv2.CHAIN_APPROX_SIMPLE вместо cv2.CHAIN_APPROX_NONE при поиске контуров. Впоследствии вы можете использовать cv2.approxPolyDP и, надеюсь, остаться с четырьмя углами квитанции (если все ваши изображения подобны этому, тогда нет причин, по которым он не должен работать).

Теперь используйте cv2.findHomography и cv2.wardPerspective для исправления изображения в соответствии с исходными точками, которые являются 4 точками, выделенными из перекошенного изображения и точек назначения, которые должны образовывать прямоугольник, например, размеры полного изображения.

Здесь вы можете найти образцы кода и дополнительную информацию: OpenCV-геометрические преобразования изображений

Также этот ответ может быть полезен - SO - Обнаружение и исправление перекоса текста

EDIT: Исправлена ​​вторая цепочка примерно на cv2.CHAIN_APPROX_NONE.

Ответ 3

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

enter image description here

import cv2

# Load in image, convert to grayscale, and threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Find and remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (35,2))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(thresh, [c], -1, (0,0,0), 3)

# Find and remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,35))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(thresh, [c], -1, (0,0,0), 3)

# Mask out unwanted areas for result
result = cv2.bitwise_and(image,image,mask=thresh)
result[thresh==0] = (255,255,255)

cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()