【问题标题】:How to identify space between paragraphs and draw a line between them using opencv2 and python如何使用opencv2和python识别段落之间的空格并在它们之间画一条线
【发布时间】:2021-03-05 14:38:21
【问题描述】:

我有一个 pdf 页面的图像,我想通过在它们之间画线来分隔段落。 输入是这样的:

我想要的输出是这样的:

到目前为止,我使用 opencv 所做的是将图像转换为二进制,应用高斯模糊并膨胀图像以获得以下输出:

代码如下:

img_path = r"C:\test\Samsung-file.JPG"
img_org = cv2.imread(img_path)

gray = cv2.cvtColor(img_org, cv2.COLOR_BGR2GRAY)


blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

# Dilate to combine adjacent text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,1))
dilate = cv2.dilate(thresh, kernel, iterations=5)

有没有什么方法可以通过识别白色块之间的空间来绘制线条(膨胀后)?任何帮助将不胜感激!

【问题讨论】:

  • 您可以在放大图像的水平投影中寻找最大的间隙。

标签: python opencv


【解决方案1】:

这是一个可能的解决方案:首先,尝试获取文本的分割掩码。使用大的、漂亮的rectangular structuring element 应用积极的dilation 操作。我们的想法是获得大块文本,这样我们就可以清楚地看到它们之间的分隔线。接下来,reduce 将图像添加到 MAX (255) 列,其中每个值都是每个扩张图像行的最大像素值。如果您反转缩小的图像并找到contours,您将获得您正在寻找的文本块之间的空间。最后,获取空白的平均值或中间点,并在此垂直高度绘制line

让我们看看代码:

# imports:
import cv2
import numpy as np

# Set image path
imagePath = "C://opencvImages//"
imageName = "PQZUL.jpg"

# Read image:
inputImage = cv2.imread(imagePath + imageName)
# Store a copy for results:
inputCopy = inputImage.copy()

# Convert BGR to grayscale:
grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Threshold via Otsu
_, binaryImage = cv2.threshold(grayInput, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

# Set kernel (structuring element) size:
kernelSize = (9, 9)

# Set operation iterations:
opIterations = 2

# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernelSize)

# Perform Dilate:
dilateImage = cv2.morphologyEx(binaryImage, cv2.MORPH_DILATE, morphKernel, 
                               None, None, opIterations, cv2.BORDER_REFLECT101)

这组操作为您提供了一个很好的分割掩码,如下所示:

现在,将此图像缩小为 MAX 列。这是图像的垂直缩小:

# Reduce matrix to a n row x 1 columns matrix:
reducedImage = cv2.reduce(dilateImage, 1, cv2.REDUCE_MAX)

# Invert the reduced image:
reducedImage = 255 - reducedImage

这是结果 - 在这里很难看到,但图像已缩减为仅一列,其中每个值都是为该特定图像行找到的最大像素强度值:

每个白色部分是每个文本块到新段落的“跳转” - 这些是我们正在寻找的 blob(或 contours)。让我们找到它们并计算它们的bounding boxes

# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# Store the poly approximation and bound
contoursPoly = [None] * len(contours)
separatingLines = [ ]

# We need some dimensions of the original image:
imageHeight = inputCopy.shape[0]
imageWidth = inputCopy.shape[1]

# Look for the outer bounding boxes:
for i, c in enumerate(contours):

    # Approximate the contour to a polygon:
    contoursPoly = cv2.approxPolyDP(c, 3, True)

    # Convert the polygon to a bounding rectangle:
    boundRect = cv2.boundingRect(contoursPoly)

    # Get the bounding rect's data:
    [x,y,w,h] = boundRect

到目前为止我们已经有了bounding boxes的坐标,我们需要想办法得到每个垂直坐标的中点。有几个解决方案,我决定只获取边界框的height 并计算它的中间坐标。在这里,仍然在 for 循环内:

    # Calculate line middle (vertical) coordinate,
    # Start point and end point:
    lineCenter = y + (0.5 * h)
    startPoint = (0,int(lineCenter))
    endPoint =  (int(imageWidth),int(lineCenter))

    # Store start and end points in list:
    separatingLines.append((startPoint, endPoint))

    # Draw the line:
    color = (0, 255, 0)
    cv2.line(inputCopy, startPoint, endPoint, color, 2)

    # Show the image:
    cv2.imshow("inputCopy", inputCopy)
    cv2.waitKey(0)

我已将起点和终点存储在separatingLines 列表中,因此您可以在需要时检索数据。结果如下:

【讨论】:

  • 谢谢,这适用于当前文档。对于带有旋转的文档,我将需要绘制水平线,以最少的修改进行相同的工作,还是完全不同的方法。有什么想法吗?
  • @Sandeep 这取决于旋转角度。由于我在此处实施的 图像到列 减少,段落之间的空间无法正确检测到超过最小旋转角度。需要的是透视校正方法。最强大的方法是perspective warping。如果透视图只是显示 2D 旋转,则通过 rotated rectangle 进行补偿可能就足够了。
猜你喜欢
  • 2013-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多