检测图像中最长的水平线和垂直线
Detect the longest horizontal and vertical line in an image
我有一个 pdf,我想从中提取文本。我将 tesseract 用于 OCR,效果很好。但我的问题是它无法识别文档的 2 列格式,因此它将 2 列合并在一起。
我想在垂直(页面中间)和水平(页面顶部)线上拆分文档,然后将其提供给 tesseract。所以我做了以下
预处理步骤:
# color to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# edge detection
edges = cv2.Canny(gray, 500, 1000, apertureSize=7)
# dialate
kernel = np.ones((5,5),np.float32)/25
edges = cv2.dilate(edges, kernel, iterations=1)
# blur
blur = cv2.GaussianBlur(edges, (7, 7), 0)
这些步骤产生:
现在,我做线检测:
minLineLength = 1000
maxLineGap = 500
lines = cv2.HoughLinesP(processed_img, 1, np.pi, 2, minLineLength, maxLineGap)
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 0), 1)
最终结果(将所有图像拼接回 pdf 后)看起来像 this。
我尝试了 theta
、minLineLength
和 maxLineGap
的各种组合,这是我能得到的最好结果。任何 help/pointers 将不胜感激!
下面描述了一种可能的解决方案:
1) 检测水平线。以下是执行此操作的一种方法:
import cv2
import numpy as np
def discard(image):
image = np.uint8(image)
_, im_label, stts, _ = cv2.connectedComponentsWithStats(image, connectivity=4)
msk1 = np.isin(im_label, np.where(stts[:, cv2.CC_STAT_WIDTH] > 500)[0])
msk2 = np.isin(im_label, np.where(stts[:, cv2.CC_STAT_HEIGHT] > 500)[0])
image[(msk1 | msk2)] = 0
return image
img = cv2.imread("page_1.jpg", 0)
img = cv2.resize(img, None, fx=0.35, fy=0.35, interpolation=cv2.INTER_LINEAR)
height, width = img.shape[:2]
# Binarization
thresh = 255 - img
ret, thresh = cv2.threshold(thresh, 5, 255, cv2.THRESH_BINARY)
# Discarding long connected components
without_lines = discard(thresh.copy())
just_lines = cv2.bitwise_xor(thresh, without_lines)
horizontal = just_lines.copy()
# separating horizontal line
h_kernel_large = np.array([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]], np.uint8)
horizontal = cv2.morphologyEx(horizontal, cv2.MORPH_OPEN, h_kernel_large, iterations=2)
cv2.imshow("horizontal_line", horizontal)
这是我们在水平矩阵中得到的:
2) 使用findContours
和boundingRect
得到那条水平线的坐标。然后使用该坐标水平裁剪图像。
upper_portion = img
lower_portion = img
contours, hierarchy = cv2.findContours(horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
upper_portion = img[0:y, 0:width]
lower_portion = img[y+h:height, 0:width]
cv2.imshow("upper_portion", upper_portion)
cv2.imshow("lower_portion", lower_portion)
cv2.waitKey(0)
下面是裁剪后的图片。
upper_portion:
lower_portion:
3) 使用与步骤 1 中描述的相同过程检测垂直线并裁剪 lower_portion 图像。
在第一步中,我基本上使用了 "Connected Component Analysis",然后是 "Opening operation"。阅读它们 here and here
我有一个 pdf,我想从中提取文本。我将 tesseract 用于 OCR,效果很好。但我的问题是它无法识别文档的 2 列格式,因此它将 2 列合并在一起。
我想在垂直(页面中间)和水平(页面顶部)线上拆分文档,然后将其提供给 tesseract。所以我做了以下
预处理步骤:
# color to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# edge detection
edges = cv2.Canny(gray, 500, 1000, apertureSize=7)
# dialate
kernel = np.ones((5,5),np.float32)/25
edges = cv2.dilate(edges, kernel, iterations=1)
# blur
blur = cv2.GaussianBlur(edges, (7, 7), 0)
这些步骤产生:
现在,我做线检测:
minLineLength = 1000
maxLineGap = 500
lines = cv2.HoughLinesP(processed_img, 1, np.pi, 2, minLineLength, maxLineGap)
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 0), 1)
最终结果(将所有图像拼接回 pdf 后)看起来像 this。
我尝试了 theta
、minLineLength
和 maxLineGap
的各种组合,这是我能得到的最好结果。任何 help/pointers 将不胜感激!
下面描述了一种可能的解决方案:
1) 检测水平线。以下是执行此操作的一种方法:
import cv2
import numpy as np
def discard(image):
image = np.uint8(image)
_, im_label, stts, _ = cv2.connectedComponentsWithStats(image, connectivity=4)
msk1 = np.isin(im_label, np.where(stts[:, cv2.CC_STAT_WIDTH] > 500)[0])
msk2 = np.isin(im_label, np.where(stts[:, cv2.CC_STAT_HEIGHT] > 500)[0])
image[(msk1 | msk2)] = 0
return image
img = cv2.imread("page_1.jpg", 0)
img = cv2.resize(img, None, fx=0.35, fy=0.35, interpolation=cv2.INTER_LINEAR)
height, width = img.shape[:2]
# Binarization
thresh = 255 - img
ret, thresh = cv2.threshold(thresh, 5, 255, cv2.THRESH_BINARY)
# Discarding long connected components
without_lines = discard(thresh.copy())
just_lines = cv2.bitwise_xor(thresh, without_lines)
horizontal = just_lines.copy()
# separating horizontal line
h_kernel_large = np.array([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]], np.uint8)
horizontal = cv2.morphologyEx(horizontal, cv2.MORPH_OPEN, h_kernel_large, iterations=2)
cv2.imshow("horizontal_line", horizontal)
这是我们在水平矩阵中得到的:
2) 使用findContours
和boundingRect
得到那条水平线的坐标。然后使用该坐标水平裁剪图像。
upper_portion = img
lower_portion = img
contours, hierarchy = cv2.findContours(horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
upper_portion = img[0:y, 0:width]
lower_portion = img[y+h:height, 0:width]
cv2.imshow("upper_portion", upper_portion)
cv2.imshow("lower_portion", lower_portion)
cv2.waitKey(0)
下面是裁剪后的图片。
upper_portion:
lower_portion:
3) 使用与步骤 1 中描述的相同过程检测垂直线并裁剪 lower_portion 图像。
在第一步中,我基本上使用了 "Connected Component Analysis",然后是 "Opening operation"。阅读它们 here and here