增加图像中文本行之间的空间 - python

我有单行间距的一段文字的输入图像。我正在尝试实现行间距选项,以增加/减少Microsoft Word中文本行之间的间距。当前图像位于单个空格中,如何将文本转换为两个空格?还是说.5空间?本质上,我试图动态调整文本行之间的间距,最好使用可调参数。像这样:

输入图像

所需结果

我目前的尝试是这样的。我已经能够略微增加间距,但文字细节似乎受到侵蚀,并且行与行之间存在随机噪声。

关于如何改进代码或任何更好方法的任何想法?

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:像素分析

  • 获取二进制图像。 加载图像,转换为灰度,然后使用Otsu的阈值
  • 总行像素。 这个想法是,一行的像素总和可用于确定它是否对应于文本或空白
  • 创建新图像并添加其他空格。 我们遍历像素数组并添加其他空格
  • 二进制图像

    # 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的数据部分表示由空白组成的图像行。这是数据数组的可视化:

    # 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)
    

    这是结果

    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:单个行提取

    对于更动态的方法,我们可以找到每条线的轮廓,然后在每个轮廓之间添加空间。我们使用与第一种方法相同的方法来添加额外的空白。

  • 获取二进制图像。 加载图像,灰度,高斯模糊和Otsu的阈值
  • 连接文本轮廓。 我们创建一个水平形状的内核,并进行扩张以将每行的单词连接到单个轮廓中
  • 提取每条线的轮廓。 我们找到轮廓,使用imtuils.contours.sort_contours()从上到下进行排序,并提取每行ROI
  • 在每行之间添加空格。 我们创建一个空数组,并通过在每条线轮廓
  • 之间添加空白来构建新图像

    二进制图像

    增加图像中文本行之间的空间 - python

    # 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]
    

    创建水平内核并扩张

    增加图像中文本行之间的空间 - python

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

    提取的单线轮廓以绿色突出显示

    增加图像中文本行之间的空间 - python

    # 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像素宽空间数组的结果

    增加图像中文本行之间的空间 - python

    结果为5像素宽空间数组

    增加图像中文本行之间的空间 - python

    # 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()
    

    Python-在图像上查找不同颜色的轮廓 - python

    我有以下图像:我使用以下代码使用以下代码来概述该图像中的所有圆形斑点:import numpy as np import cv2 im = cv2.imread('im.jpg') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,…

    单行的'if'/'for'语句是否使用Python样式好? - python

    我经常在这里看到某人的代码,看起来像是“单线”,这是一条单行语句,以传统的“if”语句或“for”循环的标准方式执行。我在Google周围搜索,无法真正找到可以执行的搜索类型?任何人都可以提出建议并最好举一些例子吗?例如,我可以一行执行此操作吗?example = "example" if "exam" in exam…

    为什么使用'=='或'is'比较字符串有时会产生不同的结果? - python

    我有一个Python程序,其中将两个变量设置为'public'值。在条件表达式中,我有比较var1 is var2失败,但如果将其更改为var1 == var2,它将返回True。现在,如果我打开Python解释器并进行相同的“是”比较,则此操作成功。>>> s1 = 'public' >>…

    在返回'Response'(Python)中传递多个参数 - python

    我在Angular工作,正在使用Http请求和响应。是否可以在“响应”中发送多个参数。角度文件:this.http.get("api/agent/applicationaware").subscribe((data:any)... python文件:def get(request): ... return Response(seriali…

    Python:图像处理可产生皱纹纸效果 - python

    也许很难描述我的问题。我正在寻找Python中的算法,以在带有某些文本的白色图像上创建皱纹纸效果。我的第一个尝试是在带有文字的图像上添加一些真实的皱纹纸图像(具有透明度)。看起来不错,但副作用是文本没有真正起皱。所以我正在寻找更好的解决方案,有什么想法吗?谢谢 参考方案 除了使用透明性之外,假设您有两张相同尺寸的图像,一张在皱纹纸上明亮,一张在白色背景上有深…