使用OpenCV进行水平线检测

Pat*_*ins 22 opencv image-processing hough-transform straight-line-detection

我试图找到来自"文档"的图像的水平和垂直线.文档是从合同中扫描的页面,因此这些行看起来就像您在表格或合同块中看到的那样.

我一直在尝试OpenCV来完成这项工作.OpenCV中的Hough变换实现似乎对这项工作很有用,但是我找不到任何能够干净地找到垂直和水平线的参数组合.我尝试了边缘检测和不边缘检测.没运气.如果有人做过类似事情,我有兴趣知道如何做.

在这里看到我在OpenCV中使用HoughP进行实验之前和之后的图像.这是我能做的最好的,http://dl.dropbox.com/u/3787481/Untitled%201.png

所以现在我想知道是否有另一种我可以使用的变换,这将允许我可靠地找到水平和垂直线(并且最好也是虚线).

我知道这个问题是可以解决的,因为我有Nuance和ABBYY OCR工具,它们可以可靠地提取水平和垂直线并返回线条的边界框.

谢谢!帕特里克.

And*_*aev 30

您是否看过HoughLinesP函数文档中的代码示例?

我认为您可以将它作为算法的起点.要选择水平垂直线,您只需要按线角度过滤掉其他线条.

更新:

我认为你不需要在页面上找到线条而是水平的垂直边缘.对于此任务,您需要组合多个处理步骤以获得良好的结果.

对于您的图像,我可以通过将Canny边缘检测与HoughLinesP相结合来获得良好的结果.这是我的代码(我使用过python,但我认为你看到了这个想法):

img = cv2.imread("C:/temp/1.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 80, 120)
lines = cv2.HoughLinesP(edges, 1, math.pi/2, 2, None, 30, 1);
for line in lines[0]:
    pt1 = (line[0],line[1])
    pt2 = (line[2],line[3])
    cv2.line(img, pt1, pt2, (0,0,255), 3)
cv2.imwrite("C:/temp/2.png", img)
Run Code Online (Sandbox Code Playgroud)

结果如下:


nat*_*ncy 12

这是使用形态学操作的完整 OpenCV 解决方案。

  • 获取二值图像
  • 创建水平内核并检测水平线
  • 创建垂直内核并检测垂直线

这是该过程的可视化。使用此输入图像:

二进制图像

import cv2

# Load image, convert to grayscale, Otsu's threshold
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]
Run Code Online (Sandbox Code Playgroud)

检测到的水平线以绿色突出显示

# Detect horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_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, (36,255,12), 2)
Run Code Online (Sandbox Code Playgroud)

检测到的垂直线以绿色突出显示

# Detect vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_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, (36,255,12), 2)
Run Code Online (Sandbox Code Playgroud)

结果

这是使用另一个输入图像的输出

输入->二进制->检测水平->检测垂直->结果


注意:根据图像,您可能需要修改内核大小。例如,要捕获更长的水平线,可能需要将水平内核从(40, 1)增加到 say (80, 1)。如果你想检测更粗的水平线,那么你可以增加内核的宽度来说(80, 2). 此外,您可以在执行cv2.morphologyEx(). 同样,您可以修改垂直内核以检测更多或更少的垂直线。增加或减少内核大小时需要权衡,因为您可能会捕获更多或更少的行。同样,这一切都取决于输入图像

完整代码

import cv2

# Load image, convert to grayscale, Otsu's threshold
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]

# Detect horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_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, (36,255,12), 2)

# Detect vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,10))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_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, (36,255,12), 2)

cv2.imshow('result', result)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)


mar*_*eto 9

如果您只想要"线"而不是"线段",我会避免使用Canny,Hough,FindContours或任何其他此类函数,以防您希望代码更快.如果您的图像没有旋转,而您想要找到的图像总是垂直的或水平的,我只会使用cv :: Sobel(一个用于垂直,另一个用于水平)并为列和行创建累积数组.然后,您可以在此类累积或配置文件中搜索最大值,例如通过设置阈值,您将知道其中存在垂直或水平边线的行或列.

  • 你知道cv::sobel中水平线和垂直线要设置的参数吗? (2认同)

小智 6

不要将RGB转换为灰度.有时,RGB中的不同颜色可以合并为相同的灰度值,因此可能会遗漏一些轮廓.您应该分别分析每个RGB通道.


Thr*_*986 5

您可以考虑离开Hough线检测,因为此方法会查找"全局"线,而不一定是线段.我最近实施了一个识别"平行四边形"的应用程序 - 基本上可以旋转的正方形和由于视角而缩短的透视图.你可能会考虑类似的东西.我的管道是:

  1. 从RGB转换为灰度(cvCvtColor)
  2. 平滑(cvSmooth)
  3. 阈值(cvThreshold)
  4. 检测边缘(cvCanny)
  5. 查找轮廓(cvFindContours)
  6. 具有线性特征的近似轮廓(cvApproxPoly)

在您的应用程序中,生成的轮廓列表可能很大(取决于平滑的"侵略性"和Canny边缘检测器的特征增强.您可以通过各种参数修剪此列表:从轮廓查找器返回的点数轮廓区域(cvContourArea)等.根据我的经验,我希望应用程序中的"有效"线条具有明确定义的区域和顶点计数属性.此外,您可以根据末端之间的距离过滤出轮廓.点,由连接端点的线定义的角度等.

根据您拥有的CPU"时间",您可以始终将Hough算法与上述算法配对,以稳健地识别水平和垂直线.