Увеличьте расстояние между строками текста в изображении - PullRequest
3 голосов
/ 10 января 2020

У меня есть входное изображение абзаца текста с межстрочным интервалом. Я пытаюсь реализовать что-то вроде параметра межстрочного интервала, чтобы увеличить / уменьшить пространство между текстовыми строками в Microsoft Word. Текущее изображение в одном пробеле, как я могу преобразовать текст в двойной пробел? Или сказать .5 пробел? По сути, я пытаюсь динамически реструктурировать интервал между строками текста, желательно с настраиваемым параметром. Примерно так:

Входное изображение

image

Желаемый результат

image

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

image

Есть какие-нибудь идеи о том, как улучшить код или какие-то лучшие подходы?

import numpy as np 
import cv2

img = cv2.imread('text.png')
H, W = img.shape[:2]
grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
threshed = cv2.threshold(grey, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

hist = cv2.reduce(threshed, 1, cv2.REDUCE_AVG).reshape(-1)
spacing = 2
delimeter = [y for y in range(H - 1) if hist[y] <= spacing < hist[y + 1]]
arr = []
y_prev, y_curr = 0, 0
for y in delimeter:
    y_prev = y_curr
    y_curr = y
    arr.append(threshed[y_prev:y_curr, 0:W])

arr.append(threshed[y_curr:H, 0:W])
space_array = np.zeros((10, W))
result = np.zeros((1, W))

for im in arr:
    v = np.concatenate((space_array, im), axis=0)
    result = np.concatenate((result, v), axis=0)

result = (255 - result).astype(np.uint8)
cv2.imshow('result', result)
cv2.waitKey()

1 Ответ

5 голосов
/ 11 января 2020

Подход № 1: Пиксельный анализ

  1. Получение двоичного изображения. Загрузка изображения, преобразование в оттенки серого и пороговое значение Оцу

  2. Сумма пикселей строки. Идея состоит в том, что сумма пикселей строки может использоваться для определения, соответствует ли она тексту или пробелу

  3. Создание нового изображения и добавление дополнительных пробелов. Перебираем массив пикселей и добавляем дополнительные пробелы


Двоичное изображение

image

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
h, w = image.shape[:2]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

Теперь мы перебираем каждую строку и суммируем белые пиксели, чтобы сгенерировать массив пикселей. Мы можем профилировать столбец данных, сгенерированный из суммы всех пикселей в каждой строке, чтобы определить, какие строки соответствуют тексту. Разделы данных, равные 0, представляют строки изображения, состоящие из пробелов. Вот визуализация массива данных:

image

# Sum white pixels in each row
# Create blank space array and and final image 
pixels = np.sum(thresh, axis=1).tolist()
space = np.ones((2, w), dtype=np.uint8) * 255
result = np.zeros((1, w), dtype=np.uint8)

Мы преобразуем данные в список и перебираем данные для построения окончательного изображения. Если строка определена как пробел, мы объединяем массив пустых пространств с конечным изображением. Регулируя размер пустого массива, мы можем изменить объем пространства, добавляемого к изображению.

# Iterate through each row and add space if entire row is empty
# otherwise add original section of image to final image
for index, value in enumerate(pixels):
    if value == 0:
        result = np.concatenate((result, space), axis=0)
    row = gray[index:index+1, 0:w]
    result = np.concatenate((result, row), axis=0)

Вот результат

image

Код

import cv2
import numpy as np 
import matplotlib.pyplot as plt
# import pandas as pd

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
h, w = image.shape[:2]
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Sum white pixels in each row
# Create blank space array and and final image 
pixels = np.sum(thresh, axis=1).tolist()
space = np.ones((1, w), dtype=np.uint8) * 255
result = np.zeros((0, w), dtype=np.uint8)

# Iterate through each row and add space if entire row is empty
# otherwise add original section of image to final image
for index, value in enumerate(pixels):
    if value == 0:
        result = np.concatenate((result, space), axis=0)
    row = gray[index:index+1, 0:w]
    result = np.concatenate((result, row), axis=0)

# Uncomment for plot visualization
'''
x = range(len(pixels))[::-1]
df = pd.DataFrame({'y': x, 'x': pixels})
df.plot(x='x', y='y', xlim=(-2000,max(pixels) + 2000), legend=None, color='teal')
'''
cv2.imshow('result', result)
cv2.imshow('thresh', thresh)
plt.show()
cv2.waitKey()

Подход № 2: Извлечение отдельной строки

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

  1. Получение двоичного изображения. Загрузка изображения, оттенков серого, размытия по Гауссу и порога Оцу

  2. Подключить текст контуры. Мы создаем горизонтальное ядро ​​в форме и расширяем, чтобы соединить слова каждой строки в один контур

  3. Извлечение каждого контура линии. Мы находим контуры Сортируйте сверху вниз, используя imtuils.contours.sort_contours() и извлекая каждую строку. ROI

  4. Добавьте пробел между каждой строкой. Мы создаем пустой массив и строим новое изображение путем добавления пробела между контурами каждой линии


Двоичное изображение

enter image description here

# Load image, grayscale, blur, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
invert = 255 - thresh  
height, width = image.shape[:2]

Создание горизонтального ядра и расширение

enter image description here

# Dilate with a horizontal kernel to connect text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,2))
dilate = cv2.dilate(thresh, kernel, iterations=2)

Извлеченный контур отдельной линии выделен зеленым

enter image description here

# Extract each line contour
lines = []
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(image, (0, y), (width, y+h), (36,255,12), 2)
    line = original[y:y+h, 0:width]
    line = cv2.cvtColor(line, cv2.COLOR_BGR2GRAY)
    lines.append(line)

Добавить пробел между каждой строкой. Вот результат с массивом шириной 1 пикселей

enter image description here

Результат с массивом шириной 5 пикселей

enter image description here

# Append white space in between each line
space = np.ones((1, width), dtype=np.uint8) * 255
result = np.zeros((0, width), dtype=np.uint8)
result = np.concatenate((result, space), axis=0)
for line in lines:
    result = np.concatenate((result, line), axis=0)
    result = np.concatenate((result, space), axis=0)

Полный код

import cv2
import numpy as np 
from imutils import contours

# Load image, grayscale, blur, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
invert = 255 - thresh  
height, width = image.shape[:2]

# Dilate with a horizontal kernel to connect text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10,2))
dilate = cv2.dilate(thresh, kernel, iterations=2)

# Extract each line contour
lines = []
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(image, (0, y), (width, y+h), (36,255,12), 2)
    line = original[y:y+h, 0:width]
    line = cv2.cvtColor(line, cv2.COLOR_BGR2GRAY)
    lines.append(line)

# Append white space in between each line
space = np.ones((1, width), dtype=np.uint8) * 255
result = np.zeros((0, width), dtype=np.uint8)
result = np.concatenate((result, space), axis=0)
for line in lines:
    result = np.concatenate((result, line), axis=0)
    result = np.concatenate((result, space), axis=0)

cv2.imshow('result', result)
cv2.imshow('image', image)
cv2.imshow('dilate', dilate)
cv2.waitKey()
...