Каким образом программно можно удалить все линии и границы изображения (сохранить тексты)?
Я пытаюсь извлечь текст из изображения с помощью Tesseract OCR. В настоящее время с исходным входным изображением (как показано ниже) вывод очень низкого качества (около 50%). Но когда я пытаюсь удалить все линии и границы на входном изображении (используя Photoshop), выход значительно улучшится (~ 90%). Итак, есть ли способ удалить все линии и границы изображения (сохранить тексты) программно (используя OpenCV, Image magick,..)?
Исходное изображение:
Ожидайте изображения:
Ответы
Ответ 1
Не использовать OpenCV, а просто однострочный образ ImageMagick в терминале, но он может дать вам представление о том, как это сделать в OpenCV. ImageMagick установлен на большинстве дистрибутивов Linux и доступен для OSX и Windows.
Суть концепции состоит в том, чтобы создать новое изображение, в котором каждый пиксель установлен на медиану из 100 соседних пикселей слева и 100 соседних пикселей справа. Таким образом, пиксели с большим количеством горизонтальных соседей, которые являются черными (то есть горизонтальными черными линиями), будут белыми на выходном изображении. Затем одна и та же обработка применяется в вертикальном направлении для удаления вертикальных линий.
Команда, которую вы вводите в терминал, будет:
convert input.png \
\( -clone 0 -threshold 50% -negate -statistic median 200x1 \) \
-compose lighten -composite \
\( -clone 0 -threshold 50% -negate -statistic median 1x200 \) \
-composite result.png
В первой строке указывается загрузка исходного изображения.
Во второй строке начинается некоторая "обработка в сторону", которая копирует исходное изображение, порождает его и инвертирует, затем вычисляется медиана всех соседних пикселей 100 с каждой стороны.
Третья строка затем берет результат второй линии и компонует ее над исходным изображением, выбирая более легкие пиксели в каждом месте - то есть те, которые моя маска горизонтальной линии отбелена.
Следующие две строки делают то же самое снова, но ориентированы вертикально для вертикальных линий.
Результат выглядит так:
Если я отличаюсь тем, что с вашим исходным изображением, вот так, я вижу, что он сделал:
convert input.png result.png -compose difference -composite diff.png
Я думаю, если бы вы хотели удалить немного больше строк, вы могли бы немного размыть изображение различий и применить это к оригиналу. Конечно, вы можете играть с длинами фильтров, порогами и т.д.
Ответ 2
Существует лучший способ сделать это с помощью ImageMagick.
Определение формы линии и ее удаление
ImageMagick имеет аккуратную функцию, называемую морфологией фигур. Вы можете использовать его для идентификации фигур, таких как строки таблицы, и их удаления.
Один лайнер
convert in.png \
-type Grayscale \
-negate \
-define morphology:compose=darken \
-morphology Thinning 'Rectangle:1x80+0+0<' \
-negate \
out.png
объяснение
- convert in.png: загрузить изображение.
- -type Оттенки серого: убедитесь, что ImageMagick знает изображение в оттенках серого.
- -negate: инвертировать цветные слои изображения (уже правильно отрегулированы путем настройки оттенков серого). Линии и символы будут белыми и черными.
- -define морфология: compose = darken: определить, что области, идентифицированные по морфологии, будут затемнены.
- -morphology Размывание "Прямоугольник: 1x80 + 0 + 0 <" определяет ядро прямоугольника размером 1px на 80 пикселей, которое будет использоваться для идентификации фигур линий. Только если это ядро вписывается в белую форму (помните, что мы отрицаем цвета) это большое или большее, оно будет затемнено. Флаг < позволяет ему вращаться.
- -negate: инвертировать цвета второй раз. Теперь символы снова будут черными, а фон будет белым.
- out.png: выходной файл, который будет сгенерирован.
Результирующее изображение
После применения
convert in.png -type Grayscale -negate -define morphology:compose=darken -morphology Thinning 'Rectangle:1x80+0+0<' -negate out.png
это было выходное изображение:
наблюдения
- Вы должны выбрать размер ядра прямоугольника, который больше размера вашего большего символа, чтобы убедиться, что прямоугольник не помещается внутри символа.
- Некоторые небольшие пунктирные линии и небольшие ячейки ячейки ячейки по-прежнему остаются, но это потому, что они меньше 80 пикселей.
- Суть этой методики заключается в том, что она сохраняет символы лучше, чем медианный подход к разнице в цветах пикселей, предложенный здесь другим пользователем, и, несмотря на небольшой беспорядок, он по-прежнему имеет действительно лучший результат, удаляя строки таблицы.
Ответ 3
Столкнулась с той же проблемой. И я считаю, что более логичным решением может быть (Ссылка: Извлечь таблицы)
//assuming, b_w is the binary image
inv = 255 - b_w
horizontal_img = new_img
vertical_img = new_img
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
horizontal_img = cv2.erode(horizontal_img, kernel, iterations=1)
horizontal_img = cv2.dilate(horizontal_img, kernel, iterations=1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,100))
vertical_img = cv2.erode(vertical_img, kernel, iterations=1)
vertical_img = cv2.dilate(vertical_img, kernel, iterations=1)
mask_img = horizontal_img + vertical_img
no_border = np.bitwise_or(b_w, mask_img)
Ответ 4
Вы можете использовать алгоритм обнаружения кромок от Sobel/Laplacian/Canny и использовать преобразование Hough для идентификации линий в OpenCV и покрасить их в белый цвет, чтобы удалить линии:
laplacian = cv2.Laplacian(img,cv2.CV_8UC1) # Laplacian OR
edges = cv2.Canny(img,80,10,apertureSize = 3) # canny Edge OR
# Output dtype = cv2.CV_8U # Sobel
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
# Hough Probabilistic Line Transform
minLineLength = 900
maxLineGap = 100
lines = cv2.HoughLinesP(edges,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(img,(x1,y1),(x2,y2),(255,255,255),2)
cv2.imwrite('houghlines.jpg',img)
Ответ 5
Поскольку никто не опубликовал полное решение OpenCV, здесь простой подход
- Преобразовать изображение в оттенки серого
- Адаптивный порог для получения двоичного изображения
- Удалить горизонтальные линии с горизонтальным ядром
- Удалить вертикальное удержание с вертикальным ядром
Адаптивный порог для получения двоичного изображения
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
Теперь мы создадим горизонтальное ядро для обнаружения горизонтальных линий с помощью cv2.getStructuringElement()
и поиска контуров. Чтобы удалить горизонтальные линии, мы используем cv2.drawContours()
и заливаем контур белым. Здесь обнаружены горизонтальные линии
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
Точно так же мы создаем вертикальное ядро, чтобы удалить вертикальные линии, найти контуры и залить белым. Здесь обнаружены вертикальные линии
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
Результат
Полный код
import cv2
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
remove_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(remove_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,40))
remove_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(remove_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(result, [c], -1, (255,255,255), 5)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.imwrite('result.png', result)
cv2.waitKey()
Ответ 6
У меня есть идея. Но он будет работать, только если у вас есть абсолютно горизонтальные и вертикальные линии. Вы можете сначала выполнить бинаризацию на этом изображении (если это еще не так). Затем напишите некоторый код, который повторяется через каждую строку изображения, в то же время проверяя, есть ли последовательность черных пикселей, содержащих более определенного порога. Например, если в некоторой строке есть непрерывная последовательность черных точек, начиная от 100-го пикселя до 150-го пикселя, сделайте эти пиксели белыми. После нахождения всех горизонтальных линий вы можете сделать то же самое, чтобы избавиться от вертикальных линий.
Здесь, в моем примере, я считаю, что черная последовательность пикселей начинается точно с 100-го пикселя и заканчивается на 150-й, потому что, если в 151-м пикселе есть еще один черный пиксель, тогда я должен добавить этот пиксель тоже. Другими словами, попробуйте полностью найти линии.
Если вы решите этот вопрос, пожалуйста, дайте мне знать)
Ответ 7
Вам нужны Лептоника и Лепт4j.
Ниже приведен пример того, как выполнить это в исходном коде проекта, в тестах здесь: LineRemovalTest.java
Входные данные:
вывод:
Ответ 8
### tested "Abhishek Chatterjee" code it works fine i just changed
### horizontal_img = new_img to horizontal_img = b_w.copy()
#### and vertical_img = new_img to vertical_img = b_w.cpy()
import cv2
import numpy as np
b_w = cv2.imread("input.png", 1)
inv = 255 - b_w
horizontal_img = b_w.copy()
vertical_img = b_w.copy()
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (100,1))
horizontal_img = cv2.erode(horizontal_img, kernel, iterations=1)
horizontal_img = cv2.dilate(horizontal_img, kernel, iterations=1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,100))
vertical_img = cv2.erode(vertical_img, kernel, iterations=1)
vertical_img = cv2.dilate(vertical_img, kernel, iterations=1)
mask_img = horizontal_img + vertical_img
no_border = np.bitwise_or(b_w, mask_img)
cv2.imshow("no_border", no_border)
cv2.waitKey(0)