Разделение цифр в изображениях - PullRequest
0 голосов
/ 14 мая 2018

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

Я играю с идеей разделения времени, например 00:30, на четыре цифры и проведя их через классификатор, обученный в MNIST для определения фактических временных отметок.

Когда я вручную извлекаю четыре цифры в Photoshop и запускаю их через классификатор MNIST, он отлично работает. Но до сих пор я не смог понять, как программно разделить числовые последовательности на однозначные числа. Я пытался использовать разные типы поиска в OpenCV, но это не очень надежно работало.

Есть предложения?

Я добавил скриншот некоторых соответствующих столбцов в отчетах.

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Я бы сделал что-то вроде этого (без кода, если это просто идея, вы можете проверить это, чтобы увидеть, работает ли):

  1. Извлеките каждую область для каждой группы чисел, как Рик М. предложил выше. Таким образом, у вас будет много Kl [hour] прямоугольников под формой изображения.

  2. Для каждого из этих прямоугольников извлекать (используя функцию контуров OpenCV) каждый ROI. Удалите Kl, если вам это не нужно (вы знаете размеры этого ROI (вы можете рассчитать его с помощью img.shape), и они имеют более или менее одинаковые размеры)

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

  4. Теперь о разделении. Мы знаем, что ROI представлены в часовом формате, поэтому hh:mm (или 4 цифры). Простое (и очень рудиментарное) решение для разделения символов, с которыми они связаны, состояло бы в том, чтобы разделить пополам рентабельность инвестиций с двумя цифрами внутри. Это сырое решение, но оно должно работать хорошо в вашем случае, потому что приложенные цифры только 2.

  5. Некоторые цифры будут выводиться с пропущенными частями. Этого можно избежать, используя некоторые эрозия / расширение / скелетизация .

Здесь у вас нет букв, только цифры, поэтому MNIST должен работать хорошо (не идеально, имейте это в виду).

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

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

РЕДАКТИРОВАТЬ - код

Это код, который я сделал. Окончательный результат такой:

output

Код работает с этим изображением на 100%, поэтому, если у вас что-то не работает, check folders/paths/modules installation.

Надеюсь, это помогло.

import cv2
import numpy as np

# 1 - remove the vertical line on the left

img = cv2.imread('image.jpg', 0)
# gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(img, 100, 150, apertureSize=5)

lines = cv2.HoughLines(edges, 1, np.pi / 50, 50)
for rho, theta in lines[0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    x1 = int(x0 + 1000 * (-b))
    y1 = int(y0 + 1000 * (a))
    x2 = int(x0 - 1000 * (-b))
    y2 = int(y0 - 1000 * (a))

    cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255), 10)

cv2.imshow('marked', img)
cv2.waitKey(0)
cv2.imwrite('image.png', img)


# 2 - remove horizontal lines

img = cv2.imread("image.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_orig = cv2.imread("image.png")

img = cv2.bitwise_not(img)
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 15, -2)
cv2.imshow("th2", th2)
cv2.waitKey(0)
cv2.destroyAllWindows()

horizontal = th2
rows, cols = horizontal.shape

# inverse the image, so that lines are black for masking
horizontal_inv = cv2.bitwise_not(horizontal)
# perform bitwise_and to mask the lines with provided mask
masked_img = cv2.bitwise_and(img, img, mask=horizontal_inv)
# reverse the image back to normal
masked_img_inv = cv2.bitwise_not(masked_img)
cv2.imshow("masked img", masked_img_inv)
cv2.waitKey(0)
cv2.destroyAllWindows()

horizontalsize = int(cols / 30)
horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontalsize, 1))
horizontal = cv2.erode(horizontal, horizontalStructure, (-1, -1))
horizontal = cv2.dilate(horizontal, horizontalStructure, (-1, -1))
cv2.imshow("horizontal", horizontal)
cv2.waitKey(0)
cv2.destroyAllWindows()

# step1
edges = cv2.adaptiveThreshold(horizontal, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, -2)
cv2.imshow("edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

# step2
kernel = np.ones((1, 2), dtype="uint8")
dilated = cv2.dilate(edges, kernel)
cv2.imshow("dilated", dilated)
cv2.waitKey(0)
cv2.destroyAllWindows()

im2, ctrs, hier = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# sort contours
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])

for i, ctr in enumerate(sorted_ctrs):
    # Get bounding box
    x, y, w, h = cv2.boundingRect(ctr)

    # Getting ROI
    roi = img[y:y + h, x:x + w]

    # show ROI
    rect = cv2.rectangle(img_orig, (x, y), (x + w, y + h), (255, 255, 255), -1)

cv2.imshow('areas', rect)
cv2.waitKey(0)

cv2.imwrite('no_lines.png', rect)


# 3 - detect and extract ROI's

image = cv2.imread('no_lines.png')
cv2.imshow('i', image)
cv2.waitKey(0)

# grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow('gray', gray)
cv2.waitKey(0)

# binary
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('thresh', thresh)
cv2.waitKey(0)

# dilation
kernel = np.ones((8, 45), np.uint8)  # values set for this image only - need to change for different images
img_dilation = cv2.dilate(thresh, kernel, iterations=1)
cv2.imshow('dilated', img_dilation)
cv2.waitKey(0)

# find contours
im2, ctrs, hier = cv2.findContours(img_dilation.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# sort contours
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)[0])

for i, ctr in enumerate(sorted_ctrs):
    # Get bounding box
    x, y, w, h = cv2.boundingRect(ctr)

    # Getting ROI
    roi = image[y:y + h, x:x + w]

    # show ROI
    # cv2.imshow('segment no:'+str(i),roi)
    cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 255), 1)
    # cv2.waitKey(0)

    # save only the ROI's which contain a valid information
    if h > 20 and w > 75:
        cv2.imwrite('roi\\{}.png'.format(i), roi)

cv2.imshow('marked areas', image)
cv2.waitKey(0)

Это следующие шаги:

  1. Понять, что я пишу;). Это самый важный шаг.

  2. Используя фрагменты кода выше (особенно step 3), вы можете удалить оставшиеся Kl в извлеченных изображениях.

  3. Создать папку для каждого изображения и извлечь цифры.

  4. Используя MNIST, распознавать каждую цифру.

0 голосов
/ 14 мая 2018

Разбить текст на отдельные символы не так просто, как кажется на первый взгляд.Вы можете попытаться найти некоторые правила и манипулировать изображением, но будет слишком много исключений.Например, вы можете попытаться найти непересекающиеся отметки, но четвертая на вашем изображении, 0715 , имеет "5", разбитую на три части, и 9 th один, 17.00 имеет перекрывающиеся два нуля.

Вам очень повезло с горизонтальными линиями - по крайней мере, легко разделить разные записи.Но вам нужно придумать много идей, связанных с полуфиксированной шириной символов, «мягким» правилом непересекаемости и т. Д.

Я сделал такой проект два года назад, и в итоге мы использовали внешнийбиблиотека с открытым исходным кодом под названием Tesseract .Вот эта статья из Римские распознавание цифр с ним, с точностью до 90%.Возможно, вы также захотите взглянуть на Lipi Toolkit , но у меня нет с этим опыта.

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

РЕДАКТИРОВАТЬ: Вдохновленный ответом @ Link, я только что придумал эту идею, вы можете попробовать.Как только вы извлекли область между двумя линиями, обрежьте изображение, чтобы избавиться от пустого пространства вокруг.Затем сделайте обоснованное предположение о том, насколько велики персонажи.Используйте, может быть, высоту области?Затем создайте скользящее окно поверх изображения и запустите распознавание полностью.Скорее всего, будет четыре пика, которые будут соответствовать четырем цифрам.

...